diff --git a/app/package-lock.json b/app/package-lock.json
index 1704b4a..131dc09 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -895,9 +895,9 @@
}
},
"@react-navigation/core": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-3.4.1.tgz",
- "integrity": "sha512-slslu4FmjKQMO/EKGGqqGsfC6evQLdbJM2ROACcC2Xxf0+nPeZV5ND8HHukUZZucJRE6Bg/NI+zC1XSBYRjhnw==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-3.4.2.tgz",
+ "integrity": "sha512-7G+iDzLSTeOUU4vVZeRZKJ+Bd7ds7ZxYNqZcB8i0KlBeQEQfR74Ounfu/p0KIEq2RiNnaE3QT7WVP3C87sebzw==",
"requires": {
"hoist-non-react-statics": "^3.3.0",
"path-to-regexp": "^1.7.0",
@@ -916,12 +916,12 @@
}
},
"@react-navigation/native": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-3.4.1.tgz",
- "integrity": "sha512-pMAPQfvwC4DvhQfsrXKAf+FiU+A5XAh216v17rEePSFcbeOEt7cvewmWxCxydN/vFjJChFiPV+xnjJyJBdPLOg==",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-3.5.0.tgz",
+ "integrity": "sha512-TmGOis++ejEXG3sqNJhCSKqB0/qLu3FQgDtO959qpqif36R/diR8SQwJqeSdofoEiK3CepdhFlTCeHdS1/+MsQ==",
"requires": {
"hoist-non-react-statics": "^3.0.1",
- "react-native-safe-area-view": "^0.13.0",
+ "react-native-safe-area-view": "^0.14.1",
"react-native-screens": "^1.0.0 || ^1.0.0-alpha"
},
"dependencies": {
@@ -934,9 +934,9 @@
}
},
"react-native-safe-area-view": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.13.1.tgz",
- "integrity": "sha512-d/pu2866jApSwLtK/xWAvMXZkNTIQcFrjjbcTATBrmIfFNnu8TNFUcMRFpfJ+eOn5nmx7uGmDvs9B53Ft7JGpQ==",
+ "version": "0.14.4",
+ "resolved": "https://registry.npmjs.org/react-native-safe-area-view/-/react-native-safe-area-view-0.14.4.tgz",
+ "integrity": "sha512-ypDQVoAyNHBhMR1IGfadm8kskNzPg5czrDAzQEu5MXG9Ahoi5f1cL/rT2KO+R9f6xRjf6b1IjY53m0B0xHRd0A==",
"requires": {
"hoist-non-react-statics": "^2.3.1"
},
@@ -3350,11 +3350,6 @@
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
- "deep-diff": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz",
- "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ="
- },
"define-property": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@@ -4823,8 +4818,8 @@
}
},
"lbry-redux": {
- "version": "github:lbryio/lbry-redux#4b3769fc2dcc4c93771aa4c5dbb64d0e97f6375f",
- "from": "github:lbryio/lbry-redux",
+ "version": "github:lbryio/lbry-redux#5cff70a26b05b40f2693bbae6bf50100a6781e50",
+ "from": "github:lbryio/lbry-redux#purchase-uri-failures",
"requires": {
"proxy-polyfill": "0.1.6",
"reselect": "^3.0.0",
@@ -4876,6 +4871,11 @@
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz",
"integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q=="
},
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
+ },
"lodash.forin": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.forin/-/lodash.forin-4.4.0.tgz",
@@ -5467,9 +5467,9 @@
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"nan": {
- "version": "2.13.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
- "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
"optional": true
},
"nanomatch": {
@@ -6356,9 +6356,9 @@
}
},
"react-native-tab-view": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-1.3.4.tgz",
- "integrity": "sha512-iufNROTPr4+Z/IKijlp5faEANiDBWxhpgx9NSCg3esZ+HN5+UtFwB0xkn4XpNRqCvbzeBkgKMRJL3V6kr5NhWg==",
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-1.4.1.tgz",
+ "integrity": "sha512-Bke8KkDcDhvB/z0AS7MnQKMD2p6Kwfc1rSKlMOvg9CC5CnClQ2QEnhPSbwegKDYhUkBI92iH/BYy7hNSm5kbUQ==",
"requires": {
"prop-types": "^15.6.1"
}
@@ -6606,15 +6606,15 @@
}
},
"react-navigation": {
- "version": "3.9.1",
- "resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-3.9.1.tgz",
- "integrity": "sha512-4rUQXGT0yvLb9yX9NDuKdrXb/NcAPGUHDTlto8Fg4Tm23uuyBBSrDVStqC59rUM4JcoQnRqhenN2wXGvWE+WYA==",
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-3.11.0.tgz",
+ "integrity": "sha512-wlPcDtNiIdPeYxNQ/MN4arY5Xe9EphD2QVpRuvvuPWW+BamF3AJaIy060r3Yz59DODAoWllscabat/yqnih8Tg==",
"requires": {
"@react-navigation/core": "~3.4.1",
- "@react-navigation/native": "~3.4.0",
- "react-navigation-drawer": "1.2.1",
- "react-navigation-stack": "1.3.0",
- "react-navigation-tabs": "1.1.2"
+ "@react-navigation/native": "~3.5.0",
+ "react-navigation-drawer": "~1.2.1",
+ "react-navigation-stack": "~1.4.0",
+ "react-navigation-tabs": "~1.1.4"
}
},
"react-navigation-drawer": {
@@ -6634,19 +6634,19 @@
}
},
"react-navigation-stack": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/react-navigation-stack/-/react-navigation-stack-1.3.0.tgz",
- "integrity": "sha512-ouyD1GkRksJSGuvAuqrJnlJnZ5g2g/+/WB/MTa8BzjSBvyOgruD5TrmEkpViCOMr1R17C8D4Htln90H4D+NV3Q=="
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/react-navigation-stack/-/react-navigation-stack-1.4.0.tgz",
+ "integrity": "sha512-zEe9wCA0Ot8agarYb//0nSWYW1GM+1R0tY/nydUV0EizeJ27At0EklYVWvYEuYU6C48va6cu8OPL7QD/CcJACw=="
},
"react-navigation-tabs": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/react-navigation-tabs/-/react-navigation-tabs-1.1.2.tgz",
- "integrity": "sha512-D4fecSwZfvNh5WHTURmUVrNSgy3tiNfID0n5eKTOhCz4Sls4EM2l27UTX833ngxXhQ1FqRtBxzQZ+Dp1FWJ1pw==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/react-navigation-tabs/-/react-navigation-tabs-1.1.4.tgz",
+ "integrity": "sha512-py2hLCRxPwXOzmY1W9XcY1rWXxdK6RGW/aXh56G9gIf8cpHNDhy/bJV4e46/JrVcse3ybFaN0liT09/DM/NdwQ==",
"requires": {
"hoist-non-react-statics": "^2.5.0",
"prop-types": "^15.6.1",
"react-lifecycles-compat": "^3.0.4",
- "react-native-tab-view": "^1.3.4"
+ "react-native-tab-view": "^1.4.1"
}
},
"react-proxy": {
@@ -6735,14 +6735,6 @@
"symbol-observable": "^1.0.3"
}
},
- "redux-logger": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz",
- "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=",
- "requires": {
- "deep-diff": "^0.3.5"
- }
- },
"redux-persist": {
"version": "4.10.2",
"resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-4.10.2.tgz",
@@ -6771,10 +6763,11 @@
}
},
"redux-persist-transform-filter": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/redux-persist-transform-filter/-/redux-persist-transform-filter-0.0.10.tgz",
- "integrity": "sha1-mjsQbOiTnSy79SEsdH7Rd//xQoA=",
+ "version": "0.0.18",
+ "resolved": "https://registry.npmjs.org/redux-persist-transform-filter/-/redux-persist-transform-filter-0.0.18.tgz",
+ "integrity": "sha512-x9NxuHNDnK/THLLBqwP1tqw0yIcuxuVYXBssgGcmm5anxL0flbpLQGB5CbFYHWGG68VdQKr1vUneVnttxWJDtA==",
"requires": {
+ "lodash.clonedeep": "^4.5.0",
"lodash.forin": "^4.4.0",
"lodash.get": "^4.4.2",
"lodash.isempty": "^4.4.0",
diff --git a/app/package.json b/app/package.json
index 538c83e..d43dbaa 100644
--- a/app/package.json
+++ b/app/package.json
@@ -8,7 +8,7 @@
"dependencies": {
"base-64": "^0.1.0",
"@expo/vector-icons": "^8.1.0",
- "lbry-redux": "lbryio/lbry-redux",
+ "lbry-redux": "lbryio/lbry-redux#purchase-uri-failures",
"lbryinc": "lbryio/lbryinc#check-sync",
"lodash": ">=4.17.11",
"merge": ">=1.2.1",
@@ -25,16 +25,15 @@
"react-native-phone-input": "lbryio/react-native-phone-input",
"react-native-vector-icons": "^6.4.2",
"react-native-video": "lbryio/react-native-video#exoplayer-lbry-android",
- "react-navigation": "^3.6.1",
+ "react-navigation": "^3.11.0",
"react-navigation-redux-helpers": "^3.0.0",
"react-redux": "^5.0.3",
"redux": "^3.6.0",
- "redux-logger": "3.0.6",
- "redux-persist": "^4.8.0",
+ "redux-persist": "^4.10.2",
"redux-persist-filesystem-storage": "^1.3.2",
"redux-persist-transform-compress": "^4.2.0",
- "redux-persist-transform-filter": "0.0.10",
- "redux-thunk": "^2.2.0",
+ "redux-persist-transform-filter": "0.0.18",
+ "redux-thunk": "^2.3.0",
"rn-fetch-blob": "^0.10.15"
},
"devDependencies": {
diff --git a/app/src/component/AppNavigator.js b/app/src/component/AppNavigator.js
index bac1441..94ad294 100644
--- a/app/src/component/AppNavigator.js
+++ b/app/src/component/AppNavigator.js
@@ -34,7 +34,6 @@ import {
TextInput,
ToastAndroid
} from 'react-native';
-import { doDeleteCompleteBlobs } from 'redux/actions/file';
import { selectDrawerStack } from 'redux/selectors/drawer';
import { SETTINGS, doDismissToast, doToast, selectToast } from 'lbry-redux';
import {
@@ -116,7 +115,7 @@ const myLbryStack = createStackNavigator({
Downloads: {
screen: DownloadsPage,
navigationOptions: ({ navigation }) => ({
- title: 'Downloads',
+ title: 'Library',
header: null
})
}
@@ -179,7 +178,7 @@ const drawer = createDrawerNavigator({
drawerIcon: ({ tintColor }) =>
}},
MyLBRYStack: { screen: myLbryStack, navigationOptions: {
- title: 'Downloads', drawerIcon: ({ tintColor }) =>
+ title: 'Library', drawerIcon: ({ tintColor }) =>
}},
Settings: { screen: SettingsPage, navigationOptions: {
drawerLockMode: 'locked-closed',
@@ -225,7 +224,6 @@ const mainStackNavigator = new createStackNavigator({
});
-
export const AppNavigator = mainStackNavigator;
export const reactNavigationMiddleware = createReactNavigationReduxMiddleware(
state => state.nav,
@@ -299,9 +297,11 @@ class AppWithNavigationState extends React.Component {
ToastAndroid.show('Your email address was successfully verified.', ToastAndroid.LONG);
- // upon successful email verification, check wallet sync
+ // upon successful email verification, do wallet sync (if password has been set)
NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
- dispatch(doGetSync(walletPassword));
+ if (walletPassword && walletPassword.trim().length > 0) {
+ dispatch(doGetSync(walletPassword));
+ }
});
}
}
@@ -343,6 +343,7 @@ class AppWithNavigationState extends React.Component {
if (!emailVerifyErrorMessage) {
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
}
+
AsyncStorage.removeItem(Constants.KEY_SHOULD_VERIFY_EMAIL);
dispatch(doToast({ message }));
}
@@ -370,8 +371,6 @@ class AppWithNavigationState extends React.Component {
}
if (AppState.currentState && AppState.currentState.match(/active/)) {
- // Cleanup blobs for completed files upon app resume to save space
- dispatch(doDeleteCompleteBlobs());
if (backgroundPlayEnabled || NativeModules.BackgroundMedia) {
NativeModules.BackgroundMedia.hidePlaybackNotification();
}
diff --git a/app/src/component/categoryList/view.js b/app/src/component/categoryList/view.js
index c86100e..52733cf 100644
--- a/app/src/component/categoryList/view.js
+++ b/app/src/component/categoryList/view.js
@@ -13,6 +13,9 @@ class CategoryList extends React.PureComponent {
(
({
@@ -16,7 +17,7 @@ const select = (state, props) => ({
});
const perform = dispatch => ({
- purchaseUri: (uri, failureCallback) => dispatch(doPurchaseUri(uri, null, failureCallback)),
+ purchaseUri: (uri, costInfo, saveFile) => dispatch(doPurchaseUri(uri, costInfo, saveFile)),
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
});
diff --git a/app/src/component/fileDownloadButton/view.js b/app/src/component/fileDownloadButton/view.js
index c326016..b9797aa 100644
--- a/app/src/component/fileDownloadButton/view.js
+++ b/app/src/component/fileDownloadButton/view.js
@@ -1,7 +1,7 @@
import React from 'react';
import { NativeModules, Text, View, TouchableOpacity } from 'react-native';
import Button from '../button';
-import fileDownloadButtonStyle from '../../styles/fileDownloadButton';
+import fileDownloadButtonStyle from 'styles/fileDownloadButton';
class FileDownloadButton extends React.PureComponent {
componentDidMount() {
@@ -13,7 +13,7 @@ class FileDownloadButton extends React.PureComponent {
componentWillReceiveProps(nextProps) {
//this.checkAvailability(nextProps.uri);
- this.restartDownload(nextProps);
+ //this.restartDownload(nextProps);
}
restartDownload(props) {
@@ -46,7 +46,6 @@ class FileDownloadButton extends React.PureComponent {
style,
openFile,
onButtonLayout,
- onStartDownloadFailed
} = this.props;
if ((fileInfo && !fileInfo.stopped) || loading || downloading) {
@@ -60,7 +59,7 @@ class FileDownloadButton extends React.PureComponent {
{label}
);
- } else if (fileInfo === null && !downloading) {
+ } else if (!fileInfo && !downloading) {
if (!costInfo) {
return (
@@ -76,7 +75,10 @@ class FileDownloadButton extends React.PureComponent {
if (NativeModules.Firebase) {
NativeModules.Firebase.track('purchase_uri', { uri: uri });
}
- purchaseUri(uri, onStartDownloadFailed);
+ purchaseUri(uri, costInfo, !isPlayable);
+ if (NativeModules.UtilityModule) {
+ NativeModules.UtilityModule.checkDownloads();
+ }
if (isPlayable && onPlay) {
this.props.onPlay();
}
diff --git a/app/src/component/fileItem/view.js b/app/src/component/fileItem/view.js
index 2808574..5f3cc6c 100644
--- a/app/src/component/fileItem/view.js
+++ b/app/src/component/fileItem/view.js
@@ -78,8 +78,10 @@ class FileItem extends React.PureComponent {
isResolvingUri={isResolvingUri}
style={mediaStyle} />
- {(!compactView && fileInfo && fileInfo.completed) && }
- {(!compactView && (!fileInfo || !fileInfo.completed)) && }
+ {(!compactView && fileInfo && fileInfo.completed && fileInfo.download_path) &&
+ }
+ {(!compactView && (!fileInfo || !fileInfo.completed || !fileInfo.download_path)) &&
+ }
{!compactView &&
{title}
{isRewardContent && }
diff --git a/app/src/component/fileListItem/view.js b/app/src/component/fileListItem/view.js
index 953a0b5..53b00cf 100644
--- a/app/src/component/fileListItem/view.js
+++ b/app/src/component/fileListItem/view.js
@@ -87,7 +87,8 @@ class FileListItem extends React.PureComponent {
resizeMode="cover"
title={(title || name)}
thumbnail={thumbnail} />
- {fileInfo && fileInfo.completed && }
+ {(fileInfo && fileInfo.completed && fileInfo.download_path) &&
+ }
{featuredResult && {uri}}
@@ -106,7 +107,8 @@ class FileListItem extends React.PureComponent {
}} />}
- {fileInfo && {this.getStorageForFileInfo(fileInfo)}}
+ {(fileInfo && !isNaN(fileInfo.written_bytes) && fileInfo.written_bytes > 0) &&
+ {this.getStorageForFileInfo(fileInfo)}}
diff --git a/app/src/component/filePrice/view.js b/app/src/component/filePrice/view.js
index 59d9daa..79183be 100644
--- a/app/src/component/filePrice/view.js
+++ b/app/src/component/filePrice/view.js
@@ -108,7 +108,7 @@ class FilePrice extends React.PureComponent {
???
diff --git a/app/src/component/fileRewardsDriver/index.js b/app/src/component/fileRewardsDriver/index.js
new file mode 100644
index 0000000..ce46fb3
--- /dev/null
+++ b/app/src/component/fileRewardsDriver/index.js
@@ -0,0 +1,4 @@
+import { connect } from 'react-redux';
+import FileRewardsDriver from './view';
+
+export default connect()(FileRewardsDriver);
diff --git a/app/src/component/fileRewardsDriver/view.js b/app/src/component/fileRewardsDriver/view.js
new file mode 100644
index 0000000..4bd8297
--- /dev/null
+++ b/app/src/component/fileRewardsDriver/view.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import { Text, TouchableOpacity } from 'react-native';
+import Colors from 'styles/colors';
+import Icon from 'react-native-vector-icons/FontAwesome5';
+import filePageStyle from 'styles/filePage';
+
+class FileRewardsDriver extends React.PureComponent {
+ render() {
+ const { navigation } = this.props;
+
+ return (
+ navigation.navigate('Rewards')}>
+
+ Earn some credits to access this content.
+
+ );
+ }
+}
+
+export default FileRewardsDriver;
diff --git a/app/src/component/mediaPlayer/index.js b/app/src/component/mediaPlayer/index.js
index a6d058a..5074699 100644
--- a/app/src/component/mediaPlayer/index.js
+++ b/app/src/component/mediaPlayer/index.js
@@ -1,14 +1,18 @@
import { connect } from 'react-redux';
import { SETTINGS, savePosition } from 'lbry-redux';
-import { makeSelectClientSetting } from '../../redux/selectors/settings';
+import { makeSelectClientSetting } from 'redux/selectors/settings';
+import { doSetPlayerVisible } from 'redux/actions/drawer';
+import { selectIsPlayerVisible } from 'redux/selectors/drawer';
import MediaPlayer from './view';
const select = state => ({
backgroundPlayEnabled: makeSelectClientSetting(SETTINGS.BACKGROUND_PLAY_ENABLED)(state),
+ isPlayerVisible: selectIsPlayerVisible(state),
});
const perform = dispatch => ({
savePosition: (claimId, outpoint, position) => dispatch(savePosition(claimId, outpoint, position)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(true)),
});
export default connect(select, perform)(MediaPlayer);
diff --git a/app/src/component/mediaPlayer/view.js b/app/src/component/mediaPlayer/view.js
index 22d54f5..289b7d1 100644
--- a/app/src/component/mediaPlayer/view.js
+++ b/app/src/component/mediaPlayer/view.js
@@ -1,6 +1,8 @@
import React from 'react';
import { Lbry } from 'lbry-redux';
import {
+ AppState,
+ ActivityIndicator,
DeviceEventEmitter,
NativeModules,
PanResponder,
@@ -9,12 +11,15 @@ import {
ScrollView,
TouchableOpacity
} from 'react-native';
+import Colors from 'styles/colors';
import FastImage from 'react-native-fast-image'
import Video from 'react-native-video';
import Icon from 'react-native-vector-icons/FontAwesome5';
import FileItemMedia from 'component/fileItemMedia';
import mediaPlayerStyle from 'styles/mediaPlayer';
+const positionSaveInterval = 10
+
class MediaPlayer extends React.PureComponent {
static ControlsTimeout = 3000;
@@ -31,7 +36,9 @@ class MediaPlayer extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
- encodedFilePath: null,
+ buffering: false,
+ backgroundPlayEnabled: false,
+ autoPaused: false,
rate: 1,
volume: 1,
muted: false,
@@ -85,6 +92,7 @@ class MediaPlayer extends React.PureComponent {
const { position } = this.props;
if (!isNaN(parseFloat(position)) && position > 0) {
this.video.seek(position);
+ this.setState({ currentTime: position }, () => this.setSeekerPosition(this.calculateSeekerPosition()));
}
if (this.props.onMediaLoaded) {
@@ -93,10 +101,13 @@ class MediaPlayer extends React.PureComponent {
}
onProgress = (data) => {
- const { savePosition, fileInfo } = this.props;
+ const { savePosition, claim } = this.props;
-
- this.setState({ currentTime: data.currentTime }, () => savePosition(fileInfo.claim_id, fileInfo.outpoint, data.currentTime));
+ this.setState({ buffering: false, currentTime: data.currentTime });
+ if (data.currentTime > 0 && Math.floor(data.currentTime) % positionSaveInterval === 0) {
+ const { claim_id: claimId, txid, nout } = claim;
+ savePosition(claimId, `${txid}:${nout}`, data.currentTime);
+ }
if (!this.state.seeking) {
this.setSeekerPosition(this.calculateSeekerPosition());
@@ -140,6 +151,11 @@ class MediaPlayer extends React.PureComponent {
}
togglePlayerControls = () => {
+ const { setPlayerVisible, isPlayerVisible } = this.props;
+ if (!isPlayerVisible) {
+ setPlayerVisible();
+ }
+
if (this.state.areControlsVisible) {
this.manualHidePlayerControls();
} else {
@@ -149,7 +165,14 @@ class MediaPlayer extends React.PureComponent {
togglePlay = () => {
this.showPlayerControls();
- this.setState({ paused: !this.state.paused });
+ this.setState({ paused: !this.state.paused }, this.handlePausedState);
+ }
+
+ handlePausedState = () => {
+ if (!this.state.paused) {
+ // onProgress will automatically clear this, so it's fine
+ this.setState({ buffering: true });
+ }
}
toggleFullscreenMode = () => {
@@ -217,7 +240,7 @@ class MediaPlayer extends React.PureComponent {
onPanResponderRelease: (evt, gestureState) => {
const time = this.getCurrentTimeForSeekerPosition();
if (time >= this.state.duration) {
- this.setState({ paused: true });
+ this.setState({ paused: true }, this.handlePausedState);
this.onEnd();
} else {
this.seekTo(time);
@@ -251,18 +274,29 @@ class MediaPlayer extends React.PureComponent {
this.initSeeker();
}
+ componentWillReceiveProps(nextProps) {
+ const { isPlayerVisible } = nextProps;
+ if (!isPlayerVisible && !this.state.backgroundPlayEnabled) {
+ // force pause if the player is not visible and background play is not enabled
+ this.setState({ paused: true });
+ }
+ }
+
componentDidMount() {
- const { assignPlayer } = this.props;
+ const { assignPlayer, backgroundPlayEnabled } = this.props;
if (assignPlayer) {
assignPlayer(this);
}
+ this.setState({ backgroundPlayEnabled: !!backgroundPlayEnabled });
this.setSeekerPosition(this.calculateSeekerPosition());
+ AppState.addEventListener('change', this.handleAppStateChange);
DeviceEventEmitter.addListener('onBackgroundPlayPressed', this.play);
DeviceEventEmitter.addListener('onBackgroundPausePressed', this.pause);
}
componentWillUnmount() {
+ AppState.removeEventListener('change', this.handleAppStateChange);
DeviceEventEmitter.removeListener('onBackgroundPlayPressed', this.play);
DeviceEventEmitter.removeListener('onBackgroundPausePressed', this.pause);
this.clearControlsTimeout();
@@ -273,6 +307,26 @@ class MediaPlayer extends React.PureComponent {
}
}
+ handleAppStateChange = () => {
+ if (AppState.currentState && AppState.currentState.match(/inactive|background/)) {
+ if (!this.state.backgroundPlayEnabled && !this.state.paused) {
+ this.setState({ paused: true, autoPaused: true });
+ }
+ }
+
+ if (AppState.currentState && AppState.currentState.match(/active/)) {
+ if (!this.state.backgroundPlayEnabled && this.state.autoPaused) {
+ this.setState({ paused: false, autoPaused: false });
+ }
+ }
+ }
+
+ onBuffer = () => {
+ if (!this.state.paused) {
+ this.setState({ buffering: true }, () => this.manualHidePlayerControls());
+ }
+ }
+
play = () => {
this.setState({ paused: false }, this.updateBackgroundMediaNotification);
}
@@ -282,6 +336,7 @@ class MediaPlayer extends React.PureComponent {
}
updateBackgroundMediaNotification = () => {
+ this.handlePausedState();
const { backgroundPlayEnabled } = this.props;
if (backgroundPlayEnabled) {
if (NativeModules.BackgroundMedia && window.currentMediaInfo) {
@@ -292,13 +347,19 @@ class MediaPlayer extends React.PureComponent {
}
renderPlayerControls() {
+ const { onBackButtonPressed } = this.props;
+
if (this.state.areControlsVisible) {
return (
+
+
+
+
- {this.state.paused && }
- {!this.state.paused && }
+ {this.state.paused && }
+ {!this.state.paused && }
@@ -315,18 +376,6 @@ class MediaPlayer extends React.PureComponent {
return null;
}
- getEncodedDownloadPath = (fileInfo) => {
- if (this.state.encodedFilePath) {
- return this.state.encodedFilePath;
- }
-
- const { file_name: fileName } = fileInfo;
- const encodedFileName = encodeURIComponent(fileName).replace(/!/g, '%21');
- const encodedFilePath = fileInfo.download_path.replace(fileName, encodedFileName);
- this.setState({ encodedFilePath });
- return encodedFilePath;
- }
-
onSeekerTouchAreaPressed = (evt) => {
if (evt && evt.nativeEvent) {
const newSeekerPosition = evt.nativeEvent.locationX;
@@ -338,8 +387,14 @@ class MediaPlayer extends React.PureComponent {
}
}
+ onTrackingLayout = (evt) => {
+ this.trackingOffset = evt.nativeEvent.layout.x;
+ this.seekerWidth = evt.nativeEvent.layout.width;
+ this.setSeekerPosition(this.calculateSeekerPosition());
+ }
+
render() {
- const { backgroundPlayEnabled, fileInfo, thumbnail, onLayout, style } = this.props;
+ const { onLayout, source, style, thumbnail } = this.props;
const completedWidth = this.getCurrentTimePercentage() * this.seekerWidth;
const remainingWidth = this.seekerWidth - completedWidth;
let styles = [this.state.fullscreenMode ? mediaPlayerStyle.fullscreenContainer : mediaPlayerStyle.container];
@@ -356,17 +411,27 @@ class MediaPlayer extends React.PureComponent {
return (
-
{(!this.state.fullscreenMode || (this.state.fullscreenMode && this.state.areControlsVisible)) &&
- {
- this.trackingOffset = evt.nativeEvent.layout.x;
- this.seekerWidth = evt.nativeEvent.layout.width;
- }}>
+
}
+ {this.state.buffering &&
+
+
+ }
+
{this.state.areControlsVisible &&
{
const { navigation } = this.props;
- navigation.navigate({ routeName: 'Verification' })
+ navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: false }});
}
render() {
diff --git a/app/src/component/storageStatsCard/view.js b/app/src/component/storageStatsCard/view.js
index c21722e..0116aa0 100644
--- a/app/src/component/storageStatsCard/view.js
+++ b/app/src/component/storageStatsCard/view.js
@@ -66,6 +66,10 @@ class StorageStatsCard extends React.PureComponent {
}
render() {
+ if (this.state.totalBytes == 0) {
+ return null;
+ }
+
return (
diff --git a/app/src/component/uriBar/view.js b/app/src/component/uriBar/view.js
index 0e74511..1702f5f 100644
--- a/app/src/component/uriBar/view.js
+++ b/app/src/component/uriBar/view.js
@@ -9,7 +9,7 @@ import discoverStyle from 'styles/discover';
import uriBarStyle from 'styles/uriBar';
class UriBar extends React.PureComponent {
- static INPUT_TIMEOUT = 1000; // 1 second
+ static INPUT_TIMEOUT = 2500; // 2.5 seconds
textInput = null;
diff --git a/app/src/component/walletAddress/view.js b/app/src/component/walletAddress/view.js
index 17d991f..778f628 100644
--- a/app/src/component/walletAddress/view.js
+++ b/app/src/component/walletAddress/view.js
@@ -32,7 +32,7 @@ class WalletAddress extends React.PureComponent {
diff --git a/app/src/component/walletRewardsDriver/index.js b/app/src/component/walletRewardsDriver/index.js
index b328bf3..ec7c64a 100644
--- a/app/src/component/walletRewardsDriver/index.js
+++ b/app/src/component/walletRewardsDriver/index.js
@@ -1,6 +1,4 @@
import { connect } from 'react-redux';
import WalletRewardsDriver from './view';
-const select = state => ({});
-
-export default connect(select, null)(WalletRewardsDriver);
+export default connect()(WalletRewardsDriver);
diff --git a/app/src/component/walletSend/view.js b/app/src/component/walletSend/view.js
index cf74a58..4901659 100644
--- a/app/src/component/walletSend/view.js
+++ b/app/src/component/walletSend/view.js
@@ -108,6 +108,7 @@ class WalletSend extends React.PureComponent {
this.amountInput = ref}
onChangeText={value => this.setState({amount: value})}
keyboardType={'numeric'}
+ placeholder={'0'}
value={this.state.amount}
style={[walletStyle.input, walletStyle.amountInput]} />
LBC
diff --git a/app/src/component/walletSyncDriver/index.js b/app/src/component/walletSyncDriver/index.js
new file mode 100644
index 0000000..e2d8ef5
--- /dev/null
+++ b/app/src/component/walletSyncDriver/index.js
@@ -0,0 +1,10 @@
+import { connect } from 'react-redux';
+import { makeSelectClientSetting } from 'redux/selectors/settings';
+import Constants from 'constants';
+import WalletSyncDriver from './view';
+
+const select = state => ({
+ deviceWalletSynced: makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state),
+});
+
+export default connect(select, null)(WalletSyncDriver);
diff --git a/app/src/component/walletSyncDriver/view.js b/app/src/component/walletSyncDriver/view.js
new file mode 100644
index 0000000..9eab91a
--- /dev/null
+++ b/app/src/component/walletSyncDriver/view.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import { Text, View } from 'react-native';
+import Button from 'component/button';
+import Link from 'component/link';
+import walletStyle from 'styles/wallet';
+
+class WalletSyncDriver extends React.PureComponent {
+ onEnableSyncPressed = () => {
+ const { navigation } = this.props;
+ navigation.navigate({ routeName: 'Verification', key: 'verification', params: { syncFlow: true } });
+ }
+
+ render() {
+ const { deviceWalletSynced } = this.props;
+
+ return (
+
+ Wallet sync is {deviceWalletSynced ? 'on' : 'off'}.
+ {!deviceWalletSynced &&
+
+
+
+ }
+
+ );
+ }
+}
+
+export default WalletSyncDriver;
diff --git a/app/src/constants.js b/app/src/constants.js
index e427705..13a2cc8 100644
--- a/app/src/constants.js
+++ b/app/src/constants.js
@@ -25,12 +25,14 @@ const Constants = {
SETTING_RATING_REMINDER_DISABLED: "ratingReminderDisabled",
SETTING_BACKUP_DISMISSED: "backupDismissed",
SETTING_REWARDS_NOT_INTERESTED: "rewardsNotInterested",
+ SETTING_DEVICE_WALLET_SYNCED: "deviceWalletSynced",
ACTION_DELETE_COMPLETED_BLOBS: "DELETE_COMPLETED_BLOBS",
ACTION_FIRST_RUN_PAGE_CHANGED: "FIRST_RUN_PAGE_CHANGED",
ACTION_PUSH_DRAWER_STACK: "PUSH_DRAWER_STACK",
ACTION_POP_DRAWER_STACK: "POP_DRAWER_STACK",
+ ACTION_SET_PLAYER_VISIBLE: "SET_PLAYER_VISIBLE",
PAGE_REWARDS: "rewards",
PAGE_SETTINGS: "settings",
diff --git a/app/src/index.js b/app/src/index.js
index 3eb4cb3..65364cc 100644
--- a/app/src/index.js
+++ b/app/src/index.js
@@ -3,7 +3,6 @@ import { setJSExceptionHandler } from 'react-native-exception-handler';
import { Provider, connect } from 'react-redux';
import {
AppRegistry,
- AppState,
Text,
View,
NativeModules
@@ -12,6 +11,7 @@ import {
Lbry,
claimsReducer,
contentReducer,
+ fileReducer,
fileInfoReducer,
notificationsReducer,
searchReducer,
@@ -28,7 +28,6 @@ import {
userReducer
} from 'lbryinc';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
-import { createLogger } from 'redux-logger';
import { AppNavigator } from 'component/AppNavigator';
import { persistStore, autoRehydrate } from 'redux-persist';
import AppWithNavigationState, { reactNavigationMiddleware } from './component/AppNavigator';
@@ -44,8 +43,7 @@ import thunk from 'redux-thunk';
const globalExceptionHandler = (error, isFatal) => {
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", JSON.stringify(error));
}
};
setJSExceptionHandler(globalExceptionHandler, true);
@@ -93,6 +91,7 @@ const reducers = combineReducers({
content: contentReducer,
costInfo: costInfoReducer,
drawer: drawerReducer,
+ file: fileReducer,
fileInfo: fileInfoReducer,
homepage: homepageReducer,
nav: navigatorReducer,
@@ -107,7 +106,6 @@ const reducers = combineReducers({
});
const bulkThunk = createBulkThunkMiddleware();
-const logger = createLogger({ collapsed: true });
const middleware = [thunk, bulkThunk, reactNavigationMiddleware];
// eslint-disable-next-line no-underscore-dangle
diff --git a/app/src/page/about/index.js b/app/src/page/about/index.js
index 612a0f9..5775c00 100644
--- a/app/src/page/about/index.js
+++ b/app/src/page/about/index.js
@@ -1,7 +1,7 @@
import { connect } from 'react-redux';
import { doToast } from 'lbry-redux';
import { doFetchAccessToken, selectAccessToken, selectUserEmail } from 'lbryinc';
-import { doPushDrawerStack, doPopDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { selectDrawerStack } from 'redux/selectors/drawer';
import AboutPage from './view';
import Constants from 'constants';
@@ -17,6 +17,7 @@ const perform = dispatch => ({
notify: data => dispatch(doToast(data)),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_ABOUT)),
popDrawerStack: () => dispatch(doPopDrawerStack()),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
});
export default connect(select, perform)(AboutPage);
\ No newline at end of file
diff --git a/app/src/page/about/view.js b/app/src/page/about/view.js
index d662772..9357049 100644
--- a/app/src/page/about/view.js
+++ b/app/src/page/about/view.js
@@ -14,7 +14,10 @@ class AboutPage extends React.PureComponent {
};
componentDidMount() {
- this.props.pushDrawerStack();
+ const { pushDrawerStack, setPlayerVisible } = this.props;
+ pushDrawerStack();
+ setPlayerVisible();
+
if (NativeModules.VersionInfo) {
NativeModules.VersionInfo.getAppVersion().then(version => {
this.setState({appVersion: version});
diff --git a/app/src/page/channel/view.js b/app/src/page/channel/view.js
index 344311e..13344b5 100644
--- a/app/src/page/channel/view.js
+++ b/app/src/page/channel/view.js
@@ -178,12 +178,10 @@ class ChannelPage extends React.PureComponent {
}
}
-
return (
-
(
({
const perform = dispatch => ({
fileList: () => dispatch(doFileList()),
- pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_MY_LBRY))
+ pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_MY_LBRY)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
});
export default connect(select, perform)(DownloadsPage);
diff --git a/app/src/page/downloads/view.js b/app/src/page/downloads/view.js
index 2c4303d..15fe31d 100644
--- a/app/src/page/downloads/view.js
+++ b/app/src/page/downloads/view.js
@@ -25,8 +25,9 @@ class DownloadsPage extends React.PureComponent {
};
componentDidMount() {
- const { fileList, pushDrawerStack } = this.props;
+ const { fileList, pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
+ setPlayerVisible();
fileList();
}
@@ -39,7 +40,7 @@ class DownloadsPage extends React.PureComponent {
{!fetching && !hasDownloads &&
- You have not downloaded anything from LBRY yet.
+ You have not watched or downloaded any content from LBRY yet.
}
{fetching && !hasDownloads &&
diff --git a/app/src/page/file/index.js b/app/src/page/file/index.js
index 94037c5..42b7bc3 100644
--- a/app/src/page/file/index.js
+++ b/app/src/page/file/index.js
@@ -1,6 +1,8 @@
import { connect } from 'react-redux';
import {
doFetchFileInfo,
+ doPurchaseUri,
+ doDeletePurchasedUri,
doResolveUri,
doSendTip,
doToast,
@@ -11,9 +13,13 @@ import {
makeSelectContentPositionForUri,
makeSelectContentTypeForUri,
makeSelectMetadataForUri,
+ makeSelectStreamingUrlForUri,
makeSelectThumbnailForUri,
makeSelectTitleForUri,
selectBalance,
+ selectPurchasedUris,
+ selectFailedPurchaseUris,
+ selectPurchaseUriErrorMessage,
} from 'lbry-redux';
import {
doFetchCostInfoForUri,
@@ -21,7 +27,15 @@ import {
selectRewardContentClaimIds,
selectBlackListedOutpoints
} from 'lbryinc';
-import { doDeleteFile, doPurchaseUri, doStopDownloadingFile } from 'redux/actions/file';
+import {
+ doStartDownload,
+ doUpdateDownload,
+ doCompleteDownload,
+ doDeleteFile,
+ doStopDownloadingFile
+} from 'redux/actions/file';
+import { doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
+import { selectDrawerStack } from 'redux/selectors/drawer';
import FilePage from './view';
const select = (state, props) => {
@@ -30,6 +44,7 @@ const select = (state, props) => {
balance: selectBalance(state),
blackListedOutpoints: selectBlackListedOutpoints(state),
claim: makeSelectClaimForUri(selectProps.uri)(state),
+ drawerStack: selectDrawerStack(state),
isResolvingUri: makeSelectIsUriResolving(selectProps.uri)(state),
contentType: makeSelectContentTypeForUri(selectProps.uri)(state),
costInfo: makeSelectCostInfoForUri(selectProps.uri)(state),
@@ -40,6 +55,10 @@ const select = (state, props) => {
rewardedContentClaimIds: selectRewardContentClaimIds(state, selectProps),
channelUri: makeSelectChannelForClaimUri(selectProps.uri, true)(state),
position: makeSelectContentPositionForUri(selectProps.uri)(state),
+ purchasedUris: selectPurchasedUris(state),
+ failedPurchaseUris: selectFailedPurchaseUris(state),
+ purchaseUriErrorMessage: selectPurchaseUriErrorMessage(state),
+ streamingUrl: makeSelectStreamingUrlForUri(selectProps.uri)(state),
thumbnail: makeSelectThumbnailForUri(selectProps.uri)(state),
title: makeSelectTitleForUri(selectProps.uri)(state),
};
@@ -52,10 +71,16 @@ const perform = dispatch => ({
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
notify: data => dispatch(doToast(data)),
- purchaseUri: (uri, failureCallback) => dispatch(doPurchaseUri(uri, null, failureCallback)),
+ popDrawerStack: () => dispatch(doPopDrawerStack()),
+ purchaseUri: (uri, costInfo, saveFile) => dispatch(doPurchaseUri(uri, costInfo, saveFile)),
+ deletePurchasedUri: uri => dispatch(doDeletePurchasedUri(uri)),
resolveUri: uri => dispatch(doResolveUri(uri)),
sendTip: (amount, claimId, uri, successCallback, errorCallback) => dispatch(doSendTip(amount, claimId, uri, successCallback, errorCallback)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(true)),
stopDownload: (uri, fileInfo) => dispatch(doStopDownloadingFile(uri, fileInfo)),
+ startDownload: (uri, outpoint, fileInfo) => dispatch(doStartDownload(uri, outpoint, fileInfo)),
+ updateDownload: (uri, outpoint, fileInfo, progress) => dispatch(doUpdateDownload(uri, outpoint, fileInfo, progress)),
+ completeDownload: (uri, outpoint, fileInfo) => dispatch(doCompleteDownload(uri, outpoint, fileInfo)),
});
export default connect(select, perform)(FilePage);
diff --git a/app/src/page/file/view.js b/app/src/page/file/view.js
index 5dcce73..c709922 100644
--- a/app/src/page/file/view.js
+++ b/app/src/page/file/view.js
@@ -4,6 +4,7 @@ import { Lbryio } from 'lbryinc';
import {
ActivityIndicator,
Alert,
+ DeviceEventEmitter,
Dimensions,
NativeModules,
ScrollView,
@@ -11,11 +12,13 @@ import {
StyleSheet,
Text,
TextInput,
+ TouchableOpacity,
TouchableWithoutFeedback,
View,
WebView
} from 'react-native';
-import { navigateToUri } from 'utils/helper';
+import { NavigationEvents } from 'react-navigation';
+import { navigateBack, navigateToUri } from 'utils/helper';
import Icon from 'react-native-vector-icons/FontAwesome5';
import ImageViewer from 'react-native-image-zoom-viewer';
import Button from 'component/button';
@@ -33,6 +36,7 @@ import SubscribeButton from 'component/subscribeButton';
import SubscribeNotificationButton from 'component/subscribeNotificationButton';
import UriBar from 'component/uriBar';
import Video from 'react-native-video';
+import FileRewardsDriver from 'component/fileRewardsDriver';
import filePageStyle from 'styles/filePage';
import uriBarStyle from 'styles/uriBar';
@@ -72,14 +76,19 @@ class FilePage extends React.PureComponent {
tipAmount: null,
uri: null,
uriVars: null,
- stopDownloadConfirmed: false
+ stopDownloadConfirmed: false,
+ streamingMode: false
};
}
componentDidMount() {
StatusBar.setHidden(false);
- const { isResolvingUri, resolveUri, navigation } = this.props;
+ DeviceEventEmitter.addListener('onDownloadStarted', this.handleDownloadStarted);
+ DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated);
+ DeviceEventEmitter.addListener('onDownloadCompleted', this.handleDownloadCompleted);
+
+ const { fileInfo, isResolvingUri, resolveUri, navigation } = this.props;
const { uri, uriVars } = navigation.state.params;
this.setState({ uri, uriVars });
@@ -96,8 +105,46 @@ class FilePage extends React.PureComponent {
}
}
+ componentWillReceiveProps(nextProps) {
+ const {
+ claim,
+ failedPurchaseUris: prevFailedPurchaseUris,
+ purchasedUris: prevPurchasedUris,
+ navigation,
+ contentType,
+ notify
+ } = this.props;
+ const { uri } = navigation.state.params;
+ const { failedPurchaseUris, fileInfo, purchasedUris, purchaseUriErrorMessage, streamingUrl } = nextProps;
+
+ if (failedPurchaseUris.includes(uri) && !purchasedUris.includes(uri)) {
+ if (purchaseUriErrorMessage && purchaseUriErrorMessage.trim().length > 0) {
+ notify({ message: purchaseUriErrorMessage, isError: true });
+ }
+ this.setState({ downloadPressed: false, fileViewLogged: false, mediaLoaded: false });
+ }
+
+ const mediaType = Lbry.getMediaType(contentType);
+ const isPlayable = mediaType === 'video' || mediaType === 'audio';
+ if (prevPurchasedUris.length != purchasedUris.length && NativeModules.UtilityModule) {
+ if (purchasedUris.includes(uri)) {
+ const { nout, txid } = claim;
+ const outpoint = `${txid}:${nout}`;
+ NativeModules.UtilityModule.queueDownload(outpoint);
+ }
+ NativeModules.UtilityModule.checkDownloads();
+ }
+
+ if (!this.state.streamingMode && isPlayable) {
+ if (streamingUrl) {
+ this.setState({ streamingMode: true, currentStreamUrl: streamingUrl });
+ } else if (fileInfo && fileInfo.streaming_url) {
+ this.setState({ streamingMode: true, currentStreamUrl: fileInfo.streaming_url });
+ }
+ }
+ }
+
componentDidUpdate(prevProps) {
- this.fetchFileInfo(this.props);
const { claim, contentType, fileInfo, isResolvingUri, resolveUri, navigation } = this.props;
const { uri } = this.state;
if (!isResolvingUri && claim === undefined && uri) {
@@ -116,12 +163,11 @@ class FilePage extends React.PureComponent {
const prevFileInfo = prevProps.fileInfo;
if (!prevFileInfo && fileInfo) {
- // started downloading
const mediaType = Lbry.getMediaType(contentType);
const isPlayable = mediaType === 'video' || mediaType === 'audio';
// If the media is playable, file/view will be done in onPlaybackStarted
if (!isPlayable && !this.state.fileViewLogged) {
- this.logFileView(uri, fileInfo);
+ this.logFileView(uri, claim);
}
}
}
@@ -161,7 +207,7 @@ class FilePage extends React.PureComponent {
}
onDeletePressed = () => {
- const { deleteFile, fileInfo } = this.props;
+ const { claim, deleteFile, deletePurchasedUri, fileInfo, navigation } = this.props;
Alert.alert(
'Delete file',
@@ -169,7 +215,12 @@ class FilePage extends React.PureComponent {
[
{ text: 'No' },
{ text: 'Yes', onPress: () => {
- deleteFile(fileInfo.outpoint, true);
+ const { uri } = navigation.state.params;
+ deleteFile(`${claim.txid}:${claim.nout}`, true);
+ deletePurchasedUri(uri);
+ if (NativeModules.UtilityModule) {
+ NativeModules.UtilityModule.deleteDownload(uri);
+ }
this.setState({
downloadPressed: false,
fileViewLogged: false,
@@ -183,7 +234,7 @@ class FilePage extends React.PureComponent {
}
onStopDownloadPressed = () => {
- const { fileInfo, navigation, notify, stopDownload } = this.props;
+ const { deletePurchasedUri, fileInfo, navigation, notify, stopDownload } = this.props;
Alert.alert(
'Stop download',
@@ -191,7 +242,12 @@ class FilePage extends React.PureComponent {
[
{ text: 'No' },
{ text: 'Yes', onPress: () => {
- stopDownload(navigation.state.params.uri, fileInfo);
+ const { uri } = navigation.state.params;
+ stopDownload(uri, fileInfo);
+ deletePurchasedUri(uri);
+ if (NativeModules.UtilityModule) {
+ NativeModules.UtilityModule.deleteDownload(uri);
+ }
this.setState({
downloadPressed: false,
fileViewLogged: false,
@@ -224,6 +280,28 @@ class FilePage extends React.PureComponent {
window.currentMediaInfo = null;
}
window.player = null;
+
+ DeviceEventEmitter.removeListener('onDownloadStarted', this.handleDownloadStarted);
+ DeviceEventEmitter.removeListener('onDownloadUpdated', this.handleDownloadUpdated);
+ DeviceEventEmitter.removeListener('onDownloadCompleted', this.handleDownloadCompleted);
+ }
+
+ handleDownloadStarted = (evt) => {
+ const { startDownload } = this.props;
+ const { uri, outpoint, fileInfo } = evt;
+ startDownload(uri, outpoint, fileInfo);
+ }
+
+ handleDownloadUpdated = (evt) => {
+ const { updateDownload } = this.props;
+ const { uri, outpoint, fileInfo, progress } = evt;
+ updateDownload(uri, outpoint, fileInfo, progress);
+ }
+
+ handleDownloadCompleted = (evt) => {
+ const { completeDownload } = this.props;
+ const { uri, outpoint, fileInfo } = evt;
+ completeDownload(uri, outpoint, fileInfo);
}
localUriForFileInfo = (fileInfo) => {
@@ -233,6 +311,33 @@ class FilePage extends React.PureComponent {
return 'file:///' + fileInfo.download_path;
}
+ playerUriForFileInfo = (fileInfo) => {
+ const { streamingUrl } = this.props;
+ if (streamingUrl) {
+ return streamingUrl;
+ }
+ if (this.state.currentStreamUrl) {
+ return this.state.currentStreamUrl;
+ }
+
+ if (fileInfo && fileInfo.download_path) {
+ return this.getEncodedDownloadPath(fileInfo);
+ }
+
+ return null;
+ }
+
+ getEncodedDownloadPath = (fileInfo) => {
+ if (this.state.encodedFilePath) {
+ return this.state.encodedFilePath;
+ }
+
+ const { file_name: fileName } = fileInfo;
+ const encodedFileName = encodeURIComponent(fileName).replace(/!/g, '%21');
+ const encodedFilePath = fileInfo.download_path.replace(fileName, encodedFileName);
+ return encodedFilePath;
+ }
+
linkify = (text) => {
let linkifiedContent = [];
let lines = text.split(/\n/g);
@@ -321,8 +426,13 @@ class FilePage extends React.PureComponent {
}
}
- logFileView = (uri, fileInfo, timeToStart) => {
- const { outpoint, claim_id: claimId } = fileInfo;
+ logFileView = (uri, claim, timeToStart) => {
+ if (!claim) {
+ return;
+ }
+
+ const { nout, claim_id: claimId, txid } = claim;
+ const outpoint = `${txid}:${nout}`;
const params = {
uri,
outpoint,
@@ -351,23 +461,30 @@ class FilePage extends React.PureComponent {
sendTip(tipAmount, claim.claim_id, uri, () => { this.setState({ tipAmount: 0, showTipView: false }) });
}
- startDownloadFailed = () => {
- this.startTime = null;
- setTimeout(() => {
- this.setState({ downloadPressed: false, fileViewLogged: false, mediaLoaded: false });
- }, 500);
- }
-
renderTags = (tags) => {
return tags.map((tag, i) => (
{tag}
));
}
+ onFileDownloadButtonPlayed = () => {
+ const { setPlayerVisible } = this.props;
+ this.startTime = Date.now();
+ this.setState({ downloadPressed: true, autoPlayMedia: true, stopDownloadConfirmed: false });
+ setPlayerVisible();
+ }
+
+ onBackButtonPressed = () => {
+ const { navigation, drawerStack, popDrawerStack } = this.props;
+ navigateBack(navigation, drawerStack, popDrawerStack);
+ }
+
render() {
const {
+ balance,
claim,
channelUri,
+ costInfo,
fileInfo,
metadata,
contentType,
@@ -442,7 +559,7 @@ class FilePage extends React.PureComponent {
const mediaType = Lbry.getMediaType(contentType);
const isPlayable = mediaType === 'video' || mediaType === 'audio';
const { height, channel_name: channelName, value } = claim;
- const showActions = !this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView;
+ const showActions = !this.state.streamingMode && !this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView;
const showFileActions = (completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes));
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
const canSendTip = this.state.tipAmount > 0;
@@ -454,8 +571,8 @@ class FilePage extends React.PureComponent {
const playerBgStyle = [filePageStyle.playerBackground, filePageStyle.containedPlayerBackground];
const fsPlayerBgStyle = [filePageStyle.playerBackground, filePageStyle.fullscreenPlayerBackground];
// at least 2MB (or the full download) before media can be loaded
- const canLoadMedia = fileInfo &&
- (fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes == fileInfo.total_bytes); // 2MB = 1024*1024*2
+ const canLoadMedia = (this.state.streamingMode) || (fileInfo &&
+ (fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes == fileInfo.total_bytes)); // 2MB = 1024*1024*2
const isViewable = (mediaType === 'image' || mediaType === 'text');
const isWebViewable = mediaType === 'text';
const canOpen = isViewable && completed;
@@ -485,7 +602,10 @@ class FilePage extends React.PureComponent {
if (fileInfo && !this.state.autoDownloadStarted && this.state.uriVars && 'true' === this.state.uriVars.download) {
this.setState({ autoDownloadStarted: true }, () => {
- purchaseUri(uri, this.startDownloadFailed);
+ purchaseUri(uri, costInfo, !isPlayable);
+ if (NativeModules.UtilityModule) {
+ NativeModules.UtilityModule.checkDownloads();
+ }
});
}
@@ -512,22 +632,23 @@ class FilePage extends React.PureComponent {
}
{((!this.state.downloadButtonShown || this.state.downloadPressed) && !this.state.mediaLoaded) &&
}
- {((isPlayable && !completed && !canLoadMedia) || !completed || canOpen) && (!this.state.downloadPressed) &&
+ {((isPlayable && !completed && !canLoadMedia) || canOpen || (!completed && !this.state.streamingMode)) &&
+ (!this.state.downloadPressed) &&
{
- this.startTime = Date.now();
- this.setState({ downloadPressed: true, autoPlayMedia: true, stopDownloadConfirmed: false });
- }}
+ onPlay={this.onFileDownloadButtonPlayed}
onView={() => this.setState({ downloadPressed: true })}
- onButtonLayout={() => this.setState({ downloadButtonShown: true })}
- onStartDownloadFailed={this.startDownloadFailed} />}
+ onButtonLayout={() => this.setState({ downloadButtonShown: true })} />}
{!fileInfo && }
+
+
+
+
- {(canLoadMedia && fileInfo && isPlayable) &&
+ {(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) &&
{ this.playerBackground = ref; }}
onLayout={(evt) => {
@@ -535,12 +656,14 @@ class FilePage extends React.PureComponent {
this.setState({ playerBgHeight: evt.nativeEvent.layout.height });
}
}} />}
- {(canLoadMedia && fileInfo && isPlayable && this.state.fullscreenMode) && }
- {(canLoadMedia && fileInfo && isPlayable) &&
+ {((this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) && this.state.fullscreenMode) &&
+ }
+ {(this.state.streamingMode || (canLoadMedia && fileInfo && isPlayable)) &&
{ this.player = ref; }}
uri={uri}
+ source={this.playerUriForFileInfo(fileInfo)}
style={playerStyle}
autoPlay={autoplay || this.state.autoPlayMedia}
onFullscreenToggled={this.handleFullscreenToggle}
@@ -550,6 +673,7 @@ class FilePage extends React.PureComponent {
}
}}
onMediaLoaded={() => this.onMediaLoaded(channelName, title, uri)}
+ onBackButtonPressed={this.onBackButtonPressed}
onPlaybackStarted={this.onPlaybackStarted}
onPlaybackFinished={this.onPlaybackFinished}
thumbnail={thumbnail}
@@ -580,13 +704,15 @@ class FilePage extends React.PureComponent {
style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}
contentContainerstyle={showActions ? null : filePageStyle.scrollContent}
ref={(ref) => { this.scrollView = ref; }}>
-
- {title}
- this.setState({ showDescription: !this.state.showDescription })}>
-
-
-
+ this.setState({ showDescription: !this.state.showDescription })}>
+
+ {title}
+
+
+
+
+
{channelName &&
@@ -630,6 +756,7 @@ class FilePage extends React.PureComponent {
this.tipAmountInput = ref}
onChangeText={value => this.setState({tipAmount: value})}
keyboardType={'numeric'}
+ placeholder={'0'}
value={this.state.tipAmount}
style={[filePageStyle.input, filePageStyle.tipAmountInput]} />
LBC
@@ -654,12 +781,15 @@ class FilePage extends React.PureComponent {
)}
)}
+ {(costInfo && parseFloat(costInfo.cost) > balance) && }
+
)}
- {!this.state.fullscreenMode && }
+ {(!this.state.fullscreenMode && !this.state.showImageViewer && !this.state.showWebView) &&
+ }
);
}
diff --git a/app/src/page/firstRun/index.js b/app/src/page/firstRun/index.js
index a134bd4..924ab56 100644
--- a/app/src/page/firstRun/index.js
+++ b/app/src/page/firstRun/index.js
@@ -3,6 +3,8 @@ import { doToast } from 'lbry-redux';
import {
doAuthenticate,
doCheckSync,
+ doGetSync,
+ doSyncApply,
doUserEmailNew,
doUserResendVerificationEmail,
selectAuthToken,
@@ -11,9 +13,14 @@ import {
selectEmailToVerify,
selectAuthenticationIsPending,
selectHasSyncedWallet,
- selectIsRetrievingSync,
+ selectGetSyncIsPending,
+ selectSyncApplyIsPending,
+ selectSyncApplyErrorMessage,
+ selectSyncData,
+ selectSyncHash,
selectUser,
} from 'lbryinc';
+import { doSetClientSetting } from 'redux/actions/settings';
import FirstRun from './view';
const select = (state) => ({
@@ -23,13 +30,20 @@ const select = (state) => ({
emailNewErrorMessage: selectEmailNewErrorMessage(state),
emailNewPending: selectEmailNewIsPending(state),
hasSyncedWallet: selectHasSyncedWallet(state),
- isRetrievingSync: selectIsRetrievingSync(state),
+ getSyncIsPending: selectGetSyncIsPending(state),
+ syncApplyErrorMessage: selectSyncApplyErrorMessage(state),
+ syncApplyIsPending: selectSyncApplyIsPending(state),
+ syncHash: selectSyncHash(state),
+ syncData: selectSyncData(state),
user: selectUser(state),
});
const perform = dispatch => ({
addUserEmail: email => dispatch(doUserEmailNew(email)),
authenticate: (appVersion, os) => dispatch(doAuthenticate(appVersion, os)),
+ setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
+ syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)),
+ getSync: password => dispatch(doGetSync(password)),
checkSync: () => dispatch(doCheckSync()),
notify: data => dispatch(doToast(data)),
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email))
diff --git a/app/src/page/firstRun/internal/email-collect-page.js b/app/src/page/firstRun/internal/email-collect-page.js
index 73ceecf..15580ce 100644
--- a/app/src/page/firstRun/internal/email-collect-page.js
+++ b/app/src/page/firstRun/internal/email-collect-page.js
@@ -96,7 +96,7 @@ class EmailCollectPage extends React.PureComponent {
);
} else if (!authToken || authenticating || this.state.verifying) {
content = (
-
+
Please wait while we get some things ready...
diff --git a/app/src/page/firstRun/internal/wallet-page.js b/app/src/page/firstRun/internal/wallet-page.js
index 13657ea..59bb05b 100644
--- a/app/src/page/firstRun/internal/wallet-page.js
+++ b/app/src/page/firstRun/internal/wallet-page.js
@@ -16,6 +16,8 @@ import Colors from 'styles/colors';
import Constants from 'constants';
import firstRunStyle from 'styles/firstRun';
+const firstRunMargins = 80;
+
class WalletPage extends React.PureComponent {
state = {
password: null,
@@ -27,20 +29,20 @@ class WalletPage extends React.PureComponent {
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 });
+ this.setState({ walletReady: true }, () => {
+ this.props.checkSync();
+ setTimeout(() => this.setState({ hasCheckedSync: true}), 1000);
+ });
return;
}
setTimeout(this.checkWalletReady, 1000);
}).catch((e) => {
- console.log(e);
setTimeout(this.checkWalletReady, 1000);
});
}
@@ -52,23 +54,24 @@ class WalletPage extends React.PureComponent {
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, isRetrievingSync, hasSyncedWallet } = this.props;
+ const { onPasswordChanged, onWalletViewLayout, getSyncIsPending, hasSyncedWallet, syncApplyIsPending } = this.props;
let content;
- if (!this.state.walletReady || !this.state.hasCheckedSync || isRetrievingSync) {
+ if (!this.state.walletReady || !this.state.hasCheckedSync || getSyncIsPending) {
content = (
-
+
- Retrieving your account information...
+ Retrieving your account information...
+
+ );
+ } else if (syncApplyIsPending) {
+ content = (
+
+
+ Validating password...
);
} else {
@@ -96,10 +99,11 @@ class WalletPage extends React.PureComponent {
}
}}
/>
- {(this.state.password && this.state.password.trim().length) > 0 &&
+
+ {(!hasSyncedWallet && this.state.password && this.state.password.trim().length) > 0 &&
}
diff --git a/app/src/page/firstRun/view.js b/app/src/page/firstRun/view.js
index cbb334e..b5a0e62 100644
--- a/app/src/page/firstRun/view.js
+++ b/app/src/page/firstRun/view.js
@@ -38,7 +38,8 @@ class FirstRunScreen extends React.PureComponent {
isEmailVerified: false,
skipAccountConfirmed: false,
showBottomContainer: true,
- walletPassword: null
+ walletPassword: null,
+ syncApplyStarted: false
};
componentDidMount() {
@@ -65,12 +66,12 @@ class FirstRunScreen extends React.PureComponent {
}
componentWillReceiveProps(nextProps) {
- const { emailNewErrorMessage, emailNewPending, user } = nextProps;
- const { notify } = this.props;
+ const { emailNewErrorMessage, emailNewPending, syncApplyErrorMessage, syncApplyIsPending, user } = nextProps;
+ const { notify, isApplyingSync, setClientSetting } = this.props;
if (this.state.emailSubmitted && !emailNewPending) {
this.setState({ emailSubmitted: false });
- if (emailNewErrorMessage) {
+ if (emailNewErrorMessage && emailNewErrorMessage.trim().length > 0) {
notify ({ message: String(emailNewErrorMessage), isError: true });
} else {
// Request successful. Navigate to email verify page.
@@ -78,6 +79,21 @@ class FirstRunScreen extends React.PureComponent {
}
}
+ if (this.state.syncApplyStarted && !syncApplyIsPending) {
+ this.setState({ syncApplyStarted: false });
+ if (syncApplyErrorMessage && syncApplyErrorMessage.trim().length > 0) {
+ notify({ message: syncApplyErrorMessage, isError: true });
+ this.setState({ showBottomContainer: true });
+ } else {
+ // password successfully verified
+ if (NativeModules.UtilityModule) {
+ NativeModules.UtilityModule.setSecureValue(Constants.KEY_FIRST_RUN_PASSWORD, this.state.walletPassword);
+ }
+ setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
+ this.closeFinalPage();
+ }
+ }
+
this.checkVerificationStatus(user);
}
@@ -121,15 +137,27 @@ class FirstRunScreen extends React.PureComponent {
}
}
+ checkWalletPassword = () => {
+ const { syncApply, syncHash, syncData } = this.props;
+ this.setState({ syncApplyStarted: true, showBottomContainer: false }, () => {
+ syncApply(syncHash, syncData, this.state.walletPassword);
+ });
+ }
+
handleContinuePressed = () => {
- const { notify, user } = this.props;
+ const { notify, user, hasSyncedWallet } = this.props;
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
if (Constants.FIRST_RUN_PAGE_WALLET === this.state.currentPage) {
if (!this.state.walletPassword || this.state.walletPassword.trim().length === 0) {
return notify({ message: 'Please enter a wallet password' });
}
- this.closeFinalPage();
+ // do apply sync to check if the password is valid
+ if (hasSyncedWallet) {
+ this.checkWalletPassword();
+ } else {
+ this.setFreshPassword();
+ }
return;
}
@@ -167,20 +195,25 @@ class FirstRunScreen extends React.PureComponent {
});
}
+ checkBottomContainer = (pageName) => {
+ if (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === pageName || Constants.FIRST_RUN_PAGE_WALLET === pageName) {
+ // do not show the buttons (because we're waiting to get things ready)
+ this.setState({ showBottomContainer: false });
+ }
+ }
+
showNextPage = () => {
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
const nextPage = FirstRunScreen.pages[pageIndex + 1];
this.setState({ currentPage: nextPage });
- if (nextPage === Constants.FIRST_RUN_PAGE_EMAIL_COLLECT) {
- // do not show the buttons (because we're waiting to get things ready)
- this.setState({ showBottomContainer: false });
- }
+ this.checkBottomContainer(nextPage);
}
showPage(pageName) {
const pageIndex = FirstRunScreen.pages.indexOf(pageName);
if (pageIndex > -1) {
this.setState({ currentPage: pageName });
+ this.checkBottomContainer(pageName);
}
}
@@ -222,6 +255,21 @@ class FirstRunScreen extends React.PureComponent {
this.setState({ skipAccountConfirmed: checked });
}
+ setFreshPassword = () => {
+ const { getSync, setClientSetting } = this.props;
+ if (NativeModules.UtilityModule) {
+ NativeModules.UtilityModule.setSecureValue(Constants.KEY_FIRST_RUN_PASSWORD, this.state.walletPassword);
+ Lbry.account_encrypt({ new_password: this.state.walletPassword }).then(() => {
+ Lbry.account_unlock({ password: this.state.walletPassword }).then(() => {
+ // fresh account, new password set
+ getSync(this.state.walletPassword);
+ setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
+ this.closeFinalPage();
+ });
+ });
+ }
+ }
+
render() {
const {
authenticate,
@@ -233,7 +281,8 @@ class FirstRunScreen extends React.PureComponent {
emailToVerify,
notify,
hasSyncedWallet,
- isRetrievingSync,
+ getSyncIsPending,
+ syncApplyIsPending,
resendVerificationEmail,
user
} = this.props;
@@ -267,7 +316,8 @@ class FirstRunScreen extends React.PureComponent {
page = ();
break;
@@ -293,7 +343,7 @@ class FirstRunScreen extends React.PureComponent {
Constants.FIRST_RUN_PAGE_EMAIL_VERIFY === this.state.currentPage) &&
- « {Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage ? 'Setup account' : 'Change Email'}
+ « {Constants.FIRST_RUN_PAGE_SKIP_ACCOUNT === this.state.currentPage ? 'Setup account' : 'Change email'}
}
{!emailNewPending && (Constants.FIRST_RUN_PAGE_EMAIL_COLLECT === this.state.currentPage) &&
diff --git a/app/src/page/rewards/index.js b/app/src/page/rewards/index.js
index 3f5d984..3cae52d 100644
--- a/app/src/page/rewards/index.js
+++ b/app/src/page/rewards/index.js
@@ -10,7 +10,7 @@ import {
selectUser,
} from 'lbryinc';
import { doToast } from 'lbry-redux';
-import { doPushDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants from 'constants';
import RewardsPage from './view';
@@ -27,7 +27,8 @@ const perform = dispatch => ({
claimReward: reward => dispatch(doClaimRewardType(reward.reward_type, true)),
fetchRewards: () => dispatch(doRewardList()),
notify: data => dispatch(doToast(data)),
- pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_REWARDS))
+ pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_REWARDS)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
});
export default connect(select, perform)(RewardsPage);
diff --git a/app/src/page/rewards/view.js b/app/src/page/rewards/view.js
index 800d86f..52f2e31 100644
--- a/app/src/page/rewards/view.js
+++ b/app/src/page/rewards/view.js
@@ -32,9 +32,10 @@ class RewardsPage extends React.PureComponent {
scrollView = null;
componentDidMount() {
- const { fetchRewards, pushDrawerStack, navigation, user } = this.props;
+ const { fetchRewards, pushDrawerStack, navigation, setPlayerVisible, user } = this.props;
pushDrawerStack();
+ setPlayerVisible();
fetchRewards();
this.setState({
@@ -156,10 +157,10 @@ class RewardsPage extends React.PureComponent {
return (
- {(!this.state.isEmailVerified || !this.state.isIdentityVerified || !this.state.isRewardApproved) &&
+ {(!this.state.isEmailVerified || !this.state.isRewardApproved) &&
}
- {(this.state.isEmailVerified && this.state.isIdentityVerified && this.state.isRewardApproved) &&
+ {(this.state.isEmailVerified && this.state.isRewardApproved) &&
this.scrollView = ref}
keyboardShouldPersistTaps={'handled'}
diff --git a/app/src/page/search/index.js b/app/src/page/search/index.js
index 580b745..344c337 100644
--- a/app/src/page/search/index.js
+++ b/app/src/page/search/index.js
@@ -8,7 +8,7 @@ import {
makeSelectQueryWithOptions,
selectSearchUrisByQuery
} from 'lbry-redux';
-import { doPushDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants from 'constants';
import SearchPage from './view';
@@ -23,6 +23,7 @@ const perform = dispatch => ({
search: (query) => dispatch(doSearch(query, 25)),
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SEARCH)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
});
export default connect(select, perform)(SearchPage);
diff --git a/app/src/page/search/view.js b/app/src/page/search/view.js
index de00d2a..02c5b0b 100644
--- a/app/src/page/search/view.js
+++ b/app/src/page/search/view.js
@@ -26,12 +26,17 @@ class SearchPage extends React.PureComponent {
};
componentWillMount() {
- this.props.pushDrawerStack();
+ const { pushDrawerStack, setPlayerVisible } = this.props;
+ pushDrawerStack();
+ setPlayerVisible();
}
componentDidMount() {
const { navigation, search } = this.props;
- const { searchQuery } = navigation.state.params;
+ let searchQuery;
+ if (navigation && navigation.state) {
+ searchQuery = navigation.state.params.searchQuery;
+ }
if (searchQuery && searchQuery.trim().length > 0) {
this.setState({ currentUri: (isURIValid(searchQuery)) ? normalizeURI(searchQuery) : null })
search(searchQuery);
diff --git a/app/src/page/settings/index.js b/app/src/page/settings/index.js
index 24a0d63..069e733 100644
--- a/app/src/page/settings/index.js
+++ b/app/src/page/settings/index.js
@@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import { SETTINGS } from 'lbry-redux';
-import { doPushDrawerStack, doPopDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetClientSetting } from 'redux/actions/settings';
import { selectDrawerStack } from 'redux/selectors/drawer';
import { makeSelectClientSetting } from 'redux/selectors/settings';
@@ -18,6 +18,7 @@ const perform = dispatch => ({
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SETTINGS)),
popDrawerStack: () => dispatch(doPopDrawerStack()),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
});
export default connect(select, perform)(SettingsPage);
diff --git a/app/src/page/settings/view.js b/app/src/page/settings/view.js
index bc8d95e..283b45f 100644
--- a/app/src/page/settings/view.js
+++ b/app/src/page/settings/view.js
@@ -11,7 +11,9 @@ class SettingsPage extends React.PureComponent {
}
componentDidMount() {
- this.props.pushDrawerStack();
+ const { pushDrawerStack, setPlayerVisible } = this.props;
+ pushDrawerStack();
+ setPlayerVisible();
}
render() {
diff --git a/app/src/page/splash/index.js b/app/src/page/splash/index.js
index 58d72be..0bf6976 100644
--- a/app/src/page/splash/index.js
+++ b/app/src/page/splash/index.js
@@ -13,7 +13,7 @@ import {
selectUser,
selectEmailToVerify
} from 'lbryinc';
-import { doDeleteCompleteBlobs } from 'redux/actions/file';
+import { doSetClientSetting } from 'redux/actions/settings';
import SplashScreen from './view';
const select = state => ({
@@ -26,11 +26,11 @@ const perform = dispatch => ({
balanceSubscribe: () => dispatch(doBalanceSubscribe()),
blacklistedOutpointsSubscribe: () => dispatch(doBlackListedOutpointsSubscribe()),
checkSubscriptionsInit: () => dispatch(doCheckSubscriptionsInit()),
- deleteCompleteBlobs: () => dispatch(doDeleteCompleteBlobs()),
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
fetchSubscriptions: (callback) => dispatch(doFetchMySubscriptions(callback)),
getSync: password => dispatch(doGetSync(password)),
notify: data => dispatch(doToast(data)),
+ setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setEmailToVerify: email => dispatch(doUserEmailToVerify(email)),
updateBlockHeight: () => dispatch(doUpdateBlockHeight()),
verifyUserEmail: (token, recaptcha) => dispatch(doUserEmailVerify(token, recaptcha)),
diff --git a/app/src/page/splash/view.js b/app/src/page/splash/view.js
index b9a4b7e..2bd7c33 100644
--- a/app/src/page/splash/view.js
+++ b/app/src/page/splash/view.js
@@ -28,7 +28,7 @@ class SplashScreen extends React.PureComponent {
componentWillMount() {
this.setState({
daemonReady: false,
- details: 'Starting daemon',
+ details: 'Starting up...',
message: 'Connecting',
isRunning: false,
isLagging: false,
@@ -114,22 +114,18 @@ class SplashScreen extends React.PureComponent {
if (this.state.daemonReady && this.state.shouldAuthenticate && user && user.id) {
this.setState({ shouldAuthenticate: false }, () => {
- AsyncStorage.getItem(Constants.KEY_FIRST_RUN_EMAIL).then(email => {
- if (email) {
- setEmailToVerify(email);
- }
-
- // user is authenticated, navigate to the main view
- if (user.has_verified_email) {
- NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
+ // user is authenticated, navigate to the main view
+ if (user.has_verified_email) {
+ NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
+ if (walletPassword && walletPassword.trim().length > 0) {
getSync(walletPassword);
- this.navigateToMain();
- });
- return;
- }
+ }
+ this.navigateToMain();
+ });
+ return;
+ }
- this.navigateToMain();
- });
+ this.navigateToMain();
});
}
}
@@ -140,9 +136,11 @@ class SplashScreen extends React.PureComponent {
balanceSubscribe,
blacklistedOutpointsSubscribe,
checkSubscriptionsInit,
- updateBlockHeight,
+ getSync,
navigation,
- notify
+ notify,
+ updateBlockHeight,
+ user
} = this.props;
Lbry.resolve({ urls: 'lbry://one' }).then(() => {
@@ -152,21 +150,30 @@ class SplashScreen extends React.PureComponent {
checkSubscriptionsInit();
updateBlockHeight();
setInterval(() => { updateBlockHeight(); }, BLOCK_HEIGHT_INTERVAL);
- NativeModules.VersionInfo.getAppVersion().then(appVersion => {
- this.setState({ shouldAuthenticate: true });
- authenticate(appVersion, Platform.OS);
- });
+
+ if (user && user.id && user.has_verified_email) {
+ // user already authenticated
+ NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
+ if (walletPassword && walletPassword.trim().length > 0) {
+ getSync(walletPassword);
+ }
+ this.navigateToMain();
+ });
+ } else {
+ NativeModules.VersionInfo.getAppVersion().then(appVersion => {
+ this.setState({ shouldAuthenticate: true });
+ authenticate(appVersion, Platform.OS);
+ });
+ }
});
}
_updateStatusCallback(status) {
- const { deleteCompleteBlobs, fetchSubscriptions } = this.props;
+ const { fetchSubscriptions, getSync, setClientSetting } = this.props;
const startupStatus = status.startup_status;
// At the minimum, wallet should be started and blocks_behind equal to 0 before calling resolve
const hasStarted = startupStatus.stream_manager && startupStatus.wallet && status.wallet.blocks_behind <= 0;
if (hasStarted) {
- deleteCompleteBlobs();
-
// Wait until we are able to resolve a name before declaring
// that we are done.
// TODO: This is a hack, and the logic should live in the daemon
@@ -179,37 +186,18 @@ class SplashScreen extends React.PureComponent {
isRunning: true,
});
- AsyncStorage.getItem(Constants.KEY_FIRST_RUN_PASSWORD).then(passwordSet => {
- if ("true" === passwordSet) {
- // encrypt the wallet
- NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(password => {
- if (!password || password.trim().length === 0) {
- this.finishSplashScreen();
- return;
- }
-
- Lbry.account_encrypt({ new_password: password }).then((result) => {
- AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_PASSWORD);
- Lbry.account_unlock({ password }).then(() => this.finishSplashScreen());
- });
- });
+ // For now, automatically unlock the wallet if a password is set so that downloads work
+ NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(password => {
+ if (password && password.trim().length > 0) {
+ // unlock the wallet and then finish the splash screen
+ Lbry.account_unlock({ password }).then(() => this.finishSplashScreen()).catch(() => this.finishSplashScreen());
return;
}
- // For now, automatically unlock the wallet if a password is set so that downloads work
- NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(password => {
- if (password && password.trim().length > 0) {
- // unlock the wallet and then finish the splash screen
- Lbry.account_unlock({ password }).then(() => this.finishSplashScreen());
- return;
- }
-
- this.finishSplashScreen();
- });
+ this.finishSplashScreen();
});
-
return;
}
diff --git a/app/src/page/subscriptions/index.js b/app/src/page/subscriptions/index.js
index 7de3864..844b75f 100644
--- a/app/src/page/subscriptions/index.js
+++ b/app/src/page/subscriptions/index.js
@@ -12,7 +12,7 @@ import {
selectFirstRunCompleted,
selectShowSuggestedSubs
} from 'lbryinc';
-import { doPushDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetClientSetting } from 'redux/actions/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import Constants from 'constants';
@@ -37,6 +37,7 @@ const perform = dispatch => ({
doSetViewMode: (viewMode) => dispatch(doSetViewMode(viewMode)),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SUBSCRIPTIONS)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
});
export default connect(select, perform)(SubscriptionsPage);
diff --git a/app/src/page/subscriptions/view.js b/app/src/page/subscriptions/view.js
index 4eda351..277ca85 100644
--- a/app/src/page/subscriptions/view.js
+++ b/app/src/page/subscriptions/view.js
@@ -34,9 +34,11 @@ class SubscriptionsPage extends React.PureComponent {
doFetchMySubscriptions,
doFetchRecommendedSubscriptions,
pushDrawerStack,
+ setPlayerVisible
} = this.props;
pushDrawerStack();
+ setPlayerVisible();
doFetchMySubscriptions();
doFetchRecommendedSubscriptions();
}
diff --git a/app/src/page/transactionHistory/index.js b/app/src/page/transactionHistory/index.js
index 746f991..7083f2a 100644
--- a/app/src/page/transactionHistory/index.js
+++ b/app/src/page/transactionHistory/index.js
@@ -4,7 +4,7 @@ import {
selectTransactionItems,
selectIsFetchingTransactions,
} from 'lbry-redux';
-import { doPushDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants from 'constants';
import TransactionHistoryPage from './view';
@@ -16,6 +16,7 @@ const select = state => ({
const perform = dispatch => ({
fetchTransactions: () => dispatch(doFetchTransactions()),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_TRANSACTION_HISTORY)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
});
export default connect(select, perform)(TransactionHistoryPage);
diff --git a/app/src/page/transactionHistory/view.js b/app/src/page/transactionHistory/view.js
index 8be76ab..c0069ef 100644
--- a/app/src/page/transactionHistory/view.js
+++ b/app/src/page/transactionHistory/view.js
@@ -6,7 +6,9 @@ import walletStyle from 'styles/wallet';
class TransactionHistoryPage extends React.PureComponent {
componentWillMount() {
- this.props.pushDrawerStack();
+ const { pushDrawerStack, setPlayerVisible } = this.props;
+ pushDrawerStack();
+ setPlayerVisible();
}
componentDidMount() {
diff --git a/app/src/page/trending/index.js b/app/src/page/trending/index.js
index bbf1226..84a2cc1 100644
--- a/app/src/page/trending/index.js
+++ b/app/src/page/trending/index.js
@@ -1,6 +1,6 @@
import { connect } from 'react-redux';
import { doFetchTrendingUris, selectTrendingUris, selectFetchingTrendingUris } from 'lbryinc';
-import { doPushDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import Constants from 'constants';
import TrendingPage from './view';
@@ -11,7 +11,8 @@ const select = state => ({
const perform = dispatch => ({
fetchTrendingUris: () => dispatch(doFetchTrendingUris()),
- pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_TRENDING))
+ pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_TRENDING)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
});
export default connect(select, perform)(TrendingPage);
\ No newline at end of file
diff --git a/app/src/page/trending/view.js b/app/src/page/trending/view.js
index 4fc042d..74abbe8 100644
--- a/app/src/page/trending/view.js
+++ b/app/src/page/trending/view.js
@@ -19,8 +19,9 @@ import UriBar from 'component/uriBar';
class TrendingPage extends React.PureComponent {
componentDidMount() {
- const { fetchTrendingUris, pushDrawerStack } = this.props;
+ const { fetchTrendingUris, pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
+ setPlayerVisible();
fetchTrendingUris();
}
diff --git a/app/src/page/verification/index.js b/app/src/page/verification/index.js
index 427c188..ac82ace 100644
--- a/app/src/page/verification/index.js
+++ b/app/src/page/verification/index.js
@@ -1,6 +1,9 @@
import { connect } from 'react-redux';
import { doToast } from 'lbry-redux';
import {
+ doCheckSync,
+ doGetSync,
+ doSyncApply,
doUserEmailNew,
doUserEmailToVerify,
doUserResendVerificationEmail,
@@ -14,8 +17,18 @@ import {
selectEmailNewErrorMessage,
selectEmailNewIsPending,
selectEmailToVerify,
+ selectHasSyncedWallet,
+ selectGetSyncIsPending,
+ selectSetSyncIsPending,
+ selectSyncApplyIsPending,
+ selectSyncApplyErrorMessage,
+ selectSyncData,
+ selectSyncHash,
selectUser,
} from 'lbryinc';
+import { doSetClientSetting } from 'redux/actions/settings';
+import { makeSelectClientSetting } from 'redux/selectors/settings';
+import Constants from 'constants';
import Verification from './view';
const select = (state) => ({
@@ -28,15 +41,27 @@ const select = (state) => ({
phone: selectPhoneToVerify(state),
phoneNewErrorMessage: selectPhoneNewErrorMessage(state),
phoneNewIsPending: selectPhoneNewIsPending(state),
+ deviceWalletSynced: makeSelectClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED)(state),
+ hasSyncedWallet: selectHasSyncedWallet(state),
+ getSyncIsPending: selectGetSyncIsPending(state),
+ setSyncIsPending: selectSetSyncIsPending(state),
+ syncApplyIsPending: selectSyncApplyIsPending(state),
+ syncApplyErrorMessage: selectSyncApplyErrorMessage(state),
+ syncData: selectSyncData(state),
+ syncHash: selectSyncHash(state),
});
const perform = dispatch => ({
addUserEmail: email => dispatch(doUserEmailNew(email)),
addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)),
+ getSync: password => dispatch(doGetSync(password)),
+ checkSync: () => dispatch(doCheckSync()),
verifyPhone: (verificationCode) => dispatch(doUserPhoneVerify(verificationCode)),
notify: data => dispatch(doToast(data)),
+ setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setEmailToVerify: email => dispatch(doUserEmailToVerify(email)),
- resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email))
+ syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)),
+ resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
});
export default connect(select, perform)(Verification);
diff --git a/app/src/page/verification/internal/email-verify-page.js b/app/src/page/verification/internal/email-verify-page.js
index b8d886e..e075778 100644
--- a/app/src/page/verification/internal/email-verify-page.js
+++ b/app/src/page/verification/internal/email-verify-page.js
@@ -142,13 +142,15 @@ class EmailVerifyPage extends React.PureComponent {
text={"Send verification email"}
onPress={this.onSendVerificationPressed} />}
{this.state.verifyStarted && emailNewPending &&
- }
+
+
+ }
}
{(Constants.PHASE_VERIFICATION === this.state.phase) &&
- An email has been sent to {this.state.email}. Please follow the instructions in the message to verify your email address.
+ An email has been sent to {this.state.email}. Please follow the instructions in the message to verify your email address.
diff --git a/app/src/page/verification/internal/manual-verify-page.js b/app/src/page/verification/internal/manual-verify-page.js
index 4179946..693755c 100644
--- a/app/src/page/verification/internal/manual-verify-page.js
+++ b/app/src/page/verification/internal/manual-verify-page.js
@@ -21,7 +21,7 @@ class ManualVerifyPage extends React.PureComponent {
return (
Manual Reward Verification
- You need to be manually verified before you can start claiming rewards. Please request to be verified on the .
+ You need to be manually verified before you can start claiming rewards. Please request to be verified on the .
);
}
diff --git a/app/src/page/verification/internal/phone-verify-page.js b/app/src/page/verification/internal/phone-verify-page.js
index 273f0a7..9ebb293 100644
--- a/app/src/page/verification/internal/phone-verify-page.js
+++ b/app/src/page/verification/internal/phone-verify-page.js
@@ -171,10 +171,12 @@ class PhoneVerifyPage extends React.PureComponent {
text={"Send verification text"}
onPress={this.onSendTextPressed} />}
{phoneNewIsPending &&
- }
+
+
+ }
}
@@ -204,12 +206,12 @@ class PhoneVerifyPage extends React.PureComponent {
}
{phoneVerifyIsPending &&
-
+
Verifying your phone number...
+ style={[rewardStyle.topMarginMedium, rewardStyle.leftRightMargin]} />
}
}
diff --git a/app/src/page/verification/internal/sync-verify-page.js b/app/src/page/verification/internal/sync-verify-page.js
new file mode 100644
index 0000000..f38c5ef
--- /dev/null
+++ b/app/src/page/verification/internal/sync-verify-page.js
@@ -0,0 +1,170 @@
+import React from 'react';
+import { Lbry } from 'lbry-redux';
+import {
+ ActivityIndicator,
+ Dimensions,
+ NativeModules,
+ Text,
+ TextInput,
+ View
+} from 'react-native';
+import { BarPasswordStrengthDisplay } from 'react-native-password-strength-meter';
+import Button from 'component/button';
+import Link from 'component/link';
+import Colors from 'styles/colors';
+import Constants from 'constants';
+import firstRunStyle from 'styles/firstRun';
+import rewardStyle from 'styles/reward';
+
+
+class SyncVerifyPage extends React.PureComponent {
+ state = {
+ checkSyncStarted: false,
+ password: null,
+ placeholder: 'password',
+ syncApplyStarted: false,
+ syncChecked: false,
+ }
+
+ componentDidMount() {
+ const { checkSync, setEmailVerificationPhase } = this.props;
+
+ this.setState({ checkSyncStarted: true }, () => checkSync());
+
+ if (setEmailVerificationPhase) {
+ setEmailVerificationPhase(false);
+ }
+ }
+
+ onEnableSyncPressed = () => {
+ const {
+ getSync,
+ hasSyncedWallet,
+ navigation,
+ setClientSetting,
+ syncApply,
+ syncData,
+ syncHash
+ } = this.props;
+
+ this.setState({ syncApplyStarted: true }, () => {
+ if (!hasSyncedWallet) {
+ // fresh account with no sync
+ Lbry.account_encrypt({ new_password: this.state.password }).then(() => {
+ Lbry.account_unlock({ password: this.state.password }).then(() => {
+ getSync(this.state.password);
+ setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
+ navigation.goBack();
+ });
+ });
+ } else {
+ syncApply(syncHash, syncData, this.state.password);
+ }
+ });
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const { getSyncIsPending, syncApplyIsPending, syncApplyErrorMessage } = nextProps;
+ const { getSync, setClientSetting, navigation, notify, hasSyncedWallet } = this.props;
+ if (this.state.checkSyncStarted && !getSyncIsPending) {
+ this.setState({ syncChecked: true });
+ }
+
+ if (this.state.syncApplyStarted && !syncApplyIsPending) {
+ if (syncApplyErrorMessage && syncApplyErrorMessage.trim().length > 0) {
+ notify({ message: syncApplyErrorMessage, isError: true });
+ this.setState({ syncApplyStarted: false });
+ } else {
+ // password successfully verified
+ if (NativeModules.UtilityModule) {
+ NativeModules.UtilityModule.setSecureValue(Constants.KEY_FIRST_RUN_PASSWORD, this.state.password);
+ }
+ setClientSetting(Constants.SETTING_DEVICE_WALLET_SYNCED, true);
+ navigation.goBack();
+ }
+ }
+ }
+
+ handleChangeText = (text) => {
+ // save the value to the state email
+ const { onPasswordChanged } = this.props;
+ this.setState({ password: text });
+ if (onPasswordChanged) {
+ onPasswordChanged(text);
+ }
+ }
+
+ render() {
+ const { hasSyncedWallet, syncApplyIsPending } = this.props;
+
+ let paragraph;
+ if (!hasSyncedWallet) {
+ paragraph = (Please enter a password to secure your account and wallet.);
+ } else {
+ paragraph = (Please enter the password you used to secure your wallet.);
+ }
+
+ let content;
+ if (!this.state.syncChecked) {
+ content = (
+
+
+ Retrieving your account information...
+
+ );
+ } else {
+ content = (
+
+ Wallet Sync
+ {paragraph}
+ 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: 'password' });
+ }
+ }}
+ />
+ {(!hasSyncedWallet && this.state.password && this.state.password.trim().length) > 0 &&
+
+
+ }
+ Note: for wallet security purposes, LBRY is unable to reset your password.
+
+
+ {!this.state.syncApplyStarted &&
+ }
+ {syncApplyIsPending &&
+
+
+ }
+
+
+ );
+ }
+
+ return (
+
+ {content}
+
+ );
+ }
+}
+
+export default SyncVerifyPage;
diff --git a/app/src/page/verification/view.js b/app/src/page/verification/view.js
index 1cc8d53..fd0a3b3 100644
--- a/app/src/page/verification/view.js
+++ b/app/src/page/verification/view.js
@@ -15,6 +15,7 @@ import Constants from 'constants';
import EmailVerifyPage from './internal/email-verify-page';
import ManualVerifyPage from './internal/manual-verify-page';
import PhoneVerifyPage from './internal/phone-verify-page';
+import SyncVerifyPage from './internal/sync-verify-page';
import firstRunStyle from 'styles/firstRun';
class VerificationScreen extends React.PureComponent {
@@ -43,7 +44,8 @@ class VerificationScreen extends React.PureComponent {
}
checkVerificationStatus = (user) => {
- const { navigation } = this.props;
+ const { deviceWalletSynced, navigation } = this.props;
+ const { syncFlow } = navigation.state.params;
this.setState({
isEmailVerified: (user && user.primary_email && user.has_verified_email),
@@ -53,11 +55,23 @@ class VerificationScreen extends React.PureComponent {
if (!this.state.isEmailVerified) {
this.setState({ currentPage: 'emailVerify' });
}
- if (this.state.isEmailVerified && !this.state.isIdentityVerified) {
- this.setState({ currentPage: 'phoneVerify' });
+
+ if (syncFlow) {
+ if (this.state.isEmailVerified && !deviceWalletSynced) {
+ this.setState({ currentPage: 'syncVerify' });
+ }
+ } else {
+ if (this.state.isEmailVerified && !this.state.isIdentityVerified) {
+ this.setState({ currentPage: 'phoneVerify' });
+ }
+ if (this.state.isEmailVerified && this.state.isIdentityVerified && !this.state.isRewardApproved) {
+ this.setState({ currentPage: 'manualVerify' });
+ }
}
- if (this.state.isEmailVerified && this.state.isIdentityVerified && !this.state.isRewardApproved) {
- this.setState({ currentPage: 'manualVerify' });
+
+ if (this.state.isEmailVerified && syncFlow && deviceWalletSynced) {
+ navigation.goBack();
+ return;
}
if (this.state.isEmailVerified && this.state.isIdentityVerified && this.state.isRewardApproved) {
@@ -81,18 +95,29 @@ class VerificationScreen extends React.PureComponent {
render() {
const {
addUserEmail,
+ checkSync,
emailNewErrorMessage,
emailNewPending,
emailToVerify,
+ getSync,
navigation,
notify,
addUserPhone,
+ getSyncIsPending,
+ hasSyncedWallet,
+ setSyncIsPending,
+ syncApplyIsPending,
+ syncApplyErrorMessage,
+ syncApply,
+ syncData,
+ syncHash,
phone,
phoneVerifyIsPending,
phoneVerifyErrorMessage,
phoneNewIsPending,
phoneNewErrorMessage,
resendVerificationEmail,
+ setClientSetting,
verifyPhone
} = this.props;
@@ -111,6 +136,7 @@ class VerificationScreen extends React.PureComponent {
/>
);
break;
+
case 'phoneVerify':
page = (
);
break;
+
+ case 'syncVerify':
+ page = (
+
+ );
+ break;
+
case 'manualVerify':
page = (
);
+ break;
}
return (
diff --git a/app/src/page/wallet/index.js b/app/src/page/wallet/index.js
index 7ab4eef..4eb9e37 100644
--- a/app/src/page/wallet/index.js
+++ b/app/src/page/wallet/index.js
@@ -1,24 +1,27 @@
import { connect } from 'react-redux';
import { doSetClientSetting } from 'redux/actions/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings';
-import { doPushDrawerStack } from 'redux/actions/drawer';
+import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { selectBalance } from 'lbry-redux';
-import { doGetSync, selectUser } from 'lbryinc';
+import { doCheckSync, doGetSync, selectUser, selectHasSyncedWallet } from 'lbryinc';
import Constants from 'constants';
import WalletPage from './view';
const select = state => ({
user: selectUser(state),
balance: selectBalance(state),
+ hasSyncedWallet: selectHasSyncedWallet(state),
understandsRisks: makeSelectClientSetting(Constants.SETTING_ALPHA_UNDERSTANDS_RISKS)(state),
backupDismissed: makeSelectClientSetting(Constants.SETTING_BACKUP_DISMISSED)(state),
rewardsNotInterested: makeSelectClientSetting(Constants.SETTING_REWARDS_NOT_INTERESTED)(state),
});
const perform = dispatch => ({
+ checkSync: () => dispatch(doCheckSync()),
getSync: password => dispatch(doGetSync(password)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
- pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_WALLET))
+ pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_WALLET)),
+ setPlayerVisible: () => dispatch(doSetPlayerVisible(false))
});
export default connect(select, perform)(WalletPage);
diff --git a/app/src/page/wallet/view.js b/app/src/page/wallet/view.js
index 37d3921..acb865e 100644
--- a/app/src/page/wallet/view.js
+++ b/app/src/page/wallet/view.js
@@ -1,10 +1,11 @@
import React from 'react';
import { NativeModules, ScrollView, Text, View } from 'react-native';
import TransactionListRecent from 'component/transactionListRecent';
-import WalletRewardsDriver from 'component/walletRewardsDriver';
import WalletAddress from 'component/walletAddress';
import WalletBalance from 'component/walletBalance';
import WalletSend from 'component/walletSend';
+import WalletRewardsDriver from 'component/walletRewardsDriver';
+import WalletSyncDriver from 'component/walletSyncDriver';
import Button from 'component/button';
import Link from 'component/link';
import UriBar from 'component/uriBar';
@@ -13,11 +14,17 @@ import walletStyle from 'styles/wallet';
class WalletPage extends React.PureComponent {
componentDidMount() {
- this.props.pushDrawerStack();
+ const { pushDrawerStack, setPlayerVisible } = this.props;
+ pushDrawerStack();
+ setPlayerVisible();
- const { user, getSync } = this.props;
+ const { getSync, user } = this.props;
if (user && user.has_verified_email) {
- NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => getSync(walletPassword));
+ NativeModules.UtilityModule.getSecureValue(Constants.KEY_FIRST_RUN_PASSWORD).then(walletPassword => {
+ if (walletPassword && walletPassword.trim().length > 0) {
+ getSync(walletPassword);
+ }
+ });
}
}
@@ -30,6 +37,7 @@ class WalletPage extends React.PureComponent {
const {
balance,
backupDismissed,
+ hasSyncedWallet,
rewardsNotInterested,
understandsRisks,
setClientSetting,
@@ -41,8 +49,15 @@ class WalletPage extends React.PureComponent {
+
+ This is beta software. You may lose any credits that you send to your wallet due to software bugs, deleted files, or malicious third-party software. You should not use this wallet as your primary wallet.
+
+ {!hasSyncedWallet &&
+
+ If you are not using the LBRY sync service, you will lose all of your credits if you uninstall this application. Instructions on how to enroll as well as how to backup your wallet manually are available on the next page.
+ }
- This is beta software. You may lose any LBC that you send to your wallet due to uninstallation, software bugs, deleted files, or malicious third-party software. You should not use this wallet as your primary wallet. If you understand the risks and you wish to continue, please tap the button below.
+ If you understand the risks and you wish to continue, please tap the button below.