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..cd597185 --- /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 155d1c9a..4d603731 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 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..4df31452 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 &&