wait for user to complete email verification (#548)

* wait for user to complete email verification on first run
* add password strength meter. fix auth bugs in first run.
This commit is contained in:
Akinwale Ariwodola 2019-05-23 22:11:52 +01:00 committed by GitHub
parent 97f1b530b2
commit 5a737ce38d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 333 additions and 82 deletions

21
app/package-lock.json generated
View file

@ -4832,8 +4832,8 @@
} }
}, },
"lbryinc": { "lbryinc": {
"version": "github:lbryio/lbryinc#a8fd592c46abb65410785fd0242e91904c158fd7", "version": "github:lbryio/lbryinc#54ef55d430db13ecd77699d23974cf871445ebd7",
"from": "github:lbryio/lbryinc", "from": "github:lbryio/lbryinc#check-sync",
"requires": { "requires": {
"reselect": "^3.0.0" "reselect": "^3.0.0"
} }
@ -6316,6 +6316,15 @@
"react-native-image-pan-zoom": "^2.1.9" "react-native-image-pan-zoom": "^2.1.9"
} }
}, },
"react-native-password-strength-meter": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/react-native-password-strength-meter/-/react-native-password-strength-meter-0.0.2.tgz",
"integrity": "sha512-jONkwnbznl2xJnJK3/eGa/EL87darI6n+/FtVFXRdyGqypWy6lFJALn/C6m9FyodVArrrpXJ60Ke8nWaGmPALw==",
"requires": {
"prop-types": "^15.6.2",
"react-native-speedometer": "^1.0.0"
}
},
"react-native-phone-input": { "react-native-phone-input": {
"version": "github:lbryio/react-native-phone-input#60fdef484e8bf27328c7fb6a203baab9eb9cd4a1", "version": "github:lbryio/react-native-phone-input#60fdef484e8bf27328c7fb6a203baab9eb9cd4a1",
"from": "github:lbryio/react-native-phone-input", "from": "github:lbryio/react-native-phone-input",
@ -6338,6 +6347,14 @@
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-1.0.0-alpha.22.tgz", "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-1.0.0-alpha.22.tgz",
"integrity": "sha512-kSyAt0AeVU6N7ZonfV6dP6iZF8B7Bce+tk3eujXhzBGsLg0VSLnU7uE9VqJF0xdQrHR91ZjGgVMieo/8df9KTA==" "integrity": "sha512-kSyAt0AeVU6N7ZonfV6dP6iZF8B7Bce+tk3eujXhzBGsLg0VSLnU7uE9VqJF0xdQrHR91ZjGgVMieo/8df9KTA=="
}, },
"react-native-speedometer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/react-native-speedometer/-/react-native-speedometer-1.0.3.tgz",
"integrity": "sha512-34Ne/ptaWv97jbSOq87b+gKHvGKeZ4AjYXZvovbEnQMnYNzCBWZbQNJqWWzOxyFZXbzeDsfiQ6d4xpq2pc1Tfw==",
"requires": {
"prop-types": "^15.6.2"
}
},
"react-native-tab-view": { "react-native-tab-view": {
"version": "1.3.4", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-1.3.4.tgz", "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-1.3.4.tgz",

View file

@ -9,7 +9,7 @@
"base-64": "^0.1.0", "base-64": "^0.1.0",
"@expo/vector-icons": "^8.1.0", "@expo/vector-icons": "^8.1.0",
"lbry-redux": "lbryio/lbry-redux", "lbry-redux": "lbryio/lbry-redux",
"lbryinc": "lbryio/lbryinc", "lbryinc": "lbryio/lbryinc#check-sync",
"lodash": ">=4.17.11", "lodash": ">=4.17.11",
"merge": ">=1.2.1", "merge": ">=1.2.1",
"moment": "^2.22.1", "moment": "^2.22.1",
@ -21,6 +21,7 @@
"react-native-fast-image": "^5.0.3", "react-native-fast-image": "^5.0.3",
"react-native-gesture-handler": "^1.1.0", "react-native-gesture-handler": "^1.1.0",
"react-native-image-zoom-viewer": "^2.2.5", "react-native-image-zoom-viewer": "^2.2.5",
"react-native-password-strength-meter": "^0.0.2",
"react-native-phone-input": "lbryio/react-native-phone-input", "react-native-phone-input": "lbryio/react-native-phone-input",
"react-native-vector-icons": "^6.4.2", "react-native-vector-icons": "^6.4.2",
"react-native-video": "lbryio/react-native-video#exoplayer-lbry-android", "react-native-video": "lbryio/react-native-video#exoplayer-lbry-android",

View file

@ -72,7 +72,16 @@ class RewardCard extends React.PureComponent<Props> {
} }
}}> }}>
<View style={rewardStyle.leftCol}> <View style={rewardStyle.leftCol}>
{!isPending && <TouchableOpacity onPress={() => {
if (!claimed) {
this.onClaimPress();
}
}}>
{claimed && <Icon name={claimed ? "check-circle" : "circle"}
style={claimed ? rewardStyle.claimed : (canClaim ? rewardStyle.unclaimed : rewardStyle.disabled)}
size={20} />}
</TouchableOpacity>}
{isPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
</View> </View>
<View style={rewardStyle.midCol}> <View style={rewardStyle.midCol}>
<Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text> <Text style={rewardStyle.rewardTitle}>{reward.reward_title}</Text>
@ -83,16 +92,6 @@ class RewardCard extends React.PureComponent<Props> {
error={'The transaction URL could not be opened'} />} error={'The transaction URL could not be opened'} />}
</View> </View>
<View style={rewardStyle.rightCol}> <View style={rewardStyle.rightCol}>
{!isPending && <TouchableOpacity onPress={() => {
if (!claimed) {
this.onClaimPress();
}
}}>
<Icon name={claimed ? "check-circle" : "circle"}
style={claimed ? rewardStyle.claimed : (canClaim ? rewardStyle.unclaimed : rewardStyle.disabled)}
size={20} />
</TouchableOpacity>}
{isPending && <ActivityIndicator size="small" color={Colors.LbryGreen} />}
<Text style={rewardStyle.rewardAmount}>{reward.reward_amount}</Text> <Text style={rewardStyle.rewardAmount}>{reward.reward_amount}</Text>
<Text style={rewardStyle.rewardCurrency}>LBC</Text> <Text style={rewardStyle.rewardCurrency}>LBC</Text>
</View> </View>

