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:
Akinwale Ariwodola 2019-06-04 05:56:45 +01:00 committed by GitHub
parent 309b1e70e6
commit 7e136faea5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 92 deletions

View file

@ -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",

View file

@ -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,25 +46,9 @@ 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 = (
const content = (
<View onLayout={onEmailViewLayout}>
<Text style={firstRunStyle.title}>Setup account</Text>
<TextInput style={firstRunStyle.emailInput}
@ -126,7 +72,6 @@ class EmailCollectPage extends React.PureComponent {
<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}>

View file

@ -1,18 +1,111 @@
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() {
return (
<View style={firstRunStyle.container}>
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}>
{content}
</View>
);
}
}
export default WelcomePage;

View file

@ -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;

View file

@ -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()));

View file

@ -22,7 +22,6 @@ class VerificationScreen extends React.PureComponent {
state = {
currentPage: null,
emailSubmitted: false,
isFirstRun: false,
launchUrl: null,
showSkip: false,
skipAccountConfirmed: false,

View file

@ -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);
}
}
}