Manage Subs: change from claim_search to batch-resolve
The idea of using claim_search to indirectly batch-resolve didn't work out well. There's something wrong with the claim_search pagination where some results are duplicated in multiple pages, while some are missing. It's also returning less results that than the given number of claim ids, so sometimes the infinite scroll breaks.
This commit is contained in:
parent
0090e9b1ae
commit
3edb00b99d
2 changed files with 56 additions and 27 deletions
|
@ -1,4 +1,5 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { doResolveUris } from 'redux/actions/claims';
|
||||||
import { selectSubscriptionUris } from 'redux/selectors/subscriptions';
|
import { selectSubscriptionUris } from 'redux/selectors/subscriptions';
|
||||||
import ChannelsFollowingManage from './view';
|
import ChannelsFollowingManage from './view';
|
||||||
|
|
||||||
|
@ -6,4 +7,8 @@ const select = (state) => ({
|
||||||
subscribedChannelUris: selectSubscriptionUris(state),
|
subscribedChannelUris: selectSubscriptionUris(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select)(ChannelsFollowingManage);
|
const perform = {
|
||||||
|
doResolveUris,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(select, perform)(ChannelsFollowingManage);
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ClaimList from 'component/claimList';
|
import ClaimList from 'component/claimList';
|
||||||
import ClaimListDiscover from 'component/claimListDiscover';
|
|
||||||
import DebouncedInput from 'component/common/debounced-input';
|
import DebouncedInput from 'component/common/debounced-input';
|
||||||
import Empty from 'component/common/empty';
|
import Empty from 'component/common/empty';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import Spinner from 'component/spinner';
|
import Spinner from 'component/spinner';
|
||||||
import * as CS from 'constants/claim_search';
|
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import { parseURI } from 'util/lbryURI';
|
|
||||||
|
|
||||||
function getFilteredUris(uris, filterQuery) {
|
function getFilteredUris(uris, filterQuery) {
|
||||||
if (filterQuery) {
|
if (filterQuery) {
|
||||||
|
@ -18,60 +15,87 @@ function getFilteredUris(uris, filterQuery) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseIdFromUri(uri) {
|
|
||||||
try {
|
|
||||||
const { channelClaimId } = parseURI(uri);
|
|
||||||
return channelClaimId;
|
|
||||||
} catch {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
// ChannelsFollowingManage
|
// ChannelsFollowingManage
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
|
|
||||||
|
const FOLLOW_PAGE_SIZE = 30;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
subscribedChannelUris: Array<string>,
|
subscribedChannelUris: Array<string>,
|
||||||
|
doResolveUris: (uris: Array<string>, returnCachedClaims: boolean, resolveReposts: boolean) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ChannelsFollowingManage(props: Props) {
|
export default function ChannelsFollowingManage(props: Props) {
|
||||||
const { subscribedChannelUris } = props;
|
const { subscribedChannelUris, doResolveUris } = props;
|
||||||
|
|
||||||
|
// The locked-on-mount full set of subscribed uris.
|
||||||
const [uris, setUris] = React.useState([]);
|
const [uris, setUris] = React.useState([]);
|
||||||
|
|
||||||
|
// Filtered query and their uris.
|
||||||
const [filterQuery, setFilterQuery] = React.useState('');
|
const [filterQuery, setFilterQuery] = React.useState('');
|
||||||
const filteredUris = getFilteredUris(uris, filterQuery);
|
const [filteredUris, setFilteredUris] = React.useState(null);
|
||||||
const [channelIds, setChannelIds] = React.useState(null);
|
|
||||||
|
// Infinite-scroll handling. 'page' is 0-indexed.
|
||||||
|
const [page, setPage] = React.useState(-1);
|
||||||
|
const lastPage = Math.max(0, Math.ceil(uris.length / FOLLOW_PAGE_SIZE) - 1);
|
||||||
|
|
||||||
|
async function resolveUris(uris) {
|
||||||
|
return doResolveUris(uris, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveNextPage(uris, currPage, pageSize = FOLLOW_PAGE_SIZE) {
|
||||||
|
const nextPage = currPage + 1;
|
||||||
|
const nextUriBatch = uris.slice(nextPage * pageSize, (nextPage + 1) * pageSize);
|
||||||
|
return resolveUris(nextUriBatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bumpPage() {
|
||||||
|
if (page < lastPage) {
|
||||||
|
resolveNextPage(uris, page).finally(() => setPage(page + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setUris(subscribedChannelUris);
|
setUris(subscribedChannelUris);
|
||||||
setChannelIds(subscribedChannelUris.map((uri) => parseIdFromUri(uri)));
|
resolveNextPage(subscribedChannelUris, -1).finally(() => setPage(0));
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps, (lock list on mount so it doesn't shift when unfollow)
|
// eslint-disable-next-line react-hooks/exhaustive-deps, (lock list on mount so it doesn't shift when unfollow)
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const filteredUris = getFilteredUris(uris, filterQuery);
|
||||||
|
if (filteredUris) {
|
||||||
|
resolveUris(filteredUris).finally(() => setFilteredUris(filteredUris));
|
||||||
|
} else {
|
||||||
|
setFilteredUris(filteredUris);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps, (only need to respond to 'filterQuery')
|
||||||
|
}, [filterQuery]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page className="followManage-wrapper" noFooter>
|
<Page className="followManage-wrapper" noFooter>
|
||||||
<div className="card__title-section">
|
<div className="card__title-section">
|
||||||
<div className="card__title"> {__('Followed Channels')}</div>
|
<div className="card__title"> {__('Followed Channels')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{channelIds === null ? (
|
{page < 0 ? (
|
||||||
<div className="main--empty">
|
<div className="main--empty">
|
||||||
<Spinner />
|
<Spinner delayed />
|
||||||
</div>
|
</div>
|
||||||
) : uris && uris.length === 0 ? (
|
) : uris && uris.length === 0 ? (
|
||||||
<Empty padded text="No followed channels." />
|
<Empty padded text="No followed channels." />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<DebouncedInput icon={ICONS.SEARCH} placeholder={__('Filter')} onChange={setFilterQuery} inline />
|
<DebouncedInput icon={ICONS.SEARCH} placeholder={__('Filter')} onChange={setFilterQuery} inline />
|
||||||
|
|
||||||
{filteredUris && <ClaimList uris={filteredUris} />}
|
{filteredUris && <ClaimList uris={filteredUris} />}
|
||||||
{!filteredUris && channelIds.length && (
|
|
||||||
<ClaimListDiscover
|
{!filteredUris && uris.length > 0 && (
|
||||||
orderBy={CS.ORDER_BY_NAME_ASC}
|
<ClaimList
|
||||||
claimType={CS.CLAIM_CHANNEL}
|
uris={uris.slice(0, (page + 1) * FOLLOW_PAGE_SIZE)}
|
||||||
claimIds={channelIds}
|
onScrollBottom={bumpPage}
|
||||||
showHeader={false}
|
page={page + 1}
|
||||||
hideFilters
|
pageSize={FOLLOW_PAGE_SIZE}
|
||||||
hideAdvancedFilter
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
Loading…
Reference in a new issue