Suggested subscriptions (#423)

* suggested subscriptions implementation
* some style and loading tweaks
* add subscribe buttons and style tweaks
This commit is contained in:
Akinwale Ariwodola 2019-02-08 16:48:35 +01:00 committed by GitHub
parent 654d50cd1b
commit 7f6874b791
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 285 additions and 47 deletions

View file

@ -33,6 +33,15 @@ class FileItem extends React.PureComponent {
} }
} }
navigateToFileUri = () => {
const { navigation, uri } = this.props;
const normalizedUri = normalizeURI(uri);
if (NativeModules.Mixpanel) {
NativeModules.Mixpanel.track('Discover Tap', { Uri: normalizeURI });
}
navigateToUri(navigation, normalizedUri);
}
render() { render() {
const { const {
claim, claim,
@ -42,7 +51,10 @@ class FileItem extends React.PureComponent {
rewardedContentClaimIds, rewardedContentClaimIds,
style, style,
mediaStyle, mediaStyle,
navigation navigation,
showDetails,
compactView,
titleBeforeThumbnail
} = this.props; } = this.props;
const uri = normalizeURI(this.props.uri); const uri = normalizeURI(this.props.uri);
@ -55,24 +67,21 @@ class FileItem extends React.PureComponent {
return ( return (
<View style={style}> <View style={style}>
<TouchableOpacity style={discoverStyle.container} onPress={() => { <TouchableOpacity style={discoverStyle.container} onPress={this.navigateToFileUri}>
if (NativeModules.Mixpanel) { {!compactView && titleBeforeThumbnail && <Text style={[discoverStyle.fileItemName, discoverStyle.rewardTitle]}>{title}</Text>}
NativeModules.Mixpanel.track('Discover Tap', { Uri: uri });
}
navigateToUri(navigation, uri);
}
}>
<FileItemMedia title={title} <FileItemMedia title={title}
thumbnail={thumbnail} thumbnail={thumbnail}
blurRadius={obscureNsfw ? 15 : 0} blurRadius={obscureNsfw ? 15 : 0}
resizeMode="cover" resizeMode="cover"
isResolvingUri={isResolvingUri} isResolvingUri={isResolvingUri}
style={mediaStyle} /> style={mediaStyle} />
<FilePrice uri={uri} style={discoverStyle.filePriceContainer} textStyle={discoverStyle.filePriceText} />
<View style={isRewardContent ? discoverStyle.rewardTitleContainer : null}> {!compactView && <FilePrice uri={uri} style={discoverStyle.filePriceContainer} textStyle={discoverStyle.filePriceText} />}
{!compactView && <View style={isRewardContent ? discoverStyle.rewardTitleContainer : null}>
<Text style={[discoverStyle.fileItemName, discoverStyle.rewardTitle]}>{title}</Text> <Text style={[discoverStyle.fileItemName, discoverStyle.rewardTitle]}>{title}</Text>
{isRewardContent && <Icon style={discoverStyle.rewardIcon} name="award" size={20} />} {isRewardContent && <Icon style={discoverStyle.rewardIcon} name="award" size={20} />}
</View> </View>}
{(!compactView && showDetails) &&
<View style={discoverStyle.detailsRow}> <View style={discoverStyle.detailsRow}>
{channelName && {channelName &&
<Link style={discoverStyle.channelName} text={channelName} onPress={() => { <Link style={discoverStyle.channelName} text={channelName} onPress={() => {
@ -80,7 +89,7 @@ class FileItem extends React.PureComponent {
navigateToUri(navigation, channelUri); navigateToUri(navigation, channelUri);
}} />} }} />}
<DateTime style={discoverStyle.dateTime} textStyle={discoverStyle.dateTimeText} timeAgo block={height} /> <DateTime style={discoverStyle.dateTime} textStyle={discoverStyle.dateTimeText} timeAgo block={height} />
</View> </View>}
</TouchableOpacity> </TouchableOpacity>
{obscureNsfw && <NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />} {obscureNsfw && <NsfwOverlay onPress={() => navigation.navigate({ routeName: 'Settings', key: 'settingsPage' })} />}
</View> </View>

View file

@ -2,8 +2,8 @@
import * as React from 'react'; import * as React from 'react';
import { buildURI } from 'lbry-redux'; import { buildURI } from 'lbry-redux';
import { FlatList } from 'react-native'; import { FlatList } from 'react-native';
import FileItem from '../fileItem'; import FileItem from 'component/fileItem';
import discoverStyle from '../../styles/discover'; import discoverStyle from 'styles/discover';
// In the future, all Flow types need to be specified in a common source (lbry-redux, perhaps?) // In the future, all Flow types need to be specified in a common source (lbry-redux, perhaps?)
type FileInfo = { type FileInfo = {
@ -175,7 +175,9 @@ class FileList extends React.PureComponent<Props, State> {
renderItem={({item}) => ( renderItem={({item}) => (
<FileItem style={discoverStyle.fileItem} <FileItem style={discoverStyle.fileItem}
uri={item} uri={item}
navigation={navigation} /> navigation={navigation}
showDetails={true}
compactView={false} />
)} /> )} />
); );
} }

View file

@ -6,7 +6,7 @@ import {
makeSelectFileInfoForUri, makeSelectFileInfoForUri,
makeSelectIsUriResolving, makeSelectIsUriResolving,
} from 'lbry-redux'; } from 'lbry-redux';
import { selectShowNsfw } from '../../redux/selectors/settings'; import { selectShowNsfw } from 'redux/selectors/settings';
import FileListItem from './view'; import FileListItem from './view';
const select = (state, props) => ({ const select = (state, props) => ({

View file

@ -0,0 +1,25 @@
import { connect } from 'react-redux';
import {
makeSelectFetchingChannelClaims,
makeSelectClaimsInChannelForPage,
doFetchClaimsByChannel,
doResolveUris,
} from 'lbry-redux';
import { selectShowNsfw } from 'redux/selectors/settings';
import SuggestedSubscriptionItem from './view';
const select = (state, props) => ({
claims: makeSelectClaimsInChannelForPage(props.categoryLink)(state),
fetching: makeSelectFetchingChannelClaims(props.categoryLink)(state),
obscureNsfw: !selectShowNsfw(state),
});
const perform = dispatch => ({
fetchChannel: channel => dispatch(doFetchClaimsByChannel(channel)),
resolveUris: uris => dispatch(doResolveUris(uris, true)),
});
export default connect(
select,
perform
)(SuggestedSubscriptionItem);

View file

@ -0,0 +1,74 @@
import React from 'react';
import { buildURI, normalizeURI } from 'lbry-redux';
import { ActivityIndicator, FlatList, Text, View } from 'react-native';
import Colors from 'styles/colors';
import discoverStyle from 'styles/discover';
import FileItem from 'component/fileItem';
import subscriptionsStyle from 'styles/subscriptions';
class SuggestedSubscriptionItem extends React.PureComponent {
componentDidMount() {
const { fetching, categoryLink, fetchChannel, resolveUris, claims } = this.props;
if (!fetching && categoryLink && (!claims || claims.length)) {
fetchChannel(categoryLink);
}
}
uriForClaim = (claim) => {
const { name: claimName, claim_name: claimNameDownloaded, claim_id: claimId } = claim;
const uriParams = {};
// This is unfortunate
// https://github.com/lbryio/lbry/issues/1159
const name = claimName || claimNameDownloaded;
uriParams.contentName = name;
uriParams.claimId = claimId;
const uri = buildURI(uriParams);
return uri;
}
render() {
const { categoryLink, fetching, obscureNsfw, claims, navigation } = this.props;
if (!claims || !claims.length) {
return (
<View style={subscriptionsStyle.busyContainer}>
<ActivityIndicator size={'small'} color={Colors.LbryGreen} />
</View>
);
}
if (claims && claims.length > 0) {
return (
<View style={subscriptionsStyle.suggestedContainer}>
<FileItem
style={subscriptionsStyle.fileItem}
mediaStyle={subscriptionsStyle.fileItemMedia}
uri={this.uriForClaim(claims[0])}
navigation={navigation} />
{(claims.length > 1) &&
<FlatList style={subscriptionsStyle.compactItems}
horizontal={true}
renderItem={ ({item}) => (
<FileItem
style={subscriptionsStyle.compactFileItem}
mediaStyle={subscriptionsStyle.compactFileItemMedia}
key={item}
uri={normalizeURI(item)}
navigation={navigation}
compactView={true} />
)
}
data={claims.slice(1, 4).map(claim => this.uriForClaim(claim))}
keyExtractor={(item, index) => item}
/>}
</View>
);
}
return null;
}
}
export default SuggestedSubscriptionItem;

View file

@ -0,0 +1,13 @@
import { connect } from 'react-redux';
import { selectSuggestedChannels, selectIsFetchingSuggested } from 'lbryinc';
import SuggestedSubscriptions from './view';
const select = state => ({
suggested: selectSuggestedChannels(state),
loading: selectIsFetchingSuggested(state),
});
export default connect(
select,
null
)(SuggestedSubscriptions);

View file

@ -0,0 +1,52 @@
import React from 'react';
import { ActivityIndicator, SectionList, Text, View } from 'react-native';
import { normalizeURI } from 'lbry-redux';
import SubscribeButton from 'component/subscribeButton';
import SuggestedSubscriptionItem from 'component/suggestedSubscriptionItem';
import Colors from 'styles/colors';
import discoverStyle from 'styles/discover';
import subscriptionsStyle from 'styles/subscriptions';
import Link from 'component/link';
class SuggestedSubscriptions extends React.PureComponent {
render() {
const { suggested, loading, navigation } = this.props;
if (loading) {
return (
<View>
<ActivityIndicator size="large" color={Colors.LbryGreen} />
</View>
);
}
return suggested ? (
<SectionList style={subscriptionsStyle.scrollContainer}
renderItem={ ({item, index, section}) => { console.log(item); return (
<SuggestedSubscriptionItem
key={item}
categoryLink={normalizeURI(item)}
navigation={navigation} />
); }
}
renderSectionHeader={
({section: {title}}) => {
const titleParts = title.split(';');
const channelName = titleParts[0];
const channelUri = normalizeURI(titleParts[1]);
return (
<View style={subscriptionsStyle.titleRow}>
<Link style={subscriptionsStyle.channelTitle} text={channelName} href={channelUri} />
<SubscribeButton style={subscriptionsStyle.subscribeButton} uri={channelUri} name={channelName} />
</View>
)
}
}
sections={suggested.map(({ uri, label }) => ({ title: (label + ';' + uri), data: [uri] }))}
keyExtractor={(item, index) => item}
/>
) : null;
}
}
export default SuggestedSubscriptions;

View file

@ -10,12 +10,12 @@ import {
} from 'react-native'; } from 'react-native';
import { normalizeURI, parseURI } from 'lbry-redux'; import { normalizeURI, parseURI } from 'lbry-redux';
import moment from 'moment'; import moment from 'moment';
import Colors from '../../styles/colors'; import Colors from 'styles/colors';
import discoverStyle from '../../styles/discover'; import discoverStyle from 'styles/discover';
import FloatingWalletBalance from '../../component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import FileItem from '../../component/fileItem'; import FileItem from 'component/fileItem';
import RewardSummary from '../../component/rewardSummary'; import RewardSummary from 'component/rewardSummary';
import UriBar from '../../component/uriBar'; import UriBar from 'component/uriBar';
class DiscoverPage extends React.PureComponent { class DiscoverPage extends React.PureComponent {
componentDidMount() { componentDidMount() {
@ -125,7 +125,9 @@ class DiscoverPage extends React.PureComponent {
mediaStyle={discoverStyle.fileItemMedia} mediaStyle={discoverStyle.fileItemMedia}
key={item} key={item}
uri={normalizeURI(item)} uri={normalizeURI(item)}
navigation={navigation} /> navigation={navigation}
compactView={false}
showDetails={true} />
) )
} }
renderSectionHeader={ renderSectionHeader={

View file

@ -31,11 +31,8 @@ const select = state => ({
}); });
const perform = dispatch => ({ const perform = dispatch => ({
doFetchMySubscriptions, doFetchMySubscriptions: () => dispatch(doFetchMySubscriptions()),
doSetViewMode, doFetchRecommendedSubscriptions: () => dispatch(doFetchRecommendedSubscriptions()),
doFetchRecommendedSubscriptions,
doCompleteFirstRun,
doShowSuggestedSubs,
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SUBSCRIPTIONS)) pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_SUBSCRIPTIONS))
}); });

View file

@ -17,18 +17,20 @@ import discoverStyle from 'styles/discover';
import subscriptionsStyle from 'styles/subscriptions'; import subscriptionsStyle from 'styles/subscriptions';
import FloatingWalletBalance from 'component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import FileItem from 'component/fileItem'; import FileItem from 'component/fileItem';
import SuggestedSubscriptions from 'component/suggestedSubscriptions';
import UriBar from 'component/uriBar'; import UriBar from 'component/uriBar';
class SubscriptionsPage extends React.PureComponent { class SubscriptionsPage extends React.PureComponent {
componentDidMount() { componentWillMount() {
const { const {
doFetchMySubscriptions, doFetchMySubscriptions,
doFetchRecommendedSubscriptions, doFetchRecommendedSubscriptions,
pushDrawerStack, pushDrawerStack,
} = this.props; } = this.props;
pushDrawerStack(); pushDrawerStack();
doFetchMySubscriptions(); doFetchMySubscriptions();
//doFetchRecommendedSubscriptions(); doFetchRecommendedSubscriptions();
} }
render() { render() {
@ -77,10 +79,12 @@ class SubscriptionsPage extends React.PureComponent {
} }
{!hasSubscriptions && {!hasSubscriptions &&
<View style={subscriptionsStyle.busyContainer}> <View style={subscriptionsStyle.container}>
<Text style={subscriptionsStyle.infoText}> <Text style={subscriptionsStyle.infoText}>
You are not subscribed to any channels. Feel free to discover new channels that you can subscribe to. You are not subscribed to any channels at the moment. Here are some channels that we think you might enjoy.
</Text> </Text>
{loadingSuggested && <ActivityIndicator size="large" colors={Colors.LbryGreen} style={subscriptionsStyle.loading} />}
{!loadingSuggested && <SuggestedSubscriptions navigation={navigation} />}
</View>} </View>}
<FloatingWalletBalance navigation={navigation} /> <FloatingWalletBalance navigation={navigation} />

View file

@ -10,11 +10,11 @@ import {
} from 'react-native'; } from 'react-native';
import { normalizeURI } from 'lbry-redux'; import { normalizeURI } from 'lbry-redux';
import moment from 'moment'; import moment from 'moment';
import FileItem from '../../component/fileItem'; import FileItem from '/component/fileItem';
import discoverStyle from '../../styles/discover'; import discoverStyle from 'styles/discover';
import Colors from '../../styles/colors'; import Colors from 'styles/colors';
import FloatingWalletBalance from '../../component/floatingWalletBalance'; import FloatingWalletBalance from 'component/floatingWalletBalance';
import UriBar from '../../component/uriBar'; import UriBar from 'component/uriBar';
class TrendingPage extends React.PureComponent { class TrendingPage extends React.PureComponent {
componentDidMount() { componentDidMount() {
@ -44,7 +44,9 @@ class TrendingPage extends React.PureComponent {
mediaStyle={discoverStyle.fileItemMedia} mediaStyle={discoverStyle.fileItemMedia}
key={item} key={item}
uri={normalizeURI(item)} uri={normalizeURI(item)}
navigation={navigation} /> navigation={navigation}
showDetails={true}
compactView={false} />
) )
} }
data={trendingUris.map(uri => uri.url)} data={trendingUris.map(uri => uri.url)}

View file

@ -2,15 +2,15 @@ import { Dimensions, PixelRatio, StyleSheet } from 'react-native';
import Colors from './colors'; import Colors from './colors';
const screenDimension = Dimensions.get('window'); const screenDimension = Dimensions.get('window');
const screenWidth = screenDimension.width; export const screenWidth = screenDimension.width;
const screenHeight = screenDimension.height; export const screenHeight = screenDimension.height;
const screenWidthPixels = PixelRatio.getPixelSizeForLayoutSize(screenWidth); const screenWidthPixels = PixelRatio.getPixelSizeForLayoutSize(screenWidth);
const screenHeightPixels = PixelRatio.getPixelSizeForLayoutSize(screenHeight); const screenHeightPixels = PixelRatio.getPixelSizeForLayoutSize(screenHeight);
// calculate thumbnail width and height based on device's aspect ratio // calculate thumbnail width and height based on device's aspect ratio
const horizontalMargin = 48; // left and right margins (24 + 24) export const horizontalMargin = 48; // left and right margins (24 + 24)
const verticalMargin = (screenWidthPixels > 720 && screenHeightPixels > 1920) ? 0 : ((screenWidthPixels <= 720) ? 20 : 16); export const verticalMargin = (screenWidthPixels > 720 && screenHeightPixels > 1920) ? 0 : ((screenWidthPixels <= 720) ? 20 : 16);
const mediaWidth = screenWidth - horizontalMargin; export const mediaWidth = screenWidth - horizontalMargin;
const mediaHeight = ((screenWidth / screenHeight) * ((screenWidthPixels <= 720) ? screenWidth : mediaWidth)) - verticalMargin; export const mediaHeight = ((screenWidth / screenHeight) * ((screenWidthPixels <= 720) ? screenWidth : mediaWidth)) - verticalMargin;
const discoverStyle = StyleSheet.create({ const discoverStyle = StyleSheet.create({
container: { container: {

View file

@ -1,4 +1,11 @@
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import {
screenWidth,
horizontalMargin,
mediaWidth,
mediaHeight
} from 'styles/discover';
import Colors from 'styles/colors';
const subscriptionsStyle = StyleSheet.create({ const subscriptionsStyle = StyleSheet.create({
container: { container: {
@ -19,14 +26,65 @@ const subscriptionsStyle = StyleSheet.create({
paddingTop: 24 paddingTop: 24
}, },
infoText: { infoText: {
textAlign: 'center',
fontFamily: 'Inter-UI-Regular', fontFamily: 'Inter-UI-Regular',
fontSize: 16, fontSize: 16,
margin: 16
},
suggestedContainer: {
flex: 1,
}, },
fileItem: { fileItem: {
marginLeft: 24, marginLeft: 24,
marginRight: 24, marginRight: 24,
marginBottom: 24 },
compactItems: {
flex: 1,
marginTop: 6,
marginLeft: 20,
marginRight: 24,
marginBottom: 24,
height: 80,
},
compactFileItem: {
width: (screenWidth - horizontalMargin - (6 * 3)) / 3,
marginLeft: 6,
height: '100%'
},
compactFileItemMedia: {
width: (screenWidth - horizontalMargin) / 3,
height: '100%'
},
fileItemMedia: {
width: mediaWidth,
height: mediaHeight,
alignItems: 'center',
justifyContent: 'center'
},
fileItemName: {
fontFamily: 'Inter-UI-Bold',
marginTop: 8,
fontSize: 18
},
channelTitle: {
fontFamily: 'Inter-UI-SemiBold',
fontSize: 20,
marginLeft: 24,
marginTop: 16,
marginBottom: 16,
color: Colors.LbryGreen
},
titleRow: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between'
},
subscribeButton: {
alignSelf: 'flex-start',
marginRight: 24,
marginTop: 8,
backgroundColor: Colors.White,
paddingLeft: 16,
paddingRight: 16,
} }
}); });