From cb605240831beb7c531f93245a7d263c549499ea Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Sat, 19 May 2018 19:30:14 +0100 Subject: [PATCH 1/3] implement direct URI navigation --- app/src/component/fileDownloadButton/index.js | 4 +- app/src/component/fileDownloadButton/view.js | 9 +- app/src/component/uriBar/index.js | 12 ++ app/src/component/uriBar/view.js | 44 ++++++ app/src/page/channel/index.js | 19 +++ app/src/page/channel/view.js | 27 ++++ app/src/page/discover/view.js | 23 ++- app/src/page/file/index.js | 10 +- app/src/page/file/view.js | 149 +++++++++++------- app/src/styles/channelPage.js | 20 +++ app/src/styles/discover.js | 11 +- app/src/styles/fileDownloadButton.js | 2 +- app/src/styles/filePage.js | 20 ++- app/src/styles/uriBar.js | 30 ++++ 14 files changed, 297 insertions(+), 83 deletions(-) create mode 100644 app/src/component/uriBar/index.js create mode 100644 app/src/component/uriBar/view.js create mode 100644 app/src/page/channel/index.js create mode 100644 app/src/page/channel/view.js create mode 100644 app/src/styles/channelPage.js create mode 100644 app/src/styles/uriBar.js diff --git a/app/src/component/fileDownloadButton/index.js b/app/src/component/fileDownloadButton/index.js index ee81d2d6..fb7256de 100644 --- a/app/src/component/fileDownloadButton/index.js +++ b/app/src/component/fileDownloadButton/index.js @@ -1,5 +1,6 @@ import { connect } from 'react-redux'; import { + doFetchCostInfoForUri, makeSelectFileInfoForUri, makeSelectDownloadingForUri, makeSelectLoadingForUri, @@ -17,7 +18,8 @@ const select = (state, props) => ({ const perform = dispatch => ({ purchaseUri: uri => dispatch(doPurchaseUri(uri)), - restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)) + restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)), + fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)), }); export default connect(select, perform)(FileDownloadButton); \ No newline at end of file diff --git a/app/src/component/fileDownloadButton/view.js b/app/src/component/fileDownloadButton/view.js index 1a7354e0..5eb2993e 100644 --- a/app/src/component/fileDownloadButton/view.js +++ b/app/src/component/fileDownloadButton/view.js @@ -3,6 +3,13 @@ import { NativeModules, Text, View, TouchableOpacity } from 'react-native'; import fileDownloadButtonStyle from '../../styles/fileDownloadButton'; class FileDownloadButton extends React.PureComponent { + componentDidMount() { + const { costInfo, fetchCostInfo, uri } = this.props; + if (costInfo === undefined) { + fetchCostInfo(uri); + } + } + componentWillReceiveProps(nextProps) { //this.checkAvailability(nextProps.uri); this.restartDownload(nextProps); @@ -54,7 +61,7 @@ class FileDownloadButton extends React.PureComponent { if (!costInfo) { return ( - Fetching cost info... + Fetching cost info... ); } diff --git a/app/src/component/uriBar/index.js b/app/src/component/uriBar/index.js new file mode 100644 index 00000000..703caa0d --- /dev/null +++ b/app/src/component/uriBar/index.js @@ -0,0 +1,12 @@ +import { connect } from 'react-redux'; +import UriBar from './view'; + +const select = state => ({ + +}); + +const perform = dispatch => ({ + +}); + +export default connect(select, perform)(UriBar); diff --git a/app/src/component/uriBar/view.js b/app/src/component/uriBar/view.js new file mode 100644 index 00000000..a66a24ba --- /dev/null +++ b/app/src/component/uriBar/view.js @@ -0,0 +1,44 @@ +// @flow +import React from 'react'; +import { normalizeURI } from 'lbry-redux'; +import { TextInput, View } from 'react-native'; +import uriBarStyle from '../../styles/uriBar'; + +class UriBar extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + uri: null, + currentValue: null + }; + } + + render() { + const { value, navigation } = this.props; + if (!this.state.currentValue) { + this.setState({ currentValue: value }); + } + + // TODO: Search and URI suggestions overlay + return ( + + this.setState({uri: text, currentValue: text})} + onSubmitEditing={() => { + if (this.state.uri) { + let uri = this.state.uri; + uri = uri.replace(/ /g, '-'); + navigation.navigate('File', { uri: normalizeURI(uri) }); + } + }}/> + + ); + } +} + +export default UriBar; diff --git a/app/src/page/channel/index.js b/app/src/page/channel/index.js new file mode 100644 index 00000000..983b9a3f --- /dev/null +++ b/app/src/page/channel/index.js @@ -0,0 +1,19 @@ +import { connect } from 'react-redux'; +import { + makeSelectClaimForUri, + makeSelectClaimsInChannelForCurrentPage, + makeSelectFetchingChannelClaims, +} from 'lbry-redux'; +import ChannelPage from './view'; + +const select = (state, props) => ({ + claim: makeSelectClaimForUri(props.uri)(state), + claimsInChannel: makeSelectClaimsInChannelForCurrentPage(props.uri)(state), + fetching: makeSelectFetchingChannelClaims(props.uri)(state), +}); + +const perform = dispatch => ({ + +}); + +export default connect(select, perform)(ChannelPage); diff --git a/app/src/page/channel/view.js b/app/src/page/channel/view.js new file mode 100644 index 00000000..8adaf064 --- /dev/null +++ b/app/src/page/channel/view.js @@ -0,0 +1,27 @@ +// @flow +import React from 'react'; +import { ScrollView, Text, View } from 'react-native'; +import UriBar from '../../component/uriBar'; +import channelPageStyle from '../../styles/channelPage'; + +class ChannelPage extends React.PureComponent { + componentDidMount() { + } + + render() { + const { claim, navigation, uri } = this.props; + const { name } = claim; + + return ( + + {name} + + + + + + ) + } +} + +export default ChannelPage; diff --git a/app/src/page/discover/view.js b/app/src/page/discover/view.js index 2c6be2b6..e0a6efdc 100644 --- a/app/src/page/discover/view.js +++ b/app/src/page/discover/view.js @@ -1,9 +1,18 @@ import React from 'react'; import FeaturedCategory from '../../component/featuredCategory'; import NavigationActions from 'react-navigation'; -import { AsyncStorage, NativeModules, ScrollView, Text, View } from 'react-native'; +import { + ActivityIndicator, + AsyncStorage, + NativeModules, + ScrollView, + Text, + View +} from 'react-native'; import moment from 'moment'; import discoverStyle from '../../styles/discover'; +import Colors from '../../styles/colors'; +import UriBar from '../../component/uriBar'; import Feather from 'react-native-vector-icons/Feather'; class DiscoverPage extends React.PureComponent { @@ -34,13 +43,18 @@ class DiscoverPage extends React.PureComponent { } render() { - const { featuredUris, fetchingFeaturedUris } = this.props; + const { featuredUris, fetchingFeaturedUris, navigation } = this.props; const hasContent = typeof featuredUris === 'object' && Object.keys(featuredUris).length, failedToLoad = !fetchingFeaturedUris && !hasContent; return ( - {!hasContent && fetchingFeaturedUris && Fetching content...} + {!hasContent && fetchingFeaturedUris && ( + + + Fetching content... + + )} {hasContent && {hasContent && @@ -51,7 +65,7 @@ class DiscoverPage extends React.PureComponent { key={category} category={category} names={featuredUris[category]} - navigation={this.props.navigation} + navigation={navigation} /> ) : ( '' @@ -59,6 +73,7 @@ class DiscoverPage extends React.PureComponent { )} } + ); } diff --git a/app/src/page/file/index.js b/app/src/page/file/index.js index 18b5bde9..19e5d49d 100644 --- a/app/src/page/file/index.js +++ b/app/src/page/file/index.js @@ -1,13 +1,16 @@ import { connect } from 'react-redux'; import { doFetchFileInfo, - makeSelectFileInfoForUri, + doResolveUri, doFetchCostInfoForUri, + makeSelectIsUriResolving, + makeSelectCostInfoForUri, + makeSelectFileInfoForUri, makeSelectClaimForUri, makeSelectContentTypeForUri, makeSelectMetadataForUri, selectRewardContentClaimIds, - makeSelectCostInfoForUri + selectBlackListedOutpoints, } from 'lbry-redux'; import { doDeleteFile, doStopDownloadingFile } from '../../redux/actions/file'; import FilePage from './view'; @@ -15,7 +18,9 @@ import FilePage from './view'; const select = (state, props) => { const selectProps = { uri: props.navigation.state.params.uri }; return { + blackListedOutpoints: selectBlackListedOutpoints(state), claim: makeSelectClaimForUri(selectProps.uri)(state), + isResolvingUri: makeSelectIsUriResolving(selectProps.uri)(state), contentType: makeSelectContentTypeForUri(selectProps.uri)(state), costInfo: makeSelectCostInfoForUri(selectProps.uri)(state), metadata: makeSelectMetadataForUri(selectProps.uri)(state), @@ -29,6 +34,7 @@ const select = (state, props) => { const perform = dispatch => ({ fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)), fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)), + resolveUri: uri => dispatch(doResolveUri(uri)), stopDownload: (uri, fileInfo) => dispatch(doStopDownloadingFile(uri, fileInfo)), deleteFile: (fileInfo, deleteFromDevice, abandonClaim) => { dispatch(doDeleteFile(fileInfo, deleteFromDevice, abandonClaim)); diff --git a/app/src/page/file/view.js b/app/src/page/file/view.js index 336be164..7a188abd 100644 --- a/app/src/page/file/view.js +++ b/app/src/page/file/view.js @@ -13,9 +13,11 @@ import { NativeModules } from 'react-native'; import Colors from '../../styles/colors'; +import ChannelPage from '../channel'; import FileItemMedia from '../../component/fileItemMedia'; import FileDownloadButton from '../../component/fileDownloadButton'; import MediaPlayer from '../../component/mediaPlayer'; +import UriBar from '../../component/uriBar'; import Video from 'react-native-video'; import filePageStyle from '../../styles/filePage'; @@ -34,15 +36,27 @@ class FilePage extends React.PureComponent { componentDidMount() { StatusBar.setHidden(false); + + const { isResolvingUri, resolveUri, navigation } = this.props; + const { uri } = navigation.state.params; + if (!isResolvingUri) resolveUri(uri); + this.fetchFileInfo(this.props); this.fetchCostInfo(this.props); + if (NativeModules.Mixpanel) { - NativeModules.Mixpanel.track('Open File Page', { Uri: this.props.navigation.state.params.uri }); + NativeModules.Mixpanel.track('Open File Page', { Uri: uri }); } } componentWillReceiveProps(nextProps) { this.fetchFileInfo(nextProps); + const { isResolvingUri, resolveUri, claim, navigation } = nextProps; + const { uri } = navigation.state.params; + + if (!isResolvingUri && claim === undefined && uri) { + resolveUri(uri); + } } fetchFileInfo(props) { @@ -113,72 +127,87 @@ class FilePage extends React.PureComponent { contentType, tab, rewardedContentClaimIds, + isResolvingUri, + blackListedOutpoints, navigation - } = this.props; + } = this.props; + const { uri } = navigation.state.params; - if (!claim || !metadata) { - return ( + let innerContent = null; + if ((isResolvingUri && !claim) || !claim) { + innerContent = ( - Empty claim or metadata info. + {isResolvingUri && + + + Loading decentralized data... + } + { claim === null && !isResolvingUri && + + There's nothing at this location. + + } + + + ); + } else if (claim && claim.name.length && claim.name[0] === '@') { + innerContent = ( + + ); + } else if (claim) { + const completed = fileInfo && fileInfo.completed; + const title = metadata.title; + const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id); + const description = metadata.description ? metadata.description : null; + const mediaType = Lbry.getMediaType(contentType); + const isPlayable = mediaType === 'video' || mediaType === 'audio'; + const { height, channel_name: channelName, value } = claim; + const showActions = !this.state.fullscreenMode && + (completed || (fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes)); + const channelClaimId = + value && value.publisherSignature && value.publisherSignature.certificateId; + + const playerStyle = [filePageStyle.player, this.state.fullscreenMode ? + filePageStyle.fullscreenPlayer : filePageStyle.containedPlayer]; + const playerBgStyle = [filePageStyle.playerBackground, this.state.fullscreenMode ? + filePageStyle.fullscreenPlayerBackground : filePageStyle.containedPlayerBackground]; + // 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 + + innerContent = ( + + + {(!fileInfo || (isPlayable && !canLoadMedia)) && + } + {isPlayable && !this.state.mediaLoaded && } + {!completed && !canLoadMedia && } + + {canLoadMedia && } + {canLoadMedia && { this.setState({ mediaLoaded: true }); }}/>} + + { showActions && + + {completed &&