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

View file

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

View file

@ -6,7 +6,7 @@ import {
makeSelectFileInfoForUri,
makeSelectIsUriResolving,
} from 'lbry-redux';
import { selectShowNsfw } from '../../redux/selectors/settings';
import { selectShowNsfw } from 'redux/selectors/settings';
import FileListItem from './view';
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';
import { normalizeURI, parseURI } from 'lbry-redux';
import moment from 'moment';
import Colors from '../../styles/colors';
import discoverStyle from '../../styles/discover';
import FloatingWalletBalance from '../../component/floatingWalletBalance';
import FileItem from '../../component/fileItem';
import RewardSummary from '../../component/rewardSummary';
import UriBar from '../../component/uriBar';
import Colors from 'styles/colors';
import discoverStyle from 'styles/discover';
import FloatingWalletBalance from 'component/floatingWalletBalance';
import FileItem from 'component/fileItem';
import RewardSummary from 'component/rewardSummary';
import UriBar from 'component/uriBar';
class DiscoverPage extends React.PureComponent {
componentDidMount() {
@ -125,7 +125,9 @@ class DiscoverPage extends React.PureComponent {
mediaStyle={discoverStyle.fileItemMedia}
key={item}
uri={normalizeURI(item)}
navigation={navigation} />
navigation={navigation}
compactView={false}
showDetails={true} />
)
}
renderSectionHeader={

View file

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

View file

@ -17,18 +17,20 @@ import discoverStyle from 'styles/discover';
import subscriptionsStyle from 'styles/subscriptions';
import FloatingWalletBalance from 'component/floatingWalletBalance';
import FileItem from 'component/fileItem';
import SuggestedSubscriptions from 'component/suggestedSubscriptions';
import UriBar from 'component/uriBar';
class SubscriptionsPage extends React.PureComponent {
componentDidMount() {
componentWillMount() {
const {
doFetchMySubscriptions,
doFetchRecommendedSubscriptions,
pushDrawerStack,
} = this.props;
pushDrawerStack();
doFetchMySubscriptions();
//doFetchRecommendedSubscriptions();
doFetchRecommendedSubscriptions();
}
render() {
@ -77,10 +79,12 @@ class SubscriptionsPage extends React.PureComponent {
}
{!hasSubscriptions &&
<View style={subscriptionsStyle.busyContainer}>
<View style={subscriptionsStyle.container}>
<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>
{loadingSuggested && <ActivityIndicator size="large" colors={Colors.LbryGreen} style={subscriptionsStyle.loading} />}
{!loadingSuggested && <SuggestedSubscriptions navigation={navigation} />}
</View>}
<FloatingWalletBalance navigation={navigation} />

View file

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

View file

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

View file

@ -1,4 +1,11 @@
import { StyleSheet } from 'react-native';
import {
screenWidth,
horizontalMargin,
mediaWidth,
mediaHeight
} from 'styles/discover';
import Colors from 'styles/colors';
const subscriptionsStyle = StyleSheet.create({
container: {
@ -19,14 +26,65 @@ const subscriptionsStyle = StyleSheet.create({
paddingTop: 24
},
infoText: {
textAlign: 'center',
fontFamily: 'Inter-UI-Regular',
fontSize: 16,
margin: 16
},
suggestedContainer: {
flex: 1,
},
fileItem: {
marginLeft: 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,
}
});