diff --git a/package.json b/package.json index 1cecaf2..96db36d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/component/claimList/index.js b/src/component/claimList/index.js index 9ac7928..8c75b89 100644 --- a/src/component/claimList/index.js +++ b/src/component/claimList/index.js @@ -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( diff --git a/src/component/claimList/view.js b/src/component/claimList/view.js index ae96f4b..f6e8884 100644 --- a/src/component/claimList/view.js +++ b/src/component/claimList/view.js @@ -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(); }); } + } - 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> diff --git a/src/component/suggestedSubscriptions/index.js b/src/component/suggestedSubscriptions/index.js index 0c6524c..9228cea 100644 --- a/src/component/suggestedSubscriptions/index.js +++ b/src/component/suggestedSubscriptions/index.js @@ -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( diff --git a/src/component/suggestedSubscriptions/view.js b/src/component/suggestedSubscriptions/view.js index 99c1301..1d4fbe6 100644 --- a/src/component/suggestedSubscriptions/view.js +++ b/src/component/suggestedSubscriptions/view.js @@ -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 [ { diff --git a/src/page/discover/index.js b/src/page/discover/index.js index e1b1faa..c5f8595 100644 --- a/src/page/discover/index.js +++ b/src/page/discover/index.js @@ -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 => ({ diff --git a/src/page/file/view.js b/src/page/file/view.js index 5e8328e..8459e3a 100644 --- a/src/page/file/view.js +++ b/src/page/file/view.js @@ -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} /> diff --git a/src/page/publish/index.js b/src/page/publish/index.js index 7fb7886..564ef70 100644 --- a/src/page/publish/index.js +++ b/src/page/publish/index.js @@ -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)), diff --git a/src/page/publish/view.js b/src/page/publish/view.js index 53851b7..4de2f7f 100644 --- a/src/page/publish/view.js +++ b/src/page/publish/view.js @@ -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, }; diff --git a/src/page/tag/view.js b/src/page/tag/view.js index 430b651..2eb0cfb 100644 --- a/src/page/tag/view.js +++ b/src/page/tag/view.js @@ -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