View file

@ -1,6 +1,7 @@
const Constants = { const Constants = {
FIRST_RUN_PAGE_WELCOME: "welcome", FIRST_RUN_PAGE_WELCOME: "welcome",
FIRST_RUN_PAGE_EMAIL_COLLECT: "email-collect", FIRST_RUN_PAGE_EMAIL_COLLECT: "email-collect",
FIRST_RUN_PAGE_EMAIL_VERIFY:"email-verify",
FIRST_RUN_PAGE_WALLET: "wallet", FIRST_RUN_PAGE_WALLET: "wallet",
FIRST_RUN_PAGE_SKIP_ACCOUNT: "skip-account", FIRST_RUN_PAGE_SKIP_ACCOUNT: "skip-account",

View file

@ -44,6 +44,7 @@ import thunk from 'redux-thunk';
const globalExceptionHandler = (error, isFatal) => { const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Firebase) { if (error && NativeModules.Firebase) {
console.log(error);
NativeModules.Firebase.logException(isFatal, error.message ? error.message : "No message", error); NativeModules.Firebase.logException(isFatal, error.message ? error.message : "No message", error);
} }
}; };

View file

@ -2,12 +2,17 @@ import { connect } from 'react-redux';
import { doToast } from 'lbry-redux'; import { doToast } from 'lbry-redux';
import { import {
doAuthenticate, doAuthenticate,
doCheckSync,
doUserEmailNew, doUserEmailNew,
doUserResendVerificationEmail,
selectAuthToken, selectAuthToken,
selectEmailNewErrorMessage, selectEmailNewErrorMessage,
selectEmailNewIsPending, selectEmailNewIsPending,
selectEmailToVerify, selectEmailToVerify,
selectAuthenticationIsPending selectAuthenticationIsPending,
selectHasSyncedWallet,
selectIsRetrievingSync,
selectUser,
} from 'lbryinc'; } from 'lbryinc';
import FirstRun from './view'; import FirstRun from './view';
@ -17,12 +22,17 @@ const select = (state) => ({
emailToVerify: selectEmailToVerify(state), emailToVerify: selectEmailToVerify(state),
emailNewErrorMessage: selectEmailNewErrorMessage(state), emailNewErrorMessage: selectEmailNewErrorMessage(state),
emailNewPending: selectEmailNewIsPending(state), emailNewPending: selectEmailNewIsPending(state),
hasSyncedWallet: selectHasSyncedWallet(state),
isRetrievingSync: selectIsRetrievingSync(state),
user: selectUser(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
addUserEmail: email => dispatch(doUserEmailNew(email)), addUserEmail: email => dispatch(doUserEmailNew(email)),
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)), authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)),
notify: data => dispatch(doToast(data)) checkSync: () => dispatch(doCheckSync()),
notify: data => dispatch(doToast(data)),
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email))
}); });
export default connect(select, perform)(FirstRun); export default connect(select, perform)(FirstRun);

