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 {