implement direct URI navigation #134
14 changed files with 287 additions and 83 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
|
doFetchCostInfoForUri,
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
makeSelectDownloadingForUri,
|
makeSelectDownloadingForUri,
|
||||||
makeSelectLoadingForUri,
|
makeSelectLoadingForUri,
|
||||||
|
@ -17,7 +18,8 @@ const select = (state, props) => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
|
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);
|
export default connect(select, perform)(FileDownloadButton);
|
|
@ -3,6 +3,13 @@ import { NativeModules, Text, View, TouchableOpacity } from 'react-native';
|
||||||
import fileDownloadButtonStyle from '../../styles/fileDownloadButton';
|
import fileDownloadButtonStyle from '../../styles/fileDownloadButton';
|
||||||
|
|
||||||
class FileDownloadButton extends React.PureComponent {
|
class FileDownloadButton extends React.PureComponent {
|
||||||
|
componentDidMount() {
|
||||||
|
const { costInfo, fetchCostInfo, uri } = this.props;
|
||||||
|
if (costInfo === undefined) {
|
||||||
|
fetchCostInfo(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
//this.checkAvailability(nextProps.uri);
|
//this.checkAvailability(nextProps.uri);
|
||||||
this.restartDownload(nextProps);
|
this.restartDownload(nextProps);
|
||||||
|
@ -54,7 +61,7 @@ class FileDownloadButton extends React.PureComponent {
|
||||||
if (!costInfo) {
|
if (!costInfo) {
|
||||||
return (
|
return (
|
||||||
<View style={[style, fileDownloadButtonStyle.container]}>
|
<View style={[style, fileDownloadButtonStyle.container]}>
|
||||||
<Text>Fetching cost info...</Text>
|
<Text style={fileDownloadButtonStyle.text}>Fetching cost info...</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
12
app/src/component/uriBar/index.js
Normal file
12
app/src/component/uriBar/index.js
Normal file
|
@ -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);
|
44
app/src/component/uriBar/view.js
Normal file
44
app/src/component/uriBar/view.js
Normal file
|
@ -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 (
|
||||||
|
<View style={uriBarStyle.uriContainer}>
|
||||||
|
<TextInput style={uriBarStyle.uriText}
|
||||||
|
placeholder={'Enter a LBRY URI or some text'}
|
||||||
|
underlineColorAndroid={'transparent'}
|
||||||
|
numberOfLines={1}
|
||||||
|
value={this.state.currentValue}
|
||||||
|
returnKeyType={'go'}
|
||||||
|
onChangeText={(text) => 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) });
|
||||||
|
}
|
||||||
|
}}/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UriBar;
|
19
app/src/page/channel/index.js
Normal file
19
app/src/page/channel/index.js
Normal file
|
@ -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);
|
24
app/src/page/channel/view.js
Normal file
24
app/src/page/channel/view.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
You can remove this You can remove this
|
|||||||
|
// @flow
|
||||||
You can remove this You can remove this
|
|||||||
|
import React from 'react';
|
||||||
You can remove this You can remove this
|
|||||||
|
import { ScrollView, Text, View } from 'react-native';
|
||||||
You can remove this You can remove this
|
|||||||
|
import UriBar from '../../component/uriBar';
|
||||||
You can remove this You can remove this
|
|||||||
|
import channelPageStyle from '../../styles/channelPage';
|
||||||
You can remove this You can remove this
|
|||||||
|
|
||||||
You can remove this You can remove this
|
|||||||
|
class ChannelPage extends React.PureComponent {
|
||||||
You can remove this You can remove this
|
|||||||
|
render() {
|
||||||
You can remove this You can remove this
|
|||||||
|
const { claim, navigation, uri } = this.props;
|
||||||
You can remove this You can remove this
|
|||||||
|
const { name } = claim;
|
||||||
You can remove this You can remove this
|
|||||||
|
|
||||||
You can remove this You can remove this
|
|||||||
|
return (
|
||||||
You can remove this You can remove this
|
|||||||
|
<View style={channelPageStyle.container}>
|
||||||
You can remove this You can remove this
|
|||||||
|
<Text style={channelPageStyle.title}>{name}</Text>
|
||||||
You can remove this You can remove this
|
|||||||
|
<ScrollView style={channelPageStyle.content}>
|
||||||
You can remove this You can remove this
|
|||||||
|
|
||||||
You can remove this You can remove this
|
|||||||
|
</ScrollView>
|
||||||
You can remove this You can remove this
|
|||||||
|
<UriBar value={uri} navigation={navigation} />
|
||||||
You can remove this You can remove this
|
|||||||
|
</View>
|
||||||
You can remove this You can remove this
|
|||||||
|
)
|
||||||
You can remove this You can remove this
|
|||||||
|
}
|
||||||
You can remove this You can remove this
|
|||||||
|
}
|
||||||
You can remove this You can remove this
|
|||||||
|
|
||||||
You can remove this You can remove this
|
|||||||
|
export default ChannelPage;
|
||||||
You can remove this You can remove this
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import NavigationActions from 'react-navigation';
|
import NavigationActions from 'react-navigation';
|
||||||
import {
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
AsyncStorage,
|
AsyncStorage,
|
||||||
NativeModules,
|
NativeModules,
|
||||||
SectionList,
|
SectionList,
|
||||||
ScrollView,
|
|
||||||
Text,
|
Text,
|
||||||
View
|
View
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
@ -12,6 +12,8 @@ import { normalizeURI } from 'lbry-redux';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import FileItem from '../../component/fileItem';
|
import FileItem from '../../component/fileItem';
|
||||||
import discoverStyle from '../../styles/discover';
|
import discoverStyle from '../../styles/discover';
|
||||||
|
import Colors from '../../styles/colors';
|
||||||
|
import UriBar from '../../component/uriBar';
|
||||||
import Feather from 'react-native-vector-icons/Feather';
|
import Feather from 'react-native-vector-icons/Feather';
|
||||||
|
|
||||||
class DiscoverPage extends React.PureComponent {
|
class DiscoverPage extends React.PureComponent {
|
||||||
|
@ -48,7 +50,12 @@ class DiscoverPage extends React.PureComponent {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={discoverStyle.container}>
|
<View style={discoverStyle.container}>
|
||||||
{!hasContent && fetchingFeaturedUris && <Text style={discoverStyle.title}>Fetching content...</Text>}
|
{!hasContent && fetchingFeaturedUris && (
|
||||||
|
<View style={discoverStyle.busyContainer}>
|
||||||
|
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||||
|
<Text style={discoverStyle.title}>Fetching content...</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
{hasContent &&
|
{hasContent &&
|
||||||
<SectionList style={discoverStyle.scrollContainer}
|
<SectionList style={discoverStyle.scrollContainer}
|
||||||
renderItem={ ({item, index, section}) => (
|
renderItem={ ({item, index, section}) => (
|
||||||
|
@ -66,6 +73,7 @@ class DiscoverPage extends React.PureComponent {
|
||||||
keyExtractor={(item, index) => item}
|
keyExtractor={(item, index) => item}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
<UriBar navigation={navigation} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
doFetchFileInfo,
|
doFetchFileInfo,
|
||||||
makeSelectFileInfoForUri,
|
doResolveUri,
|
||||||
doFetchCostInfoForUri,
|
doFetchCostInfoForUri,
|
||||||
|
makeSelectIsUriResolving,
|
||||||
|
makeSelectCostInfoForUri,
|
||||||
|
makeSelectFileInfoForUri,
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
makeSelectContentTypeForUri,
|
makeSelectContentTypeForUri,
|
||||||
makeSelectMetadataForUri,
|
makeSelectMetadataForUri,
|
||||||
selectRewardContentClaimIds,
|
selectRewardContentClaimIds,
|
||||||
makeSelectCostInfoForUri
|
selectBlackListedOutpoints,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { doDeleteFile, doStopDownloadingFile } from '../../redux/actions/file';
|
import { doDeleteFile, doStopDownloadingFile } from '../../redux/actions/file';
|
||||||
import FilePage from './view';
|
import FilePage from './view';
|
||||||
|
@ -15,7 +18,9 @@ import FilePage from './view';
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
const selectProps = { uri: props.navigation.state.params.uri };
|
const selectProps = { uri: props.navigation.state.params.uri };
|
||||||
return {
|
return {
|
||||||
|
blackListedOutpoints: selectBlackListedOutpoints(state),
|
||||||
claim: makeSelectClaimForUri(selectProps.uri)(state),
|
claim: makeSelectClaimForUri(selectProps.uri)(state),
|
||||||
|
isResolvingUri: makeSelectIsUriResolving(selectProps.uri)(state),
|
||||||
contentType: makeSelectContentTypeForUri(selectProps.uri)(state),
|
contentType: makeSelectContentTypeForUri(selectProps.uri)(state),
|
||||||
costInfo: makeSelectCostInfoForUri(selectProps.uri)(state),
|
costInfo: makeSelectCostInfoForUri(selectProps.uri)(state),
|
||||||
metadata: makeSelectMetadataForUri(selectProps.uri)(state),
|
metadata: makeSelectMetadataForUri(selectProps.uri)(state),
|
||||||
|
@ -29,6 +34,7 @@ const select = (state, props) => {
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
||||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||||
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
stopDownload: (uri, fileInfo) => dispatch(doStopDownloadingFile(uri, fileInfo)),
|
stopDownload: (uri, fileInfo) => dispatch(doStopDownloadingFile(uri, fileInfo)),
|
||||||
deleteFile: (fileInfo, deleteFromDevice, abandonClaim) => {
|
deleteFile: (fileInfo, deleteFromDevice, abandonClaim) => {
|
||||||
dispatch(doDeleteFile(fileInfo, deleteFromDevice, abandonClaim));
|
dispatch(doDeleteFile(fileInfo, deleteFromDevice, abandonClaim));
|
||||||
|
|
|
@ -13,9 +13,11 @@ import {
|
||||||
NativeModules
|
NativeModules
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Colors from '../../styles/colors';
|
import Colors from '../../styles/colors';
|
||||||
|
import ChannelPage from '../channel';
|
||||||
import FileItemMedia from '../../component/fileItemMedia';
|
import FileItemMedia from '../../component/fileItemMedia';
|
||||||
import FileDownloadButton from '../../component/fileDownloadButton';
|
import FileDownloadButton from '../../component/fileDownloadButton';
|
||||||
import MediaPlayer from '../../component/mediaPlayer';
|
import MediaPlayer from '../../component/mediaPlayer';
|
||||||
|
import UriBar from '../../component/uriBar';
|
||||||
import Video from 'react-native-video';
|
import Video from 'react-native-video';
|
||||||
import filePageStyle from '../../styles/filePage';
|
import filePageStyle from '../../styles/filePage';
|
||||||
|
|
||||||
|
@ -34,15 +36,27 @@ class FilePage extends React.PureComponent {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
StatusBar.setHidden(false);
|
StatusBar.setHidden(false);
|
||||||
|
|
||||||
|
const { isResolvingUri, resolveUri, navigation } = this.props;
|
||||||
|
const { uri } = navigation.state.params;
|
||||||
|
if (!isResolvingUri) resolveUri(uri);
|
||||||
|
|
||||||
this.fetchFileInfo(this.props);
|
this.fetchFileInfo(this.props);
|
||||||
this.fetchCostInfo(this.props);
|
this.fetchCostInfo(this.props);
|
||||||
|
|
||||||
if (NativeModules.Mixpanel) {
|
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) {
|
componentDidUpdate() {
|
||||||
this.fetchFileInfo(nextProps);
|
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) {
|
fetchFileInfo(props) {
|
||||||
|
@ -113,72 +127,87 @@ class FilePage extends React.PureComponent {
|
||||||
contentType,
|
contentType,
|
||||||
tab,
|
tab,
|
||||||
rewardedContentClaimIds,
|
rewardedContentClaimIds,
|
||||||
|
isResolvingUri,
|
||||||
|
blackListedOutpoints,
|
||||||
navigation
|
navigation
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const { uri } = navigation.state.params;
|
||||||
|
|
||||||
if (!claim || !metadata) {
|
let innerContent = null;
|
||||||
return (
|
if ((isResolvingUri && !claim) || !claim) {
|
||||||
|
innerContent = (
|
||||||
<View style={filePageStyle.container}>
|
<View style={filePageStyle.container}>
|
||||||
<Text style={filePageStyle.emptyClaimText}>Empty claim or metadata info.</Text>
|
{isResolvingUri &&
|
||||||
|
<View style={filePageStyle.busyContainer}>
|
||||||
|
<ActivityIndicator size="large" color={Colors.LbryGreen} />
|
||||||
|
<Text style={filePageStyle.infoText}>Loading decentralized data...</Text>
|
||||||
|
</View>}
|
||||||
|
{ claim === null && !isResolvingUri &&
|
||||||
|
<View style={filePageStyle.container}>
|
||||||
|
<Text style={filePageStyle.emptyClaimText}>There's nothing at this location.</Text>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
<UriBar value={uri} navigation={navigation} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
} else if (claim && claim.name.length && claim.name[0] === '@') {
|
||||||
|
innerContent = (
|
||||||
|
<ChannelPage uri={uri} navigation={navigation} />
|
||||||
|
);
|
||||||
|
} 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 = (
|
||||||
|
<View style={filePageStyle.pageContainer}>
|
||||||
|
<View style={filePageStyle.mediaContainer}>
|
||||||
|
{(!fileInfo || (isPlayable && !canLoadMedia)) &&
|
||||||
|
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />}
|
||||||
|
{isPlayable && !this.state.mediaLoaded && <ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />}
|
||||||
|
{!completed && !canLoadMedia && <FileDownloadButton uri={uri} style={filePageStyle.downloadButton} />}
|
||||||
|
</View>
|
||||||
|
{canLoadMedia && <View style={playerBgStyle} />}
|
||||||
|
{canLoadMedia && <MediaPlayer fileInfo={fileInfo}
|
||||||
|
uri={uri}
|
||||||
|
style={playerStyle}
|
||||||
|
onFullscreenToggled={this.handleFullscreenToggle}
|
||||||
|
onMediaLoaded={() => { this.setState({ mediaLoaded: true }); }}/>}
|
||||||
|
|
||||||
|
{ showActions &&
|
||||||
|
<View style={filePageStyle.actions}>
|
||||||
|
{completed && <Button color="red" title="Delete" onPress={this.onDeletePressed} />}
|
||||||
|
{!completed && fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes &&
|
||||||
|
<Button color="red" title="Stop Download" onPress={this.onStopDownloadPressed} />
|
||||||
|
}
|
||||||
|
</View>}
|
||||||
|
<ScrollView style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}>
|
||||||
|
<Text style={filePageStyle.title} selectable={true}>{title}</Text>
|
||||||
|
{channelName && <Text style={filePageStyle.channelName} selectable={true}>{channelName}</Text>}
|
||||||
|
{description && <Text style={filePageStyle.description} selectable={true}>{description}</Text>}
|
||||||
|
</ScrollView>
|
||||||
|
<UriBar value={uri} navigation={navigation} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const completed = fileInfo && fileInfo.completed;
|
return innerContent;
|
||||||
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 (
|
|
||||||
<View style={filePageStyle.pageContainer}>
|
|
||||||
<View style={filePageStyle.mediaContainer}>
|
|
||||||
{(!fileInfo || (isPlayable && !canLoadMedia)) &&
|
|
||||||
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />}
|
|
||||||
{isPlayable && !this.state.mediaLoaded && <ActivityIndicator size="large" color={Colors.LbryGreen} style={filePageStyle.loading} />}
|
|
||||||
{!completed && !canLoadMedia && <FileDownloadButton uri={navigation.state.params.uri} style={filePageStyle.downloadButton} />}
|
|
||||||
</View>
|
|
||||||
{canLoadMedia && <View style={playerBgStyle} />}
|
|
||||||
{canLoadMedia && <MediaPlayer fileInfo={fileInfo}
|
|
||||||
uri={navigation.state.params.uri}
|
|
||||||
style={playerStyle}
|
|
||||||
onFullscreenToggled={this.handleFullscreenToggle}
|
|
||||||
onMediaLoaded={() => { this.setState({ mediaLoaded: true }); }}/>}
|
|
||||||
|
|
||||||
{ showActions &&
|
|
||||||
<View style={filePageStyle.actions}>
|
|
||||||
{completed && <Button color="red" title="Delete" onPress={this.onDeletePressed} />}
|
|
||||||
{!completed && fileInfo && !fileInfo.stopped && fileInfo.written_bytes < fileInfo.total_bytes &&
|
|
||||||
<Button color="red" title="Stop Download" onPress={this.onStopDownloadPressed} />
|
|
||||||
}
|
|
||||||
</View>}
|
|
||||||
<ScrollView style={showActions ? filePageStyle.scrollContainerActions : filePageStyle.scrollContainer}>
|
|
||||||
<Text style={filePageStyle.title} selectable={true}>{title}</Text>
|
|
||||||
{channelName && <Text style={filePageStyle.channelName} selectable={true}>{channelName}</Text>}
|
|
||||||
{description && <Text style={filePageStyle.description} selectable={true}>{description}</Text>}
|
|
||||||
</ScrollView>
|
|
||||||
<View style={filePageStyle.uriContainer}>
|
|
||||||
<TextInput style={filePageStyle.uriText}
|
|
||||||
underlineColorAndroid={'transparent'}
|
|
||||||
numberOfLines={1}
|
|
||||||
value={navigation.state.params.uri} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
app/src/styles/channelPage.js
Normal file
20
app/src/styles/channelPage.js
Normal file
|
@ -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;
|
|
@ -2,17 +2,22 @@ import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
const discoverStyle = StyleSheet.create({
|
const discoverStyle = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
},
|
||||||
scrollContainer: {
|
scrollContainer: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
|
busyContainer: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
fontFamily: 'Metropolis-Regular',
|
fontFamily: 'Metropolis-Regular',
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
margin: 10,
|
marginLeft: 10
|
||||||
},
|
},
|
||||||
categoryName: {
|
categoryName: {
|
||||||
fontFamily: 'Metropolis-Regular',
|
fontFamily: 'Metropolis-Regular',
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
const fileDownloadButtonStyle = StyleSheet.create({
|
const fileDownloadButtonStyle = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
width: 120,
|
width: 160,
|
||||||
height: 36,
|
height: 36,
|
||||||
borderRadius: 18,
|
borderRadius: 18,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
|
|
@ -116,19 +116,17 @@ const filePageStyle = StyleSheet.create({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '40%'
|
top: '40%'
|
||||||
},
|
},
|
||||||
uriContainer: {
|
busyContainer: {
|
||||||
padding: 8,
|
flex: 1,
|
||||||
backgroundColor: Colors.VeryLightGrey,
|
justifyContent: 'center',
|
||||||
alignSelf: 'flex-end'
|
alignItems: 'center',
|
||||||
|
flexDirection: 'row'
|
||||||
},
|
},
|
||||||
uriText: {
|
infoText: {
|
||||||
backgroundColor: Colors.White,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: Colors.LightGrey,
|
|
||||||
padding: 8,
|
|
||||||
borderRadius: 4,
|
|
||||||
fontFamily: 'Metropolis-Regular',
|
fontFamily: 'Metropolis-Regular',
|
||||||
fontSize: 16
|
fontSize: 20,
|
||||||
|
textAlign: 'center',
|
||||||
|
marginLeft: 10
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
30
app/src/styles/uriBar.js
Normal file
30
app/src/styles/uriBar.js
Normal file
|
@ -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;
|
Loading…
Reference in a new issue
You can remove this