Redux master updates #18

Merged
akinwale merged 7 commits from redux-master-updates into master 2019-08-15 05:55:02 +02:00
10 changed files with 147 additions and 205 deletions
package.json
src
component
claimList
suggestedSubscriptions
page

View file

@ -12,7 +12,7 @@
"base-64": "^0.1.0",
"@expo/vector-icons": "^8.1.0",
"gfycat-style-urls": "^1.0.3",
"lbry-redux": "lbryio/lbry-redux#1a8ce5ee1397e101f2a015f1c3a9050c15ca6157",
"lbry-redux": "lbryio/lbry-redux#e10986e8e54d25480ac2b5dd2a31fec8b254471a",
"lbryinc": "lbryio/lbryinc#430c280789a5031c2e49ca5bf8a7d90ccccc4cdb",
"lodash": ">=4.17.11",
"merge": ">=1.2.1",

View file

@ -2,11 +2,10 @@ import { connect } from 'react-redux';
import {
MATURE_TAGS,
doClaimSearch,
doClaimSearchByTags,
makeSelectClaimSearchUrisForTags,
makeSelectFetchingClaimSearchForTags,
selectClaimSearchByQuery,
selectClaimSearchByQueryLastPageReached,
selectFetchingClaimSearchByQuery,
selectFetchingClaimSearch,
selectLastClaimSearchUris,
} from 'lbry-redux';
import { selectShowNsfw } from 'redux/selectors/settings';
import Constants from 'constants'; // eslint-disable-line node/no-deprecated-api
@ -14,33 +13,15 @@ import ClaimList from './view';
const select = (state, props) => {
return {
loading: makeSelectFetchingClaimSearchForTags(props.tags)(state),
uris: makeSelectClaimSearchUrisForTags(props.tags)(state),
// for subscriptions
claimSearchLoading: selectFetchingClaimSearch(state),
claimSearchUris: selectLastClaimSearchUris(state),
showNsfwContent: selectShowNsfw(state),
claimSearchByQuery: selectClaimSearchByQuery(state),
lastPageReached: selectClaimSearchByQueryLastPageReached(state),
loadingByQuery: selectFetchingClaimSearchByQuery(state),
loading: selectFetchingClaimSearch(state),
};
};
const perform = dispatch => ({
claimSearch: options => dispatch(doClaimSearch(Constants.DEFAULT_PAGE_SIZE, options)),
searchByTags: (tags, orderBy = Constants.DEFAULT_ORDER_BY, page = 1, additionalOptions = {}) =>
dispatch(
doClaimSearchByTags(
tags,
Constants.DEFAULT_PAGE_SIZE,
Object.assign(
{},
{
no_totals: true,
order_by: orderBy,
page,
},
additionalOptions
)
)
),
claimSearch: options => dispatch(doClaimSearch(options)),
});
export default connect(

View file

@ -1,7 +1,7 @@
import React from 'react';
import NavigationActions from 'react-navigation';
import { ActivityIndicator, FlatList, Text, TouchableOpacity, View } from 'react-native';
import { MATURE_TAGS, normalizeURI } from 'lbry-redux';
import { MATURE_TAGS, normalizeURI, createNormalizedClaimSearchKey } from 'lbry-redux';
import _ from 'lodash';
import FileItem from 'component/fileItem';
import FileListItem from 'component/fileListItem';
@ -26,58 +26,30 @@ class ClaimList extends React.PureComponent {
};
componentDidMount() {
const {
channelIds,
trendingForAll,
claimSearch,
orderBy = Constants.DEFAULT_ORDER_BY,
searchByTags,
showNsfwContent,
tags,
time,
} = this.props;
if (channelIds || trendingForAll) {
const options = {
order_by: orderBy,
no_totals: true,
page: this.state.currentPage,
};
if (!showNsfwContent) {
options.not_tags = MATURE_TAGS;
}
if (channelIds) {
this.setState({ subscriptionsView: true });
options.channel_ids = channelIds;
} else if (trendingForAll) {
this.setState({ trendingForAllView: true });
}
const { channelIds, trendingForAll } = this.props;
claimSearch(options);
} else if (tags && tags.length > 0) {
const additionalOptions = {};
if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) {
additionalOptions.release_time = this.getReleaseTimeOption(time);
}
if (!showNsfwContent) {
additionalOptions.not_tags = MATURE_TAGS;
}
searchByTags(tags, orderBy, this.state.currentPage, additionalOptions);
if (channelIds) {
this.setState({ subscriptionsView: true });
} else if (trendingForAll) {
this.setState({ trendingForAllView: true });
}
this.doClaimSearch();
}
componentWillReceiveProps(nextProps) {
componentDidUpdate(prevProps) {
const {
claimSearch,
claimSearchByQuery: prevClaimSearchByQuery,
orderBy: prevOrderBy,
searchByTags,
tags: prevTags,
channelIds: prevChannelIds,
trendingForAll: prevTrendingForAll,
time: prevTime,
claimSearchUris: prevClaimSearchUris,
showNsfwContent,
} = this.props;
const { orderBy, tags, channelIds, trendingForAll, time, claimSearchUris } = nextProps;
} = prevProps;
const { claimSearchByQuery, orderBy, tags, channelIds, trendingForAll, time } = this.props;
if (
!_.isEqual(orderBy, prevOrderBy) ||
!_.isEqual(tags, prevTags) ||
@ -90,49 +62,55 @@ class ClaimList extends React.PureComponent {
if (this.scrollView) {
this.scrollView.scrollToOffset({ animated: true, offset: 0 });
}
if (trendingForAll || (prevChannelIds && channelIds)) {
const options = {
order_by: orderBy,
no_totals: true,
page: this.state.currentPage,
};
if (!showNsfwContent) {
options.not_tags = MATURE_TAGS;
}
if (channelIds) {
this.setState({ subscriptionsView: true });
options.channel_ids = channelIds;
}
if (trendingForAll) {
this.setState({ trendingForAllView: true });
}
claimSearch(options);
} else if (tags && tags.length > 0) {
this.setState({ subscriptionsView: false, trendingForAllView: false });
const additionalOptions = {};
if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) {
additionalOptions.release_time = this.getReleaseTimeOption(time);
}
if (!showNsfwContent) {
additionalOptions.not_tags = MATURE_TAGS;
}
searchByTags(tags, orderBy, this.state.currentPage, additionalOptions);
}
this.doClaimSearch();
});
kauffj commented 2019-08-15 00:01:18 +02:00 (Migrated from github.com)
Review

would it make sense to have a single view parameter rather than binary parameters for each view? what happens if trendingForAllView and subscriptionView both end up true? (yes, this probably shouldn't be possible)

would it make sense to have a single view parameter rather than binary parameters for each view? what happens if `trendingForAllView` and `subscriptionView` both end up true? (yes, this probably shouldn't be possible)
akinwale commented 2019-08-15 05:51:56 +02:00 (Migrated from github.com)
Review

It's possible to have tags set for the component in the trending view, so trending for all has to be explicitly set in order to not pass those tags when building the options for the request. channelIds is used to filter for content from subscribed channels, which is a whole different condition. So that's effectively 3 different states:

  • Trending for tags you follow: tags is set and is an array, channelIds is null or empty
  • Trending for all: tags is null or empty, channelIds is null or empty
  • Channels you follow: channelIds is set and is an array, tags is null or empty
It's possible to have `tags` set for the component in the trending view, so trending for all has to be explicitly set in order to not pass those tags when building the options for the request. `channelIds` is used to filter for content from subscribed channels, which is a whole different condition. So that's effectively 3 different states: * Trending for tags you follow: `tags` is set and is an array, `channelIds` is null or empty * Trending for all: `tags` is null or empty, `channelIds` is null or empty * Channels you follow: `channelIds` is set and is an array, `tags` is null or empty
}
}
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 });
buildClaimSearchOptions() {
const {
orderBy = Constants.DEFAULT_ORDER_BY,
channelIds,
showNsfwContent,
tags,
time,
trendingForAll,
} = this.props;
const { currentPage, subscriptionsView, trendingForAllView } = this.state;
const options = {
order_by: orderBy,
no_totals: true,
page: currentPage,
page_size: Constants.DEFAULT_PAGE_SIZE,
};
if (channelIds) {
options.channel_ids = channelIds;
} else if (!trendingForAll && !trendingForAllView && tags && tags.length > 0) {
options.any_tags = tags;
}
if (!showNsfwContent) {
options.not_tags = MATURE_TAGS;
}
if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) {
options.release_time = this.getReleaseTimeOption(time);
}
return options;
}
getReleaseTimeOption = time => {
@ -143,56 +121,27 @@ class ClaimList extends React.PureComponent {
)}`;
};
doClaimSearch() {
const { claimSearch } = this.props;
const options = this.buildClaimSearchOptions();
claimSearch(options);
}
handleVerticalEndReached = () => {
// fetch more content
const {
channelIds,
claimSearch,
claimSearchUris,
orderBy,
searchByTags,
showNsfwContent,
tags,
time,
uris,
} = this.props;
const { subscriptionsView, trendingForAllView } = this.state;
const { claimSearchByQuery, lastPageReached } = this.props;
const options = this.buildClaimSearchOptions();
const claimSearchKey = createNormalizedClaimSearchKey(options);
const uris = claimSearchByQuery[claimSearchKey];
if (
this.state.lastPageReached ||
((claimSearchUris.length > 0 && claimSearchUris.length < Constants.DEFAULT_PAGE_SIZE) ||
claimSearchUris.length >= softLimit) ||
(uris && uris.length >= softLimit)
lastPageReached[claimSearchKey] ||
((uris.length > 0 && uris.length < Constants.DEFAULT_PAGE_SIZE) || uris.length >= softLimit)
) {
return;
}
this.setState({ currentPage: this.state.currentPage + 1 }, () => {
if (subscriptionsView || trendingForAllView) {
const options = {
order_by: orderBy,
no_totals: true,
page: this.state.currentPage,
};
if (!showNsfwContent) {
options.not_tags = MATURE_TAGS;
}
if (subscriptionsView) {
options.channel_ids = channelIds;
}
claimSearch(options);
} else {
const additionalOptions = {};
if (orderBy && orderBy[0] === Constants.ORDER_BY_EFFECTIVE_AMOUNT && Constants.TIME_ALL !== time) {
additionalOptions.release_time = this.getReleaseTimeOption(time);
}
if (!showNsfwContent) {
additionalOptions.not_tags = MATURE_TAGS;
}
searchByTags(tags, orderBy, this.state.currentPage, additionalOptions);
}
});
this.setState({ currentPage: this.state.currentPage + 1 }, () => this.doClaimSearch());
};
appendMorePlaceholder = items => {
@ -253,19 +202,19 @@ class ClaimList extends React.PureComponent {
const {
ListHeaderComponent,
loading,
claimSearchLoading,
claimSearchUris,
morePlaceholder,
navigation,
orientation = Constants.ORIENTATION_VERTICAL,
style,
uris,
claimSearchByQuery,
} = this.props;
const { subscriptionsView, trendingForAllView } = this.state;
if (Constants.ORIENTATION_VERTICAL === orientation) {
const data = subscriptionsView || trendingForAllView ? claimSearchUris : uris;
const options = this.buildClaimSearchOptions();
const claimSearchKey = createNormalizedClaimSearchKey(options);
const uris = claimSearchByQuery[claimSearchKey];
if (Constants.ORIENTATION_VERTICAL === orientation) {
return (
<View style={style}>
<FlatList
@ -273,19 +222,19 @@ class ClaimList extends React.PureComponent {
this.scrollView = ref;
}}
ListHeaderComponent={ListHeaderComponent}
ListEmptyComponent={loading || claimSearchLoading ? null : this.verticalListEmptyComponent}
ListEmptyComponent={loading ? null : this.verticalListEmptyComponent}
style={claimListStyle.verticalScrollContainer}
contentContainerStyle={claimListStyle.verticalScrollPadding}
initialNumToRender={10}
maxToRenderPerBatch={20}
removeClippedSubviews
renderItem={this.renderVerticalItem}
data={data}
data={uris}
keyExtractor={(item, index) => item}
onEndReached={this.handleVerticalEndReached}
onEndReachedThreshold={0.2}
/>
{(((subscriptionsView || trendingForAllView) && claimSearchLoading) || loading) && (
{loading && (
<View style={claimListStyle.verticalLoading}>
<ActivityIndicator size={'small'} color={Colors.LbryGreen} />
</View>

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux';
import { doClaimSearch, selectFetchingClaimSearch, selectLastClaimSearchUris, selectFollowedTags } from 'lbry-redux';
import { doClaimSearch, selectFetchingClaimSearch, selectClaimSearchByQuery, selectFollowedTags } from 'lbry-redux';
import { selectSuggestedChannels, selectIsFetchingSuggested } from 'lbryinc';
import SuggestedSubscriptions from './view';
@ -7,11 +7,11 @@ const select = state => ({
followedTags: selectFollowedTags(state),
suggested: selectSuggestedChannels(state),
loading: selectIsFetchingSuggested(state) || selectFetchingClaimSearch(state),
claimSearchUris: selectLastClaimSearchUris(state),
claimSearchByQuery: selectClaimSearchByQuery(state),
});
const perform = dispatch => ({
claimSearch: options => dispatch(doClaimSearch(10, options)),
claimSearch: options => dispatch(doClaimSearch(options)),
});
export default connect(

View file

@ -1,6 +1,6 @@
import React from 'react';
import { ActivityIndicator, FlatList, SectionList, Text, View } from 'react-native';
import { normalizeURI } from 'lbry-redux';
import { createNormalizedClaimSearchKey, normalizeURI } from 'lbry-redux';
import { __, navigateToUri } from 'utils/helper';
import SubscribeButton from 'component/subscribeButton';
import SuggestedSubscriptionItem from 'component/suggestedSubscriptionItem';
@ -11,19 +11,27 @@ import Link from 'component/link';
import _ from 'lodash';
class SuggestedSubscriptions extends React.PureComponent {
state = {
options: {},
};
componentDidMount() {
const { claimSearch, followedTags } = this.props;
const options = {
any_tags: _.shuffle(followedTags.map(tag => tag.name)).slice(0, 2),
any_tags: followedTags.map(tag => tag.name),
page: 1,
no_totals: true,
claim_type: 'channel',
};
this.setState({ options });
claimSearch(options);
}
buildSections = () => {
const { suggested, claimSearchUris } = this.props;
const { suggested, claimSearchByQuery } = this.props;
const claimSearchKey = createNormalizedClaimSearchKey(this.state.options);
const claimSearchUris = claimSearchByQuery[claimSearchKey];
const suggestedUris = suggested ? suggested.map(suggested => suggested.uri) : [];
return [
{

View file

@ -1,12 +1,5 @@
import { connect } from 'react-redux';
import {
doClaimSearch,
doFileList,
selectBalance,
selectFileInfosDownloaded,
selectLastClaimSearchUris,
selectFollowedTags,
} from 'lbry-redux';
import { doClaimSearch, doFileList, selectBalance, selectFileInfosDownloaded, selectFollowedTags } from 'lbry-redux';
import {
doFetchFeaturedUris,
doFetchRewardedContent,
@ -35,7 +28,6 @@ const select = state => ({
ratingReminderLastShown: makeSelectClientSetting(Constants.SETTING_RATING_REMINDER_LAST_SHOWN)(state),
sortByItem: selectSortByItem(state),
unreadSubscriptions: selectUnreadSubscriptions(state),
uris: selectLastClaimSearchUris(state),
});
const perform = dispatch => ({

View file

@ -957,7 +957,9 @@ class FilePage extends React.PureComponent {
</View>
)}
{costInfo && parseFloat(costInfo.cost) > balance && <FileRewardsDriver navigation={navigation} />}
{costInfo && parseFloat(costInfo.cost) > balance && !fileInfo && (
<FileRewardsDriver navigation={navigation} />
)}
<View onLayout={this.setRelatedContentPosition} />
<RelatedContent navigation={navigation} uri={uri} fullUri={fullUri} />

View file

@ -21,7 +21,7 @@ const perform = dispatch => ({
notify: data => dispatch(doToast(data)),
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
uploadThumbnail: (filePath, fsAdapter) => dispatch(doUploadThumbnail(filePath, null, fsAdapter)),
publish: params => dispatch(doPublish(params)),
publish: (success, fail) => dispatch(doPublish(success, fail)),
resolveUri: uri => dispatch(doResolveUri(uri)),
pushDrawerStack: () => dispatch(doPushDrawerStack(Constants.DRAWER_ROUTE_PUBLISH)),
setPlayerVisible: () => dispatch(doSetPlayerVisible(false)),

View file

@ -42,6 +42,7 @@ import Tag from 'component/tag';
import TagSearch from 'component/tagSearch';
import UriBar from 'component/uriBar';
import publishStyle from 'styles/publish';
import __ from 'utils/helper';
const languages = {
en: 'English',
@ -185,7 +186,7 @@ class PublishPage extends React.PureComponent {
};
handlePublishPressed = () => {
const { notify, publish } = this.props;
const { notify, publish, updatePublishForm } = this.props;
const {
bid,
channelName,
@ -239,7 +240,18 @@ class PublishPage extends React.PureComponent {
},
};
this.setState({ publishStarted: true }, () => publish(publishParams));
updatePublishForm(publishParams);
this.setState({ publishStarted: true }, () => publish(this.handlePublishSuccess, this.handlePublishFailure));
};
handlePublishSuccess = data => {
this.setState({ publishStarted: false, currentPhase: Constants.PHASE_PUBLISH });
};
handlePublishFailure = error => {
const { notify } = this.props;
notify({ message: __('Your content could not be published at this time. Please try again.') });
this.setState({ publishStarted: false });
};
componentDidMount() {
@ -256,30 +268,21 @@ class PublishPage extends React.PureComponent {
if (publishFormValues) {
if (publishFormValues.thumbnail && !this.state.uploadedThumbnailUri) {
this.setState({ uploadedThumbnailUri: publishFormValues.thumbnail });
}
if (this.state.publishStarted) {
if (publishFormValues.publishSuccess) {
this.setState({ publishStarted: false, currentPhase: Constants.PHASE_PUBLISH });
} else if (publishFormValues.publishError) {
// TODO: Display error if any
notify({ message: 'Your content could not be published at this time. Please try again.' });
}
if (!publishFormValues.publishing && this.state.publishStarted) {
this.setState({ publishStarted: false });
}
this.setState({
currentThumbnailUri: publishFormValues.thumbnail,
uploadedThumbnailUri: publishFormValues.thumbnail,
});
}
}
}
setCurrentMedia(media) {
const name = generateCombination(2, ' ', true);
this.setState(
{
currentMedia: media,
title: media.name,
name: this.formatNameForTitle(media.name),
title: name,
name: this.formatNameForTitle(name),
currentPhase: Constants.PHASE_DETAILS,
},
() => this.handleNameChange(this.state.name)
@ -365,7 +368,6 @@ class PublishPage extends React.PureComponent {
const currentMedia = {
id: -1,
filePath: this.getFilePathFromUri(data.uri),
name: generateCombination(2, ' ', true),
type: 'video/mp4', // always MP4
duration: 0,
};

View file

@ -75,29 +75,37 @@ class TagPage extends React.PureComponent {
return (
<View style={discoverStyle.container}>
<UriBar navigation={navigation} belowOverlay={showSortPicker || showTimePicker} />
<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>
{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>
)}
<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>
}
style={discoverStyle.tagPageClaimList}
orderBy={this.state.orderBy}
time={this.state.time}
tags={[tag]}
navigation={navigation}
orientation={Constants.ORIENTATION_VERTICAL}
/>
</View>
}
style={discoverStyle.tagPageClaimList}
orderBy={this.state.orderBy}
time={this.state.time}
tags={[tag]}
navigation={navigation}
orientation={Constants.ORIENTATION_VERTICAL}
/>
)}
{!showSortPicker && !showTimePicker && <FloatingWalletBalance navigation={navigation} />}
{showSortPicker && (
<ModalPicker