openContextMenu(e)}>
-
-
-
-
-
+
+
+
diff --git a/src/ui/component/channelEdit/view.jsx b/src/ui/component/channelEdit/view.jsx
index 561c05a77..77c963402 100644
--- a/src/ui/component/channelEdit/view.jsx
+++ b/src/ui/component/channelEdit/view.jsx
@@ -118,14 +118,14 @@ function ChannelForm(props: Props) {
onUpdate={v => handleThumbnailChange(v)}
currentValue={params.thumbnail}
assetName={'Thumbnail'}
- recommended={'(400x400)'}
+ recommended={'(300 x 300)'}
/>
handleCoverChange(v)}
currentValue={params.cover}
assetName={'Cover'}
- recommended={'(1000x300)'}
+ recommended={'(1000 x 160)'}
/>
0 && (currentSort === SORT_NEW ? uris : uris.slice().reverse())) || [];
function handleSortChange() {
setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW);
}
- const urisLength = uris && uris.length;
useEffect(() => {
function handleScroll(e) {
if (pageSize && onScrollBottom) {
@@ -71,7 +69,7 @@ export default function ClaimList(props: Props) {
window.removeEventListener('scroll', handleScroll);
};
}
- }, [loading, onScrollBottom, urisLength]);
+ }, [loading, onScrollBottom, urisLength, pageSize]);
return (
)}
- {hasUris && (
+ {urisLength > 0 && (
{sortedUris.map((uri, index) => (
-
+
{index === 4 && injectedItem && - {injectedItem}
}
))}
)}
- {!hasUris && !loading &&
{empty || __('No results')}
}
+ {urisLength === 0 && !loading &&
{empty || __('No results')}
}
);
}
diff --git a/src/ui/component/claimListDiscover/index.js b/src/ui/component/claimListDiscover/index.js
index c8c1b12ca..1007c230b 100644
--- a/src/ui/component/claimListDiscover/index.js
+++ b/src/ui/component/claimListDiscover/index.js
@@ -1,12 +1,12 @@
import * as SETTINGS from 'constants/settings';
import { connect } from 'react-redux';
-import { doClaimSearch, selectLastClaimSearchUris, selectFetchingClaimSearch, doToggleTagFollow } from 'lbry-redux';
+import { doClaimSearch, selectClaimSearchByQuery, selectFetchingClaimSearch, doToggleTagFollow } from 'lbry-redux';
import { selectSubscriptions } from 'redux/selectors/subscriptions';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import ClaimListDiscover from './view';
const select = state => ({
- uris: selectLastClaimSearchUris(state),
+ claimSearchByQuery: selectClaimSearchByQuery(state),
loading: selectFetchingClaimSearch(state),
subscribedChannels: selectSubscriptions(state),
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state),
diff --git a/src/ui/component/claimListDiscover/view.jsx b/src/ui/component/claimListDiscover/view.jsx
index 214a753a5..7308a38ad 100644
--- a/src/ui/component/claimListDiscover/view.jsx
+++ b/src/ui/component/claimListDiscover/view.jsx
@@ -1,13 +1,15 @@
// @flow
import type { Node } from 'react';
-import React, { useEffect, useState } from 'react';
-import moment from 'moment';
-import usePersistedState from 'util/use-persisted-state';
-import { MATURE_TAGS } from 'lbry-redux';
+import React, { useEffect } from 'react';
+import { withRouter } from 'react-router';
+import { buildClaimSearchCacheQuery, MATURE_TAGS } from 'lbry-redux';
import { FormField } from 'component/common/form';
+import moment from 'moment';
import ClaimList from 'component/claimList';
import Tag from 'component/tag';
import ClaimPreview from 'component/claimPreview';
+import { updateQueryParam } from 'util/query-params';
+import { toCapitalCase } from 'util/string';
const PAGE_SIZE = 20;
const TIME_DAY = 'day';
@@ -33,75 +35,118 @@ type Props = {
injectedItem: any,
tags: Array
,
loading: boolean,
- personal: boolean,
+ personalView: boolean,
doToggleTagFollow: string => void,
meta?: Node,
showNsfw: boolean,
+ history: { action: string, push: string => void, replace: string => void },
+ location: { search: string, pathname: string },
+ claimSearchByQuery: {
+ [string]: Array,
+ },
};
function ClaimListDiscover(props: Props) {
- const { doClaimSearch, uris, tags, loading, personal, injectedItem, meta, subscribedChannels, showNsfw } = props;
- const [personalSort, setPersonalSort] = usePersistedState('claim-list-discover:personalSort', SEARCH_SORT_YOU);
- const [typeSort, setTypeSort] = usePersistedState('claim-list-discover:typeSort', TYPE_TRENDING);
- const [timeSort, setTimeSort] = usePersistedState('claim-list-discover:timeSort', TIME_WEEK);
- const [page, setPage] = useState(1);
+ const {
+ doClaimSearch,
+ claimSearchByQuery,
+ tags,
+ loading,
+ personalView,
+ injectedItem,
+ meta,
+ subscribedChannels,
+ showNsfw,
+ history,
+ location,
+ } = props;
+ const didNavigateForward = history.action === 'PUSH';
+ const { search, pathname } = location;
+ const urlParams = new URLSearchParams(search);
+ const personalSort = urlParams.get('sort') || SEARCH_SORT_YOU;
+ const typeSort = urlParams.get('type') || TYPE_TRENDING;
+ const timeSort = urlParams.get('time') || TIME_WEEK;
+ const page = Number(urlParams.get('page')) || 1;
+ const tagsInUrl = urlParams.get('t') || '';
+ const url = `${pathname}${search}`;
+ const options: {
+ page_size: number,
+ page: number,
+ no_totals: boolean,
+ any_tags: Array,
+ channel_ids: Array,
+ not_tags: Array,
+ order_by: Array,
+ release_time?: string,
+ } = {
+ page_size: PAGE_SIZE,
+ page,
+ // no_totals makes it so the sdk doesn't have to calculate total number pages for pagination
+ // it's faster, but we will need to remove it if we start using total_pages
+ no_totals: true,
+ any_tags: (personalView && personalSort === SEARCH_SORT_YOU) || !personalView ? tags : [],
+ channel_ids: personalSort === SEARCH_SORT_CHANNELS ? subscribedChannels.map(sub => sub.uri.split('#')[1]) : [],
+ not_tags: !showNsfw ? MATURE_TAGS : [],
+ order_by:
+ typeSort === TYPE_TRENDING
+ ? ['trending_global', 'trending_mixed']
+ : typeSort === TYPE_NEW
+ ? ['release_time']
+ : ['effective_amount'], // Sort by top
+ };
+
+ if (typeSort === TYPE_TOP && timeSort !== TIME_ALL) {
+ options.release_time = `>${Math.floor(
+ moment()
+ .subtract(1, timeSort)
+ .unix()
+ )}`;
+ }
+
+ const claimSearchCacheQuery = buildClaimSearchCacheQuery(options);
+ const uris = claimSearchByQuery[claimSearchCacheQuery] || [];
+ const shouldPerformSearch = uris.length === 0 || didNavigateForward || (!loading && uris.length < PAGE_SIZE * page);
+ // Don't use the query from buildClaimSearchCacheQuery for the effect since that doesn't include page & release_time
+ const optionsStringForEffect = JSON.stringify(options);
+
+ function getSearch() {
+ let search = `?`;
+ if (!personalView) {
+ search += `t=${tagsInUrl}&`;
+ }
+
+ return search;
+ }
+
+ function handleTypeSort(newTypeSort) {
+ let url = `${getSearch()}type=${newTypeSort}&sort=${personalSort}`;
+ if (newTypeSort === TYPE_TOP) {
+ url += `&time=${timeSort}`;
+ }
+ history.push(url);
+ }
+
+ function handlePersonalSort(newPersonalSort) {
+ history.push(`${getSearch()}type=${typeSort}&sort=${newPersonalSort}`);
+ }
+
+ function handleTimeSort(newTimeSort) {
+ history.push(`${getSearch()}type=${typeSort}&sort=${personalSort}&time=${newTimeSort}`);
+ }
+
+ function handleScrollBottom() {
+ if (!loading) {
+ const uri = updateQueryParam(url, 'page', page + 1);
+ history.replace(uri);
+ }
+ }
- const toCapitalCase = string => string.charAt(0).toUpperCase() + string.slice(1);
- const tagsString = tags.join(',');
- const channelsIdString = subscribedChannels.map(channel => channel.uri.split('#')[1]).join(',');
useEffect(() => {
- const options: {
- page_size: number,
- any_tags?: Array,
- order_by?: Array,
- channel_ids?: Array,
- release_time?: string,
- not_tags?: Array,
- } = { page_size: PAGE_SIZE, page, no_totals: true };
- const newTags = tagsString.split(',');
- const newChannelIds = channelsIdString.split(',');
-
- if ((newTags && !personal) || (newTags && personal && personalSort === SEARCH_SORT_YOU)) {
- options.any_tags = newTags;
- } else if (personalSort === SEARCH_SORT_CHANNELS) {
- options.channel_ids = newChannelIds;
+ if (shouldPerformSearch) {
+ const searchOptions = JSON.parse(optionsStringForEffect);
+ doClaimSearch(searchOptions);
}
-
- if (!showNsfw) {
- options.not_tags = MATURE_TAGS;
- }
-
- if (typeSort === TYPE_TRENDING) {
- options.order_by = ['trending_global', 'trending_mixed'];
- } else if (typeSort === TYPE_NEW) {
- options.order_by = ['release_time'];
- } else if (typeSort === TYPE_TOP) {
- options.order_by = ['effective_amount'];
- if (timeSort !== TIME_ALL) {
- const time = Math.floor(
- moment()
- .subtract(1, timeSort)
- .unix()
- );
- options.release_time = `>${time}`;
- }
- }
-
- options.page_size = PAGE_SIZE;
- doClaimSearch(options);
- }, [personal, personalSort, typeSort, timeSort, doClaimSearch, page, tagsString, channelsIdString, showNsfw]);
-
- function getLabel(type) {
- if (type === SEARCH_SORT_ALL) {
- return __('Everyone');
- }
-
- return type === SEARCH_SORT_YOU ? __('Tags You Follow') : __('Channels You Follow');
- }
-
- function resetList() {
- setPage(1);
- }
+ }, [doClaimSearch, shouldPerformSearch, optionsStringForEffect]);
const header = (
@@ -110,10 +155,7 @@ function ClaimListDiscover(props: Props) {
type="select"
name="trending_sort"
value={typeSort}
- onChange={e => {
- resetList();
- setTypeSort(e.target.value);
- }}
+ onChange={e => handleTypeSort(e.target.value)}
>
{SEARCH_TYPES.map(type => (
@@ -174,14 +216,14 @@ function ClaimListDiscover(props: Props) {
injectedItem={personalSort === SEARCH_SORT_YOU && injectedItem}
header={header}
headerAltControls={meta}
- onScrollBottom={() => setPage(page + 1)}
+ onScrollBottom={handleScrollBottom}
page={page}
pageSize={PAGE_SIZE}
/>
- {loading && page > 1 && new Array(PAGE_SIZE).fill(1).map((x, i) => )}
+ {loading && new Array(PAGE_SIZE).fill(1).map((x, i) => )}
);
}
-export default ClaimListDiscover;
+export default withRouter(ClaimListDiscover);
diff --git a/src/ui/component/claimPreview/view.jsx b/src/ui/component/claimPreview/view.jsx
index 8d178fa28..f50dd39b9 100644
--- a/src/ui/component/claimPreview/view.jsx
+++ b/src/ui/component/claimPreview/view.jsx
@@ -100,13 +100,13 @@ function ClaimPreview(props: Props) {
if (isValid && !isResolvingUri && haventFetched && uri) {
resolveUri(uri);
}
- }, [isResolvingUri, uri, resolveUri, haventFetched]);
+ }, [isValid, isResolvingUri, uri, resolveUri, haventFetched]);
if (shouldHide) {
return null;
}
- if (placeholder || isResolvingUri) {
+ if (placeholder || (isResolvingUri && !claim)) {
return (