View file

@ -22,29 +22,35 @@ class EmailCollectPage extends React.PureComponent {
authenticationStarted: false, authenticationStarted: false,
authenticationFailed: false, authenticationFailed: false,
placeholder: 'you@example.com', placeholder: 'you@example.com',
statusTries: 0 statusTries: 0,
verifying: true
}; };
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { authenticating, authToken } = this.props; const { authenticating, authToken, showNextPage } = this.props;
const { user } = nextProps;
if (this.state.authenticationStarted && !authenticating && authToken === null) { if (this.state.authenticationStarted && !authenticating && authToken === null) {
this.setState({ authenticationFailed: true, authenticationStarted: false }); this.setState({ authenticationFailed: true, authenticationStarted: false });
} }
if (this.state.verifying) {
if (user && user.primary_email && user.has_verified_email) {
if (showNextPage) {
showNextPage();
}
} else {
this.setState({ verifying: false });
}
}
} }
componentDidMount() { componentDidMount() {
// call user/new // call user/new
const { generateAuthToken, authenticating, authToken } = this.props; const { generateAuthToken, authenticating, authToken } = this.props;
if (!authToken && !authenticating) { if (!authenticating) {
this.startAuthenticating(); this.startAuthenticating();
} }
AsyncStorage.getItem('firstRunEmail').then(email => {
if (email) {
this.setState({ email });
}
});
} }
startAuthenticating = () => { startAuthenticating = () => {
@ -88,7 +94,7 @@ class EmailCollectPage extends React.PureComponent {
<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> <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> </View>
); );
} else if (!authToken || authenticating) { } else if (!authToken || authenticating || this.state.verifying) {
content = ( content = (
<View> <View>
<ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} /> <ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} />
@ -115,7 +121,7 @@ class EmailCollectPage extends React.PureComponent {
} }
}} }}
/> />
<Text style={firstRunStyle.paragraph}>An account will allow you to earn rewards and keep your content and settings synced.</Text> <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> <Text style={firstRunStyle.infoParagraph}>This information is disclosed only to LBRY, Inc. and not to the LBRY network.</Text>
</View> </View>
); );

View file

@ -0,0 +1,50 @@
import React from 'react';
import { Lbry } from 'lbry-redux';
import {
ActivityIndicator,
Linking,
NativeModules,
Platform,
Switch,
Text,
TextInput,
View
} from 'react-native';
import AsyncStorage from '@react-native-community/async-storage';
import Button from 'component/button';
import Colors from 'styles/colors';
import Constants from 'constants';
import Icon from 'react-native-vector-icons/FontAwesome5';
import firstRunStyle from 'styles/firstRun';
class EmailVerifyPage extends React.PureComponent {
onResendPressed = () => {
const { email, notify, resendVerificationEmail } = this.props;
resendVerificationEmail(email);
AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'true');
notify({ message: 'Please follow the instructions in the email sent to your address to continue.' });
}
render() {
const { onEmailViewLayout, email } = this.props;
const content = (
<View onLayout={onEmailViewLayout}>
<Text style={firstRunStyle.title}>Verify Email</Text>
<Text style={firstRunStyle.paragraph}>An email has been sent to <Text style={firstRunStyle.nowrap} numberOfLines={1}>{email}</Text>. Please follow the instructions in the message to verify your email address.</Text>
<View style={firstRunStyle.buttonContainer}>
<Button style={firstRunStyle.verificationButton} theme={"light"} text={"Resend"} onPress={this.onResendPressed} />
</View>
</View>
);
return (
<View style={firstRunStyle.container}>
{content}
</View>
);
}
}
export default EmailVerifyPage;

View file

