rework discovery views with new/top/trending sorting #25

Merged
akinwale merged 9 commits from content-views into master 2019-08-20 10:03:34 +02:00
38 changed files with 670 additions and 291 deletions

View file

@ -141,7 +141,7 @@ const drawer = createDrawerNavigator(
Trending: {
screen: TrendingPage,
navigationOptions: {
title: 'Trending',
title: 'All Content',
drawerIcon: ({ tintColor }) => <Icon name="fire" size={drawerIconSize} style={{ color: tintColor }} />,
},
},

View file

@ -34,14 +34,14 @@ export default class Button extends React.PureComponent {
}
let renderIcon = (
<Icon name={icon} size={18} color={iconColor ? iconColor : 'light' === theme ? Colors.DarkGrey : Colors.White} />
<Icon name={icon} size={16} color={iconColor || (theme === 'light' ? Colors.DarkGrey : Colors.White)} />
);
if (solid) {
renderIcon = (
<Icon
name={icon}
size={18}
color={iconColor ? iconColor : 'light' === theme ? Colors.DarkGrey : Colors.White}
size={16}
color={iconColor || (theme === 'light' ? Colors.DarkGrey : Colors.White)}
solid
/>
);

View file

@ -21,17 +21,14 @@ class ClaimList extends React.PureComponent {
state = {
currentPage: 1, // initial page load is page 1
subscriptionsView: false, // whether or not this claim list is for subscriptions
trendingForAllView: false,
lastPageReached: false,
};
componentDidMount() {
const { channelIds, trendingForAll } = this.props;
const { channelIds } = this.props;
if (channelIds) {
this.setState({ subscriptionsView: true });
} else if (trendingForAll) {
this.setState({ trendingForAllView: true });
}
this.doClaimSearch();
@ -44,18 +41,16 @@ class ClaimList extends React.PureComponent {
searchByTags,
tags: prevTags,
channelIds: prevChannelIds,
trendingForAll: prevTrendingForAll,
time: prevTime,
showNsfwContent,
} = prevProps;
const { claimSearchByQuery, orderBy, tags, channelIds, trendingForAll, time } = this.props;
const { claimSearchByQuery, orderBy, tags, channelIds, time } = this.props;
if (
!_.isEqual(orderBy, prevOrderBy) ||
!_.isEqual(tags, prevTags) ||
!_.isEqual(channelIds, prevChannelIds) ||
time !== prevTime ||
trendingForAll !== prevTrendingForAll
time !== prevTime
) {
// reset to page 1 because the order, tags or channelIds changed
this.setState({ currentPage: 1 }, () => {
@ -63,15 +58,12 @@ class ClaimList extends React.PureComponent {
this.scrollView.scrollToOffset({ animated: true, offset: 0 });
}
if (trendingForAll || (prevChannelIds && channelIds)) {
if (prevChannelIds && channelIds) {
if (channelIds) {
this.setState({ subscriptionsView: true });
}
if (trendingForAll) {
this.setState({ trendingForAllView: true });
}
} else if (tags && tags.length > 0) {
this.setState({ subscriptionsView: false, trendingForAllView: false });
this.setState({ subscriptionsView: false });
}
this.doClaimSearch();
@ -80,15 +72,8 @@ class ClaimList extends React.PureComponent {
}
buildClaimSearchOptions() {
const {
orderBy = Constants.DEFAULT_ORDER_BY,
channelIds,
showNsfwContent,
tags,
time,
trendingForAll,
} = this.props;
const { currentPage, subscriptionsView, trendingForAllView } = this.state;
const { orderBy = Constants.DEFAULT_ORDER_BY, channelIds, showNsfwContent, tags, time } = this.props;
const { currentPage, subscriptionsView } = this.state;
const options = {
order_by: orderBy,
@ -99,7 +84,7 @@ class ClaimList extends React.PureComponent {
if (channelIds) {
options.channel_ids = channelIds;
} else if (!trendingForAll && !trendingForAllView && tags && tags.length > 0) {
} else if (tags && tags.length > 0) {
options.any_tags = tags;
}
if (!showNsfwContent) {
@ -156,7 +141,7 @@ class ClaimList extends React.PureComponent {
if (tags.length === 1) {
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TAG, key: 'tagPage', params: { tag: tags[0] } });
} else {
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TRENDING });
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TRENDING, params: { filterForTags: true } });
}
};
@ -176,8 +161,16 @@ class ClaimList extends React.PureComponent {
};
renderVerticalItem = ({ item }) => {
const { navigation } = this.props;
return <FileListItem key={item} uri={item} style={claimListStyle.verticalListItem} navigation={navigation} />;
const { hideChannel, navigation } = this.props;
return (
<FileListItem
key={item}
uri={item}
hideChannel={hideChannel}
style={claimListStyle.verticalListItem}
navigation={navigation}
/>
);
};
renderHorizontalItem = ({ item }) => {
@ -208,7 +201,7 @@ class ClaimList extends React.PureComponent {
style,
claimSearchByQuery,
} = this.props;
const { subscriptionsView, trendingForAllView } = this.state;
const { subscriptionsView } = this.state;
const options = this.buildClaimSearchOptions();
const claimSearchKey = createNormalizedClaimSearchKey(options);

View file

@ -1,34 +1,76 @@
import React from 'react';
import { DrawerItems, SafeAreaView } from 'react-navigation';
import { ScrollView } from 'react-native';
import Constants from 'constants';
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Icon from 'react-native-vector-icons/FontAwesome5';
import discoverStyle from 'styles/discover';
const groupedMenuItems = {
'Find content': [
{ icon: 'hashtag', label: 'Your tags', route: Constants.FULL_ROUTE_NAME_DISCOVER },
{ icon: 'heart', solid: true, label: 'Subscriptions', route: Constants.DRAWER_ROUTE_SUBSCRIPTIONS },
{ icon: 'globe-americas', label: 'All content', route: Constants.DRAWER_ROUTE_TRENDING },
],
'Your content': [
{ icon: 'download', label: 'Library', route: Constants.DRAWER_ROUTE_MY_LBRY },
{ icon: 'cloud-upload-alt', label: 'Publishes', route: Constants.DRAWER_ROUTE_PUBLISHES },
{ icon: 'upload', label: 'New publish', route: Constants.DRAWER_ROUTE_PUBLISH },
],
Wallet: [
{ icon: 'wallet', label: 'Wallet', route: Constants.FULL_ROUTE_NAME_WALLET },
{ icon: 'award', label: 'Rewards', route: Constants.DRAWER_ROUTE_REWARDS },
],
Settings: [
{ icon: 'cog', label: 'Settings', route: Constants.DRAWER_ROUTE_SETTINGS },
{ icon: 'info', label: 'About', route: Constants.DRAWER_ROUTE_ABOUT },
],
};
const groupNames = Object.keys(groupedMenuItems);
class DrawerContent extends React.PureComponent {
render() {
const props = this.props;
const { navigation, onItemPress } = props;
const { activeTintColor, navigation, onItemPress } = this.props;
const { state } = navigation;
const activeItemKey = state.routes[state.index] ? state.routes[state.index].key : null;
return (
<ScrollView>
<ScrollView contentContainerStyle={discoverStyle.menuScrollContent}>
<SafeAreaView style={discoverStyle.drawerContentContainer} forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems
{...props}
onItemPress={route => {
const { routeName } = route.route;
if (Constants.FULL_ROUTE_NAME_DISCOVER === routeName) {
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_DISCOVER });
return;
}
{groupNames.map(groupName => {
const menuItems = groupedMenuItems[groupName];
if (Constants.FULL_ROUTE_NAME_WALLET === routeName) {
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_WALLET });
return;
}
onItemPress(route);
}}
/>
return (
<View key={groupName} style={discoverStyle.menuGroup}>
{groupNames[3] !== groupName && (
<Text key={`${groupName}-title`} style={discoverStyle.menuGroupName}>
{groupName}
</Text>
)}
{menuItems.map(item => {
const focused = activeItemKey === item.route;
return (
<TouchableOpacity
accessible
accessibilityLabel={item.label}
style={[discoverStyle.menuItemTouchArea, focused ? discoverStyle.menuItemTouchAreaFocused : null]}
key={item.label}
onPress={() => navigation.navigate({ routeName: item.route })}
delayPressIn={0}
>
<View style={discoverStyle.menuItemIcon}>
<Icon name={item.icon} size={16} solid={item.solid} color={focused ? activeTintColor : null} />
</View>
<Text style={[discoverStyle.menuItem, focused ? discoverStyle.menuItemFocused : null]}>
{item.label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
})}
</SafeAreaView>
</ScrollView>
);

View file

@ -54,6 +54,7 @@ const Constants = {
ACTION_REACT_NAVIGATION_REPLACE: 'Navigation/REPLACE',
ACTION_SORT_BY_ITEM_CHANGED: 'SORT_BY_ITEM_CHANGED',
ACTION_TIME_ITEM_CHANGED: 'TIME_ITEM_CHANGED',
ORIENTATION_HORIZONTAL: 'horizontal',
ORIENTATION_VERTICAL: 'vertical',

View file

@ -43,6 +43,7 @@ import thunk from 'redux-thunk';
const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Firebase) {
console.log(error);
NativeModules.Firebase.logException(isFatal, error.message ? error.message : 'No message', JSON.stringify(error));
}
};

View file

@ -43,6 +43,7 @@ class AboutPage extends React.PureComponent {
const { pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('About');
if (NativeModules.VersionInfo) {
NativeModules.VersionInfo.getAppVersion().then(version => {

View file

@ -1,26 +1,23 @@
import { connect } from 'react-redux';
import {
doFetchClaimsByChannel,
makeSelectClaimForUri,
makeSelectClaimsInChannelForCurrentPageState,
makeSelectFetchingChannelClaims,
makeSelectTotalPagesForChannel,
} from 'lbry-redux';
import { doFetchClaimsByChannel, makeSelectClaimForUri } from 'lbry-redux';
import { doPopDrawerStack } from 'redux/actions/drawer';
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { selectDrawerStack } from 'redux/selectors/drawer';
import { selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
import ChannelPage from './view';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
claimsInChannel: makeSelectClaimsInChannelForCurrentPageState(props.uri)(state),
drawerStack: selectDrawerStack(state),
fetching: makeSelectFetchingChannelClaims(props.uri)(state),
totalPages: makeSelectTotalPagesForChannel(props.uri, 10)(state), // Update to use a default PAGE_SIZE constant
sortByItem: selectSortByItem(state),
timeItem: selectTimeItem(state),
});
const perform = dispatch => ({
fetchClaims: (uri, page) => dispatch(doFetchClaimsByChannel(uri, page)),
popDrawerStack: () => dispatch(doPopDrawerStack()),
setSortByItem: item => dispatch(doSetSortByItem(item)),
setTimeItem: item => dispatch(doSetTimeItem(item)),
});
export default connect(

View file

@ -1,113 +1,111 @@
// @flow
import React from 'react';
import { ActivityIndicator, Dimensions, Image, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import {
ActivityIndicator,
Dimensions,
Image,
NativeModules,
ScrollView,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { TabView, SceneMap } from 'react-native-tab-view';
import { normalizeURI } from 'lbry-redux';
import { navigateBack } from 'utils/helper';
import { navigateBack, getOrderBy } from 'utils/helper';
import ChannelIconItem from 'component/channelIconItem';
import ClaimList from 'component/claimList';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Button from 'component/button';
import Icon from 'react-native-vector-icons/FontAwesome5';
import Link from 'component/link';
import FileList from 'component/fileList';
import ModalPicker from 'component/modalPicker';
import PageHeader from 'component/pageHeader';
import SubscribeButton from 'component/subscribeButton';
import SubscribeNotificationButton from 'component/subscribeNotificationButton';
import UriBar from 'component/uriBar';
import channelIconStyle from 'styles/channelIcon';
import channelPageStyle from 'styles/channelPage';
import discoverStyle from 'styles/discover';
class ChannelPage extends React.PureComponent {
state = {
page: 1,
showPageButtons: false,
autoStyle: null,
showSortPicker: false,
showTimePicker: false,
orderBy: Constants.DEFAULT_ORDER_BY,
activeTab: Constants.CONTENT_TAB,
};
componentDidMount() {
const { uri, page, claimsInChannel, fetchClaims, fetchClaimCount } = this.props;
if (!claimsInChannel || !claimsInChannel.length) {
fetchClaims(uri, page || this.state.page);
}
componentWillMount() {
this.setState({
autoStyle:
ChannelIconItem.AUTO_THUMB_STYLES[Math.floor(Math.random() * ChannelIconItem.AUTO_THUMB_STYLES.length)],
});
}
handlePreviousPage = () => {
const { uri, fetchClaims } = this.props;
if (this.state.page > 1) {
this.setState({ page: this.state.page - 1, showPageButtons: false }, () => {
fetchClaims(uri, this.state.page);
});
}
componentDidMount() {
NativeModules.Firebase.setCurrentScreen('Channel');
}
handleSortByItemSelected = item => {
const { setSortByItem } = this.props;
setSortByItem(item);
this.setState({ orderBy: getOrderBy(item), showSortPicker: false });
};
handleNextPage = () => {
const { uri, fetchClaims, totalPages } = this.props;
if (this.state.page < totalPages) {
this.setState({ page: this.state.page + 1, showPageButtons: false }, () => {
fetchClaims(uri, this.state.page);
});
}
handleTimeItemSelected = item => {
const { setTimeItem } = this.props;
setTimeItem(item);
this.setState({ showTimePicker: false });
};
listHeader = () => {
const { sortByItem, timeItem } = this.props;
return (
<View style={channelPageStyle.listHeader}>
<View style={discoverStyle.pickerRow}>
<View style={discoverStyle.leftPickerRow}>
<TouchableOpacity style={discoverStyle.tagSortBy} onPress={() => this.setState({ showSortPicker: true })}>
<Text style={discoverStyle.tagSortText}>{sortByItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
{Constants.SORT_BY_TOP === sortByItem.name && (
<TouchableOpacity style={discoverStyle.tagTime} onPress={() => this.setState({ showTimePicker: true })}>
<Text style={discoverStyle.tagSortText}>{timeItem.label}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
)}
</View>
</View>
</View>
);
};
renderContent = () => {
const { fetching, claimsInChannel, totalPages, navigation } = this.props;
const { claim, navigation, timeItem } = this.props;
let contentList;
if (fetching) {
contentList = (
<View style={channelPageStyle.busyContainer}>
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
<Text style={channelPageStyle.infoText}>Fetching content...</Text>
</View>
);
} else {
contentList =
claimsInChannel && claimsInChannel.length ? (
<FileList
sortByHeight
hideFilter
fileInfos={claimsInChannel}
navigation={navigation}
style={channelPageStyle.fileList}
contentContainerStyle={channelPageStyle.fileListContent}
onEndReached={() => this.setState({ showPageButtons: true })}
/>
) : (
<View style={channelPageStyle.busyContainer}>
<Text style={channelPageStyle.infoText}>No content found.</Text>
</View>
);
}
let pageButtons;
if (totalPages > 1 && this.state.showPageButtons) {
pageButtons = (
<View style={channelPageStyle.pageButtons}>
<View>
{this.state.page > 1 && (
<Button
style={channelPageStyle.button}
text={'Previous'}
disabled={!!fetching}
onPress={this.handlePreviousPage}
/>
)}
</View>
{this.state.page < totalPages && (
<Button
style={[channelPageStyle.button, channelPageStyle.nextButton]}
text={'Next'}
disabled={!!fetching}
onPress={this.handleNextPage}
/>
)}
</View>
);
let channelId;
if (claim) {
channelId = claim.claim_id;
}
return (
<View style={channelPageStyle.contentTab}>
{contentList}
{pageButtons}
{channelId && (
<ClaimList
ListHeaderComponent={this.listHeader}
hideChannel
orderBy={this.state.orderBy}
time={timeItem.name}
navigation={navigation}
orientation={Constants.ORIENTATION_VERTICAL}
channelIds={[channelId]}
style={channelPageStyle.claimList}
/>
)}
</View>
);
};
@ -162,10 +160,16 @@ class ChannelPage extends React.PureComponent {
};
render() {
const { fetching, claimsInChannel, claim, navigation, totalPages, uri, drawerStack, popDrawerStack } = this.props;
const { claim, navigation, uri, drawerStack, popDrawerStack, sortByItem, timeItem } = this.props;
const { name, permanent_url: permanentUrl } = claim;
const { autoStyle, showSortPicker, showTimePicker } = this.state;
let thumbnailUrl, coverUrl, title, fullUri;
let thumbnailUrl,
coverUrl,
title,
fullUri,
displayName = null,
substrIndex = 0;
if (claim) {
if (claim.value) {
title = claim.value.title;
@ -177,6 +181,8 @@ class ChannelPage extends React.PureComponent {
}
}
displayName = title || claim.name;
substrIndex = displayName.startsWith('@') ? 1 : 0;
fullUri = normalizeURI(`${claim.name}#${claim.claim_id}`);
}
@ -200,16 +206,15 @@ class ChannelPage extends React.PureComponent {
<Text style={channelPageStyle.channelName}>{title && title.trim().length > 0 ? title : name}</Text>
</View>
<View style={channelPageStyle.avatarImageContainer}>
<Image
style={channelPageStyle.avatarImage}
resizeMode={'cover'}
source={
thumbnailUrl && thumbnailUrl.trim().length > 0
? { uri: thumbnailUrl }
: require('../../assets/default_avatar.jpg')
}
/>
<View style={[channelPageStyle.avatarImageContainer, autoStyle]}>
{thumbnailUrl && (
<Image style={channelPageStyle.avatarImage} resizeMode={'cover'} source={{ uri: thumbnailUrl }} />
)}
{(!thumbnailUrl || thumbnailUrl.trim().length === 0) && (
<Text style={channelIconStyle.autothumbCharacter}>
{displayName.substring(substrIndex, substrIndex + 1).toUpperCase()}
</Text>
)}
</View>
<View style={channelPageStyle.subscribeButtonContainer}>
@ -242,6 +247,25 @@ class ChannelPage extends React.PureComponent {
{Constants.CONTENT_TAB === this.state.activeTab && this.renderContent()}
{Constants.ABOUT_TAB === this.state.activeTab && this.renderAbout()}
</View>
{showSortPicker && (
<ModalPicker
title={__('Sort content by')}
onOverlayPress={() => this.setState({ showSortPicker: false })}
onItemSelected={this.handleSortByItemSelected}
selectedItem={sortByItem}
items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS}
/>
)}
{showTimePicker && (
<ModalPicker
title={__('Content from')}
onOverlayPress={() => this.setState({ showTimePicker: false })}
onItemSelected={this.handleTimeItemSelected}
selectedItem={timeItem}
items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/>
)}
</View>
);
}

View file

@ -11,8 +11,8 @@ import {
selectSubscriptionClaims,
selectUnreadSubscriptions,
} from 'lbryinc';
import { doSetClientSetting, doSetSortByItem } from 'redux/actions/settings';
import { makeSelectClientSetting, selectSortByItem } from 'redux/selectors/settings';
import { doSetClientSetting, doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { makeSelectClientSetting, selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import DiscoverPage from './view';
@ -27,6 +27,7 @@ const select = state => ({
ratingReminderDisabled: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_DISABLED)(state),
ratingReminderLastShown: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN)(state),
sortByItem: selectSortByItem(state),
timeItem: selectTimeItem(state),
unreadSubscriptions: selectUnreadSubscriptions(state),
});
@ -39,6 +40,7 @@ const perform = dispatch => ({
removeUnreadSubscriptions: () => dispatch(doRemoveUnreadSubscriptions()),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setSortByItem: item => dispatch(doSetSortByItem(item)),
setTimeItem: item => dispatch(doSetTimeItem(item)),
});
export default connect(

View file

@ -33,10 +33,13 @@ class DiscoverPage extends React.PureComponent {
remainingTags: [],
showModalTagSelector: false,
showSortPicker: false,
showTimePicker: false,
orderBy: Constants.DEFAULT_ORDER_BY,
};
componentDidMount() {
NativeModules.Firebase.setCurrentScreen('Your tags');
// Track the total time taken if this is the first launch
AsyncStorage.getItem('firstLaunchTime').then(startTime => {
if (startTime !== null && !isNaN(parseInt(startTime, 10))) {
@ -78,6 +81,12 @@ class DiscoverPage extends React.PureComponent {
this.setState({ orderBy, showSortPicker: false });
};
handleTimeItemSelected = item => {
const { setTimeItem } = this.props;
setTimeItem(item);
this.setState({ showTimePicker: false });
};
subscriptionForUri = (uri, channelName) => {
const { allSubscriptions } = this.props;
const { claimId, claimName } = parseURI(uri);
@ -230,26 +239,41 @@ class DiscoverPage extends React.PureComponent {
});
} else {
// navigate to the trending page
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TRENDING });
navigation.navigate({ routeName: Constants.DRAWER_ROUTE_TRENDING, params: { filterForTags: true } });
}
};
sectionListHeader = () => (
<View style={discoverStyle.titleRow}>
<Text style={discoverStyle.pageTitle}>Explore</Text>
<View style={discoverStyle.rightTitleRow}>
<Link
style={discoverStyle.customizeLink}
text={'Customize'}
onPress={() => this.setState({ showModalTagSelector: true })}
/>
<TouchableOpacity style={discoverStyle.tagSortBy} onPress={() => this.setState({ showSortPicker: true })}>
<Text style={discoverStyle.tagSortText}>{this.props.sortByItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
sectionListHeader = () => {
const { sortByItem, timeItem } = this.props;
return (
<View style={discoverStyle.listHeader}>
<View style={discoverStyle.titleRow}>
<Text style={discoverStyle.pageTitle}>Your tags</Text>
</View>
<View style={discoverStyle.pickerRow}>
<View style={discoverStyle.leftPickerRow}>
<TouchableOpacity style={discoverStyle.tagSortBy} onPress={() => this.setState({ showSortPicker: true })}>
<Text style={discoverStyle.tagSortText}>{sortByItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
{Constants.SORT_BY_TOP === sortByItem.name && (
<TouchableOpacity style={discoverStyle.tagTime} onPress={() => this.setState({ showTimePicker: true })}>
<Text style={discoverStyle.tagSortText}>{timeItem.label}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
)}
</View>
<Link
style={discoverStyle.customizeLink}
text={'Customize'}
onPress={() => this.setState({ showModalTagSelector: true })}
/>
</View>
</View>
</View>
);
);
};
sectionListFooter = () => {
const { remainingTags } = this.state;
@ -279,7 +303,7 @@ class DiscoverPage extends React.PureComponent {
<ClaimList
key={item.sort().join(',')}
orderBy={this.state.orderBy}
time={Constants.TIME_WEEK}
time={this.props.timeItem.name}
tags={item}
morePlaceholder
navigation={this.props.navigation}
@ -299,8 +323,8 @@ class DiscoverPage extends React.PureComponent {
);
render() {
const { navigation, sortByItem } = this.props;
const { orderBy, showModalTagSelector, showSortPicker } = this.state;
const { navigation, sortByItem, timeItem } = this.props;
const { orderBy, showModalTagSelector, showSortPicker, showTimePicker } = this.state;
return (
<View style={discoverStyle.container}>
@ -318,7 +342,9 @@ class DiscoverPage extends React.PureComponent {
sections={this.buildSections()}
keyExtractor={(item, index) => item}
/>
{!showModalTagSelector && !showSortPicker && <FloatingWalletBalance navigation={navigation} />}
{!showModalTagSelector && !showSortPicker && !showTimePicker && (
<FloatingWalletBalance navigation={navigation} />
)}
{showModalTagSelector && (
<ModalTagSelector
onOverlayPress={() => this.setState({ showModalTagSelector: false })}
@ -334,6 +360,15 @@ class DiscoverPage extends React.PureComponent {
items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS}
/>
)}
{showTimePicker && (
<ModalPicker
title={__('Content from')}
onOverlayPress={() => this.setState({ showTimePicker: false })}
onItemSelected={this.handleTimeItemSelected}
selectedItem={timeItem}
items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/>
)}
</View>
);
}

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Lbry, buildURI, normalizeURI } from 'lbry-redux';
import { ActivityIndicator, Button, FlatList, Text, TextInput, View, ScrollView } from 'react-native';
import { ActivityIndicator, Button, FlatList, NativeModules, Text, TextInput, View, ScrollView } from 'react-native';
import { navigateToUri, uriFromFileInfo } from 'utils/helper';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -34,6 +34,8 @@ class DownloadsPage extends React.PureComponent {
const { fetchMyClaims, fileList, pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Library');
fetchMyClaims();
fileList();
};

View file

@ -93,6 +93,7 @@ class FilePage extends React.PureComponent {
onComponentFocused = () => {
StatusBar.setHidden(false);
NativeModules.Firebase.setCurrentScreen('File');
DeviceEventEmitter.addListener('onDownloadStarted', this.handleDownloadStarted);
DeviceEventEmitter.addListener('onDownloadUpdated', this.handleDownloadUpdated);

View file

@ -45,6 +45,8 @@ class FirstRunScreen extends React.PureComponent {
});
if (NativeModules.FirstRun) {
NativeModules.Firebase.setCurrentScreen('First Run');
NativeModules.FirstRun.isFirstRun().then(firstRun => {
AsyncStorage.removeItem(Constants.KEY_FIRST_RUN_EMAIL);
AsyncStorage.removeItem(Constants.KEY_EMAIL_VERIFY_PENDING);

View file

@ -79,6 +79,7 @@ class PublishPage extends React.PureComponent {
canUseCamera: false,
titleFocused: false,
descriptionFocused: false,
loadingVideos: false,
// gallery videos
videos: null,
@ -151,13 +152,20 @@ class PublishPage extends React.PureComponent {
onComponentFocused = () => {
const { pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Publish');
NativeModules.Gallery.canUseCamera().then(canUseCamera => this.setState({ canUseCamera }));
NativeModules.Gallery.getThumbnailPath().then(thumbnailPath => this.setState({ thumbnailPath }));
NativeModules.Gallery.getVideos().then(videos => this.setState({ videos }));
this.setState(
{
loadingVideos: true,
},
() => {
NativeModules.Gallery.getVideos().then(videos => this.setState({ videos, loadingVideos: false }));
}
);
};
getNewUri(name, channel) {
@ -586,7 +594,7 @@ class PublishPage extends React.PureComponent {
render() {
const { balance, navigation, notify, publishFormValues } = this.props;
const { canUseCamera, currentPhase, checkedThumbnails, thumbnailPath, videos } = this.state;
const { canUseCamera, currentPhase, checkedThumbnails, loadingVideos, thumbnailPath, videos } = this.state;
let content;
if (Constants.PHASE_SELECTOR === currentPhase) {
@ -625,13 +633,13 @@ class PublishPage extends React.PureComponent {
</View>
</View>
</View>
{(!videos || !thumbnailPath || checkedThumbnails.length === 0) && (
{(loadingVideos || !thumbnailPath || checkedThumbnails.length === 0) && (
<View style={publishStyle.loadingView}>
<ActivityIndicator size="large" color={Colors.NextLbryGreen} />
</View>
)}
{thumbnailPath && (!videos || videos.length === 0) && (
<View style={publishStyle.centered}>
{!loadingVideos && (!videos || videos.length === 0) && (
<View style={publishStyle.relativeCentered}>
<Text style={publishStyle.noVideos}>
We could not find any videos on your device. Take a photo or record a video to get started.
</Text>

View file

@ -1,5 +1,5 @@
import React from 'react';
import { ActivityIndicator, Alert, FlatList, Text, TouchableOpacity, View } from 'react-native';
import { ActivityIndicator, Alert, FlatList, NativeModules, Text, TouchableOpacity, View } from 'react-native';
import Button from 'component/button';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -35,9 +35,10 @@ class PublishesPage extends React.PureComponent {
onComponentFocused = () => {
const { checkPendingPublishes, fetchMyClaims, pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Publishes');
fetchMyClaims();
checkPendingPublishes();
};

View file

@ -42,6 +42,8 @@ class RewardsPage extends React.PureComponent {
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Rewards');
fetchRewards();
this.setState({

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Lbry, parseURI, normalizeURI, isURIValid } from 'lbry-redux';
import { ActivityIndicator, Button, Text, TextInput, View, ScrollView } from 'react-native';
import { ActivityIndicator, Button, NativeModules, Text, TextInput, View, ScrollView } from 'react-native';
import { navigateToUri } from 'utils/helper';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -37,6 +37,7 @@ class SearchPage extends React.PureComponent {
const { pushDrawerStack, setPlayerVisible, query, search } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Search');
const searchQuery = query || this.getSearchQuery();
if (searchQuery && searchQuery.trim().length > 0) {

View file

@ -28,6 +28,7 @@ class SettingsPage extends React.PureComponent {
const { pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Settings');
};
componentDidMount() {

View file

@ -239,6 +239,8 @@ class SplashScreen extends React.PureComponent {
NativeModules.Firebase.track('app_launch', null);
}
NativeModules.Firebase.setCurrentScreen('Splash');
this.props.fetchRewardedContent();
Linking.getInitialURL().then(url => {
if (url) {

View file

@ -14,8 +14,8 @@ import {
selectShowSuggestedSubs,
} from 'lbryinc';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetClientSetting } from 'redux/actions/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { doSetClientSetting, doSetTimeItem } from 'redux/actions/settings';
import { makeSelectClientSetting, selectTimeItem } from 'redux/selectors/settings';
import { selectCurrentRoute } from 'redux/selectors/drawer';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import SubscriptionsPage from './view';
@ -32,6 +32,7 @@ const select = state => ({
viewMode: selectViewMode(state),
firstRunCompleted: selectFirstRunCompleted(state),
showSuggestedSubs: selectShowSuggestedSubs(state),
timeItem: selectTimeItem(state),
});
const perform = dispatch => ({
@ -41,6 +42,7 @@ const perform = dispatch => ({
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SUBSCRIPTIONS)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
setTimeItem: item => dispatch(doSetTimeItem(item)),
});
export default connect(

View file

@ -11,7 +11,7 @@ import {
View,
} from 'react-native';
import { buildURI, parseURI } from 'lbry-redux';
import { __, uriFromFileInfo } from 'utils/helper';
import { __, getOrderBy } from 'utils/helper';
import AsyncStorage from '@react-native-community/async-storage';
import moment from 'moment';
import Button from 'component/button';
@ -33,6 +33,7 @@ class SubscriptionsPage extends React.PureComponent {
state = {
showingSuggestedSubs: false,
showSortPicker: false,
showTimePicker: false,
orderBy: ['release_time'],
filteredChannels: [],
currentSortByItem: Constants.CLAIM_SEARCH_SORT_BY_ITEMS[1], // should always default to sorting subscriptions by new
@ -63,6 +64,8 @@ class SubscriptionsPage extends React.PureComponent {
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Subscriptions');
doFetchMySubscriptions();
doFetchRecommendedSubscriptions();
};
@ -80,22 +83,13 @@ class SubscriptionsPage extends React.PureComponent {
}
handleSortByItemSelected = item => {
let orderBy = [];
switch (item.name) {
case Constants.SORT_BY_HOT:
orderBy = Constants.DEFAULT_ORDER_BY;
break;
this.setState({ currentSortByItem: item, orderBy: getOrderBy(item), showSortPicker: false });
};
case Constants.SORT_BY_NEW:
orderBy = ['release_time'];
break;
case Constants.SORT_BY_TOP:
orderBy = ['effective_amount'];
break;
}
this.setState({ currentSortByItem: item, orderBy, showSortPicker: false });
handleTimeItemSelected = item => {
const { setTimeItem } = this.props;
setTimeItem(item);
this.setState({ showTimePicker: false });
};
handleChannelSelected = channelUri => {
@ -118,18 +112,17 @@ class SubscriptionsPage extends React.PureComponent {
suggestedChannels,
subscribedChannels,
allSubscriptions,
viewMode,
doSetViewMode,
doCompleteFirstRun,
doShowSuggestedSubs,
loading,
loadingSuggested,
firstRunCompleted,
doCompleteFirstRun,
doShowSuggestedSubs,
showSuggestedSubs,
timeItem,
unreadSubscriptions,
navigation,
} = this.props;
const { currentSortByItem, filteredChannels } = this.state;
const { currentSortByItem, filteredChannels, showSortPicker, showTimePicker } = this.state;
const numberOfSubscriptions = subscribedChannels ? subscribedChannels.length : 0;
const hasSubscriptions = numberOfSubscriptions > 0;
@ -155,7 +148,9 @@ class SubscriptionsPage extends React.PureComponent {
<UriBar navigation={navigation} belowOverlay={this.state.showSortPicker} />
<View style={subscriptionsStyle.titleRow}>
<Text style={subscriptionsStyle.pageTitle}>Channels you follow</Text>
{!this.state.showingSuggestedSubs && hasSubscriptions && (
</View>
{!this.state.showingSuggestedSubs && hasSubscriptions && (
<View style={subscriptionsStyle.pickerRow}>
<TouchableOpacity
style={subscriptionsStyle.tagSortBy}
onPress={() => this.setState({ showSortPicker: true })}
@ -163,8 +158,18 @@ class SubscriptionsPage extends React.PureComponent {
<Text style={subscriptionsStyle.tagSortText}>{currentSortByItem.label.split(' ')[0]}</Text>
<Icon style={subscriptionsStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
)}
</View>
{Constants.SORT_BY_TOP === currentSortByItem.name && (
<TouchableOpacity
style={subscriptionsStyle.tagSortBy}
onPress={() => this.setState({ showTimePicker: true })}
>
<Text style={subscriptionsStyle.tagSortText}>{timeItem.label}</Text>
<Icon style={subscriptionsStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
)}
</View>
)}
{!this.state.showingSuggestedSubs && hasSubscriptions && !loading && (
<View style={subscriptionsStyle.subContainer}>
<SubscribedChannelList
@ -175,6 +180,7 @@ class SubscriptionsPage extends React.PureComponent {
style={subscriptionsStyle.claimList}
channelIds={channelIds}
orderBy={this.state.orderBy}
time={timeItem.name}
navigation={navigation}
orientation={Constants.ORIENTATION_VERTICAL}
/>
@ -217,8 +223,8 @@ class SubscriptionsPage extends React.PureComponent {
</View>
)}
{!this.state.showSortPicker && <FloatingWalletBalance navigation={navigation} />}
{this.state.showSortPicker && (
{!showSortPicker && !showTimePicker && <FloatingWalletBalance navigation={navigation} />}
{showSortPicker && (
<ModalPicker
title={__('Sort content by')}
onOverlayPress={() => this.setState({ showSortPicker: false })}
@ -227,6 +233,15 @@ class SubscriptionsPage extends React.PureComponent {
items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS}
/>
)}
{showTimePicker && (
<ModalPicker
title={__('Content from')}
onOverlayPress={() => this.setState({ showTimePicker: false })}
onItemSelected={this.handleTimeItemSelected}
selectedItem={timeItem}
items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/>
)}
</View>
);
}

View file

@ -1,19 +1,24 @@
import { connect } from 'react-redux';
import { selectFollowedTags, doToggleTagFollow } from 'lbry-redux';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetSortByItem } from 'redux/actions/settings';
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { selectCurrentRoute } from 'redux/selectors/drawer';
import { selectSortByItem } from 'redux/selectors/settings';
import { selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
import TagPage from './view';
const select = state => ({
currentRoute: selectCurrentRoute(state),
sortByItem: selectSortByItem(state),
timeItem: selectTimeItem(state),
followedTags: selectFollowedTags(state),
});
const perform = dispatch => ({
pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
setSortByItem: item => dispatch(doSetSortByItem(item)),
setTimeItem: item => dispatch(doSetTimeItem(item)),
toggleTagFollow: tag => dispatch(doToggleTagFollow(tag)),
});
export default connect(

View file

@ -12,6 +12,7 @@ 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 UriBar from 'component/uriBar';
@ -21,8 +22,6 @@ class TagPage extends React.PureComponent {
showSortPicker: false,
showTimePicker: false,
orderBy: Constants.DEFAULT_ORDER_BY,
time: Constants.TIME_WEEK,
currentTimeItem: Constants.CLAIM_SEARCH_TIME_ITEMS[1],
};
didFocusListener;
@ -41,9 +40,10 @@ class TagPage extends React.PureComponent {
onComponentFocused = () => {
const { navigation, pushDrawerStack, setPlayerVisible, sortByItem } = this.props;
const { tag } = navigation.state.params;
this.setState({ tag, sortByItem, orderBy: getOrderBy(sortByItem) });
this.setState({ tag, orderBy: getOrderBy(sortByItem) });
pushDrawerStack(Constants.DRAWER_ROUTE_TAG, navigation.state.params);
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Tag');
};
componentDidMount() {
@ -65,42 +65,80 @@ class TagPage extends React.PureComponent {
};
handleTimeItemSelected = item => {
this.setState({ currentTimeItem: item, time: item.name, showTimePicker: false });
const { setTimeItem } = this.props;
setTimeItem(item);
this.setState({ showTimePicker: false });
};
isFollowingTag = tag => {
const { followedTags } = this.props;
return followedTags.map(tag => tag.name).includes(tag);
};
handleFollowTagToggle = () => {
const { toggleTagFollow } = this.props;
const { tag } = this.state;
const isFollowing = this.isFollowingTag(tag);
if (isFollowing) {
// unfollow
NativeModules.Firebase.track('tag_unfollow', { tag });
} else {
// follow
NativeModules.Firebase.track('tag_follow', { tag });
}
toggleTagFollow(tag);
if (window.persistor) {
window.persistor.flush();
}
};
listHeader = () => {
const { sortByItem, timeItem } = this.props;
const { tag } = this.state;
return (
<View style={discoverStyle.listHeader}>
<View style={discoverStyle.titleRow}>
<Text style={discoverStyle.pageTitle}>{formatTagTitle(tag)}</Text>
</View>
<View style={discoverStyle.pickerRow}>
<View style={discoverStyle.leftPickerRow}>
<TouchableOpacity style={discoverStyle.tagSortBy} onPress={() => this.setState({ showSortPicker: true })}>
<Text style={discoverStyle.tagSortText}>{sortByItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
{Constants.SORT_BY_TOP === sortByItem.name && (
<TouchableOpacity style={discoverStyle.tagTime} onPress={() => this.setState({ showTimePicker: true })}>
<Text style={discoverStyle.tagSortText}>{timeItem.label}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
)}
</View>
<Link
style={discoverStyle.customizeLink}
text={this.isFollowingTag(tag) ? 'Unfollow' : 'Follow'}
onPress={this.handleFollowTagToggle}
/>
</View>
</View>
);
};
render() {
const { navigation, sortByItem } = this.props;
const { tag, currentTimeItem, showSortPicker, showTimePicker } = this.state;
const { navigation, sortByItem, timeItem } = this.props;
const { tag, showSortPicker, showTimePicker } = this.state;
return (
<View style={discoverStyle.container}>
<UriBar navigation={navigation} belowOverlay={showSortPicker || showTimePicker} />
{this.state.tag && (
<ClaimList
ListHeaderComponent={
<View style={discoverStyle.tagTitleRow}>
<Text style={discoverStyle.tagPageTitle}>{formatTagTitle(tag)}</Text>
{Constants.SORT_BY_TOP === sortByItem.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 })}
>
<Text style={discoverStyle.tagSortText}>{sortByItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
</View>
}
ListHeaderComponent={this.listHeader}
style={discoverStyle.tagPageClaimList}
orderBy={this.state.orderBy}
time={this.state.time}
time={timeItem.name}
tags={[tag]}
navigation={navigation}
orientation={Constants.ORIENTATION_VERTICAL}
@ -112,7 +150,7 @@ class TagPage extends React.PureComponent {
title={__('Sort content by')}
onOverlayPress={() => this.setState({ showSortPicker: false })}
onItemSelected={this.handleSortByItemSelected}
selectedItem={this.state.sortByItem}
selectedItem={sortByItem}
items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS}
/>
)}
@ -121,7 +159,7 @@ class TagPage extends React.PureComponent {
title={__('Content from')}
onOverlayPress={() => this.setState({ showTimePicker: false })}
onItemSelected={this.handleTimeItemSelected}
selectedItem={this.state.currentTimeItem}
selectedItem={timeItem}
items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/>
)}

View file

@ -1,5 +1,5 @@
import React from 'react';
import { View, ScrollView, Text } from 'react-native';
import { NativeModules, View, ScrollView, Text } from 'react-native';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import TransactionList from 'component/transactionList';
import UriBar from 'component/uriBar';
@ -23,6 +23,8 @@ class TransactionHistoryPage extends React.PureComponent {
const { fetchTransactions, pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Transaction History');
fetchTransactions();
};

View file

@ -1,18 +1,23 @@
import { connect } from 'react-redux';
import { selectFollowedTags } from 'lbry-redux';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetSortByItem, doSetTimeItem } from 'redux/actions/settings';
import { selectCurrentRoute } from 'redux/selectors/drawer';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import { selectSortByItem, selectTimeItem } from 'redux/selectors/settings';
import TrendingPage from './view';
const select = state => ({
currentRoute: selectCurrentRoute(state),
sortByItem: selectSortByItem(state),
timeItem: selectTimeItem(state),
followedTags: selectFollowedTags(state),
});
const perform = dispatch => ({
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_TRENDING)),
pushDrawerStack: (routeName, params) => dispatch(doPushDrawerStack(routeName, params)),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
setSortByItem: item => dispatch(doSetSortByItem(item)),
setTimeItem: item => dispatch(doSetTimeItem(item)),
});
export default connect(

View file

@ -1,6 +1,7 @@
import React from 'react';
import { ActivityIndicator, NativeModules, FlatList, Text, TouchableOpacity, View } from 'react-native';
import { DEFAULT_FOLLOWED_TAGS, normalizeURI } from 'lbry-redux';
import { normalizeURI } from 'lbry-redux';
import { getOrderBy } from 'utils/helper';
import AsyncStorage from '@react-native-community/async-storage';
import moment from 'moment';
import ClaimList from 'component/claimList';
@ -23,7 +24,10 @@ const TRENDING_FOR_ITEMS = [
class TrendingPage extends React.PureComponent {
state = {
showModalTagSelector: false,
showSortByPicker: false,
showTimePicker: false,
showTrendingForPicker: false,
orderBy: Constants.DEFAULT_ORDER_BY,
currentTrendingForItem: TRENDING_FOR_ITEMS[0],
};
@ -41,9 +45,12 @@ class TrendingPage extends React.PureComponent {
}
onComponentFocused = () => {
const { pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
const { pushDrawerStack, setPlayerVisible, navigation } = this.props;
const { filterForTags } = navigation.state.params ? navigation.state.params : { filterForTags: false };
this.setState({ currentTrendingForItem: TRENDING_FOR_ITEMS[filterForTags ? 1 : 0] });
pushDrawerStack(Constants.DRAWER_ROUTE_TRENDING, navigation.state.params);
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('All content');
};
componentDidMount() {
@ -62,44 +69,96 @@ class TrendingPage extends React.PureComponent {
this.setState({ currentTrendingForItem: item, showTrendingForPicker: false });
};
handleSortByItemSelected = item => {
const { setSortByItem } = this.props;
setSortByItem(item);
this.setState({ orderBy: getOrderBy(item), showSortPicker: false });
};
handleTimeItemSelected = item => {
const { setTimeItem } = this.props;
setTimeItem(item);
this.setState({ showTimePicker: false });
};
listHeader = () => {
const { sortByItem, timeItem } = this.props;
const { currentTrendingForItem } = this.state;
const sortByTop = Constants.SORT_BY_TOP === sortByItem.name;
return (
<View style={discoverStyle.listHeader}>
<View style={discoverStyle.titleRow}>
<Text style={discoverStyle.pageTitle}>All content</Text>
</View>
<View style={discoverStyle.pickerRow}>
<View style={discoverStyle.leftPickerRow}>
<TouchableOpacity
style={discoverStyle.allTagSortBy}
onPress={() => this.setState({ showSortPicker: true })}
>
<Text style={discoverStyle.tagSortText}>{sortByItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
<Text style={discoverStyle.pickerLabel}>for</Text>
<TouchableOpacity
style={discoverStyle.allTagSortBy}
onPress={() => this.setState({ showTrendingForPicker: true })}
>
<Text style={discoverStyle.tagSortText}>{currentTrendingForItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
{sortByTop && <Text style={discoverStyle.pickerLabel}>in the</Text>}
{sortByTop && (
<TouchableOpacity style={discoverStyle.tagTime} onPress={() => this.setState({ showTimePicker: true })}>
<Text style={discoverStyle.tagSortText}>{timeItem.label}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
)}
</View>
{TRENDING_FOR_ITEMS[1].name === currentTrendingForItem.name && (
<Link
style={discoverStyle.customizeLink}
text={'Customize'}
onPress={() => this.setState({ showModalTagSelector: true })}
/>
)}
</View>
</View>
);
};
render() {
const { followedTags, navigation } = this.props;
const { currentTrendingForItem, showModalTagSelector, showTrendingForPicker } = this.state;
const { followedTags, navigation, sortByItem, timeItem } = this.props;
const {
currentTrendingForItem,
orderBy,
showModalTagSelector,
showSortPicker,
showTimePicker,
showTrendingForPicker,
} = this.state;
const filteredForTags = TRENDING_FOR_ITEMS[1].name === currentTrendingForItem.name;
return (
<View style={discoverStyle.container}>
<UriBar navigation={navigation} />
<ClaimList
ListHeaderComponent={
<View style={discoverStyle.titleRow}>
<Text style={discoverStyle.pageTitle}>Trending</Text>
<View style={discoverStyle.rightTitleRow}>
{TRENDING_FOR_ITEMS[1].name === currentTrendingForItem.name && (
<Link
style={discoverStyle.customizeLink}
text={'Customize'}
onPress={() => this.setState({ showModalTagSelector: true })}
/>
)}
<TouchableOpacity
style={discoverStyle.tagSortBy}
onPress={() => this.setState({ showTrendingForPicker: true })}
>
<Text style={discoverStyle.tagSortText}>{currentTrendingForItem.label.split(' ')[0]}</Text>
<Icon style={discoverStyle.tagSortIcon} name={'sort-down'} size={14} />
</TouchableOpacity>
</View>
</View>
}
ListHeaderComponent={this.listHeader}
style={discoverStyle.verticalClaimList}
orderBy={Constants.DEFAULT_ORDER_BY}
trendingForAll={TRENDING_FOR_ITEMS[0].name === currentTrendingForItem.name}
tags={followedTags.map(tag => tag.name)}
time={null}
orderBy={orderBy}
tags={filteredForTags ? followedTags.map(tag => tag.name) : null}
time={timeItem.name}
navigation={navigation}
orientation={Constants.ORIENTATION_VERTICAL}
/>
{!showModalTagSelector && <FloatingWalletBalance navigation={navigation} />}
{!showModalTagSelector && !showTrendingForPicker && !showSortPicker && !showTimePicker && (
<FloatingWalletBalance navigation={navigation} />
)}
{showModalTagSelector && (
<ModalTagSelector
onOverlayPress={() => this.setState({ showModalTagSelector: false })}
@ -108,13 +167,31 @@ class TrendingPage extends React.PureComponent {
)}
{showTrendingForPicker && (
<ModalPicker
title={'Trending for'}
title={'Filter for'}
onOverlayPress={() => this.setState({ showTrendingForPicker: false })}
onItemSelected={this.handleTrendingForItemSelected}
selectedItem={currentTrendingForItem}
items={TRENDING_FOR_ITEMS}
/>
)}
{showSortPicker && (
<ModalPicker
title={__('Sort content by')}
onOverlayPress={() => this.setState({ showSortPicker: false })}
onItemSelected={this.handleSortByItemSelected}
selectedItem={sortByItem}
items={Constants.CLAIM_SEARCH_SORT_BY_ITEMS}
/>
)}
{showTimePicker && (
<ModalPicker
title={__('Content from')}
onOverlayPress={() => this.setState({ showTimePicker: false })}
onItemSelected={this.handleTimeItemSelected}
selectedItem={timeItem}
items={Constants.CLAIM_SEARCH_TIME_ITEMS}
/>
)}
</View>
);
}

View file

@ -29,6 +29,7 @@ class VerificationScreen extends React.PureComponent {
componentDidMount() {
const { user } = this.props;
this.checkVerificationStatus(user);
NativeModules.Firebase.setCurrentScreen('Verification');
}
setEmailVerificationPhase = value => {

View file

@ -42,6 +42,7 @@ class WalletPage extends React.PureComponent {
const { pushDrawerStack, setPlayerVisible } = this.props;
pushDrawerStack();
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Wallet');
const { deviceWalletSynced, getSync, user } = this.props;
if (deviceWalletSynced && user && user.has_verified_email) {

View file

@ -27,3 +27,14 @@ export function doSetSortByItem(item) {
});
};
}
export function doSetTimeItem(item) {
return dispatch => {
dispatch({
type: Constants.ACTION_TIME_ITEM_CHANGED,
data: {
name: item.name,
},
});
};
}

View file

@ -5,6 +5,7 @@ const reducers = {};
const defaultState = {
clientSettings: {},
sortByItemName: Constants.SORT_BY_HOT,
timeItemName: Constants.TIME_WEEK,
};
reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
@ -23,6 +24,11 @@ reducers[Constants.ACTION_SORT_BY_ITEM_CHANGED] = (state, action) =>
sortByItemName: action.data.name,
});
reducers[Constants.ACTION_TIME_ITEM_CHANGED] = (state, action) =>
Object.assign({}, state, {
timeItemName: action.data.name,
});
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);

View file

@ -1,6 +1,6 @@
import { SETTINGS } from 'lbry-redux';
import { createSelector } from 'reselect';
import { getSortByItemForName } from 'utils/helper';
import { getSortByItemForName, getTimeItemForName } from 'utils/helper';
const selectState = state => state.settings || {};
@ -19,6 +19,11 @@ export const selectSortByItem = createSelector(
state => getSortByItemForName(state.sortByItemName)
);
export const selectTimeItem = createSelector(
selectState,
state => getTimeItemForName(state.timeItemName)
);
export const makeSelectClientSetting = setting =>
createSelector(
selectClientSettings,

View file

@ -33,7 +33,7 @@ const buttonStyle = StyleSheet.create({
fontSize: 14,
},
textWithIcon: {
marginLeft: 8,
marginLeft: 4,
},
});

View file

@ -31,13 +31,11 @@ const channelPageStyle = StyleSheet.create({
flex: 1,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'row',
},
infoText: {
fontFamily: 'Inter-UI-Regular',
fontSize: 20,
textAlign: 'center',
marginLeft: 10,
},
pageButtons: {
width: '100%',
@ -69,7 +67,7 @@ const channelPageStyle = StyleSheet.create({
subscribeButtonContainer: {
position: 'absolute',
flexDirection: 'row',
left: 8,
right: 8,
bottom: -90,
zIndex: 100,
},
@ -148,11 +146,25 @@ const channelPageStyle = StyleSheet.create({
left: 24,
bottom: -40,
zIndex: 100,
alignItems: 'center',
justifyContent: 'center',
},
avatarImage: {
width: '100%',
height: '100%',
},
listHeader: {
marginTop: 16,
marginBottom: 8,
marginLeft: 16,
marginRight: 16,
},
claimList: {
flex: 1,
},
claimListContent: {
paddingTop: 16,
},
});
export default channelPageStyle;

View file

@ -28,16 +28,25 @@ const discoverStyle = StyleSheet.create({
scrollContainer: {
flex: 1,
},
listHeader: {
marginBottom: 8,
marginLeft: 16,
marginRight: 16,
},
titleRow: {
flexDirection: 'row',
marginTop: 76,
marginBottom: 8,
alignItems: 'center',
justifyContent: 'space-between',
marginLeft: 16,
marginRight: 16,
},
rightTitleRow: {
pickerRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 8,
},
leftPickerRow: {
flexDirection: 'row',
alignItems: 'center',
},
@ -48,7 +57,6 @@ const discoverStyle = StyleSheet.create({
customizeLink: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
marginRight: 48,
},
trendingContainer: {
flex: 1,
@ -258,7 +266,12 @@ const discoverStyle = StyleSheet.create({
tagSortBy: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 4,
marginRight: 24,
},
allTagSortBy: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 8,
},
tagTime: {
flexDirection: 'row',
@ -272,6 +285,53 @@ const discoverStyle = StyleSheet.create({
tagSortIcon: {
marginTop: -6,
},
pickerLabel: {
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
color: Colors.DescriptionGrey,
marginRight: 6,
},
menuScrollContent: {
paddingTop: 16,
},
menuGroup: {
marginTop: 8,
marginBottom: 8,
},
menuGroupName: {
fontFamily: 'Inter-UI-Regular',
fontSize: 12,
color: Colors.DescriptionGrey,
textTransform: 'uppercase',
marginBottom: 4,
marginLeft: 16,
marginRight: 16,
},
menuItemTouchArea: {
flex: 1,
flexDirection: 'row',
paddingTop: 12,
paddingBottom: 12,
paddingLeft: 16,
paddingRight: 16,
alignItems: 'center',
},
menuItemTouchAreaFocused: {
backgroundColor: Colors.VeryLightGrey,
},
menuItemFocused: {
color: Colors.LbryGreen,
},
menuItemIcon: {
alignItems: 'center',
justifyContent: 'center',
width: 24,
},
menuItem: {
marginLeft: 8,
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
},
});
export default discoverStyle;

View file

@ -341,6 +341,11 @@ const publishStyle = StyleSheet.create({
fontSize: 14,
marginLeft: 8,
},
relativeCentered: {
alignItems: 'center',
justifyContent: 'center',
padding: 16,
},
centered: {
position: 'absolute',
left: 0,

View file

@ -156,10 +156,18 @@ const subscriptionsStyle = StyleSheet.create({
alignItems: 'center',
justifyContent: 'space-between',
},
pickerRow: {
flexDirection: 'row',
alignItems: 'center',
marginLeft: 16,
marginRight: 16,
marginTop: 8,
marginBottom: 8,
},
tagSortBy: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 4,
marginRight: 24,
},
tagSortText: {
fontFamily: 'Inter-UI-Regular',

View file

@ -217,6 +217,16 @@ export function getSortByItemForName(name) {
return null;
}
export function getTimeItemForName(name) {
for (let i = 0; i < Constants.CLAIM_SEARCH_TIME_ITEMS.length; i++) {
if (name === Constants.CLAIM_SEARCH_TIME_ITEMS[i].name) {
return Constants.CLAIM_SEARCH_TIME_ITEMS[i];
}
}
return null;
}
export function getOrderBy(item) {
let orderBy = [];
switch (item.name) {