perform user authentication on first page of first run (#572)
* perform user authentication on first page of first run * additional firebase events for first run
This commit is contained in:
parent
309b1e70e6
commit
7e136faea5
7 changed files with 145 additions and 92 deletions
|
@ -16,6 +16,7 @@ const Constants = {
|
|||
|
||||
KEY_FIRST_RUN_EMAIL: "firstRunEmail",
|
||||
KEY_FIRST_RUN_PASSWORD: "firstRunPassword",
|
||||
KEY_FIRST_USER_AUTH: "firstUserAuth",
|
||||
KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail",
|
||||
KEY_EMAIL_VERIFY_PENDING: "emailVerifyPending",
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Linking,
|
||||
NativeModules,
|
||||
Platform,
|
||||
Text,
|
||||
|
@ -15,25 +13,16 @@ import Constants from 'constants';
|
|||
import firstRunStyle from 'styles/firstRun';
|
||||
|
||||
class EmailCollectPage extends React.PureComponent {
|
||||
static MAX_STATUS_TRIES = 30;
|
||||
|
||||
state = {
|
||||
email: null,
|
||||
authenticationStarted: false,
|
||||
authenticationFailed: false,
|
||||
placeholder: 'you@example.com',
|
||||
statusTries: 0,
|
||||
verifying: true
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { authenticating, authToken, showNextPage } = this.props;
|
||||
const { showNextPage } = this.props;
|
||||
const { user } = nextProps;
|
||||
|
||||
if (this.state.authenticationStarted && !authenticating && authToken === null) {
|
||||
this.setState({ authenticationFailed: true, authenticationStarted: false });
|
||||
}
|
||||
|
||||
if (this.state.verifying) {
|
||||
if (user && user.primary_email && user.has_verified_email) {
|
||||
if (showNextPage) {
|
||||
|
@ -45,33 +34,6 @@ class EmailCollectPage extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// call user/new
|
||||
const { generateAuthToken, authenticating, authToken } = this.props;
|
||||
if (!authenticating) {
|
||||
this.startAuthenticating();
|
||||
}
|
||||
}
|
||||
|
||||
startAuthenticating = () => {
|
||||
const { authenticate } = this.props;
|
||||
this.setState({ authenticationStarted: true, authenticationFailed: false });
|
||||
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
||||
Lbry.status().then(info => {
|
||||
authenticate(appVersion, Platform.OS);
|
||||
}).catch(error => {
|
||||
if (this.state.statusTries >= EmailCollectPage.MAX_STATUS_TRIES) {
|
||||
this.setState({ authenticationFailed: true });
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.startAuthenticating();
|
||||
this.setState({ statusTries: this.state.statusTries + 1 });
|
||||
}, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (30 seconds)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeText = (text) => {
|
||||
// save the value to the state email
|
||||
const { onEmailChanged } = this.props;
|
||||
|
@ -84,49 +46,32 @@ class EmailCollectPage extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { authenticating, authToken, onEmailChanged, onEmailViewLayout, emailToVerify } = this.props;
|
||||
const { onEmailViewLayout } = this.props;
|
||||
|
||||
let content;
|
||||
if (this.state.authenticationFailed) {
|
||||
// Ask the user to try again
|
||||
content = (
|
||||
<View>
|
||||
<Text style={firstRunStyle.paragraph}>The LBRY servers were unreachable at this time. Please check your Internet connection and then restart the app to try again.</Text>
|
||||
</View>
|
||||
);
|
||||
} else if (!authToken || authenticating || this.state.verifying) {
|
||||
content = (
|
||||
<View style={firstRunStyle.centered}>
|
||||
<ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} />
|
||||
<Text style={firstRunStyle.paragraph}>Please wait while we get some things ready...</Text>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<View onLayout={onEmailViewLayout}>
|
||||
<Text style={firstRunStyle.title}>Setup account</Text>
|
||||
<TextInput style={firstRunStyle.emailInput}
|
||||
placeholder={this.state.placeholder}
|
||||
underlineColorAndroid="transparent"
|
||||
selectionColor={Colors.NextLbryGreen}
|
||||
value={this.state.email}
|
||||
onChangeText={text => this.handleChangeText(text)}
|
||||
onFocus={() => {
|
||||
if (!this.state.email || this.state.email.length === 0) {
|
||||
this.setState({ placeholder: '' });
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (!this.state.email || this.state.email.length === 0) {
|
||||
this.setState({ placeholder: 'you@example.com' });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Text style={firstRunStyle.paragraph}>An account will allow you to earn rewards and keep your account and settings synced.</Text>
|
||||
<Text style={firstRunStyle.infoParagraph}>This information is disclosed only to LBRY, Inc. and not to the LBRY network.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
const content = (
|
||||
<View onLayout={onEmailViewLayout}>
|
||||
<Text style={firstRunStyle.title}>Setup account</Text>
|
||||
<TextInput style={firstRunStyle.emailInput}
|
||||
placeholder={this.state.placeholder}
|
||||
underlineColorAndroid="transparent"
|
||||
selectionColor={Colors.NextLbryGreen}
|
||||
value={this.state.email}
|
||||
onChangeText={text => this.handleChangeText(text)}
|
||||
onFocus={() => {
|
||||
if (!this.state.email || this.state.email.length === 0) {
|
||||
this.setState({ placeholder: '' });
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (!this.state.email || this.state.email.length === 0) {
|
||||
this.setState({ placeholder: 'you@example.com' });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Text style={firstRunStyle.paragraph}>An account will allow you to earn rewards and keep your account and settings synced.</Text>
|
||||
<Text style={firstRunStyle.infoParagraph}>This information is disclosed only to LBRY, Inc. and not to the LBRY network.</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={firstRunStyle.container}>
|
||||
|
|
|
@ -1,15 +1,108 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { View, Text, Linking } from 'react-native';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
NativeModules,
|
||||
Platform,
|
||||
Text,
|
||||
View
|
||||
} from 'react-native';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import Colors from 'styles/colors';
|
||||
import Constants from 'constants';
|
||||
import firstRunStyle from 'styles/firstRun';
|
||||
|
||||
class WelcomePage extends React.PureComponent {
|
||||
static MAX_STATUS_TRIES = 60;
|
||||
|
||||
state = {
|
||||
authenticationStarted: false,
|
||||
authenticationFailed: false,
|
||||
sdkStarted: false,
|
||||
statusTries: 0,
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { authenticating, authToken } = this.props;
|
||||
|
||||
if (this.state.authenticationStarted && !authenticating) {
|
||||
if (authToken === null) {
|
||||
this.setState({ authenticationFailed: true, authenticationStarted: false });
|
||||
} else {
|
||||
// first_user_auth because it's the first time
|
||||
AsyncStorage.getItem(Constants.KEY_FIRST_USER_AUTH).then(firstUserAuth => {
|
||||
if ('true' !== firstUserAuth) {
|
||||
// first_user_auth
|
||||
NativeModules.Firebase.track('first_user_auth', null);
|
||||
AsyncStorage.setItem(Constants.KEY_FIRST_USER_AUTH, 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// call user/new
|
||||
const { generateAuthToken, authenticating, authToken } = this.props;
|
||||
if (!authenticating) {
|
||||
this.startAuthenticating();
|
||||
}
|
||||
}
|
||||
|
||||
startAuthenticating = () => {
|
||||
const { authenticate } = this.props;
|
||||
this.setState({ authenticationStarted: true, authenticationFailed: false });
|
||||
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
||||
Lbry.status().then(info => {
|
||||
this.setState({ sdkStarted: true });
|
||||
|
||||
authenticate(appVersion, Platform.OS);
|
||||
}).catch(error => {
|
||||
if (this.state.statusTries >= WelcomePage.MAX_STATUS_TRIES) {
|
||||
this.setState({ authenticationFailed: true });
|
||||
|
||||
// sdk_start_failed
|
||||
NativeModules.Firebase.track('sdk_start_failed', null);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
this.startAuthenticating();
|
||||
this.setState({ statusTries: this.state.statusTries + 1 });
|
||||
}, 1000); // Retry every second for a maximum of MAX_STATUS_TRIES tries (60 seconds)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { authenticating, authToken, onWelcomePageLayout } = this.props;
|
||||
|
||||
let content;
|
||||
if (this.state.authenticationFailed) {
|
||||
// Ask the user to try again
|
||||
content = (
|
||||
<View>
|
||||
<Text style={firstRunStyle.paragraph}>The LBRY servers were unreachable at this time. Please check your Internet connection and then restart the app to try again.</Text>
|
||||
</View>
|
||||
);
|
||||
} else if (!authToken || authenticating) {
|
||||
content = (
|
||||
<View style={firstRunStyle.centered}>
|
||||
<ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} />
|
||||
<Text style={firstRunStyle.paragraph}>Please wait while we get some things ready...</Text>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<View onLayout={onWelcomePageLayout}>
|
||||
<Text style={firstRunStyle.title}>Welcome to LBRY.</Text>
|
||||
<Text style={firstRunStyle.paragraph}>LBRY is a community-controlled content platform where you can find and publish videos, music, books, and more.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={firstRunStyle.container}>
|
||||
<Text style={firstRunStyle.title}>Welcome to LBRY.</Text>
|
||||
<Text style={firstRunStyle.paragraph}>LBRY is a community-controlled content platform where you can find and publish videos, music, books, and more.</Text>
|
||||
{content}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class FirstRunScreen extends React.PureComponent {
|
|||
showSkip: false,
|
||||
isEmailVerified: false,
|
||||
skipAccountConfirmed: false,
|
||||
showBottomContainer: true,
|
||||
showBottomContainer: false,
|
||||
walletPassword: null,
|
||||
syncApplyStarted: false
|
||||
};
|
||||
|
@ -252,6 +252,10 @@ class FirstRunScreen extends React.PureComponent {
|
|||
this.setState({ showBottomContainer: true });
|
||||
}
|
||||
|
||||
onWelcomePageLayout = () => {
|
||||
this.setState({ showBottomContainer: true });
|
||||
}
|
||||
|
||||
onSkipSwitchChanged = (checked) => {
|
||||
this.setState({ skipAccountConfirmed: checked });
|
||||
}
|
||||
|
@ -291,16 +295,17 @@ class FirstRunScreen extends React.PureComponent {
|
|||
let page = null;
|
||||
switch (this.state.currentPage) {
|
||||
case Constants.FIRST_RUN_PAGE_WELCOME:
|
||||
page = (<WelcomePage />);
|
||||
page = (<WelcomePage
|
||||
authenticating={authenticating}
|
||||
authToken={authToken}
|
||||
authenticate={authenticate}
|
||||
onWelcomePageLayout={this.onWelcomePageLayout} />);
|
||||
break;
|
||||
|
||||
case Constants.FIRST_RUN_PAGE_EMAIL_COLLECT:
|
||||
page = (<EmailCollectPage
|
||||
user={user}
|
||||
showNextPage={this.showNextPage}
|
||||
authenticating={authenticating}
|
||||
authToken={authToken}
|
||||
authenticate={authenticate}
|
||||
onEmailChanged={this.onEmailChanged}
|
||||
onEmailViewLayout={this.onEmailViewLayout} />);
|
||||
break;
|
||||
|
|
|
@ -241,9 +241,10 @@ class SplashScreen extends React.PureComponent {
|
|||
}
|
||||
});
|
||||
|
||||
// Start measuring the first launch time from the splash screen (time from daemon start to user interaction)
|
||||
// Start measuring the first launch time from the splash screen
|
||||
// (time to first user interaction - after first run completed)
|
||||
AsyncStorage.getItem('hasLaunched').then(value => {
|
||||
if (value == null || value !== 'true') {
|
||||
if ('true' !== value) {
|
||||
AsyncStorage.setItem('hasLaunched', 'true');
|
||||
// only set firstLaunchTime since we've determined that this is the first app launch ever
|
||||
AsyncStorage.setItem('firstLaunchTime', String(moment().unix()));
|
||||
|
|
|
@ -22,7 +22,6 @@ class VerificationScreen extends React.PureComponent {
|
|||
state = {
|
||||
currentPage: null,
|
||||
emailSubmitted: false,
|
||||
isFirstRun: false,
|
||||
launchUrl: null,
|
||||
showSkip: false,
|
||||
skipAccountConfirmed: false,
|
||||
|
|
|
@ -4,12 +4,15 @@ import android.content.Context;
|
|||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
|
||||
public class FirstRunModule extends ReactContextBaseJavaModule {
|
||||
|
@ -40,5 +43,11 @@ public class FirstRunModule extends ReactContextBaseJavaModule {
|
|||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean("firstRun", false);
|
||||
editor.commit();
|
||||
|
||||
FirebaseAnalytics firebase = FirebaseAnalytics.getInstance(context);
|
||||
if (firebase != null) {
|
||||
Bundle bundle = new Bundle();
|
||||
firebase.logEvent("first_run_completed", bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue