diff --git a/src/constants.js b/src/constants.js index 1f7c7d7..9bae614 100644 --- a/src/constants.js +++ b/src/constants.js @@ -95,6 +95,7 @@ const Constants = { FULL_ROUTE_NAME_WALLET: 'WalletStack', ROUTE_FILE: 'File', + DRAWER_ROUTE_FILE_VIEW: 'FileView', ITEM_CREATE_A_CHANNEL: 'Create a channel...', ITEM_ANONYMOUS: 'Publish anonymously', @@ -167,4 +168,8 @@ export const DrawerRoutes = [ ]; // sub-pages for main routes -export const InnerDrawerRoutes = [Constants.DRAWER_ROUTE_CHANNEL_CREATOR_FORM, Constants.DRAWER_ROUTE_PUBLISH_FORM]; +export const InnerDrawerRoutes = [ + Constants.DRAWER_ROUTE_CHANNEL_CREATOR_FORM, + Constants.DRAWER_ROUTE_PUBLISH_FORM, + Constants.DRAWER_ROUTE_FILE_VIEW, +]; diff --git a/src/page/file/index.js b/src/page/file/index.js index 4fd413a..71a9ddc 100644 --- a/src/page/file/index.js +++ b/src/page/file/index.js @@ -44,7 +44,7 @@ import { doDeleteFile, doStopDownloadingFile, } from 'redux/actions/file'; -import { doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; +import { doPushDrawerStack, doPopDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer'; import { doToggleFullscreenMode } from 'redux/actions/settings'; import { selectDrawerStack } from 'redux/selectors/drawer'; import FilePage from './view'; @@ -92,6 +92,7 @@ const perform = dispatch => ({ fileGet: (uri, saveFile) => dispatch(doFileGet(uri, saveFile)), notify: data => dispatch(doToast(data)), popDrawerStack: () => dispatch(doPopDrawerStack()), + pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)), purchaseUri: (uri, costInfo, saveFile) => dispatch(doPurchaseUri(uri, costInfo, saveFile)), deletePurchasedUri: uri => dispatch(doDeletePurchasedUri(uri)), resolveUri: uri => dispatch(doResolveUri(uri)), diff --git a/src/page/file/view.js b/src/page/file/view.js index 394a12b..31af1af 100644 --- a/src/page/file/view.js +++ b/src/page/file/view.js @@ -44,6 +44,7 @@ import Video from 'react-native-video'; import FileRewardsDriver from 'component/fileRewardsDriver'; import filePageStyle from 'styles/filePage'; import uriBarStyle from 'styles/uriBar'; +import _ from 'lodash'; class FilePage extends React.PureComponent { static navigationOptions = { @@ -62,8 +63,9 @@ class FilePage extends React.PureComponent { super(props); this.state = { attemptAutoGet: false, - autoPlayMedia: false, + autoOpened: false, autoDownloadStarted: false, + autoPlayMedia: false, downloadButtonShown: false, downloadPressed: false, fileViewLogged: false, @@ -127,6 +129,17 @@ class FilePage extends React.PureComponent { this.onComponentFocused(); } + difference = (object, base) => { + function changes(object, base) { + return _.transform(object, function(result, value, key) { + if (!_.isEqual(value, base[key])) { + result[key] = _.isObject(value) && _.isObject(base[key]) ? changes(value, base[key]) : value; + } + }); + } + return changes(object, base); + }; + componentWillReceiveProps(nextProps) { const { claim, @@ -136,6 +149,7 @@ class FilePage extends React.PureComponent { navigation, contentType, notify, + drawerStack: prevDrawerStack, } = this.props; const { uri } = navigation.state.params; const { @@ -145,6 +159,7 @@ class FilePage extends React.PureComponent { purchasedUris, purchaseUriErrorMessage, streamingUrl, + drawerStack, } = nextProps; if (Constants.ROUTE_FILE === currentRoute && currentRoute !== prevRoute) { @@ -184,6 +199,17 @@ class FilePage extends React.PureComponent { this.setState({ streamingMode: true, currentStreamUrl: fileInfo.streaming_url }); } } + + if ( + prevDrawerStack[prevDrawerStack.length - 1].route === Constants.DRAWER_ROUTE_FILE_VIEW && + prevDrawerStack.length !== drawerStack.length + ) { + this.setState({ + downloadPressed: false, + showImageViewer: false, + showWebView: false, + }); + } } componentDidUpdate(prevProps) { @@ -633,6 +659,39 @@ class FilePage extends React.PureComponent { } }; + openFile = (localFileUri, mediaType) => { + const { pushDrawerStack } = this.props; + const isWebViewable = mediaType === 'text'; + + if (mediaType === 'image') { + // use image viewer + if (!this.state.showImageViewer) { + this.setState( + { + imageUrls: [ + { + url: localFileUri, + }, + ], + showImageViewer: true, + }, + () => pushDrawerStack(Constants.DRAWER_ROUTE_FILE_VIEW) + ); + } + } + if (isWebViewable) { + // show webview + if (!this.state.showWebView) { + this.setState( + { + showWebView: true, + }, + () => pushDrawerStack(Constants.DRAWER_ROUTE_FILE_VIEW) + ); + } + } + }; + render() { const { balance, @@ -651,6 +710,7 @@ class FilePage extends React.PureComponent { navigation, position, purchaseUri, + pushDrawerStack, isSearchingRecommendContent, recommendedContent, thumbnail, @@ -746,6 +806,7 @@ class FilePage extends React.PureComponent { const description = metadata.description ? metadata.description : null; const mediaType = Lbry.getMediaType(contentType); const isPlayable = mediaType === 'video' || mediaType === 'audio'; + const isWebViewable = mediaType === 'text'; const { height, signing_channel: signingChannel, value } = claim; const channelName = signingChannel && signingChannel.name; const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id; @@ -784,35 +845,10 @@ class FilePage extends React.PureComponent { (fileInfo && (fileInfo.written_bytes >= 2097152 || fileInfo.written_bytes === fileInfo.total_bytes)); // 2MB = 1024*1024*2 const duration = claim && claim.value && claim.value.video ? claim.value.video.duration : null; const isViewable = mediaType === 'image' || mediaType === 'text'; - const isWebViewable = mediaType === 'text'; const canOpen = isViewable && completed; const localFileUri = this.localUriForFileInfo(fileInfo); const unsupported = !isPlayable && !canOpen; - const openFile = () => { - if (mediaType === 'image') { - // use image viewer - if (!this.state.showImageViewer) { - this.setState({ - imageUrls: [ - { - url: localFileUri, - }, - ], - showImageViewer: true, - }); - } - } - if (isWebViewable) { - // show webview - if (!this.state.showWebView) { - this.setState({ - showWebView: true, - }); - } - } - }; - if (fileInfo && !this.state.autoDownloadStarted && this.state.uriVars && this.state.uriVars.download === 'true') { this.setState({ autoDownloadStarted: true }, () => { purchaseUri(uri, costInfo, !isPlayable); @@ -822,9 +858,9 @@ class FilePage extends React.PureComponent { }); } - if (this.state.downloadPressed && canOpen) { + if (this.state.downloadPressed && canOpen && !this.state.autoOpened) { // automatically open a web viewable or image file after the download button is pressed - openFile(); + this.setState({ autoOpened: true }, () => this.openFile(localFileUri, mediaType)); } return ( @@ -889,7 +925,7 @@ class FilePage extends React.PureComponent { this.openFile(localFileUri, mediaType)} isPlayable={isPlayable} isViewable={isViewable} onPlay={this.onFileDownloadButtonPlayed} @@ -975,7 +1011,7 @@ class FilePage extends React.PureComponent { - + {__('Share')} @@ -983,51 +1019,61 @@ class FilePage extends React.PureComponent { style={filePageStyle.largeButton} onPress={() => this.setState({ showTipView: true })} > - + {__('Tip')} - - {!isPlayable && !fileInfo && ( - - - {__('Download')} - - )} + {!canEdit && ( + + {!fileInfo || + (fileInfo.written_bytes <= 0 && !completed && ( + + + {__('Download')} + + ))} - {!completed && - fileInfo && - !fileInfo.stopped && - fileInfo.written_bytes > 0 && - fileInfo.written_bytes < fileInfo.total_bytes && - !this.state.stopDownloadConfirmed && ( - - - {__('Stop')} - - )} - + {!completed && + fileInfo && + !fileInfo.stopped && + fileInfo.written_bytes > 0 && + fileInfo.written_bytes < fileInfo.total_bytes && + !this.state.stopDownloadConfirmed && ( + + + {__('Stop')} + + )} + + {completed && fileInfo && fileInfo.written_bytes >= fileInfo.total_bytes && ( + + + {__('Open')} + + )} + + )} {!canEdit && ( Linking.openURL(`https://lbry.com/dmca/${claim.claim_id}`)} > - + {__('Report')} )} {canEdit && ( - + {__('Edit')} )} {(completed || canEdit) && ( - + {__('Delete')} )} diff --git a/src/page/settings/view.js b/src/page/settings/view.js index 0860bf4..53bcd25 100644 --- a/src/page/settings/view.js +++ b/src/page/settings/view.js @@ -11,7 +11,6 @@ import settingsStyle from 'styles/settings'; const languageOptions = [ { code: 'default', name: 'Use device language' }, - { code: 'ar', name: 'Arabic' }, { code: 'en', name: 'English' }, { code: 'gu', name: 'Gujarati' }, { code: 'hi', name: 'Hindi' }, @@ -90,33 +89,41 @@ class SettingsPage extends React.PureComponent { } // check the local filesystem for the language first? Or download remote strings first? + if (language === 'en') { + // don't attempt to download English + NativeModules.UtilityModule.setNativeStringSetting(SETTINGS.LANGUAGE, language); - // download and save the language file - this.setState({ downloadingLanguage: true }, () => { - fetch('https://lbry.com/i18n/get/lbry-mobile/app-strings/' + language + '.json') - .then(r => r.json()) - .then(j => { - window.i18n_messages[language] = j; + // update state and client setting + window.language = language; + setClientSetting(SETTINGS.LANGUAGE, value); + } else { + // download and save the language file + this.setState({ downloadingLanguage: true }, () => { + fetch('https://lbry.com/i18n/get/lbry-mobile/app-strings/' + language + '.json') + .then(r => r.json()) + .then(j => { + window.i18n_messages[language] = j; - // write the language file to the filesystem - const langFilePath = RNFS.ExternalDirectoryPath + '/' + language + '.json'; - RNFS.writeFile(langFilePath, JSON.stringify(j), 'utf8'); + // write the language file to the filesystem + const langFilePath = RNFS.ExternalDirectoryPath + '/' + language + '.json'; + RNFS.writeFile(langFilePath, JSON.stringify(j), 'utf8'); - // save the setting outside redux because when the first component mounts, the redux value isn't loaded yet - // so we have to load it from native settings - NativeModules.UtilityModule.setNativeStringSetting(SETTINGS.LANGUAGE, value); + // save the setting outside redux because when the first component mounts, the redux value isn't loaded yet + // so we have to load it from native settings + NativeModules.UtilityModule.setNativeStringSetting(SETTINGS.LANGUAGE, language); - // update state and client setting - window.language = language; - setClientSetting(SETTINGS.LANGUAGE, value); + // update state and client setting + window.language = language; + setClientSetting(SETTINGS.LANGUAGE, value); - this.setState({ downloadingLanguage: false }); - }) - .catch(e => { - notify({ message: __('Failed to load %language% translations.', { language: language }), isError: true }); - this.setState({ downloadingLanguage: false }); - }); - }); + this.setState({ downloadingLanguage: false }); + }) + .catch(e => { + notify({ message: __('Failed to load %language% translations.', { language: language }), isError: true }); + this.setState({ downloadingLanguage: false }); + }); + }); + } }; handleBackPressed = () => { diff --git a/src/page/splash/view.js b/src/page/splash/view.js index 3250202..c4eeb93 100644 --- a/src/page/splash/view.js +++ b/src/page/splash/view.js @@ -235,7 +235,7 @@ class SplashScreen extends React.PureComponent { } if (headerSyncProgress < 100) { - const downloadProgress = isNaN(parseInt(headerSyncProgress, 10)) ? '0' : headerSyncProgress; + const downloadProgress = isNaN(parseInt(headerSyncProgress, 10)) ? '0' : headerSyncProgress || '0'; this.setState({ message: __('Blockchain Sync'), details: __('Catching up with the blockchain (%progress%%)', { progress: downloadProgress }), diff --git a/src/styles/filePage.js b/src/styles/filePage.js index 078b53b..0c2ae8e 100644 --- a/src/styles/filePage.js +++ b/src/styles/filePage.js @@ -384,7 +384,7 @@ const filePageStyle = StyleSheet.create({ sharedLargeButton: { alignItems: 'center', justifyContent: 'center', - marginRight: 36, + flex: 0.2, }, innerLargeButton: { alignItems: 'center', @@ -393,7 +393,7 @@ const filePageStyle = StyleSheet.create({ largeButton: { alignItems: 'center', justifyContent: 'center', - marginRight: 36, + flex: 0.2, }, largeButtonIcon: { color: Colors.DescriptionGrey, @@ -406,8 +406,8 @@ const filePageStyle = StyleSheet.create({ largeButtonsRow: { flexDirection: 'row', alignItems: 'center', - marginLeft: 20, - marginRight: 20, + marginLeft: 16, + marginRight: 16, marginTop: 12, marginBottom: 12, }, diff --git a/src/utils/helper.js b/src/utils/helper.js index 4b94ac7..bf3e938 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -155,6 +155,14 @@ export function navigateToUri(navigation, uri, additionalParams, isNavigatingBac } export function navigateBack(navigation, drawerStack, popDrawerStack) { + if (drawerStack[drawerStack.length - 1].route === Constants.DRAWER_ROUTE_FILE_VIEW) { + // inner file_view (web / image view) is handled differently + if (popDrawerStack) { + popDrawerStack(); + } + return; + } + if (popDrawerStack) { popDrawerStack(); } @@ -186,6 +194,12 @@ export function navigateBack(navigation, drawerStack, popDrawerStack) { } export function dispatchNavigateBack(dispatch, nav, drawerStack) { + if (drawerStack[drawerStack.length - 1].route === Constants.DRAWER_ROUTE_FILE_VIEW) { + // inner file_view (web / image view) is handled differently + dispatch(doPopDrawerStack()); + return; + } + dispatch(doPopDrawerStack()); const target = drawerStack[drawerStack.length > 1 ? drawerStack.length - 2 : 0];