@ -2,6 +2,7 @@ import React from 'react';
import { Lbry } from 'lbry-redux'; import { Lbry } from 'lbry-redux';
import { import {
ActivityIndicator, ActivityIndicator,
Dimensions,
Linking, Linking,
NativeModules, NativeModules,
Platform, Platform,
@ -9,6 +10,7 @@ import {
TextInput, TextInput,
View View
} from 'react-native'; } from 'react-native';
import { BarPasswordStrengthDisplay } from 'react-native-password-strength-meter';
import AsyncStorage from '@react-native-community/async-storage'; import AsyncStorage from '@react-native-community/async-storage';
import Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; import Constants from 'constants';
@ -18,9 +20,31 @@ class WalletPage extends React.PureComponent {
state = { state = {
password: null, password: null,
placeholder: 'password', placeholder: 'password',
statusTries: 0 statusTries: 0,
walletReady: false,
hasCheckedSync: false
}; };
componentDidMount() {
this.checkWalletReady();
this.props.checkSync();
setTimeout(() => this.setState({ hasCheckedSync: true}), 1000);
}
checkWalletReady = () => {
// make sure the sdk wallet component is ready
Lbry.status().then(status => {
if (status.startup_status && status.startup_status.wallet) {
this.setState({ walletReady: true });
return;
}
setTimeout(this.checkWalletReady, 1000);
}).catch((e) => {
console.log(e);
setTimeout(this.checkWalletReady, 1000);
});
}
handleChangeText = (text) => { handleChangeText = (text) => {
// save the value to the state email // save the value to the state email
const { onPasswordChanged } = this.props; const { onPasswordChanged } = this.props;
@ -37,12 +61,24 @@ class WalletPage extends React.PureComponent {
} }
render() { render() {
const { onPasswordChanged, onWalletViewLayout } = this.props; const { onPasswordChanged, onWalletViewLayout, isRetrievingSync, hasSyncedWallet } = this.props;
const content = ( let content;
if (!this.state.walletReady || !this.state.hasCheckedSync || isRetrievingSync) {
content = (
<View>
<ActivityIndicator size="large" color={Colors.White} style={firstRunStyle.waiting} />
<Text style={firstRunStyle.paragraph}>Retrieving your account information...</Text>
</View>
);
} else {
content = (
<View onLayout={onWalletViewLayout}> <View onLayout={onWalletViewLayout}>
<Text style={firstRunStyle.title}>Password</Text> <Text style={firstRunStyle.title}>Password</Text>
<Text style={firstRunStyle.paragraph}>Please enter a password to secure your account and wallet.</Text> <Text style={firstRunStyle.paragraph}>
{hasSyncedWallet ? "Please enter the password you used to secure your wallet." :
"Please enter a password to secure your account and wallet."}
</Text>
<TextInput style={firstRunStyle.passwordInput} <TextInput style={firstRunStyle.passwordInput}
placeholder={this.state.placeholder} placeholder={this.state.placeholder}
underlineColorAndroid="transparent" underlineColorAndroid="transparent"
@ -60,9 +96,17 @@ class WalletPage extends React.PureComponent {
} }
}} }}
/> />
{(this.state.password && this.state.password.trim().length) > 0 &&
<View style={firstRunStyle.passwordStrength}>
<BarPasswordStrengthDisplay
width={Dimensions.get('window').width - 80}
minLength={1}
password={this.state.password} />
</View>}
<Text style={firstRunStyle.infoParagraph}>Note: for wallet security purposes, LBRY is unable to reset your password.</Text> <Text style={firstRunStyle.infoParagraph}>Note: for wallet security purposes, LBRY is unable to reset your password.</Text>
</View> </View>
); );
}
return ( return (
<View style={firstRunStyle.container}> <View style={firstRunStyle.container}>

View file

@ -15,6 +15,7 @@ import Constants from 'constants';
import WalletPage from './internal/wallet-page'; import WalletPage from './internal/wallet-page';
import WelcomePage from './internal/welcome-page'; import WelcomePage from './internal/welcome-page';
import EmailCollectPage from './internal/email-collect-page'; import EmailCollectPage from './internal/email-collect-page';
import EmailVerifyPage from './internal/email-verify-page';
import SkipAccountPage from './internal/skip-account-page'; import SkipAccountPage from './internal/skip-account-page';
import firstRunStyle from 'styles/firstRun'; import firstRunStyle from 'styles/firstRun';
@ -22,16 +23,19 @@ class FirstRunScreen extends React.PureComponent {
static pages = [ static pages = [
Constants.FIRST_RUN_PAGE_WELCOME, Constants.FIRST_RUN_PAGE_WELCOME,
Constants.FIRST_RUN_PAGE_EMAIL_COLLECT, Constants.FIRST_RUN_PAGE_EMAIL_COLLECT,
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY,
Constants.FIRST_RUN_PAGE_WALLET, Constants.FIRST_RUN_PAGE_WALLET,
Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT, Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT,
]; ];
state = { state = {
currentPage: null, currentPage: null,
email: null,
emailSubmitted: false, emailSubmitted: false,
isFirstRun: false, isFirstRun: false,
launchUrl: null, launchUrl: null,
showSkip: false, showSkip: false,
isEmailVerified: false,
skipAccountConfirmed: false, skipAccountConfirmed: false,
showBottomContainer: true, showBottomContainer: true,
walletPassword: null walletPassword: null
@ -61,7 +65,7 @@ class FirstRunScreen extends React.PureComponent {
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { emailNewErrorMessage, emailNewPending } = nextProps; const { emailNewErrorMessage, emailNewPending, user } = nextProps;
const { notify } = this.props; const { notify } = this.props;
if (this.state.emailSubmitted && !emailNewPending) { if (this.state.emailSubmitted && !emailNewPending) {
@ -69,10 +73,24 @@ class FirstRunScreen extends React.PureComponent {
if (emailNewErrorMessage) { if (emailNewErrorMessage) {
notify ({ message: String(emailNewErrorMessage), isError: true }); notify ({ message: String(emailNewErrorMessage), isError: true });
} else { } else {
// Request successful. Navigate to next page (wallet). // Request successful. Navigate to email verify page.
this.showNextPage(); this.showPage(Constants.FIRST_RUN_PAGE_EMAIL_VERIFY)
} }
} }
this.checkVerificationStatus(user);
}
checkVerificationStatus = (user) => {
const { navigation } = this.props;
this.setState({
isEmailVerified: (user && user.primary_email && user.has_verified_email),
}, () => {
if (this.state.isEmailVerified) {
this.showPage(Constants.FIRST_RUN_PAGE_WALLET);
}
});
} }
launchSplashScreen() { launchSplashScreen() {
@ -96,14 +114,19 @@ class FirstRunScreen extends React.PureComponent {
if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === this.state.currentPage) { if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === this.state.currentPage) {
this.showPage(Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT); this.showPage(Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT);
} }
// Go to email collection page if user cancels from email verification
if (Constants.FIRST_RUN_PAGE_EMAIL_VERIFY === this.state.currentPage) {
this.showPage(Constants.FIRST_RUN_PAGE_EMAIL_COLLECT);
}
} }
handleContinuePressed = () => { handleContinuePressed = () => {
const { notify } = this.props; const { notify, user } = this.props;
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage); const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
if (Constants.FIRST_RUN_PAGE_WALLET === this.state.currentPage) { if (Constants.FIRST_RUN_PAGE_WALLET === this.state.currentPage) {
if (!this.state.walletPassword || this.state.walletPassword.trim().length < 6) { if (!this.state.walletPassword || this.state.walletPassword.trim().length === 0) {
return notify({ message: 'Your wallet password should be at least 6 characters long' }); return notify({ message: 'Please enter a wallet password' });
} }
this.closeFinalPage(); this.closeFinalPage();
@ -144,11 +167,11 @@ class FirstRunScreen extends React.PureComponent {
}); });
} }
showNextPage() { showNextPage = () => {
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage); const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
const nextPage = FirstRunScreen.pages[pageIndex + 1]; const nextPage = FirstRunScreen.pages[pageIndex + 1];
this.setState({ currentPage: nextPage }); this.setState({ currentPage: nextPage });
if (nextPage === 'email-collect') { if (nextPage === Constants.FIRST_RUN_PAGE_EMAIL_COLLECT) {
// do not show the buttons (because we're waiting to get things ready) // do not show the buttons (because we're waiting to get things ready)
this.setState({ showBottomContainer: false }); this.setState({ showBottomContainer: false });
} }
@ -172,6 +195,7 @@ class FirstRunScreen extends React.PureComponent {
} }
onEmailChanged = (email) => { onEmailChanged = (email) => {
this.setState({ email });
if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT == this.state.currentPage) { if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT == this.state.currentPage) {
this.setState({ showSkip: (!email || email.trim().length === 0) }); this.setState({ showSkip: (!email || email.trim().length === 0) });
} else { } else {
@ -203,19 +227,27 @@ class FirstRunScreen extends React.PureComponent {
authenticate, authenticate,
authenticating, authenticating,
authToken, authToken,
checkSync,
emailNewErrorMessage, emailNewErrorMessage,
emailNewPending, emailNewPending,
emailToVerify emailToVerify,
notify,
hasSyncedWallet,
isRetrievingSync,
resendVerificationEmail,
user
} = this.props; } = this.props;
let page = null; let page = null;
switch (this.state.currentPage) { switch (this.state.currentPage) {
case 'welcome': case Constants.FIRST_RUN_PAGE_WELCOME:
page = (<WelcomePage />); page = (<WelcomePage />);
break; break;
case 'email-collect': case Constants.FIRST_RUN_PAGE_EMAIL_COLLECT:
page = (<EmailCollectPage page = (<EmailCollectPage
user={user}
showNextPage={this.showNextPage}
authenticating={authenticating} authenticating={authenticating}
authToken={authToken} authToken={authToken}
authenticate={authenticate} authenticate={authenticate}
@ -223,13 +255,24 @@ class FirstRunScreen extends React.PureComponent {
onEmailViewLayout={this.onEmailViewLayout} />); onEmailViewLayout={this.onEmailViewLayout} />);
break; break;
case 'wallet': case Constants.FIRST_RUN_PAGE_EMAIL_VERIFY:
page = (<EmailVerifyPage
onEmailViewLayout={this.onEmailViewLayout}
email={this.state.email}
notify={notify}
resendVerificationEmail={resendVerificationEmail} />);
break;
case Constants.FIRST_RUN_PAGE_WALLET:
page = (<WalletPage page = (<WalletPage
checkSync={checkSync}
hasSyncedWallet={hasSyncedWallet}
isRetrievingSync={isRetrievingSync}
onWalletViewLayout={this.onWalletViewLayout} onWalletViewLayout={this.onWalletViewLayout}
onPasswordChanged={this.onWalletPasswordChanged} />); onPasswordChanged={this.onWalletPasswordChanged} />);
break; break;
case 'skip-account': case Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT:
page = (<SkipAccountPage page = (<SkipAccountPage
onSkipAccountViewLayout={this.onSkipAccountViewLayout} onSkipAccountViewLayout={this.onSkipAccountViewLayout}
onSkipSwitchChanged={this.onSkipSwitchChanged} />); onSkipSwitchChanged={this.onSkipSwitchChanged} />);
@ -246,9 +289,11 @@ class FirstRunScreen extends React.PureComponent {
<View style={firstRunStyle.buttonRow}> <View style={firstRunStyle.buttonRow}>
{([Constants.FIRST_RUN_PAGE_WELCOME, Constants.FIRST_RUN_PAGE_WALLET].indexOf(this.state.currentPage) > -1) && <View />} {([Constants.FIRST_RUN_PAGE_WELCOME, Constants.FIRST_RUN_PAGE_WALLET].indexOf(this.state.currentPage) > -1) && <View />}
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage && {(Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage ||
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY === this.state.currentPage) &&
<TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}> <TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}>
<Text style={firstRunStyle.buttonText}>« Setup account</Text> <Text style={firstRunStyle.buttonText}>
« {Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage ? 'Setup account' : 'Change Email'}</Text>
</TouchableOpacity>} </TouchableOpacity>}
{!emailNewPending && (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === this.state.currentPage) && {!emailNewPending && (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === this.state.currentPage) &&
<TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}> <TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleLeftButtonPressed}>
@ -259,7 +304,9 @@ class FirstRunScreen extends React.PureComponent {
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}> <TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage && {Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage &&
<Text style={firstRunStyle.smallButtonText}>Use LBRY »</Text>} <Text style={firstRunStyle.smallButtonText}>Use LBRY »</Text>}
{Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT !== this.state.currentPage && {Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT !== this.state.currentPage &&
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY !== this.state.currentPage &&
<Text style={firstRunStyle.buttonText}>{Constants.FIRST_RUN_PAGE_WALLET === this.state.currentPage ? 'Use LBRY' : 'Continue'} »</Text>} <Text style={firstRunStyle.buttonText}>{Constants.FIRST_RUN_PAGE_WALLET === this.state.currentPage ? 'Use LBRY' : 'Continue'} »</Text>}
</TouchableOpacity>} </TouchableOpacity>}
</View> </View>

View file

@ -18,6 +18,18 @@ class EmailVerifyPage extends React.PureComponent {
previousEmail: null previousEmail: null
} }
componentDidMount() {
const { setEmailVerificationPhase } = this.props;
if (setEmailVerificationPhase) {
setEmailVerificationPhase(false);
}
}
componentWillUmount() {
AsyncStorage.removeItem(Constants.KEY_EMAIL_VERIFY_PENDING);
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
}
handleChangeText = (text) => { handleChangeText = (text) => {
this.setState({ email: text }); this.setState({ email: text });
AsyncStorage.setItem(Constants.KEY_FIRST_RUN_EMAIL, text); AsyncStorage.setItem(Constants.KEY_FIRST_RUN_EMAIL, text);
@ -25,7 +37,7 @@ class EmailVerifyPage extends React.PureComponent {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const { emailNewErrorMessage, emailNewPending, emailToVerify } = nextProps; const { emailNewErrorMessage, emailNewPending, emailToVerify } = nextProps;
const { notify } = this.props; const { notify, setEmailVerificationPhase } = this.props;
if (this.state.verifyStarted && !emailNewPending) { if (this.state.verifyStarted && !emailNewPending) {
if (emailNewErrorMessage) { if (emailNewErrorMessage) {
@ -33,6 +45,9 @@ class EmailVerifyPage extends React.PureComponent {
this.setState({ verifyStarted: false }); this.setState({ verifyStarted: false });
} else { } else {
this.setState({ phase: Constants.PHASE_VERIFICATION }); this.setState({ phase: Constants.PHASE_VERIFICATION });
if (setEmailVerificationPhase) {
setEmailVerificationPhase(true);
}
//notify({ message: 'Please follow the instructions in the email sent to your address to continue.' }); //notify({ message: 'Please follow the instructions in the email sent to your address to continue.' });
AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'true'); AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'true');
} }
@ -40,7 +55,13 @@ class EmailVerifyPage extends React.PureComponent {
} }
onSendVerificationPressed = () => { onSendVerificationPressed = () => {
const { addUserEmail, emailNewPending, notify, resendVerificationEmail } = this.props; const {
addUserEmail,
emailNewPending,
notify,
resendVerificationEmail,
setEmailVerificationPhase
} = this.props;
if (emailNewPending) { if (emailNewPending) {
return; return;
@ -58,6 +79,9 @@ class EmailVerifyPage extends React.PureComponent {
resendVerificationEmail(this.state.email); resendVerificationEmail(this.state.email);
AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'true'); AsyncStorage.setItem(Constants.KEY_EMAIL_VERIFY_PENDING, 'true');
this.setState({ verifyStarted: true, phase: Constants.PHASE_VERIFICATION }); this.setState({ verifyStarted: true, phase: Constants.PHASE_VERIFICATION });
if (setEmailVerificationPhase) {
setEmailVerificationPhase(true);
}
return; return;
} }
@ -74,7 +98,15 @@ class EmailVerifyPage extends React.PureComponent {
} }
onEditPressed = () => { onEditPressed = () => {
this.setState({ verifyStarted: false, phase: Constants.PHASE_COLLECTION, previousEmail: this.state.email }); const { setEmailVerificationPhase } = this.props;
this.setState({ verifyStarted: false, phase: Constants.PHASE_COLLECTION, previousEmail: this.state.email }, () => {
if (setEmailVerificationPhase) {
setEmailVerificationPhase(false);
}
// clear the previous email
AsyncStorage.removeItem(Constants.KEY_EMAIL_VERIFY_PENDING);
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
});
} }
render() { render() {

View file

@ -10,6 +10,13 @@ import firstRunStyle from 'styles/firstRun';
import rewardStyle from 'styles/reward'; import rewardStyle from 'styles/reward';
class ManualVerifyPage extends React.PureComponent { class ManualVerifyPage extends React.PureComponent {
componentDidMount() {
const { setEmailVerificationPhase } = this.props;
if (setEmailVerificationPhase) {
setEmailVerificationPhase(false);
}
}
render() { render() {
return ( return (
<View style={firstRunStyle.container}> <View style={firstRunStyle.container}>

View file

@ -43,10 +43,13 @@ class PhoneVerifyPage extends React.PureComponent {
} }
componentDidMount() { componentDidMount() {
const { phone } = this.props; const { phone, setEmailVerificationPhase } = this.props;
if (phone && String(phone).trim().length > 0) { if (phone && String(phone).trim().length > 0) {
this.setState({ newPhoneAdded: true, phase: Constants.PHASE_VERIFICATION }); this.setState({ newPhoneAdded: true, phase: Constants.PHASE_VERIFICATION });
} }
if (setEmailVerificationPhase) {
setEmailVerificationPhase(false);
}
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {

View file

@ -27,6 +27,7 @@ class VerificationScreen extends React.PureComponent {
skipAccountConfirmed: false, skipAccountConfirmed: false,
showBottomContainer: true, showBottomContainer: true,
walletPassword: null, walletPassword: null,
isEmailVerificationPhase: false,
isEmailVerified: false, isEmailVerified: false,
isIdentityVerified: false, isIdentityVerified: false,
isRewardApproved: false isRewardApproved: false
@ -37,6 +38,10 @@ class VerificationScreen extends React.PureComponent {
this.checkVerificationStatus(user); this.checkVerificationStatus(user);
} }
setEmailVerificationPhase = (value) => {
this.setState({ isEmailVerificationPhase: value });
}
checkVerificationStatus = (user) => { checkVerificationStatus = (user) => {
const { navigation } = this.props; const { navigation } = this.props;
@ -101,6 +106,7 @@ class VerificationScreen extends React.PureComponent {
emailNewPending={emailNewPending} emailNewPending={emailNewPending}
emailToVerify={emailToVerify} emailToVerify={emailToVerify}
notify={notify} notify={notify}
setEmailVerificationPhase={this.setEmailVerificationPhase}
resendVerificationEmail={resendVerificationEmail} resendVerificationEmail={resendVerificationEmail}
/> />
); );
@ -114,6 +120,7 @@ class VerificationScreen extends React.PureComponent {
phoneVerifyErrorMessage={phoneVerifyErrorMessage} phoneVerifyErrorMessage={phoneVerifyErrorMessage}
phoneNewIsPending={phoneNewIsPending} phoneNewIsPending={phoneNewIsPending}
phoneNewErrorMessage={phoneNewErrorMessage} phoneNewErrorMessage={phoneNewErrorMessage}
setEmailVerificationPhase={this.setEmailVerificationPhase}
notify={notify} notify={notify}
verifyPhone={verifyPhone} verifyPhone={verifyPhone}
/> />
@ -121,7 +128,7 @@ class VerificationScreen extends React.PureComponent {
break; break;
case 'manualVerify': case 'manualVerify':
page = ( page = (
<ManualVerifyPage /> <ManualVerifyPage setEmailVerificationPhase={this.setEmailVerificationPhase} />
); );
} }
@ -129,9 +136,10 @@ class VerificationScreen extends React.PureComponent {
<View style={firstRunStyle.screenContainer}> <View style={firstRunStyle.screenContainer}>
{page} {page}
<TouchableOpacity style={firstRunStyle.closeButton} onPress={this.onCloseButtonPressed}> {!this.state.isEmailVerificationPhase &&
(<TouchableOpacity style={firstRunStyle.closeButton} onPress={this.onCloseButtonPressed}>
<Text style={firstRunStyle.closeButtonText}>x</Text> <Text style={firstRunStyle.closeButtonText}>x</Text>
</TouchableOpacity> </TouchableOpacity>)}
</View> </View>
); );
} }

View file

@ -143,6 +143,29 @@ const firstRunStyle = StyleSheet.create({
alignSelf: 'center', alignSelf: 'center',
color: Colors.White, color: Colors.White,
fontSize: 16 fontSize: 16
},
buttonContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
paddingLeft: 32,
paddingRight: 32,
marginTop: 16
},
verificationButton: {
backgroundColor: Colors.White,
paddingLeft: 16,
paddingRight: 16
},
passwordStrength: {
marginLeft: 32,
marginRight: 32,
marginBottom: 48
},
nowrap: {
flex: 1,
flexWrap: 'nowrap'
} }
}); });

View file

@ -139,12 +139,12 @@ const rewardStyle = StyleSheet.create({
color: Colors.LbryGreen color: Colors.LbryGreen
}, },
leftCol: { leftCol: {
width: '5%', width: '15%',
alignItems: 'center', alignItems: 'center',
paddingLeft: 6 paddingLeft: 6
}, },
midCol: { midCol: {
width: '75%' width: '65%'
}, },
rightCol: { rightCol: {
width: '18%', width: '18%',
@ -278,7 +278,7 @@ const rewardStyle = StyleSheet.create({
width: '100%', width: '100%',
paddingLeft: 32, paddingLeft: 32,
paddingRight: 32, paddingRight: 32,
marginTop: 16 marginTop: 8
}, },
verificationTitle: { verificationTitle: {
fontSize: 32, fontSize: 32,

View file

@ -2,9 +2,10 @@
<resources> <resources>
<color name="colorPrimary">#3F51B5</color> <color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color> <color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color> <color name="colorAccent">#FFFFFF</color>
<color name="red">#FF0000</color> <color name="red">#FF0000</color>
<color name="green">#00C000</color> <color name="green">#00C000</color>
<color name="lbrygreen">#40B89A</color> <color name="lbrygreen">#40B89A</color>
<color name="white">#FFFFFF</color>
</resources> </resources>

View file

@ -1,5 +1,6 @@
<resources> <resources>
<style name="LbryAppTheme" parent="@android:style/Theme.Material.Light"> <style name="LbryAppTheme" parent="@android:style/Theme.Material.Light">
<item name="android:windowBackground">@color/lbrygreen</item> <item name="android:windowBackground">@color/lbrygreen</item>
<item name="colorControlActivated">@color/white</item>
</style> </style>
</resources> </resources>

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB