Editor's Choice page
This commit is contained in:
parent
aa5dd878c0
commit
f16d068a7d
10 changed files with 258 additions and 5 deletions
2
android
2
android
|
@ -1 +1 @@
|
||||||
Subproject commit ff30e7f6a4358fd997a9e6d9f75bfe6959eafcb6
|
Subproject commit cc3055f1c98c4dd5e5bbccdd9d4a39a5a37fdf82
|
|
@ -4,6 +4,7 @@ import ChannelCreatorPage from 'page/channelCreator';
|
||||||
import DiscoverPage from 'page/discover';
|
import DiscoverPage from 'page/discover';
|
||||||
import DownloadsPage from 'page/downloads';
|
import DownloadsPage from 'page/downloads';
|
||||||
import DrawerContent from 'component/drawerContent';
|
import DrawerContent from 'component/drawerContent';
|
||||||
|
import EditorsChoicePage from 'page/editorsChoice';
|
||||||
import FilePage from 'page/file';
|
import FilePage from 'page/file';
|
||||||
import LiteFilePage from 'page/liteFile';
|
import LiteFilePage from 'page/liteFile';
|
||||||
import FirstRunScreen from 'page/firstRun';
|
import FirstRunScreen from 'page/firstRun';
|
||||||
|
@ -164,6 +165,13 @@ const drawer = createDrawerNavigator(
|
||||||
drawerIcon: ({ tintColor }) => <Icon name="home" size={drawerIconSize} style={{ color: tintColor }} />,
|
drawerIcon: ({ tintColor }) => <Icon name="home" size={drawerIconSize} style={{ color: tintColor }} />,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
EditorsChoice: {
|
||||||
|
screen: EditorsChoicePage,
|
||||||
|
navigationOptions: {
|
||||||
|
title: "Editor's Choice",
|
||||||
|
drawerIcon: ({ tintColor }) => <Icon name="star" size={drawerIconSize} style={{ color: tintColor }} />,
|
||||||
|
},
|
||||||
|
},
|
||||||
Discover: {
|
Discover: {
|
||||||
screen: DiscoverPage,
|
screen: DiscoverPage,
|
||||||
navigationOptions: ({ navigation }) => ({
|
navigationOptions: ({ navigation }) => ({
|
||||||
|
|
|
@ -3,6 +3,7 @@ import NavigationActions from 'react-navigation';
|
||||||
import { ActivityIndicator, FlatList, Text, TouchableOpacity, View } from 'react-native';
|
import { ActivityIndicator, FlatList, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { MATURE_TAGS, normalizeURI, createNormalizedClaimSearchKey } from 'lbry-redux';
|
import { MATURE_TAGS, normalizeURI, createNormalizedClaimSearchKey } from 'lbry-redux';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import EditorsChoiceItem from 'component/editorsChoiceItem';
|
||||||
import FileItem from 'component/fileItem';
|
import FileItem from 'component/fileItem';
|
||||||
import FileListItem from 'component/fileListItem';
|
import FileListItem from 'component/fileListItem';
|
||||||
import Icon from 'react-native-vector-icons/FontAwesome5';
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
|
@ -72,7 +73,7 @@ class ClaimList extends React.PureComponent {
|
||||||
|
|
||||||
buildClaimSearchOptions() {
|
buildClaimSearchOptions() {
|
||||||
const { orderBy, channelIds, showNsfwContent, tags, time } = this.props;
|
const { orderBy, channelIds, showNsfwContent, tags, time } = this.props;
|
||||||
const { currentPage, subscriptionsView } = this.state;
|
const { currentPage } = this.state;
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
order_by: orderBy,
|
order_by: orderBy,
|
||||||
|
@ -101,7 +102,7 @@ class ClaimList extends React.PureComponent {
|
||||||
return `>${Math.floor(
|
return `>${Math.floor(
|
||||||
moment()
|
moment()
|
||||||
.subtract(1, time)
|
.subtract(1, time)
|
||||||
.unix()
|
.unix(),
|
||||||
)}`;
|
)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ class ClaimList extends React.PureComponent {
|
||||||
const uris = claimSearchByQuery[claimSearchKey];
|
const uris = claimSearchByQuery[claimSearchKey];
|
||||||
if (
|
if (
|
||||||
lastPageReached[claimSearchKey] ||
|
lastPageReached[claimSearchKey] ||
|
||||||
((uris.length > 0 && uris.length < Constants.DEFAULT_PAGE_SIZE) || uris.length >= softLimit)
|
(uris.length > 0 && uris.length < Constants.DEFAULT_PAGE_SIZE) || uris.length >= softLimit
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -174,6 +175,12 @@ class ClaimList extends React.PureComponent {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
renderEditorsChoiceItem = ({ item }) => {
|
||||||
|
const { navigation } = this.props;
|
||||||
|
|
||||||
|
return <EditorsChoiceItem style={discoverStyle.fileItem} key={item} uri={normalizeURI(item)} />;
|
||||||
|
};
|
||||||
|
|
||||||
renderHorizontalItem = ({ item }) => {
|
renderHorizontalItem = ({ item }) => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
|
||||||
|
@ -195,6 +202,7 @@ class ClaimList extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
ListHeaderComponent,
|
ListHeaderComponent,
|
||||||
|
editorsChoice,
|
||||||
loading,
|
loading,
|
||||||
morePlaceholder,
|
morePlaceholder,
|
||||||
navigation,
|
navigation,
|
||||||
|
@ -226,7 +234,7 @@ class ClaimList extends React.PureComponent {
|
||||||
initialNumToRender={10}
|
initialNumToRender={10}
|
||||||
maxToRenderPerBatch={20}
|
maxToRenderPerBatch={20}
|
||||||
removeClippedSubviews
|
removeClippedSubviews
|
||||||
renderItem={this.renderVerticalItem}
|
renderItem={editorsChoice ? this.renderEditorsChoiceItem : this.renderVerticalItem}
|
||||||
data={uris}
|
data={uris}
|
||||||
keyExtractor={(item, index) => item}
|
keyExtractor={(item, index) => item}
|
||||||
onEndReached={this.handleVerticalEndReached}
|
onEndReached={this.handleVerticalEndReached}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { formatUsd } from 'utils/helper';
|
||||||
const groupedMenuItems = {
|
const groupedMenuItems = {
|
||||||
'Find content': [
|
'Find content': [
|
||||||
{ icon: 'heart', solid: true, label: 'Following', route: Constants.DRAWER_ROUTE_SUBSCRIPTIONS },
|
{ icon: 'heart', solid: true, label: 'Following', route: Constants.DRAWER_ROUTE_SUBSCRIPTIONS },
|
||||||
|
{ icon: 'star', solid: true, label: "Editor's Choice", route: Constants.DRAWER_ROUTE_EDITORS_CHOICE },
|
||||||
{ icon: 'hashtag', label: 'Your Tags', route: Constants.DRAWER_ROUTE_DISCOVER },
|
{ icon: 'hashtag', label: 'Your Tags', route: Constants.DRAWER_ROUTE_DISCOVER },
|
||||||
{ icon: 'globe-americas', label: 'All Content', route: Constants.DRAWER_ROUTE_TRENDING },
|
{ icon: 'globe-americas', label: 'All Content', route: Constants.DRAWER_ROUTE_TRENDING },
|
||||||
],
|
],
|
||||||
|
|
22
src/component/editorsChoiceItem/index.js
Normal file
22
src/component/editorsChoiceItem/index.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
doResolveUri,
|
||||||
|
makeSelectClaimForUri,
|
||||||
|
makeSelectMetadataForUri,
|
||||||
|
makeSelectTitleForUri,
|
||||||
|
makeSelectThumbnailForUri,
|
||||||
|
} from 'lbry-redux';
|
||||||
|
import EditorsChoiceItem from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
|
title: makeSelectTitleForUri(props.uri)(state),
|
||||||
|
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(EditorsChoiceItem);
|
63
src/component/editorsChoiceItem/view.js
Normal file
63
src/component/editorsChoiceItem/view.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { normalizeURI, parseURI } from 'lbry-redux';
|
||||||
|
import { ActivityIndicator, Platform, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
import { navigateToUri } from 'utils/helper';
|
||||||
|
import Colors from 'styles/colors';
|
||||||
|
import ChannelIconItem from 'component/channelIconItem';
|
||||||
|
import channelIconStyle from 'styles/channelIcon';
|
||||||
|
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||||
|
import DateTime from 'component/dateTime';
|
||||||
|
import FastImage from 'react-native-fast-image';
|
||||||
|
import FileItemMedia from 'component/fileItemMedia';
|
||||||
|
import FilePrice from 'component/filePrice';
|
||||||
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
|
import Link from 'component/link';
|
||||||
|
import NsfwOverlay from 'component/nsfwOverlay';
|
||||||
|
import ProgressBar from 'component/progressBar';
|
||||||
|
import editorsChoiceStyle from 'styles/editorsChoice';
|
||||||
|
|
||||||
|
class EditorsChoiceItem extends React.PureComponent {
|
||||||
|
componentDidMount() {
|
||||||
|
const { claim, resolveUri, uri, batchResolve } = this.props;
|
||||||
|
if (!claim && !batchResolve) {
|
||||||
|
resolveUri(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultOnPress = () => {
|
||||||
|
const { autoplay, claim, navigation, uri } = this.props;
|
||||||
|
navigateToUri(navigation, uri, { autoplay }, false, claim ? claim.permanent_url : null);
|
||||||
|
};
|
||||||
|
|
||||||
|
onPressHandler = () => {
|
||||||
|
const { claim, onPress } = this.props;
|
||||||
|
if (onPress) {
|
||||||
|
onPress(claim);
|
||||||
|
} else {
|
||||||
|
this.defaultOnPress();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { metadata, title, thumbnail } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity style={editorsChoiceStyle.item}>
|
||||||
|
<FastImage
|
||||||
|
style={editorsChoiceStyle.thumbnail}
|
||||||
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
|
source={{ uri: thumbnail }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<View style={editorsChoiceStyle.detailsContainer}>
|
||||||
|
<Text style={editorsChoiceStyle.title}>{title}</Text>
|
||||||
|
<Text style={editorsChoiceStyle.description}>
|
||||||
|
{metadata.description ? metadata.description.substring(0, 400) : __('No description available')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditorsChoiceItem;
|
|
@ -81,6 +81,7 @@ const Constants = {
|
||||||
PAGE_WALLET: 'wallet',
|
PAGE_WALLET: 'wallet',
|
||||||
|
|
||||||
DRAWER_ROUTE_DISCOVER: 'Discover',
|
DRAWER_ROUTE_DISCOVER: 'Discover',
|
||||||
|
DRAWER_ROUTE_EDITORS_CHOICE: 'EditorsChoice',
|
||||||
DRAWER_ROUTE_TRENDING: 'Trending',
|
DRAWER_ROUTE_TRENDING: 'Trending',
|
||||||
DRAWER_ROUTE_SUBSCRIPTIONS: 'Subscriptions',
|
DRAWER_ROUTE_SUBSCRIPTIONS: 'Subscriptions',
|
||||||
DRAWER_ROUTE_MY_LBRY: 'Downloads',
|
DRAWER_ROUTE_MY_LBRY: 'Downloads',
|
||||||
|
|
16
src/page/editorsChoice/index.js
Normal file
16
src/page/editorsChoice/index.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
|
||||||
|
import { selectCurrentRoute } from 'redux/selectors/drawer';
|
||||||
|
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||||
|
import EditorsChoicePage from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
currentRoute: selectCurrentRoute(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_EDITORS_CHOICE)),
|
||||||
|
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(EditorsChoicePage);
|
90
src/page/editorsChoice/view.js
Normal file
90
src/page/editorsChoice/view.js
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ActivityIndicator, NativeModules, FlatList, Text, TouchableOpacity, ScrollView, View } from 'react-native';
|
||||||
|
import { DEFAULT_FOLLOWED_TAGS, normalizeURI } from 'lbry-redux';
|
||||||
|
import { formatTagTitle, getOrderBy } from 'utils/helper';
|
||||||
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
|
import moment from 'moment';
|
||||||
|
import ClaimList from 'component/claimList';
|
||||||
|
import FileItem from 'component/fileItem';
|
||||||
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
||||||
|
import discoverStyle from 'styles/discover';
|
||||||
|
import fileListStyle from 'styles/fileList';
|
||||||
|
import Colors from 'styles/colors';
|
||||||
|
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
|
||||||
|
import FloatingWalletBalance from 'component/floatingWalletBalance';
|
||||||
|
import Link from 'component/link';
|
||||||
|
import ModalPicker from 'component/modalPicker';
|
||||||
|
import SdkLoadingStatus from 'component/sdkLoadingStatus';
|
||||||
|
import UriBar from 'component/uriBar';
|
||||||
|
import editorsChoiceStyle from 'styles/editorsChoice';
|
||||||
|
|
||||||
|
class EditorsChoicePage extends React.PureComponent {
|
||||||
|
onComponentFocused = () => {
|
||||||
|
const { pushDrawerStack, setPlayerVisible } = this.props;
|
||||||
|
pushDrawerStack();
|
||||||
|
setPlayerVisible();
|
||||||
|
NativeModules.Firebase.setCurrentScreen('EditorsChoice');
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.onComponentFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { currentRoute } = nextProps;
|
||||||
|
const { currentRoute: prevRoute } = this.props;
|
||||||
|
if (Constants.DRAWER_ROUTE_EDITORS_CHOICE === currentRoute && currentRoute !== prevRoute) {
|
||||||
|
this.onComponentFocused();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { navigation } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={editorsChoiceStyle.container}>
|
||||||
|
<UriBar navigation={navigation} />
|
||||||
|
|
||||||
|
<ScrollView style={editorsChoiceStyle.categories} contentContainerStyle={editorsChoiceStyle.categoriesContent}>
|
||||||
|
<Text style={editorsChoiceStyle.category}>{__('Short Films')}</Text>
|
||||||
|
<ClaimList
|
||||||
|
style={editorsChoiceStyle.claimList}
|
||||||
|
channelIds={['7056f8267188fc49cd3f7162b4115d9e3c8216f6']}
|
||||||
|
editorsChoice
|
||||||
|
navigation={navigation}
|
||||||
|
orientation={Constants.ORIENTATION_VERTICAL}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text style={editorsChoiceStyle.category}>{__('Feature-Length Films')}</Text>
|
||||||
|
<ClaimList
|
||||||
|
style={editorsChoiceStyle.claimList}
|
||||||
|
channelIds={['7aad6f36f61da95cb02471fae55f736b28e3bca7']}
|
||||||
|
editorsChoice
|
||||||
|
navigation={navigation}
|
||||||
|
orientation={Constants.ORIENTATION_VERTICAL}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text style={editorsChoiceStyle.category}>{__('Documentaries')}</Text>
|
||||||
|
<ClaimList
|
||||||
|
style={editorsChoiceStyle.claimList}
|
||||||
|
channelIds={['d57c606e11462e821d5596430c336b58716193bb']}
|
||||||
|
editorsChoice
|
||||||
|
navigation={navigation}
|
||||||
|
orientation={Constants.ORIENTATION_VERTICAL}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text style={editorsChoiceStyle.category}>{__('Episodic Content')}</Text>
|
||||||
|
<ClaimList
|
||||||
|
style={editorsChoiceStyle.claimList}
|
||||||
|
channelIds={['ea5fc1bd3e1335776fe2641a539a47850606d7db']}
|
||||||
|
editorsChoice
|
||||||
|
navigation={navigation}
|
||||||
|
orientation={Constants.ORIENTATION_VERTICAL}
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditorsChoicePage;
|
44
src/styles/editorsChoice.js
Normal file
44
src/styles/editorsChoice.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import Colors from './colors';
|
||||||
|
|
||||||
|
const editorsChoiceStyle = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: Colors.PageBackground,
|
||||||
|
},
|
||||||
|
categories: {
|
||||||
|
marginTop: 60,
|
||||||
|
},
|
||||||
|
categoriesContent: {
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
flex: 1,
|
||||||
|
marginTop: 8,
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
fontFamily: 'Inter-SemiBold',
|
||||||
|
fontSize: 24,
|
||||||
|
color: Colors.LbryGreen,
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
thumbnail: {
|
||||||
|
width: '100%',
|
||||||
|
height: 240,
|
||||||
|
},
|
||||||
|
detailsContainer: {
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontFamily: 'Inter-SemiBold',
|
||||||
|
fontSize: 18,
|
||||||
|
marginTop: 8,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
fontFamily: 'Inter-Regular',
|
||||||
|
fontSize: 12,
|
||||||
|
marginTop: 8,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default editorsChoiceStyle;
|
Loading…
Reference in a new issue