diff --git a/app/package-lock.json b/app/package-lock.json
index 1704b4a0..131dc098 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 538c83ec..d43dbaa9 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 bac1441a..94ad294b 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 c86100e2..52733cf2 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 c3260167..b9797aaf 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 28085740..5f3cc6c2 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 953a0b56..53b00cfc 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 59d9daae..79183be3 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 00000000..ce46fb3a
--- /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 00000000..4bd82972
--- /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 a6d058a1..5074699c 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 22d54f51..289b7d10 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 c21722ed..0116aa06 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 0e74511b..1702f5f3 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 17d991fe..778f628d 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 b328bf3b..ec7c64a4 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 cf74a586..49016592 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 00000000..e2d8ef57
--- /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 00000000..9eab91a6
--- /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 e4277059..13a2cc80 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 3eb4cb33..65364cc0 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 612a0f97..5775c000 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 d662772d..93570495 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 344311eb..13344b57 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 2c4303d6..15fe31dd 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 94037c54..42b7bc3f 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 5dcce737..c709922d 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 a134bd44..924ab56d 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 73ceecf9..15580ceb 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 13657eab..59bb05be 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 cbb334ed..b5a0e62f 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 3f5d984a..3cae52d3 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 800d86fd..52f2e319 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 580b745d..344c337f 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 de00d2ad..02c5b0b2 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 24a0d638..069e733e 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 bc8d95e0..283b45fd 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 58d72be4..0bf6976f 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 b9a4b7e3..2bd7c336 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 7de38642..844b75f5 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 4eda3518..277ca851 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 746f991a..7083f2a1 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 8be76ab6..c0069ef4 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 bbf1226a..84a2cc11 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 4fc042dd..74abbe89 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 427c188d..ac82aceb 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 b8d886ec..e075778a 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 41799468..693755c8 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 273f0a7c..9ebb2938 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 00000000..f38c5ef0
--- /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 1cc8d53f..fd0a3b3d 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 7ab4eef6..4eb9e37c 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 37d39214..acb865e3 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.