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 && }
+ {!completed && fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes &&
+
+ }
+ }
+
+ {title}
+ {channelName && {channelName}}
+ {description && {description}}
+
+
);
}
- 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
-
- return (
-
-
- {(!fileInfo || (isPlayable && !canLoadMedia)) &&
- }
- {isPlayable && !this.state.mediaLoaded && }
- {!completed && !canLoadMedia && }
-
- {canLoadMedia && }
- {canLoadMedia && { this.setState({ mediaLoaded: true }); }}/>}
-
- { showActions &&
-
- {completed && }
- {!completed && fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes &&
-
- }
- }
-
- {title}
- {channelName && {channelName}}
- {description && {description}}
-
-
-
-
-
- );
+ return innerContent;
}
}
diff --git a/app/src/styles/channelPage.js b/app/src/styles/channelPage.js
new file mode 100644
index 00000000..6177d2b6
--- /dev/null
+++ b/app/src/styles/channelPage.js
@@ -0,0 +1,20 @@
+import { StyleSheet } from 'react-native';
+import Colors from './colors';
+
+const channelPageStyle = StyleSheet.create({
+ container: {
+ flex: 1,
+ justifyContent: 'center',
+ },
+ content: {
+ flex: 1
+ },
+ title: {
+ color: Colors.LbryGreen,
+ fontFamily: 'Metropolis-SemiBold',
+ fontSize: 30,
+ margin: 16
+ }
+});
+
+export default channelPageStyle;
diff --git a/app/src/styles/discover.js b/app/src/styles/discover.js
index 0bed76ad..d6897d2d 100644
--- a/app/src/styles/discover.js
+++ b/app/src/styles/discover.js
@@ -2,17 +2,22 @@ import { StyleSheet } from 'react-native';
const discoverStyle = StyleSheet.create({
container: {
- flex: 1,
- justifyContent: 'center',
+ flex: 1
},
scrollContainer: {
flex: 1
},
+ busyContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ flexDirection: 'row'
+ },
title: {
fontFamily: 'Metropolis-Regular',
fontSize: 20,
textAlign: 'center',
- margin: 10,
+ marginLeft: 10
},
categoryName: {
fontFamily: 'Metropolis-Regular',
diff --git a/app/src/styles/fileDownloadButton.js b/app/src/styles/fileDownloadButton.js
index 8257f2df..0b0f2b4d 100644
--- a/app/src/styles/fileDownloadButton.js
+++ b/app/src/styles/fileDownloadButton.js
@@ -2,7 +2,7 @@ import { StyleSheet } from 'react-native';
const fileDownloadButtonStyle = StyleSheet.create({
container: {
- width: 120,
+ width: 160,
height: 36,
borderRadius: 18,
justifyContent: 'center',
diff --git a/app/src/styles/filePage.js b/app/src/styles/filePage.js
index ec1c0c28..aa9c1aaa 100644
--- a/app/src/styles/filePage.js
+++ b/app/src/styles/filePage.js
@@ -116,19 +116,17 @@ const filePageStyle = StyleSheet.create({
position: 'absolute',
top: '40%'
},
- uriContainer: {
- padding: 8,
- backgroundColor: Colors.VeryLightGrey,
- alignSelf: 'flex-end'
+ busyContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ flexDirection: 'row'
},
- uriText: {
- backgroundColor: Colors.White,
- borderWidth: 1,
- borderColor: Colors.LightGrey,
- padding: 8,
- borderRadius: 4,
+ infoText: {
fontFamily: 'Metropolis-Regular',
- fontSize: 16
+ fontSize: 20,
+ textAlign: 'center',
+ marginLeft: 10
}
});
diff --git a/app/src/styles/uriBar.js b/app/src/styles/uriBar.js
new file mode 100644
index 00000000..9f9ab752
--- /dev/null
+++ b/app/src/styles/uriBar.js
@@ -0,0 +1,30 @@
+import { StyleSheet } from 'react-native';
+import Colors from './colors';
+
+const uriBarStyle = StyleSheet.create({
+ uriContainer: {
+ backgroundColor: Colors.White,
+ padding: 8,
+ alignSelf: 'flex-end',
+ width: '100%',
+ shadowColor: Colors.Black,
+ shadowOpacity: 0.1,
+ shadowRadius: StyleSheet.hairlineWidth,
+ shadowOffset: {
+ height: StyleSheet.hairlineWidth,
+ },
+ elevation: 4
+ },
+ uriText: {
+ backgroundColor: Colors.White,
+ borderWidth: 1,
+ borderColor: Colors.LightGrey,
+ padding: 8,
+ borderRadius: 4,
+ fontFamily: 'Metropolis-Regular',
+ fontSize: 16,
+ width: '100%'
+ }
+});
+
+export default uriBarStyle;