From 7e136faea5d02e86849db36121e05af61abcc5b5 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Tue, 4 Jun 2019 05:56:45 +0100 Subject: [PATCH] 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 --- app/src/constants.js | 1 + .../firstRun/internal/email-collect-page.js | 107 +++++------------- .../page/firstRun/internal/welcome-page.js | 99 +++++++++++++++- app/src/page/firstRun/view.js | 15 ++- app/src/page/splash/view.js | 5 +- app/src/page/verification/view.js | 1 - .../browser/reactmodules/FirstRunModule.java | 9 ++ 7 files changed, 145 insertions(+), 92 deletions(-) diff --git a/app/src/constants.js b/app/src/constants.js index 62c6c06..a48197e 100644 --- a/app/src/constants.js +++ b/app/src/constants.js @@ -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", diff --git a/app/src/page/firstRun/internal/email-collect-page.js b/app/src/page/firstRun/internal/email-collect-page.js index 93ea2cf..0d37045 100644 --- a/app/src/page/firstRun/internal/email-collect-page.js +++ b/app/src/page/firstRun/internal/email-collect-page.js @@ -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 = ( - - The LBRY servers were unreachable at this time. Please check your Internet connection and then restart the app to try again. - - ); - } else if (!authToken || authenticating || this.state.verifying) { - content = ( - - - Please wait while we get some things ready... - - ); - } else { - content = ( - - Setup account - 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' }); - } - }} - /> - An account will allow you to earn rewards and keep your account and settings synced. - This information is disclosed only to LBRY, Inc. and not to the LBRY network. - - ); - } + const content = ( + + Setup account + 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' }); + } + }} + /> + An account will allow you to earn rewards and keep your account and settings synced. + This information is disclosed only to LBRY, Inc. and not to the LBRY network. + + ); return ( diff --git a/app/src/page/firstRun/internal/welcome-page.js b/app/src/page/firstRun/internal/welcome-page.js index 30698a2..0451959 100644 --- a/app/src/page/firstRun/internal/welcome-page.js +++ b/app/src/page/firstRun/internal/welcome-page.js @@ -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 = ( + + The LBRY servers were unreachable at this time. Please check your Internet connection and then restart the app to try again. + + ); + } else if (!authToken || authenticating) { + content = ( + + + Please wait while we get some things ready... + + ); + } else { + content = ( + + Welcome to LBRY. + LBRY is a community-controlled content platform where you can find and publish videos, music, books, and more. + + ); + } + return ( - Welcome to LBRY. - LBRY is a community-controlled content platform where you can find and publish videos, music, books, and more. + {content} ); } diff --git a/app/src/page/firstRun/view.js b/app/src/page/firstRun/view.js index 58313f3..be70f04 100644 --- a/app/src/page/firstRun/view.js +++ b/app/src/page/firstRun/view.js @@ -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 = (); + page = (); break; case Constants.FIRST_RUN_PAGE_EMAIL_COLLECT: page = (); break; diff --git a/app/src/page/splash/view.js b/app/src/page/splash/view.js index 1588c4f..0daa2c2 100644 --- a/app/src/page/splash/view.js +++ b/app/src/page/splash/view.js @@ -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())); diff --git a/app/src/page/verification/view.js b/app/src/page/verification/view.js index 89fe42a..d1bedf7 100644 --- a/app/src/page/verification/view.js +++ b/app/src/page/verification/view.js @@ -22,7 +22,6 @@ class VerificationScreen extends React.PureComponent { state = { currentPage: null, emailSubmitted: false, - isFirstRun: false, launchUrl: null, showSkip: false, skipAccountConfirmed: false, diff --git a/src/main/java/io/lbry/browser/reactmodules/FirstRunModule.java b/src/main/java/io/lbry/browser/reactmodules/FirstRunModule.java index 6e8b2b8..7321201 100644 --- a/src/main/java/io/lbry/browser/reactmodules/FirstRunModule.java +++ b/src/main/java/io/lbry/browser/reactmodules/FirstRunModule.java @@ -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); + } } }