Merge pull request #6 from lbryio/discovery-updates

final discovery fixes
This commit is contained in:
Akinwale Ariwodola 2019-07-29 23:43:47 +01:00 committed by GitHub
commit da9276ea75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 171 additions and 34 deletions

View file

@ -23,14 +23,22 @@ const select = (state, props) => {
const perform = dispatch => ({ const perform = dispatch => ({
claimSearch: options => dispatch(doClaimSearch(Constants.DEFAULT_PAGE_SIZE, options)), claimSearch: options => dispatch(doClaimSearch(Constants.DEFAULT_PAGE_SIZE, options)),
searchByTags: (tags, orderBy = Constants.DEFAULT_ORDER_BY, page = 1) => searchByTags: (tags, orderBy = Constants.DEFAULT_ORDER_BY, page = 1, additionalOptions = {}) =>
dispatch( dispatch(
doClaimSearchByTags(tags, Constants.DEFAULT_PAGE_SIZE, { doClaimSearchByTags(
tags,
Constants.DEFAULT_PAGE_SIZE,
Object.assign(
{},
{
no_totals: true, no_totals: true,
order_by: orderBy, order_by: orderBy,
page, page,
not_tags: MATURE_TAGS, not_tags: MATURE_TAGS,
}) },
additionalOptions
)
)
), ),
}); });

View file

@ -1,14 +1,16 @@
import React from 'react'; import React from 'react';
import NavigationActions from 'react-navigation'; import NavigationActions from 'react-navigation';
import { ActivityIndicator, FlatList, Text, View } from 'react-native'; import { ActivityIndicator, FlatList, Text, TouchableOpacity, View } from 'react-native';
import { MATURE_TAGS, normalizeURI } from 'lbry-redux'; import { MATURE_TAGS, normalizeURI } from 'lbry-redux';
import _ from 'lodash'; import _ from 'lodash';
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 Colors from 'styles/colors'; import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import claimListStyle from 'styles/claimList'; import claimListStyle from 'styles/claimList';
import discoverStyle from 'styles/discover'; import discoverStyle from 'styles/discover';
import moment from 'moment';
const horizontalLimit = 10; const horizontalLimit = 10;
const softLimit = 500; const softLimit = 500;
@ -30,6 +32,7 @@ class ClaimList extends React.PureComponent {
orderBy = Constants.DEFAULT_ORDER_BY, orderBy = Constants.DEFAULT_ORDER_BY,
searchByTags, searchByTags,
tags, tags,
time,
} = this.props; } = this.props;
if (channelIds || trendingForAll) { if (channelIds || trendingForAll) {
const options = { const options = {
@ -47,7 +50,11 @@ class ClaimList extends React.PureComponent {
claimSearch(options); claimSearch(options);
} else if (tags && tags.length > 0) { } else if (tags && tags.length > 0) {
searchByTags(tags, orderBy, this.state.currentPgae); const additionalOptions = {};
if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) {
additionalOptions.release_time = this.getReleaseTimeOption(time);
}
searchByTags(tags, orderBy, this.state.currentPage, additionalOptions);
} }
} }
@ -59,12 +66,14 @@ class ClaimList extends React.PureComponent {
tags: prevTags, tags: prevTags,
channelIds: prevChannelIds, channelIds: prevChannelIds,
trendingForAll: prevTrendingForAll, trendingForAll: prevTrendingForAll,
time: prevTime,
} = this.props; } = this.props;
const { orderBy, tags, channelIds, trendingForAll } = nextProps; const { orderBy, tags, channelIds, trendingForAll, time } = nextProps;
if ( if (
!_.isEqual(orderBy, prevOrderBy) || !_.isEqual(orderBy, prevOrderBy) ||
!_.isEqual(tags, prevTags) || !_.isEqual(tags, prevTags) ||
!_.isEqual(channelIds, prevChannelIds) || !_.isEqual(channelIds, prevChannelIds) ||
time !== prevTime ||
trendingForAll !== prevTrendingForAll trendingForAll !== prevTrendingForAll
) { ) {
// reset to page 1 because the order, tags or channelIds changed // reset to page 1 because the order, tags or channelIds changed
@ -90,15 +99,27 @@ class ClaimList extends React.PureComponent {
claimSearch(options); claimSearch(options);
} else if (tags && tags.length > 0) { } else if (tags && tags.length > 0) {
this.setState({ subscriptionsView: false, trendingForAllView: false }); this.setState({ subscriptionsView: false, trendingForAllView: false });
searchByTags(tags, orderBy, this.state.currentPage); const additionalOptions = {};
if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) {
additionalOptions.release_time = this.getReleaseTimeOption(time);
}
searchByTags(tags, orderBy, this.state.currentPage, additionalOptions);
} }
}); });
} }
} }
getReleaseTimeOption = time => {
return `>${Math.floor(
moment()
.subtract(1, time)
.unix()
)}`;
};
handleVerticalEndReached = () => { handleVerticalEndReached = () => {
// fetch more content // fetch more content
const { channelIds, claimSearch, claimSearchUris, orderBy, searchByTags, tags, uris } = this.props; const { channelIds, claimSearch, claimSearchUris, orderBy, searchByTags, tags, time, uris } = this.props;
const { subscriptionsView, trendingForAllView } = this.state; const { subscriptionsView, trendingForAllView } = this.state;
if ((claimSearchUris && claimSearchUris.length >= softLimit) || (uris && uris.length >= softLimit)) { if ((claimSearchUris && claimSearchUris.length >= softLimit) || (uris && uris.length >= softLimit)) {
// don't fetch more than the specified limit to be displayed // don't fetch more than the specified limit to be displayed
@ -119,17 +140,47 @@ class ClaimList extends React.PureComponent {
claimSearch(options); claimSearch(options);
} else { } else {
searchByTags(tags, orderBy, this.state.currentPage); const additionalOptions = {};
if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) {
additionalOptions.release_time = this.getReleaseTimeOption(time);
}
searchByTags(tags, orderBy, this.state.currentPage, additionalOptions);
} }
}); });
}; };
appendMorePlaceholder = items => {
items.push(Constants.MORE_PLACEHOLDER);
return items;
};
onMorePressed = () => {
const { navigation, tags } = this.props;
// tags.length > 1 means this is the Trending list
if (tags.length === 1) {
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TAG, key: 'tagPage', params: { tag: tags[0] } });
} else {
navigation.navigate({ routeName: Constants.FULL_ROUTE_NAME_TRENDING });
}
};
renderMorePlaceholder = () => {
return (
<TouchableOpacity style={discoverStyle.fileItemMore} onPress={this.onMorePressed}>
<Text style={discoverStyle.moreText}>more</Text>
<Icon style={discoverStyle.moreIcon} name={'angle-double-down'} color={Colors.White} size={16} />
</TouchableOpacity>
);
};
render() { render() {
const { const {
ListHeaderComponent, ListHeaderComponent,
loading, loading,
claimSearchLoading, claimSearchLoading,
claimSearchUris, claimSearchUris,
morePlaceholder,
navigation, navigation,
orientation = Constants.ORIENTATION_VERTICAL, orientation = Constants.ORIENTATION_VERTICAL,
style, style,
@ -184,7 +235,10 @@ class ClaimList extends React.PureComponent {
initialNumToRender={3} initialNumToRender={3}
maxToRenderPerBatch={3} maxToRenderPerBatch={3}
removeClippedSubviews removeClippedSubviews
renderItem={({ item }) => ( renderItem={({ item }) => {
return item === Constants.MORE_PLACEHOLDER ? (
this.renderMorePlaceholder()
) : (
<FileItem <FileItem
style={discoverStyle.fileItem} style={discoverStyle.fileItem}
mediaStyle={discoverStyle.fileItemMedia} mediaStyle={discoverStyle.fileItemMedia}
@ -194,10 +248,11 @@ class ClaimList extends React.PureComponent {
showDetails showDetails
compactView={false} compactView={false}
/> />
)} );
}}
horizontal horizontal
showsHorizontalScrollIndicator={false} showsHorizontalScrollIndicator={false}
data={uris ? uris.slice(0, horizontalLimit) : []} data={uris ? this.appendMorePlaceholder(uris.slice(0, horizontalLimit)) : []}
keyExtractor={(item, index) => item} keyExtractor={(item, index) => item}
/> />
); );

View file

@ -45,7 +45,7 @@ export default class ModalPicker extends React.PureComponent {
style={modalPickerStyle.listItem} style={modalPickerStyle.listItem}
onPress={() => onItemSelected(item)} onPress={() => onItemSelected(item)}
> >
<Icon style={modalPickerStyle.itemIcon} name={item.icon} size={16} /> {item.icon && <Icon style={modalPickerStyle.itemIcon} name={item.icon} size={16} />}
<Text style={modalPickerStyle.itemLabel}>{item.label}</Text> <Text style={modalPickerStyle.itemLabel}>{item.label}</Text>
{selectedItem && selectedItem.name === item.name && ( {selectedItem && selectedItem.name === item.name && (
<Icon style={modalPickerStyle.itemSelected} name={'check'} color={Colors.LbryGreen} size={16} /> <Icon style={modalPickerStyle.itemSelected} name={'check'} color={Colors.LbryGreen} size={16} />

View file

@ -2,6 +2,12 @@ const SORT_BY_NEW = 'new';
const SORT_BY_HOT = 'hot'; const SORT_BY_HOT = 'hot';
const SORT_BY_TOP = 'top'; const SORT_BY_TOP = 'top';
const TIME_DAY = 'day';
const TIME_WEEK = 'week';
const TIME_MONTH = 'month';
const TIME_YEAR = 'year';
const TIME_ALL = 'all';
const Constants = { const Constants = {
FIRST_RUN_PAGE_WELCOME: 'welcome', FIRST_RUN_PAGE_WELCOME: 'welcome',
FIRST_RUN_PAGE_EMAIL_COLLECT: 'email-collect', FIRST_RUN_PAGE_EMAIL_COLLECT: 'email-collect',
@ -90,18 +96,36 @@ const Constants = {
SORT_BY_NEW, SORT_BY_NEW,
SORT_BY_TOP, SORT_BY_TOP,
TIME_DAY,
TIME_WEEK,
TIME_MONTH,
TIME_YEAR,
TIME_ALL,
CLAIM_SEARCH_SORT_BY_ITEMS: [ CLAIM_SEARCH_SORT_BY_ITEMS: [
{ icon: 'fire-alt', name: SORT_BY_HOT, label: 'Hot content' }, { icon: 'fire-alt', name: SORT_BY_HOT, label: 'Hot content' },
{ icon: 'certificate', name: SORT_BY_NEW, label: 'New content' }, { icon: 'certificate', name: SORT_BY_NEW, label: 'New content' },
{ icon: 'chart-line', name: SORT_BY_TOP, label: 'Top content' }, { icon: 'chart-line', name: SORT_BY_TOP, label: 'Top content' },
], ],
CLAIM_SEARCH_TIME_ITEMS: [
{ name: TIME_DAY, label: 'Past 24 hours' },
{ name: TIME_WEEK, label: 'Past week' },
{ name: TIME_MONTH, label: 'Past month' },
{ name: TIME_YEAR, label: 'Past year' },
{ name: TIME_ALL, label: 'All time' },
],
DEFAULT_ORDER_BY: ['trending_global', 'trending_mixed'], DEFAULT_ORDER_BY: ['trending_global', 'trending_mixed'],
ORDER_BY_EFFECTIVE_AMOUNT: 'effective_amount',
DEFAULT_PAGE_SIZE: 10, DEFAULT_PAGE_SIZE: 10,
ALL_PLACEHOLDER: '_all', ALL_PLACEHOLDER: '_all',
MORE_PLACEHOLDER: '_more',
TRUE_STRING: 'true', TRUE_STRING: 'true',
}; };
@ -112,6 +136,8 @@ export const DrawerRoutes = [
Constants.DRAWER_ROUTE_TRENDING, Constants.DRAWER_ROUTE_TRENDING,
Constants.DRAWER_ROUTE_SUBSCRIPTIONS, Constants.DRAWER_ROUTE_SUBSCRIPTIONS,
Constants.DRAWER_ROUTE_MY_LBRY, Constants.DRAWER_ROUTE_MY_LBRY,
Constants.DRAWER_ROUTE_TAG,
Constants.DRAWER_ROUTE_PUBLISH,
Constants.DRAWER_ROUTE_REWARDS, Constants.DRAWER_ROUTE_REWARDS,
Constants.DRAWER_ROUTE_WALLET, Constants.DRAWER_ROUTE_WALLET,
Constants.DRAWER_ROUTE_PUBLISH, Constants.DRAWER_ROUTE_PUBLISH,

View file

@ -267,9 +267,10 @@ class DiscoverPage extends React.PureComponent {
removeClippedSubviews removeClippedSubviews
renderItem={({ item, index, section }) => ( renderItem={({ item, index, section }) => (
<ClaimList <ClaimList
key={item.join(',')} key={item.sort().join(',')}
orderBy={item.length > 1 ? Constants.DEFAULT_ORDER_BY : orderBy} orderBy={item.length > 1 ? Constants.DEFAULT_ORDER_BY : orderBy}
tags={item} tags={item}
morePlaceholder
navigation={navigation} navigation={navigation}
orientation={Constants.ORIENTATION_HORIZONTAL} orientation={Constants.ORIENTATION_HORIZONTAL}
/> />
@ -280,7 +281,7 @@ class DiscoverPage extends React.PureComponent {
{formatTagTitle(title)} {formatTagTitle(title)}
</Text> </Text>
<TouchableOpacity onPress={() => this.handleTagPress(title)}> <TouchableOpacity onPress={() => this.handleTagPress(title)}>
<Icon name={'ellipsis-v'} size={16} /> <Icon name={'angle-double-down'} size={16} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
)} )}

View file

@ -19,8 +19,11 @@ class TagPage extends React.PureComponent {
state = { state = {
tag: null, tag: null,
showSortPicker: false, showSortPicker: false,
showTimePicker: false,
orderBy: Constants.DEFAULT_ORDER_BY, orderBy: Constants.DEFAULT_ORDER_BY,
time: Constants.TIME_WEEK,
currentSortByItem: Constants.CLAIM_SEARCH_SORT_BY_ITEMS[0], currentSortByItem: Constants.CLAIM_SEARCH_SORT_BY_ITEMS[0],
currentTimeItem: Constants.CLAIM_SEARCH_TIME_ITEMS[1],
}; };
didFocusListener; didFocusListener;
@ -67,24 +70,34 @@ class TagPage extends React.PureComponent {
break; break;
case Constants.SORT_BY_TOP: case Constants.SORT_BY_TOP:
orderBy = ['effective_amount']; orderBy = [Constants.ORDER_BY_EFFECTIVE_AMOUNT];
break; break;
} }
this.setState({ currentSortByItem: item, orderBy, showSortPicker: false }); this.setState({ currentSortByItem: item, orderBy, showSortPicker: false });
}; };
handleTimeItemSelected = item => {
this.setState({ time: item.name });
};
render() { render() {
const { navigation } = this.props; const { navigation } = this.props;
const { tag, currentSortByItem } = this.state; const { tag, currentSortByItem, currentTimeItem, showSortPicker, showTimePicker } = this.state;
return ( return (
<View style={discoverStyle.container}> <View style={discoverStyle.container}>
<UriBar navigation={navigation} belowOverlay={this.state.showSortPicker} /> <UriBar navigation={navigation} belowOverlay={showSortPicker || showTimePicker} />
<ClaimList <ClaimList
ListHeaderComponent={ ListHeaderComponent={
<View style={discoverStyle.tagTitleRow}> <View style={discoverStyle.tagTitleRow}>
<Text style={discoverStyle.tagPageTitle}>{formatTagTitle(tag)}</Text> <Text style={discoverStyle.tagPageTitle}>{formatTagTitle(tag)}</Text>
{Constants.SORT_BY_TOP === currentSortByItem.name && (
<TouchableOpacity style={discoverStyle.tagTime} onPress={() => this.setState({ showTimePicker: true })}>
<Text style={discoverStyle.tagSortText}>{currentTimeItem.label}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
)}
<TouchableOpacity style={discoverStyle.tagSortBy} onPress={() => this.setState({ showSortPicker: true })}> <TouchableOpacity style={discoverStyle.tagSortBy} onPress={() => this.setState({ showSortPicker: true })}>
<Text style={discoverStyle.tagSortText}>{currentSortByItem.label.split(' ')[0]}</Text> <Text style={discoverStyle.tagSortText}>{currentSortByItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} /> <Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
@ -93,12 +106,13 @@ class TagPage extends React.PureComponent {
} }
style={discoverStyle.tagPageClaimList} style={discoverStyle.tagPageClaimList}
orderBy={this.state.orderBy} orderBy={this.state.orderBy}
time={this.state.time}
tags={[tag]} tags={[tag]}
navigation={navigation} navigation={navigation}
orientation={Constants.ORIENTATION_VERTICAL} orientation={Constants.ORIENTATION_VERTICAL}
/> />
{!this.state.showSortPicker && <FloatingWalletBalance navigation={navigation} />} {!showSortPicker && !showTimePicker && <FloatingWalletBalance navigation={navigation} />}
{this.state.showSortPicker && ( {showSortPicker && (
<ModalPicker <ModalPicker
title={__('Sort content by')} title={__('Sort content by')}
onOverlayPress={() => this.setState({ showSortPicker: false })} onOverlayPress={() => this.setState({ showSortPicker: false })}
@ -107,6 +121,15 @@ class TagPage extends React.PureComponent {
items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS} items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS}
/> />
)} )}
{showTimePicker && (
<ModalPicker
title={__('Content from')}
onOverlayPress={() => this.setState({ showTimePicker: false })}
onItemSelected={this.handleTimeItemSelected}
selectedItem={this.state.currentTimeItem}
items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/>
)}
</View> </View>
); );
} }

View file

@ -85,6 +85,25 @@ const discoverStyle = StyleSheet.create({
width: fileItemWidth, width: fileItemWidth,
marginRight: 12, marginRight: 12,
}, },
fileItemMore: {
alignSelf: 'flex-start',
backgroundColor: Colors.LbryGreen,
flexDirection: 'row',
width: fileItemWidth,
height: fileItemMediaHeight,
marginRight: 12,
alignItems: 'center',
justifyContent: 'center',
},
moreText: {
fontFamily: 'Inter-UI-Regular',
color: Colors.White,
fontSize: 24,
},
moreIcon: {
marginLeft: 12,
marginBottom: -4,
},
fileItemMedia: { fileItemMedia: {
width: fileItemMediaWidth, width: fileItemMediaWidth,
height: fileItemMediaHeight, height: fileItemMediaHeight,
@ -224,6 +243,10 @@ const discoverStyle = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
marginRight: 4, marginRight: 4,
}, },
tagTime: {
flexDirection: 'row',
alignItems: 'center',
},
tagSortText: { tagSortText: {
fontFamily: 'Inter-UI-Regular', fontFamily: 'Inter-UI-Regular',
fontSize: 14, fontSize: 14,

View file

@ -49,9 +49,10 @@ const modalPickerStyle = StyleSheet.create({
}, },
itemIcon: { itemIcon: {
marginLeft: 8, marginLeft: 8,
marginRight: 12, marginRight: 4,
}, },
itemLabel: { itemLabel: {
marginLeft: 8,
alignSelf: 'flex-start', alignSelf: 'flex-start',
fontFamily: 'Inter-UI-Regular', fontFamily: 'Inter-UI-Regular',
fontSize: 16, fontSize: 16,