Unverified Commit f05d357f authored by Akinwale Ariwodola's avatar Akinwale Ariwodola Committed by GitHub
Browse files

Following rework (#124)

* make following the default page. add suggested grd.
* suggested grid vertical scrolling. semi-infinite scroll
* search and related content fixes
parent 1f649b9d
......@@ -7067,8 +7067,8 @@
}
},
"lbry-redux": {
"version": "github:lbryio/lbry-redux#1de1d534c982db913f145a6171f39d7b8ebd61af",
"from": "github:lbryio/lbry-redux#1de1d534c982db913f145a6171f39d7b8ebd61af",
"version": "github:lbryio/lbry-redux#5c874e921769093428966fa7ecdf723719cb9067",
"from": "github:lbryio/lbry-redux#5c874e921769093428966fa7ecdf723719cb9067",
"requires": {
"proxy-polyfill": "0.1.6",
"reselect": "^3.0.0",
......@@ -7076,8 +7076,8 @@
}
},
"lbryinc": {
"version": "github:lbryio/lbryinc#138a053754ec8e3da8e9bf153d32f527c962f25c",
"from": "github:lbryio/lbryinc#138a053754ec8e3da8e9bf153d32f527c962f25c",
"version": "github:lbryio/lbryinc#0dc8829a319a708f45a855765f70a193ccb72676",
"from": "github:lbryio/lbryinc#0dc8829a319a708f45a855765f70a193ccb72676",
"requires": {
"reselect": "^3.0.0"
}
......
......@@ -88,12 +88,13 @@ const menuNavigationButton = navigation => (
const discoverStack = createStackNavigator(
{
Discover: {
screen: DiscoverPage,
navigationOptions: ({ navigation }) => ({
title: 'Explore',
Subscriptions: {
screen: SubscriptionsPage,
navigationOptions: {
title: 'Following',
header: null,
}),
drawerIcon: ({ tintColor }) => <Icon name="heart" solid size={drawerIconSize} style={{ color: tintColor }} />,
},
},
File: {
screen: FilePage,
......@@ -160,10 +161,17 @@ const drawer = createDrawerNavigator(
DiscoverStack: {
screen: discoverStack,
navigationOptions: {
title: 'Explore',
title: 'Following',
drawerIcon: ({ tintColor }) => <Icon name="home" size={drawerIconSize} style={{ color: tintColor }} />,
},
},
Discover: {
screen: DiscoverPage,
navigationOptions: ({ navigation }) => ({
title: 'Your Tags',
header: null,
}),
},
Trending: {
screen: TrendingPage,
navigationOptions: {
......@@ -171,13 +179,6 @@ const drawer = createDrawerNavigator(
drawerIcon: ({ tintColor }) => <Icon name="fire" size={drawerIconSize} style={{ color: tintColor }} />,
},
},
Subscriptions: {
screen: SubscriptionsPage,
navigationOptions: {
title: 'Subscriptions',
drawerIcon: ({ tintColor }) => <Icon name="heart" solid size={drawerIconSize} style={{ color: tintColor }} />,
},
},
WalletStack: {
screen: walletStack,
navigationOptions: {
......
......@@ -9,8 +9,8 @@ import discoverStyle from 'styles/discover';
const groupedMenuItems = {
'Find content': [
{ icon: 'hashtag', label: 'Your Tags', route: Constants.DRAWER_ROUTE_DISCOVER },
{ icon: 'heart', solid: true, label: 'Following', route: Constants.DRAWER_ROUTE_SUBSCRIPTIONS },
{ icon: 'hashtag', label: 'Your Tags', route: Constants.DRAWER_ROUTE_DISCOVER },
{ icon: 'globe-americas', label: 'All Content', route: Constants.DRAWER_ROUTE_TRENDING },
],
'Your content': [
......@@ -145,7 +145,7 @@ class DrawerContent extends React.PureComponent {
const focused =
activeItemKey === item.route ||
(activeItemKey === Constants.FULL_ROUTE_NAME_DISCOVER &&
item.route === Constants.DRAWER_ROUTE_DISCOVER) ||
item.route === Constants.DRAWER_ROUTE_SUBSCRIPTIONS) ||
(activeItemKey === Constants.FULL_ROUTE_NAME_WALLET &&
item.route === Constants.DRAWER_ROUTE_WALLET);
return (
......
import { connect } from 'react-redux';
import { selectFetchingClaimSearch } from 'lbry-redux';
import ModalSuggestedSubscriptions from './view';
export default connect()(ModalSuggestedSubscriptions);
const select = state => ({
loadingSuggested: selectFetchingClaimSearch(state),
});
export default connect(select)(ModalSuggestedSubscriptions);
import React from 'react';
import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { ActivityIndicator, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import modalStyle from 'styles/modal';
import subscriptionsStyle from 'styles/subscriptions';
import Button from 'component/button';
import Colors from 'styles/colors';
import SuggestedSubscriptions from 'component/suggestedSubscriptions';
import SuggestedSubscriptionsGrid from 'component/suggestedSubscriptionsGrid';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import Icon from 'react-native-vector-icons/FontAwesome5';
export default class ModalSuggestedSubcriptions extends React.PureComponent {
render() {
const { navigation, onDonePress, onOverlayPress } = this.props;
const { loadingSuggested, navigation, onDonePress, onOverlayPress } = this.props;
return (
<TouchableOpacity style={modalStyle.overlay} activeOpacity={1} onPress={onOverlayPress}>
<TouchableOpacity style={[modalStyle.container, subscriptionsStyle.modalContainer]} activeOpacity={1}>
<SuggestedSubscriptions inModal navigation={navigation} />
<View style={modalStyle.buttons}>
<Button style={modalStyle.doneButton} text={__('Done')} onPress={onDonePress} />
<SuggestedSubscriptionsGrid inModal navigation={navigation} />
<View style={modalStyle.wideButtons}>
<Button style={modalStyle.wideDoneButton} text={__('Done')} onPress={onDonePress} />
{loadingSuggested && (
<ActivityIndicator size="small" color={Colors.White} style={subscriptionsStyle.modalLoading} />
)}
</View>
</TouchableOpacity>
</TouchableOpacity>
......
import { connect } from 'react-redux';
import { doChannelSubscribe, doChannelUnsubscribe, selectSubscriptions, makeSelectIsSubscribed } from 'lbryinc';
import { doToast } from 'lbry-redux';
import SubscribeButtonOverlay from './view';
const select = (state, props) => ({
subscriptions: selectSubscriptions(state),
isSubscribed: makeSelectIsSubscribed(props.uri, true)(state),
});
export default connect(
select,
{
doChannelSubscribe,
doChannelUnsubscribe,
doToast,
},
)(SubscribeButtonOverlay);
import React from 'react';
import { normalizeURI, parseURI } from 'lbry-redux';
import { NativeModules, Text, View, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome5';
import Button from 'component/button';
import Colors from 'styles/colors';
class SubscribeButtonOverlay extends React.PureComponent {
handlePress = () => {
const { claim, isSubscribed, doChannelSubscribe, doChannelUnsubscribe, uri } = this.props;
if (!claim) {
return;
}
const subscriptionHandler = isSubscribed ? doChannelUnsubscribe : doChannelSubscribe;
const { name: claimName } = claim;
subscriptionHandler({
channelName: claimName,
uri: normalizeURI(uri),
});
};
render() {
const { uri, isSubscribed, style } = this.props;
let styles = style.length ? style : [style];
return (
<TouchableOpacity style={styles} opacity={0.7} onPress={this.handlePress}>
{isSubscribed && <Icon name={'heart'} size={20} solid color={Colors.Red} />}
{!isSubscribed && <Icon name={'heart'} size={20} color={Colors.Red} />}
</TouchableOpacity>
);
}
}
export default SubscribeButtonOverlay;
......@@ -6,6 +6,7 @@ import {
makeSelectTitleForUri,
makeSelectIsUriResolving,
} from 'lbry-redux';
import { doChannelSubscribe, doChannelUnsubscribe, makeSelectIsSubscribed } from 'lbryinc';
import SuggestedSubscriptionItem from './view';
const select = (state, props) => ({
......@@ -13,13 +14,16 @@ const select = (state, props) => ({
title: makeSelectTitleForUri(props.uri)(state),
claim: makeSelectClaimForUri(props.uri)(state),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
isSubscribed: makeSelectIsSubscribed(props.uri, true)(state),
});
const perform = dispatch => ({
resolveUri: uri => dispatch(doResolveUri(uri)),
subscribe: subscription => doChannelSubscribe(subscription),
unsubscribe: subscription => doChannelUnsubscribe(subscription),
});
export default connect(
select,
perform
perform,
)(SuggestedSubscriptionItem);
import React from 'react';
import { buildURI, normalizeURI } from 'lbry-redux';
import { ActivityIndicator, FlatList, Image, Text, View } from 'react-native';
import { ActivityIndicator, FlatList, Image, 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 discoverStyle from 'styles/discover';
import FileItem from 'component/fileItem';
import SubscribeButton from 'component/subscribeButton';
import SubscribeButtonOverlay from 'component/subscribeButtonOverlay';
import subscriptionsStyle from 'styles/subscriptions';
import Link from 'component/link';
import Tag from 'component/tag';
......@@ -31,6 +31,7 @@ class SuggestedSubscriptionItem extends React.PureComponent {
render() {
const { claim, isResolvingUri, navigation, thumbnail, title, uri } = this.props;
let shortUrl, tags;
if (claim) {
shortUrl = claim.short_url;
......@@ -49,7 +50,7 @@ class SuggestedSubscriptionItem extends React.PureComponent {
}
return (
<View style={subscriptionsStyle.suggestedItem}>
<TouchableOpacity style={subscriptionsStyle.suggestedItem}>
<View style={[subscriptionsStyle.suggestedItemThumbnailContainer, this.state.autoStyle]}>
{hasThumbnail && (
<Image style={subscriptionsStyle.suggestedItemThumbnail} resizeMode={'cover'} source={{ uri: thumbnail }} />
......@@ -62,35 +63,37 @@ class SuggestedSubscriptionItem extends React.PureComponent {
</View>
<View style={subscriptionsStyle.suggestedItemDetails}>
{title && (
<Text style={subscriptionsStyle.suggestedItemTitle} numberOfLines={1}>
{title}
</Text>
)}
{claim && (
<Link
style={subscriptionsStyle.suggestedItemName}
numberOfLines={1}
text={claim.name}
onPress={() => navigateToUri(navigation, normalizeURI(shortUrl || uri), null, false, claim.permanent_url)}
/>
)}
<Text style={subscriptionsStyle.suggestedItemTitle} numberOfLines={2}>
{title || claim.name}
</Text>
{tags && (
<View style={subscriptionsStyle.suggestedItemTagList}>
{tags &&
tags
.slice(0, 3)
.slice(0, 1)
.map(tag => (
<Tag style={subscriptionsStyle.tag} key={tag} name={tag} navigation={navigation} truncate />
<Tag
numberOfLines={1}
onPress={this.handleItemPress}
style={subscriptionsStyle.tag}
key={tag}
name={tag}
navigation={navigation}
truncate
/>
))}
</View>
)}
</View>
{claim && (
<SubscribeButton style={subscriptionsStyle.suggestedItemSubscribe} uri={normalizeURI(claim.permanent_url)} />
<SubscribeButtonOverlay
claim={claim}
style={subscriptionsStyle.suggestedItemSubscribeOverlay}
uri={normalizeURI(claim.permanent_url)}
/>
)}
</View>
</TouchableOpacity>
);
}
}
......
import { connect } from 'react-redux';
import {
doClaimSearch,
selectFetchingClaimSearch,
selectClaimSearchByQuery,
selectClaimSearchByQueryLastPageReached,
selectFollowedTags,
} from 'lbry-redux';
import { selectSubscriptions, selectSuggestedChannels, selectIsFetchingSuggested } from 'lbryinc';
import { selectShowNsfw } from 'redux/selectors/settings';
import SuggestedSubscriptionsGrid from './view';
const select = state => ({
followedTags: selectFollowedTags(state),
subscriptions: selectSubscriptions(state),
suggested: selectSuggestedChannels(state),
loading: selectIsFetchingSuggested(state) || selectFetchingClaimSearch(state),
claimSearchByQuery: selectClaimSearchByQuery(state),
lastPageReached: selectClaimSearchByQueryLastPageReached(state),
showNsfwContent: selectShowNsfw(state),
});
const perform = dispatch => ({
claimSearch: options => dispatch(doClaimSearch(options)),
});
export default connect(
select,
perform,
)(SuggestedSubscriptionsGrid);
import React from 'react';
import { ActivityIndicator, SectionList, Text, View } from 'react-native';
import { MATURE_TAGS, createNormalizedClaimSearchKey, normalizeURI } from 'lbry-redux';
import { navigateToUri } from 'utils/helper';
import { FlatGrid } from 'react-native-super-grid';
import SubscribeButton from 'component/subscribeButton';
import SuggestedSubscriptionItem from 'component/suggestedSubscriptionItem';
import Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
import discoverStyle from 'styles/discover';
import subscriptionsStyle from 'styles/subscriptions';
import Link from 'component/link';
import _ from 'lodash';
const suggestedPageSize = 24;
const softLimit = 2400;
class SuggestedSubscriptionsGrid extends React.PureComponent {
state = {
currentPage: 1,
options: {},
};
buildClaimSearchOptions() {
const { showNsfwContent, subscriptions } = this.props;
const { currentPage } = this.state;
const options = {
no_totals: true,
page: currentPage,
page_size: suggestedPageSize,
claim_type: 'channel',
order_by: [Constants.ORDER_BY_EFFECTIVE_AMOUNT],
};
if (!showNsfwContent) {
options.not_tags = MATURE_TAGS;
}
/* if (subscriptions && subscriptions.length > 0) {
options.not_channel_ids = subscriptions.map(subscription => subscription.uri.split('#')[1]);
} */
return options;
}
doClaimSearch() {
const { claimSearch } = this.props;
const options = this.buildClaimSearchOptions();
claimSearch(options);
}
handleVerticalEndReached = () => {
// fetch more content
const { claimSearchByQuery, lastPageReached } = this.props;
const options = this.buildClaimSearchOptions();
const claimSearchKey = createNormalizedClaimSearchKey(options);
const uris = claimSearchByQuery[claimSearchKey];
if (
lastPageReached[claimSearchKey] ||
((uris.length > 0 && uris.length < suggestedPageSize) || uris.length >= softLimit)
) {
return;
}
this.setState({ currentPage: this.state.currentPage + 1 }, () => this.doClaimSearch());
};
componentDidMount() {
const { claimSearch, followedTags, showNsfwContent } = this.props;
this.doClaimSearch();
}
render() {
const { claimSearchByQuery, suggested, inModal, navigation } = this.props;
const options = this.buildClaimSearchOptions();
const claimSearchKey = createNormalizedClaimSearchKey(options);
const claimSearchUris = claimSearchByQuery[claimSearchKey];
return (
<FlatGrid
initialNumToRender={24}
maxToRenderPerBatch={48}
removeClippedSubviews
itemDimension={120}
spacing={2}
items={claimSearchUris}
style={inModal ? subscriptionsStyle.modalScrollContainer : subscriptionsStyle.scrollContainer}
contentContainerStyle={
inModal ? subscriptionsStyle.modalSuggestedScrollContent : subscriptionsStyle.suggestedScrollContent
}
renderItem={({ item, index }) => (
<SuggestedSubscriptionItem key={item} uri={normalizeURI(item)} navigation={navigation} />
)}
onEndReached={this.handleVerticalEndReached}
onEndReachedThreshold={0.2}
/>
);
}
}
export default SuggestedSubscriptionsGrid;
......@@ -30,7 +30,7 @@ export default class Tag extends React.PureComponent {
};
render() {
const { name, onPress, style, type, truncate } = this.props;
const { name, numberOfLines, onPress, style, type, truncate } = this.props;
let styles = [];
if (style) {
......@@ -50,7 +50,9 @@ export default class Tag extends React.PureComponent {
return (
<TouchableOpacity style={styles} onPress={onPress || this.onPressDefault}>
<View style={tagStyle.content}>
<Text style={tagStyle.text}>{truncate ? formatTagName(name) : name}</Text>
<Text style={tagStyle.text} numberOfLines={numberOfLines}>
{truncate ? formatTagName(name) : name}
</Text>
{type && <Icon style={tagStyle.icon} name={type === 'add' ? 'plus' : 'times'} size={8} />}
</View>
</TouchableOpacity>
......
......@@ -64,9 +64,7 @@ class DiscoverPage extends React.PureComponent {
onComponentFocused = () => {
const { pushDrawerStack, setPlayerVisible, currentRoute } = this.props;
if (currentRoute === Constants.DRAWER_ROUTE_DISCOVER) {
pushDrawerStack();
}
pushDrawerStack();
NativeModules.Firebase.setCurrentScreen('Your tags');
setPlayerVisible();
......
......@@ -26,10 +26,14 @@ const select = state => ({
query: selectSearchValue(state),
resolvingUris: selectResolvingUris(state),
showNsfwContent: selectShowNsfw(state),
uris: makeSelectSearchUris(makeSelectQueryWithOptions(null, Constants.DEFAULT_PAGE_SIZE)(state))(state),
results: makeSelectResolvedSearchResults(makeSelectQueryWithOptions(null, Constants.DEFAULT_PAGE_SIZE)(state))(state),
uris: makeSelectSearchUris(
makeSelectQueryWithOptions(null, { size: Constants.DEFAULT_PAGE_SIZE, isBackgroundSearch: false })(state),
)(state),
results: makeSelectResolvedSearchResults(
makeSelectQueryWithOptions(null, { size: Constants.DEFAULT_PAGE_SIZE, isBackgroundSearch: false })(state),
)(state),
lastPageReached: makeSelectResolvedSearchResultsLastPageReached(
makeSelectQueryWithOptions(null, Constants.DEFAULT_PAGE_SIZE)(state),
makeSelectQueryWithOptions(null, { size: Constants.DEFAULT_PAGE_SIZE, isBackgroundSearch: false })(state),
)(state),
});
......
......@@ -7,13 +7,13 @@ import {
selectSubscriptionClaims,
selectSubscriptions,
selectIsFetchingSubscriptions,
selectIsFetchingSuggested,
selectSuggestedChannels,
selectUnreadSubscriptions,
selectViewMode,
selectFirstRunCompleted,
selectShowSuggestedSubs,
} from 'lbryinc';
import { doToast, selectFetchingClaimSearch } from 'lbry-redux';
import { doPushDrawerStack, doSetPlayerVisible } from 'redux/actions/drawer';
import { doSetClientSetting, doSetTimeItem } from 'redux/actions/settings';
import { makeSelectClientSetting, selectTimeItem } from 'redux/selectors/settings';
......@@ -24,7 +24,7 @@ import SubscriptionsPage from './view';
const select = state => ({
currentRoute: selectCurrentRoute(state),
loading: selectIsFetchingSubscriptions(state),
loadingSuggested: selectIsFetchingSuggested(state),
loadingSuggested: selectFetchingClaimSearch(state),
subscribedChannels: selectSubscriptions(state),
suggestedChannels: selectSuggestedChannels(state),
subscriptionsViewMode: makeSelectClientSetting(Constants.SETTING_SUBSCRIPTIONS_VIEW_MODE)(state),
......@@ -41,6 +41,7 @@ const perform = dispatch => ({
doFetchMySubscriptions: () => dispatch(doFetchMySubscriptions()),
doFetchRecommendedSubscriptions: () => dispatch(doFetchRecommendedSubscriptions()),
doSetViewMode: viewMode => dispatch(doSetViewMode(viewMode)),
notify: data => dispatch(doToast(data)),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SUBSCRIPTIONS)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),
......@@ -49,5 +50,5 @@ const perform = dispatch => ({
export default connect(
select,
perform
perform,
)(SubscriptionsPage);
......@@ -28,6 +28,7 @@ import ModalPicker from 'component/modalPicker';
import ModalSuggestedSubscriptions from 'component/modalSuggestedSubscriptions';
import SubscribedChannelList from 'component/subscribedChannelList';
import SuggestedSubscriptions from 'component/suggestedSubscriptions';
import SuggestedSubscriptionsGrid from 'component/suggestedSubscriptionsGrid';
import UriBar from 'component/uriBar';
class SubscriptionsPage extends React.PureComponent {
......@@ -56,6 +57,7 @@ class SubscriptionsPage extends React.PureComponent {
onComponentFocused = () => {
const {
currentRoute,
doFetchMySubscriptions,
doFetchRecommendedSubscriptions,
doSetViewMode,
......@@ -64,12 +66,13 @@ class SubscriptionsPage extends React.PureComponent {
subscriptionsViewMode,
} = this.props;
pushDrawerStack();
if (currentRoute === Constants.DRAWER_ROUTE_SUBSCRIPTIONS) {
pushDrawerStack();
}
setPlayerVisible();
NativeModules.Firebase.setCurrentScreen('Subscriptions');
doFetchMySubscriptions();
doFetchRecommendedSubscriptions();
};
componentDidMount() {
......@@ -90,6 +93,15 @@ class SubscriptionsPage extends React.PureComponent {
this.setState({ currentSortByItem: item, orderBy: getOrderBy(item), showSortPicker: false });
};
shouldComponentUpdate(nextProps, nextState) {
const { showModalSuggestedSubs: prevShowModalSuggestedSubs } = this.state;
const { showModalSuggestedSubs } = this.state;
if (prevShowModalSuggestedSubs && showModalSuggestedSubs) {
return false;
}
return true;
}
handleTimeItemSelected = item => {
const { setTimeItem } = this.props;
setTimeItem(item);
......@@ -138,6 +150,7 @@ class SubscriptionsPage extends React.PureComponent {
timeItem,