Misc tweaks and fixes (#13)

* channel icon auto thumbs. fix infinite claim list reload.
* tweak last page check
* additional tweaks and fixes before release
This commit is contained in:
Akinwale Ariwodola 2019-08-11 08:52:17 +01:00 committed by GitHub
parent 89222bc9ee
commit 38618080a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 212 additions and 119 deletions

View file

@ -144,7 +144,7 @@ const drawer = createDrawerNavigator(
drawerIcon: ({ tintColor }) => <Icon name="fire" size={drawerIconSize} style={{ color: tintColor }} />,
},
},
MySubscriptionsStack: {
Subscriptions: {
screen: SubscriptionsPage,
navigationOptions: {
title: 'Subscriptions',
@ -270,21 +270,11 @@ class AppWithNavigationState extends React.Component {
'hardwareBackPress',
function() {
const { dispatch, nav, drawerStack } = this.props;
// There should be a better way to check this
if (nav.routes.length > 0) {
if (nav.routes[0].routeName === 'Main') {
const mainRoute = nav.routes[0];
if (
mainRoute.index > 0 ||
mainRoute.routes[0].index > 0 /* Discover stack index */ ||
mainRoute.routes[4].index > 0 /* Wallet stack index */ ||
mainRoute.index >= 5 /* Settings and About screens */
) {
if (drawerStack.length > 1) {
dispatchNavigateBack(dispatch, nav, drawerStack);
return true;
}
}
}
return false;
}.bind(this)
);

View file

@ -1,10 +1,17 @@
import { connect } from 'react-redux';
import { doResolveUri, makeSelectClaimForUri, makeSelectThumbnailForUri, makeSelectIsUriResolving } from 'lbry-redux';
import {
doResolveUri,
makeSelectClaimForUri,
makeSelectThumbnailForUri,
makeSelectTitleForUri,
makeSelectIsUriResolving,
} from 'lbry-redux';
import ChannelIconItem from './view';
const select = (state, props) => ({
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
claim: makeSelectClaimForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
});

View file

@ -1,9 +1,35 @@
import React from 'react';
import { ActivityIndicator, Image, Text, TouchableOpacity, View } from 'react-native';
import Colors from 'styles/colors';
import autothumbStyle from 'styles/autothumb';
import channelIconStyle from 'styles/channelIcon';
export default class ChannelIconItem extends React.PureComponent {
static AUTO_THUMB_STYLES = [
autothumbStyle.autothumbPurple,
autothumbStyle.autothumbRed,
autothumbStyle.autothumbPink,
autothumbStyle.autothumbIndigo,
autothumbStyle.autothumbBlue,
autothumbStyle.autothumbLightBlue,
autothumbStyle.autothumbCyan,
autothumbStyle.autothumbTeal,
autothumbStyle.autothumbGreen,
autothumbStyle.autothumbYellow,
autothumbStyle.autothumbOrange,
];
state = {
autoStyle: null,
};
componentWillMount() {
this.setState({
autoStyle:
ChannelIconItem.AUTO_THUMB_STYLES[Math.floor(Math.random() * ChannelIconItem.AUTO_THUMB_STYLES.length)],
});
}
componentDidMount() {
const { claim, isPlaceholder, uri, resolveUri } = this.props;
if (!claim && !isPlaceholder) {
@ -13,6 +39,8 @@ export default class ChannelIconItem extends React.PureComponent {
render() {
const { claim, isPlaceholder, isResolvingUri, onPress, thumbnail, title } = this.props;
const displayName = title || (claim ? claim.name : '');
const substrIndex = displayName.startsWith('@') ? 1 : 0;
return (
<TouchableOpacity style={channelIconStyle.container} onPress={onPress}>
@ -25,6 +53,7 @@ export default class ChannelIconItem extends React.PureComponent {
style={[
channelIconStyle.thumbnailContainer,
isPlaceholder ? channelIconStyle.borderedThumbnailContainer : null,
isPlaceholder ? null : this.state.autoStyle,
]}
>
{isPlaceholder && (
@ -32,17 +61,18 @@ export default class ChannelIconItem extends React.PureComponent {
<Text style={channelIconStyle.placeholderText}>ALL</Text>
</View>
)}
{!isPlaceholder && (
<Image
style={channelIconStyle.thumbnail}
resizeMode={'cover'}
source={thumbnail ? { uri: thumbnail } : require('../../assets/default_avatar.jpg')}
/>
{!isPlaceholder && thumbnail && (
<Image style={channelIconStyle.thumbnail} resizeMode={'cover'} source={{ uri: thumbnail }} />
)}
{!isPlaceholder && !thumbnail && (
<Text style={channelIconStyle.autothumbCharacter}>
{displayName.substring(substrIndex, substrIndex + 1).toUpperCase()}
</Text>
)}
</View>
{!isPlaceholder && (
<Text style={channelIconStyle.title} numberOfLines={1}>
{title || (claim ? claim.name : '')}
{displayName}
</Text>
)}
</TouchableOpacity>

View file

@ -22,6 +22,7 @@ class ClaimList extends React.PureComponent {
currentPage: 1, // initial page load is page 1
subscriptionsView: false, // whether or not this claim list is for subscriptions
trendingForAllView: false,
lastPageReached: false,
};
componentDidMount() {
@ -73,9 +74,10 @@ class ClaimList extends React.PureComponent {
channelIds: prevChannelIds,
trendingForAll: prevTrendingForAll,
time: prevTime,
claimSearchUris: prevClaimSearchUris,
showNsfwContent,
} = this.props;
const { orderBy, tags, channelIds, trendingForAll, time } = nextProps;
const { orderBy, tags, channelIds, trendingForAll, time, claimSearchUris } = nextProps;
if (
!_.isEqual(orderBy, prevOrderBy) ||
!_.isEqual(tags, prevTags) ||
@ -119,6 +121,18 @@ class ClaimList extends React.PureComponent {
}
});
}
if (
(this.state.subscriptionsView || this.state.trendingForAllView) &&
this.state.currentPage > 1 &&
prevClaimSearchUris &&
prevClaimSearchUris.length > 0 &&
_.isEqual(prevClaimSearchUris, claimSearchUris)
) {
this.setState({ lastPageReached: true });
} else {
this.setState({ lastPageReached: false });
}
}
getReleaseTimeOption = time => {
@ -130,6 +144,10 @@ class ClaimList extends React.PureComponent {
};
handleVerticalEndReached = () => {
if (this.state.lastPageReached) {
return;
}
// fetch more content
const {
channelIds,
@ -240,6 +258,17 @@ class ClaimList extends React.PureComponent {
if (Constants.ORIENTATION_VERTICAL === orientation) {
const data = subscriptionsView || trendingForAllView ? claimSearchUris : uris;
if (!loading && !claimSearchLoading && (!data || data.length === 0)) {
return (
<View style={style}>
<Text style={claimListStyle.noContentText}>
No content to display at this time. Please check back later.
</Text>
</View>
);
}
return (
<View style={style}>
<FlatList

View file

@ -8,6 +8,7 @@ import {
makeSelectTitleForUri,
makeSelectIsUriResolving,
makeSelectClaimIsNsfw,
makeSelectShortUrlForUri,
} from 'lbry-redux';
import { selectRewardContentClaimIds } from 'lbryinc';
import { selectShowNsfw } from 'redux/selectors/settings';
@ -20,6 +21,7 @@ const select = (state, props) => ({
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
obscureNsfw: !selectShowNsfw(state),
shortUrl: makeSelectShortUrlForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
nsfw: makeSelectClaimIsNsfw(props.uri)(state),

View file

@ -30,12 +30,12 @@ class FileItem extends React.PureComponent {
}
navigateToFileUri = () => {
const { navigation, uri } = this.props;
const { navigation, uri, shortUrl } = this.props;
const normalizedUri = normalizeURI(uri);
if (NativeModules.Firebase) {
NativeModules.Firebase.track('explore_click', { uri: normalizedUri });
NativeModules.Firebase.track('explore_click', { uri: normalizedUri, short_url: shortUrl });
}
navigateToUri(navigation, normalizedUri);
navigateToUri(navigation, shortUrl || uri);
};
render() {
@ -57,6 +57,11 @@ class FileItem extends React.PureComponent {
titleBeforeThumbnail,
} = this.props;
if (claim && claim.value_type === 'channel') {
// don't display channels in the lists on the Explore page
return null;
}
const uri = normalizeURI(this.props.uri);
const obscure = obscureNsfw && nsfw;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
@ -64,6 +69,7 @@ class FileItem extends React.PureComponent {
const channelName = signingChannel ? signingChannel.name : null;
const channelClaimId = signingChannel ? signingChannel.claim_id : null;
const fullChannelUri = channelClaimId ? `${channelName}#${channelClaimId}` : channelName;
const shortChannelUri = signingChannel ? signingChannel.short_url : null;
const height = claim ? claim.height : null;
return (
@ -104,7 +110,7 @@ class FileItem extends React.PureComponent {
style={discoverStyle.channelName}
text={channelName}
onPress={() => {
navigateToUri(navigation, normalizeURI(fullChannelUri));
navigateToUri(navigation, normalizeURI(shortChannelUri || fullChannelUri));
}}
/>
)}

View file

@ -2,24 +2,25 @@ import React from 'react';
import { ActivityIndicator, Image, Text, View } from 'react-native';
import Colors from 'styles/colors';
import FastImage from 'react-native-fast-image';
import autothumbStyle from 'styles/autothumb';
import fileItemMediaStyle from 'styles/fileItemMedia';
class FileItemMedia extends React.PureComponent {
static AUTO_THUMB_STYLES = [
fileItemMediaStyle.autothumbPurple,
fileItemMediaStyle.autothumbRed,
fileItemMediaStyle.autothumbPink,
fileItemMediaStyle.autothumbIndigo,
fileItemMediaStyle.autothumbBlue,
fileItemMediaStyle.autothumbLightBlue,
fileItemMediaStyle.autothumbCyan,
fileItemMediaStyle.autothumbTeal,
fileItemMediaStyle.autothumbGreen,
fileItemMediaStyle.autothumbYellow,
fileItemMediaStyle.autothumbOrange,
autothumbStyle.autothumbPurple,
autothumbStyle.autothumbRed,
autothumbStyle.autothumbPink,
autothumbStyle.autothumbIndigo,
autothumbStyle.autothumbBlue,
autothumbStyle.autothumbLightBlue,
autothumbStyle.autothumbCyan,
autothumbStyle.autothumbTeal,
autothumbStyle.autothumbGreen,
autothumbStyle.autothumbYellow,
autothumbStyle.autothumbOrange,
];
state: {
state = {
imageLoadFailed: false,
};
@ -48,7 +49,7 @@ class FileItemMedia extends React.PureComponent {
return false;
}
if (thumbnail.substring(0, 7) != 'http://' && thumbnail.substring(0, 8) != 'https://') {
if (thumbnail.substring(0, 7) !== 'http://' && thumbnail.substring(0, 8) !== 'https://') {
return false;
}
@ -67,12 +68,7 @@ class FileItemMedia extends React.PureComponent {
if (blurRadius > 0) {
// No blur radius support in FastImage yet
return (
<Image
source={{ uri: thumbnail }}
blurRadius={blurRadius}
resizeMode={resizeMode ? resizeMode : 'cover'}
style={style}
/>
<Image source={{ uri: thumbnail }} blurRadius={blurRadius} resizeMode={resizeMode || 'cover'} style={style} />
);
}
@ -87,7 +83,7 @@ class FileItemMedia extends React.PureComponent {
}
return (
<View style={[style ? style : fileItemMediaStyle.autothumb, atStyle]}>
<View style={[style || fileItemMediaStyle.autothumb, atStyle]}>
{isResolvingUri && (
<View style={fileItemMediaStyle.resolving}>
<ActivityIndicator color={Colors.White} size={'large'} />

View file

@ -6,6 +6,7 @@ import {
makeSelectFileInfoForUri,
makeSelectIsUriResolving,
makeSelectClaimIsNsfw,
makeSelectShortUrlForUri,
makeSelectTitleForUri,
makeSelectThumbnailForUri,
} from 'lbry-redux';
@ -23,6 +24,7 @@ const select = (state, props) => ({
nsfw: makeSelectClaimIsNsfw(props.uri)(state),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
obscureNsfw: !selectShowNsfw(state),
shortUrl: makeSelectShortUrlForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
});

View file

@ -42,8 +42,8 @@ class FileListItem extends React.PureComponent {
}
defaultOnPress = () => {
const { navigation, uri } = this.props;
navigateToUri(navigation, uri);
const { autoplay, navigation, uri, shortUrl } = this.props;
navigateToUri(navigation, shortUrl || uri, { autoplay });
};
render() {
@ -70,7 +70,7 @@ class FileListItem extends React.PureComponent {
const obscure = obscureNsfw && nsfw;
const isResolving = !fileInfo && isResolvingUri;
let name, channel, height, channelClaimId, fullChannelUri, shouldHide, signingChannel;
let name, channel, height, channelClaimId, fullChannelUri, shortChannelUri, shouldHide, signingChannel;
if (claim) {
name = claim.name;
signingChannel = claim.signing_channel;
@ -78,14 +78,18 @@ class FileListItem extends React.PureComponent {
height = claim.height;
channelClaimId = signingChannel ? signingChannel.claim_id : null;
fullChannelUri = channelClaimId ? `${channel}#${channelClaimId}` : channel;
shortChannelUri = signingChannel ? signingChannel.short_url : null;
if (blackListedOutpoints || filteredOutpoints) {
const outpointsToHide = blackListedOutpoints.concat(filteredOutpoints);
shouldHide = outpointsToHide.some(outpoint => outpoint.txid === claim.txid && outpoint.nout === claim.nout);
}
// TODO: hide channels on tag pages?
// shouldHide = 'channel' === claim.value_type;
}
if (shouldHide || (featuredResult && !isResolvingUri && !claim && !title && !name)) {
if (shouldHide || (!isResolvingUri && !claim) || (featuredResult && !isResolvingUri && !claim && !title && !name)) {
return null;
}
@ -130,7 +134,7 @@ class FileListItem extends React.PureComponent {
style={fileListStyle.publisher}
text={channel}
onPress={() => {
navigateToUri(navigation, normalizeURI(fullChannelUri));
navigateToUri(navigation, normalizeURI(shortChannelUri || fullChannelUri));
}}
/>
)}

View file

@ -10,6 +10,8 @@ import TagSearch from 'component/tagSearch';
import modalTagSelectorStyle from 'styles/modalTagSelector';
import { __ } from 'utils/helper';
const minimumTags = 2;
export default class ModalTagSelector extends React.PureComponent {
handleAddTag = tag => {
if (!tag) {
@ -34,6 +36,12 @@ export default class ModalTagSelector extends React.PureComponent {
return;
}
const { followedTags, doToast } = this.props;
if (followedTags.length <= minimumTags) {
doToast({ message: __(`You can follow a minimum of ${minimumTags} tags.`) });
return;
}
this.props.doToggleTagFollow(tag);
if (window.persistor) {
window.persistor.flush();

View file

@ -57,7 +57,7 @@ export default class RelatedContent extends React.PureComponent {
key={recommendedUri}
uri={recommendedUri}
navigation={navigation}
onPress={() => navigateToUri(navigation, recommendedUri, { autoplay: true })}
autoplay
/>
))}
{isSearching && (

View file

@ -20,10 +20,13 @@ class SuggestedSubscriptionItem extends React.PureComponent {
render() {
const { claim, isResolvingUri, navigation, thumbnail, title, uri } = this.props;
let tags;
if (claim && claim.value) {
let shortUrl, tags;
if (claim) {
shortUrl = claim.short_url;
if (claim.value) {
tags = claim.value.tags;
}
}
if (isResolvingUri) {
return (
@ -54,7 +57,7 @@ class SuggestedSubscriptionItem extends React.PureComponent {
style={subscriptionsStyle.suggestedItemName}
numberOfLines={1}
text={claim.name}
onPress={() => navigateToUri(navigation, normalizeURI(uri))}
onPress={() => navigateToUri(navigation, normalizeURI(shortUrl || uri))}
/>
)}
{tags && (

View file

@ -79,7 +79,6 @@ const Constants = {
FULL_ROUTE_NAME_DISCOVER: 'DiscoverStack',
FULL_ROUTE_NAME_TRENDING: 'TrendingStack',
FULL_ROUTE_NAME_MY_SUBSCRIPTIONS: 'MySubscriptionsStack',
FULL_ROUTE_NAME_WALLET: 'WalletStack',
ROUTE_FILE: 'File',

View file

@ -2,6 +2,7 @@
import React from 'react';
import { ActivityIndicator, Dimensions, Image, 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 Colors from 'styles/colors';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -164,8 +165,9 @@ class ChannelPage extends React.PureComponent {
const { fetching, claimsInChannel, claim, navigation, totalPages, uri, drawerStack, popDrawerStack } = this.props;
const { name, permanent_url: permanentUrl } = claim;
let thumbnailUrl, coverUrl, title;
if (claim && claim.value) {
let thumbnailUrl, coverUrl, title, fullUri;
if (claim) {
if (claim.value) {
title = claim.value.title;
if (claim.value.cover) {
coverUrl = claim.value.cover.url;
@ -175,6 +177,9 @@ class ChannelPage extends React.PureComponent {
}
}
fullUri = normalizeURI(`${claim.name}#${claim.claim_id}`);
}
return (
<View style={channelPageStyle.container}>
<UriBar value={uri} navigation={navigation} />
@ -208,10 +213,10 @@ class ChannelPage extends React.PureComponent {
</View>
<View style={channelPageStyle.subscribeButtonContainer}>
<SubscribeButton style={channelPageStyle.subscribeButton} uri={uri} name={name} />
<SubscribeButton style={channelPageStyle.subscribeButton} uri={fullUri} name={name} />
<SubscribeNotificationButton
style={[channelPageStyle.subscribeButton, channelPageStyle.bellButton]}
uri={uri}
uri={fullUri}
name={name}
/>
</View>

View file

@ -83,12 +83,7 @@ class DownloadsPage extends React.PureComponent {
style={downloadsStyle.scrollContainer}
contentContainerStyle={downloadsStyle.scrollPadding}
renderItem={({ item }) => (
<FileListItem
style={fileListStyle.item}
uri={item}
navigation={navigation}
onPress={() => navigateToUri(navigation, item, { autoplay: true })}
/>
<FileListItem style={fileListStyle.item} uri={item} navigation={navigation} autoplay />
)}
data={downloadedUris}
keyExtractor={(item, index) => item}

View file

@ -638,7 +638,10 @@ class FilePage extends React.PureComponent {
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
const canSendTip = this.state.tipAmount > 0;
const fullChannelUri =
channelClaimId && channelClaimId.trim().length > 0 ? `${channelName}#${channelClaimId}` : channelName;
channelClaimId && channelClaimId.trim().length > 0
? normalizeURI(`${channelName}#${channelClaimId}`)
: normalizeURI(channelName);
const shortChannelUri = signingChannel ? signingChannel.short_url : null;
const playerStyle = [
filePageStyle.player,
@ -855,7 +858,7 @@ class FilePage extends React.PureComponent {
numberOfLines={1}
ellipsizeMode={'tail'}
onPress={() => {
navigateToUri(navigation, normalizeURI(fullChannelUri));
navigateToUri(navigation, normalizeURI(shortChannelUri || fullChannelUri));
}}
/>
)}

View file

@ -113,13 +113,7 @@ class SearchPage extends React.PureComponent {
)}
{uris && uris.length
? uris.map(uri => (
<FileListItem
key={uri}
uri={uri}
style={searchStyle.resultItem}
navigation={navigation}
onPress={() => navigateToUri(navigation, uri)}
/>
<FileListItem key={uri} uri={uri} style={searchStyle.resultItem} navigation={navigation} />
))
: null}
{(!uris || uris.length === 0) && (

View file

@ -74,7 +74,7 @@ class SubscriptionsPage extends React.PureComponent {
componentWillReceiveProps(nextProps) {
const { currentRoute } = nextProps;
const { currentRoute: prevRoute } = this.props;
if (Constants.FULL_ROUTE_NAME_MY_SUBSCRIPTIONS === currentRoute && currentRoute !== prevRoute) {
if (Constants.DRAWER_ROUTE_SUBSCRIPTIONS === currentRoute && currentRoute !== prevRoute) {
this.onComponentFocused();
}
}

39
src/styles/autothumb.js Normal file
View file

@ -0,0 +1,39 @@
import { StyleSheet } from 'react-native';
const autothumbStyle = StyleSheet.create({
autothumbPurple: {
backgroundColor: '#9c27b0',
},
autothumbRed: {
backgroundColor: '#e53935',
},
autothumbPink: {
backgroundColor: '#e91e63',
},
autothumbIndigo: {
backgroundColor: '#3f51b5',
},
autothumbBlue: {
backgroundColor: '#2196f3',
},
autothumbLightBlue: {
backgroundColor: '#039be5',
},
autothumbCyan: {
backgroundColor: '#00acc1',
},
autothumbTeal: {
backgroundColor: '#009688',
},
autothumbGreen: {
backgroundColor: '#43a047',
},
autothumbYellow: {
backgroundColor: '#ffeb3b',
},
autothumbOrange: {
backgroundColor: '#ffa726',
},
});
export default autothumbStyle;

View file

@ -18,6 +18,8 @@ const channelIconStyle = StyleSheet.create({
height: 80,
borderRadius: 160,
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
},
borderedThumbnailContainer: {
borderWidth: 1,
@ -43,6 +45,11 @@ const channelIconStyle = StyleSheet.create({
marginTop: 4,
textAlign: 'center',
},
autothumbCharacter: {
fontFamily: 'Inter-UI-Regular',
fontSize: 48,
color: Colors.White,
},
});
export default channelIconStyle;

View file

@ -11,6 +11,13 @@ const claimListStyle = StyleSheet.create({
verticalScrollContainer: {
flex: 1,
},
noContentText: {
color: Colors.DescriptionGrey,
fontFamily: 'Inter-UI-Regular',
fontSize: 14,
margin: 16,
textAlign: 'center',
},
verticalScrollPadding: {
paddingBottom: 16,
},

View file

@ -80,7 +80,7 @@ const discoverStyle = StyleSheet.create({
marginTop: 12,
marginLeft: 16,
marginRight: 16,
marginBottom: 64,
marginBottom: 56,
},
footerTitle: {
fontFamily: 'Inter-UI-Regular',

View file

@ -15,39 +15,6 @@ const fileItemMediaStyle = StyleSheet.create({
color: Colors.White,
fontSize: 40,
},
autothumbPurple: {
backgroundColor: '#9c27b0',
},
autothumbRed: {
backgroundColor: '#e53935',
},
autothumbPink: {
backgroundColor: '#e91e63',
},
autothumbIndigo: {
backgroundColor: '#3f51b5',
},
autothumbBlue: {
backgroundColor: '#2196f3',
},
autothumbLightBlue: {
backgroundColor: '#039be5',
},
autothumbCyan: {
backgroundColor: '#00acc1',
},
autothumbTeal: {
backgroundColor: '#009688',
},
autothumbGreen: {
backgroundColor: '#43a047',
},
autothumbYellow: {
backgroundColor: '#ffeb3b',
},
autothumbOrange: {
backgroundColor: '#ffa726',
},
resolving: {
alignItems: 'center',
flex: 1,