From 4fbf90654ef46d779ab9e50ed476815a2c4c98b3 Mon Sep 17 00:00:00 2001 From: akinwale Date: Thu, 24 May 2018 23:47:55 +0100 Subject: [PATCH] implement direct URI navigation (#134) --- 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 | 24 +++ app/src/page/discover/view.js | 12 +- app/src/page/file/index.js | 10 +- app/src/page/file/view.js | 153 +++++++++++------- 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, 287 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 ee81d2d..fb7256d 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 1a7354e..5eb2993 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 0000000..703caa0 --- /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 0000000..a66a24b --- /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 0000000..983b9a3 --- /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 0000000..cd59718 --- /dev/null +++ b/app/src/page/channel/view.js @@ -0,0 +1,24 @@ +// @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 { + 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 155d1c9..4d60373 100644 --- a/app/src/page/discover/view.js +++ b/app/src/page/discover/view.js @@ -1,10 +1,10 @@ import React from 'react'; import NavigationActions from 'react-navigation'; import { + ActivityIndicator, AsyncStorage, NativeModules, SectionList, - ScrollView, Text, View } from 'react-native'; @@ -12,6 +12,8 @@ import { normalizeURI } from 'lbry-redux'; import moment from 'moment'; import FileItem from '../../component/fileItem'; 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 { @@ -48,7 +50,12 @@ class DiscoverPage extends React.PureComponent { return ( - {!hasContent && fetchingFeaturedUris && Fetching content...} + {!hasContent && fetchingFeaturedUris && ( + + + Fetching content... + + )} {hasContent && ( @@ -66,6 +73,7 @@ class DiscoverPage extends React.PureComponent { keyExtractor={(item, index) => item} /> } + ); } diff --git a/app/src/page/file/index.js b/app/src/page/file/index.js index 18b5bde..19e5d49 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 336be16..4df3145 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); + componentDidUpdate() { + this.fetchFileInfo(this.props); + const { isResolvingUri, resolveUri, claim, navigation } = this.props; + 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 &&