first run updates for sync
This commit is contained in:
parent
877ce8d008
commit
171f7841c9
15 changed files with 330 additions and 63 deletions
4
app/package-lock.json
generated
4
app/package-lock.json
generated
|
@ -4067,8 +4067,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lbryinc": {
|
"lbryinc": {
|
||||||
"version": "github:lbryio/lbryinc#437260f3dad2321e892388b68749ee6af01aff2b",
|
"version": "github:lbryio/lbryinc#416f27c945e14deef4c0f4915a72c7bd0f39775e",
|
||||||
"from": "github:lbryio/lbryinc",
|
"from": "github:lbryio/lbryinc#test-api",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"reselect": "^3.0.0"
|
"reselect": "^3.0.0"
|
||||||
|
|
|
@ -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#test-api",
|
||||||
"lodash": ">=4.17.11",
|
"lodash": ">=4.17.11",
|
||||||
"merge": ">=1.2.1",
|
"merge": ">=1.2.1",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
const Constants = {
|
const Constants = {
|
||||||
|
FIRST_RUN_PAGE_WELCOME: "welcome",
|
||||||
|
FIRST_RUN_PAGE_EMAIL_COLLECT: "email-collect",
|
||||||
|
FIRST_RUN_PAGE_WALLET: "wallet",
|
||||||
|
FIRST_RUN_PAGE_SKIP_ACCOUNT: "skip-account",
|
||||||
|
|
||||||
KEY_FIRST_RUN_EMAIL: "firstRunEmail",
|
KEY_FIRST_RUN_EMAIL: "firstRunEmail",
|
||||||
|
KEY_FIRST_RUN_PASSWORD: "firstRunPassword",
|
||||||
KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail",
|
KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail",
|
||||||
KEY_EMAIL_VERIFY_PENDING: "emailVerifyPending",
|
KEY_EMAIL_VERIFY_PENDING: "emailVerifyPending",
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
View
|
View
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Colors from '../../../styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from '../../../constants';
|
import Constants from 'constants';
|
||||||
import firstRunStyle from '../../../styles/firstRun';
|
import firstRunStyle from 'styles/firstRun';
|
||||||
|
|
||||||
class EmailCollectPage extends React.PureComponent {
|
class EmailCollectPage extends React.PureComponent {
|
||||||
static MAX_STATUS_TRIES = 30;
|
static MAX_STATUS_TRIES = 30;
|
||||||
|
@ -52,7 +52,7 @@ class EmailCollectPage extends React.PureComponent {
|
||||||
this.setState({ authenticationStarted: true, authenticationFailed: false });
|
this.setState({ authenticationStarted: true, authenticationFailed: false });
|
||||||
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
||||||
Lbry.status().then(info => {
|
Lbry.status().then(info => {
|
||||||
authenticate(appVersion, Platform.OS)
|
authenticate(appVersion, Platform.OS);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (this.state.statusTries >= EmailCollectPage.MAX_STATUS_TRIES) {
|
if (this.state.statusTries >= EmailCollectPage.MAX_STATUS_TRIES) {
|
||||||
this.setState({ authenticationFailed: true });
|
this.setState({ authenticationFailed: true });
|
||||||
|
@ -98,9 +98,7 @@ class EmailCollectPage extends React.PureComponent {
|
||||||
} else {
|
} else {
|
||||||
content = (
|
content = (
|
||||||
<View onLayout={onEmailViewLayout}>
|
<View onLayout={onEmailViewLayout}>
|
||||||
<Text style={firstRunStyle.title}>Rewards.</Text>
|
<Text style={firstRunStyle.title}>Setup account</Text>
|
||||||
<Text style={firstRunStyle.paragraph}>You can earn LBRY Credits (LBC) rewards by completing various tasks in the app.</Text>
|
|
||||||
<Text style={firstRunStyle.paragraph}>Please provide a valid email address below to be able to claim your rewards.</Text>
|
|
||||||
<TextInput style={firstRunStyle.emailInput}
|
<TextInput style={firstRunStyle.emailInput}
|
||||||
placeholder={this.state.placeholder}
|
placeholder={this.state.placeholder}
|
||||||
underlineColorAndroid="transparent"
|
underlineColorAndroid="transparent"
|
||||||
|
@ -117,7 +115,8 @@ class EmailCollectPage extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text style={firstRunStyle.infoParagraph}>This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards and may be used to sync usage data across devices.</Text>
|
<Text style={firstRunStyle.paragraph}>Setting up your account will allow you to earn rewards and keep your content and settings synced..</Text>
|
||||||
|
<Text style={firstRunStyle.infoParagraph}>This information is disclosed only to LBRY, Inc. and not to the LBRY network.</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
37
app/src/page/firstRun/internal/skip-account-page.js
Normal file
37
app/src/page/firstRun/internal/skip-account-page.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Lbry } from 'lbry-redux';
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
AsyncStorage,
|
||||||
|
Linking,
|
||||||
|
NativeModules,
|
||||||
|
Platform,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
import Colors from 'styles/colors';
|
||||||
|
import Constants from 'constants';
|
||||||
|
import firstRunStyle from 'styles/firstRun';
|
||||||
|
|
||||||
|
class SkipAccountPage extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const { onSkipAccountViewLayout } = this.props;
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<View onLayout={onSkipAccountViewLayout}>
|
||||||
|
<Text style={firstRunStyle.title}>Are you sure?</Text>
|
||||||
|
<Text style={firstRunStyle.paragraph}>If you do not provide an email address, you will not be eligible for free LBC from LBRY, Inc.</Text>
|
||||||
|
<Text style={firstRunStyle.paragraph}>Additionally, all of your earnings and settings will be stored locally on this device. Uninstalling the app will delete all of your content and credits permanently.</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={firstRunStyle.container}>
|
||||||
|
{content}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SkipAccountPage;
|
75
app/src/page/firstRun/internal/wallet-page.js
Normal file
75
app/src/page/firstRun/internal/wallet-page.js
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Lbry } from 'lbry-redux';
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
AsyncStorage,
|
||||||
|
Linking,
|
||||||
|
NativeModules,
|
||||||
|
Platform,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
import Colors from 'styles/colors';
|
||||||
|
import Constants from 'constants';
|
||||||
|
import firstRunStyle from 'styles/firstRun';
|
||||||
|
|
||||||
|
class WalletPage extends React.PureComponent {
|
||||||
|
state = {
|
||||||
|
password: null,
|
||||||
|
placeholder: 'passphrase',
|
||||||
|
statusTries: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChangeText = (text) => {
|
||||||
|
// save the value to the state email
|
||||||
|
const { onPasswordChanged } = this.props;
|
||||||
|
this.setState({ password: text });
|
||||||
|
if (onPasswordChanged) {
|
||||||
|
onPasswordChanged(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NativeModules.UtilityModule) {
|
||||||
|
NativeModules.UtilityModule.setSecureValue(Constants.KEY_FIRST_RUN_PASSWORD, text);
|
||||||
|
// simply set any string value to indicate that a passphrase was set on first run
|
||||||
|
AsyncStorage.setItem(Constants.KEY_FIRST_RUN_PASSWORD, "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { onPasswordChanged, onWalletViewLayout } = this.props;
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<View onLayout={onWalletViewLayout}>
|
||||||
|
<Text style={firstRunStyle.title}>Your Wallet</Text>
|
||||||
|
<Text style={firstRunStyle.paragraph}>LBRY Credits (or LBC) are tokens which you can use to purchase content and support creators in the digital marketplace.</Text>
|
||||||
|
<Text style={firstRunStyle.paragraph}>We use a wallet to store these tokens, which needs to be secured. Please enter a passphrase to secure your wallet.</Text>
|
||||||
|
<TextInput style={firstRunStyle.passwordInput}
|
||||||
|
placeholder={this.state.placeholder}
|
||||||
|
underlineColorAndroid="transparent"
|
||||||
|
value={this.state.email}
|
||||||
|
onChangeText={text => this.handleChangeText(text)}
|
||||||
|
onFocus={() => {
|
||||||
|
if (!this.state.password || this.state.password.length === 0) {
|
||||||
|
this.setState({ placeholder: '' });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (!this.state.password || this.state.password.length === 0) {
|
||||||
|
this.setState({ placeholder: 'passphrase' });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text style={firstRunStyle.infoParagraph}>If you forget your passphrase, you will be unable to make use of your LBC. Make sure to use a memorable passphrase.</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={firstRunStyle.container}>
|
||||||
|
{content}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WalletPage;
|
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Lbry } from 'lbry-redux';
|
import { Lbry } from 'lbry-redux';
|
||||||
import { View, Text, Linking } from 'react-native';
|
import { View, Text, Linking } from 'react-native';
|
||||||
import Colors from '../../../styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import firstRunStyle from '../../../styles/firstRun';
|
import firstRunStyle from 'styles/firstRun';
|
||||||
|
|
||||||
class WelcomePage extends React.PureComponent {
|
class WelcomePage extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -10,16 +10,20 @@ import {
|
||||||
View
|
View
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { NavigationActions, StackActions } from 'react-navigation';
|
import { NavigationActions, StackActions } from 'react-navigation';
|
||||||
import Colors from '../../styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from '../../constants';
|
import Constants from 'constants';
|
||||||
|
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 firstRunStyle from '../../styles/firstRun';
|
import SkipAccountPage from './internal/skip-account-page';
|
||||||
|
import firstRunStyle from 'styles/firstRun';
|
||||||
|
|
||||||
class FirstRunScreen extends React.PureComponent {
|
class FirstRunScreen extends React.PureComponent {
|
||||||
static pages = [
|
static pages = [
|
||||||
'welcome',
|
Constants.FIRST_RUN_PAGE_WELCOME,
|
||||||
'email-collect'
|
Constants.FIRST_RUN_PAGE_EMAIL_COLLECT,
|
||||||
|
Constants.FIRST_RUN_PAGE_WALLET,
|
||||||
|
Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT,
|
||||||
];
|
];
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -28,7 +32,8 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
isFirstRun: false,
|
isFirstRun: false,
|
||||||
launchUrl: null,
|
launchUrl: null,
|
||||||
showSkip: false,
|
showSkip: false,
|
||||||
showBottomContainer: true
|
showBottomContainer: true,
|
||||||
|
walletPassword: null
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -63,8 +68,9 @@ 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 discover.
|
// Request successful. Navigate to next page (wallet).
|
||||||
this.closeFinalPage();
|
this.showNextPage();
|
||||||
|
//this.closeFinalPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,14 +86,29 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
navigation.dispatch(resetAction);
|
navigation.dispatch(resetAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleBackPressed = () => {
|
||||||
|
if (this.state.currentPage === Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT) {
|
||||||
|
this.showPage(Constants.FIRST_RUN_PAGE_EMAIL_COLLECT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleContinuePressed = () => {
|
handleContinuePressed = () => {
|
||||||
|
const { notify } = this.props;
|
||||||
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
|
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
|
||||||
if (this.state.currentPage !== 'email-collect' &&
|
if (Constants.FIRST_RUN_PAGE_WALLET == this.state.currentPage) {
|
||||||
pageIndex === (FirstRunScreen.pages.length - 1)) {
|
if (!this.state.walletPassword || this.state.walletPassword.trim().length < 6) {
|
||||||
|
return notify({ message: 'Your wallet passphrase should be at least 6 characters long' });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.closeFinalPage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.currentPage !== Constants.FIRST_RUN_PAGE_EMAIL_COLLECT && pageIndex === (FirstRunScreen.pages.length - 1)) {
|
||||||
this.closeFinalPage();
|
this.closeFinalPage();
|
||||||
} else {
|
} else {
|
||||||
// TODO: Actions and page verification for specific pages
|
// TODO: Actions and page verification for specific pages
|
||||||
if (this.state.currentPage === 'email-collect') {
|
if (this.state.currentPage === Constants.FIRST_RUN_PAGE_EMAIL_COLLECT) {
|
||||||
// handle email collect
|
// handle email collect
|
||||||
this.handleEmailCollectPageContinue();
|
this.handleEmailCollectPageContinue();
|
||||||
} else {
|
} else {
|
||||||
|
@ -103,10 +124,9 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
AsyncStorage.getItem(Constants.KEY_FIRST_RUN_EMAIL).then(email => {
|
AsyncStorage.getItem(Constants.KEY_FIRST_RUN_EMAIL).then(email => {
|
||||||
if (!email || email.trim().length === 0) {
|
if (!email || email.trim().length === 0) {
|
||||||
// no email provided. Skip.
|
// no email provided. Skip.
|
||||||
if (this.state.currentPage === 'email-collect' && pageIndex === (FirstRunScreen.pages.length - 1)) {
|
if (this.state.currentPage === Constants.FIRST_RUN_PAGE_EMAIL_COLLECT) {
|
||||||
this.closeFinalPage();
|
// go directly to the "Are You Sure?" page (instead of "Your Wwallet" first run page)
|
||||||
} else {
|
this.showPage(Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT);
|
||||||
this.showNextPage();
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -133,6 +153,13 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showPage(pageName) {
|
||||||
|
const pageIndex = FirstRunScreen.pages.indexOf(pageName);
|
||||||
|
if (pageIndex > -1) {
|
||||||
|
this.setState({ currentPage: pageName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
closeFinalPage() {
|
closeFinalPage() {
|
||||||
// Final page. Let the app know that first run experience is completed.
|
// Final page. Let the app know that first run experience is completed.
|
||||||
if (NativeModules.FirstRun) {
|
if (NativeModules.FirstRun) {
|
||||||
|
@ -144,7 +171,7 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmailChanged = (email) => {
|
onEmailChanged = (email) => {
|
||||||
if ('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 {
|
||||||
this.setState({ showSkip: false });
|
this.setState({ showSkip: false });
|
||||||
|
@ -158,6 +185,14 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onWalletPasswordChanged = (password) => {
|
||||||
|
this.setState({ walletPassword: password });
|
||||||
|
}
|
||||||
|
|
||||||
|
onWalletViewLayout = () => {
|
||||||
|
this.setState({ showBottomContainer: true });
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
authenticate,
|
authenticate,
|
||||||
|
@ -178,6 +213,12 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
authenticate={authenticate}
|
authenticate={authenticate}
|
||||||
onEmailChanged={this.onEmailChanged}
|
onEmailChanged={this.onEmailChanged}
|
||||||
onEmailViewLayout={this.onEmailViewLayout} />);
|
onEmailViewLayout={this.onEmailViewLayout} />);
|
||||||
|
} else if (this.state.currentPage === 'wallet') {
|
||||||
|
page = (<WalletPage
|
||||||
|
onWalletViewLayout={this.onWalletViewLayout}
|
||||||
|
onPasswordChanged={this.onWalletPasswordChanged} />);
|
||||||
|
} else if (this.state.currentPage === 'skip-account') {
|
||||||
|
page = (<SkipAccountPage onSkipAccountViewLayout={this.onSkipAccountViewLayout} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -188,10 +229,22 @@ class FirstRunScreen extends React.PureComponent {
|
||||||
{emailNewPending &&
|
{emailNewPending &&
|
||||||
<ActivityIndicator size="small" color={Colors.White} style={firstRunStyle.pageWaiting} />}
|
<ActivityIndicator size="small" color={Colors.White} style={firstRunStyle.pageWaiting} />}
|
||||||
|
|
||||||
{!emailNewPending &&
|
<View style={firstRunStyle.buttonRow}>
|
||||||
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
|
{this.state.currentPage !== Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT && <View />}
|
||||||
<Text style={firstRunStyle.buttonText}>{this.state.showSkip ? 'Skip': 'Continue'}</Text>
|
{this.state.currentPage === Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT &&
|
||||||
</TouchableOpacity>}
|
<TouchableOpacity style={firstRunStyle.leftButton} onPress={this.handleBackPressed}>
|
||||||
|
<Text style={firstRunStyle.buttonText}>Setup account</Text>
|
||||||
|
</TouchableOpacity>}
|
||||||
|
|
||||||
|
{!emailNewPending &&
|
||||||
|
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
|
||||||
|
{this.state.currentPage === Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT &&
|
||||||
|
<Text style={firstRunStyle.smallButtonText}>Continue without account</Text>}
|
||||||
|
{this.state.currentPage !== Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT &&
|
||||||
|
<Text style={firstRunStyle.buttonText}>{this.state.showSkip ? 'No, thanks': 'Continue'}</Text>}
|
||||||
|
</TouchableOpacity>}
|
||||||
|
</View>
|
||||||
|
|
||||||
</View>}
|
</View>}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,11 +12,11 @@ import {
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { NavigationActions, StackActions } from 'react-navigation';
|
import { NavigationActions, StackActions } from 'react-navigation';
|
||||||
import { decode as atob } from 'base-64';
|
import { decode as atob } from 'base-64';
|
||||||
import { navigateToUri } from '../../utils/helper';
|
import { navigateToUri } from 'utils/helper';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Colors from '../../styles/colors';
|
import Colors from 'styles/colors';
|
||||||
import Constants from '../../constants';
|
import Constants from 'constants';
|
||||||
import splashStyle from '../../styles/splash';
|
import splashStyle from 'styles/splash';
|
||||||
|
|
||||||
const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes
|
const BLOCK_HEIGHT_INTERVAL = 1000 * 60 * 2.5; // every 2.5 minutes
|
||||||
|
|
||||||
|
@ -121,6 +121,31 @@ class SplashScreen extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finishSplashScreen = () => {
|
||||||
|
Lbry.resolve({ urls: 'lbry://one' }).then(() => {
|
||||||
|
// Leave the splash screen
|
||||||
|
const {
|
||||||
|
authenticate,
|
||||||
|
balanceSubscribe,
|
||||||
|
blacklistedOutpointsSubscribe,
|
||||||
|
checkSubscriptionsInit,
|
||||||
|
updateBlockHeight,
|
||||||
|
navigation,
|
||||||
|
notify
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
balanceSubscribe();
|
||||||
|
blacklistedOutpointsSubscribe();
|
||||||
|
checkSubscriptionsInit();
|
||||||
|
updateBlockHeight();
|
||||||
|
setInterval(() => { updateBlockHeight(); }, BLOCK_HEIGHT_INTERVAL);
|
||||||
|
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
||||||
|
this.setState({ shouldAuthenticate: true });
|
||||||
|
authenticate(appVersion, Platform.OS);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_updateStatusCallback(status) {
|
_updateStatusCallback(status) {
|
||||||
const { deleteCompleteBlobs, fetchSubscriptions } = this.props;
|
const { deleteCompleteBlobs, fetchSubscriptions } = this.props;
|
||||||
const startupStatus = status.startup_status;
|
const startupStatus = status.startup_status;
|
||||||
|
@ -141,30 +166,28 @@ class SplashScreen extends React.PureComponent {
|
||||||
isRunning: true,
|
isRunning: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// fetch subscriptions, so that we can check for new content after resolve
|
AsyncStorage.getItem(Constants.KEY_FIRST_RUN_PASSWORD).then(passwordSet => {
|
||||||
Lbry.resolve({ urls: 'lbry://one' }).then(() => {
|
if ("true" === passwordSet) {
|
||||||
// Leave the splash screen
|
// enrypt the wallet
|
||||||
const {
|
NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(password => {
|
||||||
authenticate,
|
if (!password || password.trim().length === 0) {
|
||||||
balanceSubscribe,
|
this.finishSplashScreen();
|
||||||
blacklistedOutpointsSubscribe,
|
return;
|
||||||
checkSubscriptionsInit,
|
}
|
||||||
updateBlockHeight,
|
|
||||||
navigation,
|
|
||||||
notify
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
balanceSubscribe();
|
Lbry.account_encrypt({ new_password: password }).then((result) => {
|
||||||
blacklistedOutpointsSubscribe();
|
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_PASSWORD);
|
||||||
checkSubscriptionsInit();
|
this.finishSplashScreen();
|
||||||
updateBlockHeight();
|
});
|
||||||
setInterval(() => { updateBlockHeight(); }, BLOCK_HEIGHT_INTERVAL);
|
});
|
||||||
NativeModules.VersionInfo.getAppVersion().then(appVersion => {
|
|
||||||
this.setState({ shouldAuthenticate: true });
|
return;
|
||||||
authenticate(appVersion, Platform.OS);
|
}
|
||||||
});
|
|
||||||
|
this.finishSplashScreen();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,15 +46,30 @@ const firstRunStyle = StyleSheet.create({
|
||||||
marginBottom: 20,
|
marginBottom: 20,
|
||||||
textAlign: 'center'
|
textAlign: 'center'
|
||||||
},
|
},
|
||||||
|
passwordInput: {
|
||||||
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
fontSize: 24,
|
||||||
|
lineHeight: 24,
|
||||||
|
marginLeft: 32,
|
||||||
|
marginRight: 32,
|
||||||
|
marginBottom: 20,
|
||||||
|
textAlign: 'center'
|
||||||
|
},
|
||||||
leftButton: {
|
leftButton: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignSelf: 'flex-start',
|
alignSelf: 'flex-end',
|
||||||
|
paddingBottom: 16,
|
||||||
marginLeft: 32,
|
marginLeft: 32,
|
||||||
marginRight: 32
|
marginRight: 32
|
||||||
},
|
},
|
||||||
bottomContainer: {
|
bottomContainer: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
|
buttonRow: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
},
|
||||||
actionButton: {
|
actionButton: {
|
||||||
backgroundColor: Colors.White,
|
backgroundColor: Colors.White,
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
@ -71,7 +86,12 @@ const firstRunStyle = StyleSheet.create({
|
||||||
},
|
},
|
||||||
buttonText: {
|
buttonText: {
|
||||||
fontFamily: 'Inter-UI-Regular',
|
fontFamily: 'Inter-UI-Regular',
|
||||||
fontSize: 24,
|
fontSize: 20,
|
||||||
|
color: Colors.White
|
||||||
|
},
|
||||||
|
smallButtonText: {
|
||||||
|
fontFamily: 'Inter-UI-Regular',
|
||||||
|
fontSize: 14,
|
||||||
color: Colors.White
|
color: Colors.White
|
||||||
},
|
},
|
||||||
waiting: {
|
waiting: {
|
||||||
|
|
|
@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
|
||||||
|
|
||||||
# (list) Application requirements
|
# (list) Application requirements
|
||||||
# comma seperated e.g. requirements = sqlite3,kivy
|
# comma seperated e.g. requirements = sqlite3,kivy
|
||||||
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.32.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve
|
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.34.0#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve
|
||||||
|
|
||||||
# (str) Custom source folders for requirements
|
# (str) Custom source folders for requirements
|
||||||
# Sets custom source for any requirements with recipes
|
# Sets custom source for any requirements with recipes
|
||||||
|
|
|
@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
|
||||||
|
|
||||||
# (list) Application requirements
|
# (list) Application requirements
|
||||||
# comma seperated e.g. requirements = sqlite3,kivy
|
# comma seperated e.g. requirements = sqlite3,kivy
|
||||||
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.32.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve
|
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.34.0#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve
|
||||||
|
|
||||||
# (str) Custom source folders for requirements
|
# (str) Custom source folders for requirements
|
||||||
# Sets custom source for any requirements with recipes
|
# Sets custom source for any requirements with recipes
|
||||||
|
|
|
@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
|
||||||
|
|
||||||
# (list) Application requirements
|
# (list) Application requirements
|
||||||
# comma seperated e.g. requirements = sqlite3,kivy
|
# comma seperated e.g. requirements = sqlite3,kivy
|
||||||
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.32.4#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve
|
requirements = python3crystax, openssl, sqlite3, hostpython3crystax, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, miniupnpc==1.9, gmpy, appdirs==1.4.3, argparse==1.2.1, docopt, base58==1.0.0, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2, pyyaml, qrcode==5.2.2, requests, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, txJSON-RPC, zope.interface==4.3.3, protobuf==3.6.1, keyring==10.4.0, txupnp, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbry.git@v0.34.0#egg=lbrynet, git+https://github.com/lbryio/aioupnp.git#egg=aioupnp, asn1crypto, treq==17.8.0, funcsigs, mock, pbr, pyopenssl, twisted, idna, Automat, hyperlink, PyHamcrest, netifaces, cryptography, aiohttp==3.5.4, multidict==4.5.2, idna_ssl==1.1.0, typing_extensions==3.6.5, yarl, chardet==3.0.4, async_timeout==3.0.1, aiorpcX==0.9.0, git+https://github.com/lbryio/torba#egg=torba, coincurve
|
||||||
|
|
||||||
# (str) Custom source folders for requirements
|
# (str) Custom source folders for requirements
|
||||||
# Sets custom source for any requirements with recipes
|
# Sets custom source for any requirements with recipes
|
||||||
|
|
|
@ -124,6 +124,35 @@ public final class Utils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setSecureValue(String key, String value, Context context, KeyStore keyStore) {
|
||||||
|
try {
|
||||||
|
String encryptedValue = encrypt(value.getBytes(), context, keyStore);
|
||||||
|
SharedPreferences pref = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = pref.edit();
|
||||||
|
editor.putString(key, encryptedValue);
|
||||||
|
editor.commit();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.e(TAG, "utils - Could not set a secure value", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSecureValue(String key, Context context, KeyStore keyStore) {
|
||||||
|
try {
|
||||||
|
SharedPreferences pref = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
|
||||||
|
String encryptedValue = pref.getString(key, null);
|
||||||
|
if (encryptedValue == null || encryptedValue.trim().length() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] decoded = Base64.decode(encryptedValue, Base64.DEFAULT);
|
||||||
|
return new String(decrypt(decoded, context, keyStore), Charset.forName("UTF8"));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.e(TAG, "utils - Could not retrieve a secure value", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setPassword(String serviceName, String username, String password, Context context, KeyStore keyStore) {
|
public static void setPassword(String serviceName, String username, String password, Context context, KeyStore keyStore) {
|
||||||
try {
|
try {
|
||||||
String encryptedUsername = String.format("u_%s_%s", serviceName, encrypt(username.getBytes(), context, keyStore));
|
String encryptedUsername = String.format("u_%s_%s", serviceName, encrypt(username.getBytes(), context, keyStore));
|
||||||
|
|
|
@ -33,6 +33,7 @@ import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
|
||||||
import io.lbry.browser.MainActivity;
|
import io.lbry.browser.MainActivity;
|
||||||
import io.lbry.browser.R;
|
import io.lbry.browser.R;
|
||||||
|
@ -52,9 +53,16 @@ public class UtilityModule extends ReactContextBaseJavaModule {
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
|
private KeyStore keyStore;
|
||||||
|
|
||||||
public UtilityModule(ReactApplicationContext reactContext) {
|
public UtilityModule(ReactApplicationContext reactContext) {
|
||||||
super(reactContext);
|
super(reactContext);
|
||||||
this.context = reactContext;
|
this.context = reactContext;
|
||||||
|
try {
|
||||||
|
this.keyStore = Utils.initKeyStore(context);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// continue without keystore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -300,4 +308,21 @@ public class UtilityModule extends ReactContextBaseJavaModule {
|
||||||
(Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|
(Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void setSecureValue(String key, String value) {
|
||||||
|
if (keyStore != null) {
|
||||||
|
Utils.setSecureValue(key, value, context, keyStore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void getSecureValue(String key, Promise promise) {
|
||||||
|
if (keyStore == null) {
|
||||||
|
promise.reject("no keyStore found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.resolve(Utils.getSecureValue(key, context, keyStore));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue