ClaimList: Factor out infinite-scroll and batch-resolve handling into an effect.

It's also possible to move the use of `useClaimListInfiniteScroll` into `claimList` itself, but opted to keep them separate for now so that `claimList` doesn't end up a bloated file like `claimListDiscover`.
This commit is contained in:
infinite-persistence 2022-05-16 14:42:17 +08:00 committed by Thomas Zarebczan
parent a4bb276608
commit 51616956d4
2 changed files with 69 additions and 31 deletions

View file

@ -0,0 +1,62 @@
// @flow
/**
* Infinite-scroll and batch-resolving handling for `claimList` instances.
*
* By default, the list is "locked" during initial mount to have a stable
* pagination, but can be disabled through the `disableListLock` parameter.
*/
import React from 'react';
export default function useClaimListInfiniteScroll(
allUris: Array<string>,
doResolveUris: (uris: Array<string>, returnCachedClaims: boolean, resolveReposts: boolean) => void,
pageSize: number = 30,
disableListLock: boolean = false
) {
// Lock the full set of uris on mount, so we have a stable list for pagination.
const [uris, setUris] = React.useState([]);
// Infinite-scroll handling. 'page' is 0-indexed.
const [page, setPage] = React.useState(-1);
const lastPage = Math.max(0, Math.ceil(allUris.length / pageSize) - 1);
const [isLoadingPage, setIsLoadingPage] = React.useState(true);
async function resolveUris(uris) {
return doResolveUris(uris, true, false);
}
async function resolveNextPage(uris, currPage, pageSize) {
const nextPage = currPage + 1;
const nextUriBatch = uris.slice(nextPage * pageSize, (nextPage + 1) * pageSize);
return resolveUris(nextUriBatch);
}
function bumpPage() {
if (page < lastPage) {
setIsLoadingPage(true);
resolveNextPage(uris, page, pageSize).finally(() => {
setIsLoadingPage(false);
setPage(page + 1);
});
}
}
// -- Initial mount: lock list and resolve first batch:
React.useEffect(() => {
setUris(allUris);
resolveNextPage(allUris, -1, pageSize).finally(() => {
setIsLoadingPage(false);
setPage(0);
});
}, []);
// -- Optional: remove the locking behavior for whatever reason:
React.useEffect(() => {
if (disableListLock) {
setUris(allUris);
}
}, [allUris]);
return { uris, page, isLoadingPage, bumpPage };
}

View file

@ -10,6 +10,7 @@ import Page from 'component/page';
import Spinner from 'component/spinner'; import Spinner from 'component/spinner';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import { SIDEBAR_SUBS_DISPLAYED } from 'constants/subscriptions'; import { SIDEBAR_SUBS_DISPLAYED } from 'constants/subscriptions';
import useClaimListInfiniteScroll from 'effects/use-claimList-infinite-scroll';
function getFilteredUris(uris, filterQuery) { function getFilteredUris(uris, filterQuery) {
if (filterQuery) { if (filterQuery) {
@ -34,45 +35,20 @@ type Props = {
export default function ChannelsFollowingManage(props: Props) { export default function ChannelsFollowingManage(props: Props) {
const { subscribedChannelUris, lastActiveSubs, doResolveUris, doFetchLastActiveSubs } = props; const { subscribedChannelUris, lastActiveSubs, doResolveUris, doFetchLastActiveSubs } = props;
const { uris, page, isLoadingPage, bumpPage } = useClaimListInfiniteScroll(
// The locked-on-mount full set of subscribed uris. subscribedChannelUris,
const [uris, setUris] = React.useState([]); doResolveUris,
FOLLOW_PAGE_SIZE
);
// Filtered query and their uris. // Filtered query and their uris.
const [filterQuery, setFilterQuery] = React.useState(''); const [filterQuery, setFilterQuery] = React.useState('');
const [filteredUris, setFilteredUris] = React.useState(null); const [filteredUris, setFilteredUris] = 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);
const [loadingPage, setLoadingPage] = React.useState(false);
async function resolveUris(uris) { async function resolveUris(uris) {
return doResolveUris(uris, true, false); 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) {
setLoadingPage(true);
resolveNextPage(uris, page).finally(() => {
setLoadingPage(false);
setPage(page + 1);
});
}
}
React.useEffect(() => {
setUris(subscribedChannelUris);
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)
}, []);
React.useEffect(() => { React.useEffect(() => {
const filteredUris = getFilteredUris(uris, filterQuery); const filteredUris = getFilteredUris(uris, filterQuery);
if (filteredUris) { if (filteredUris) {
@ -144,7 +120,7 @@ export default function ChannelsFollowingManage(props: Props) {
onScrollBottom={bumpPage} onScrollBottom={bumpPage}
page={page + 1} page={page + 1}
pageSize={FOLLOW_PAGE_SIZE} pageSize={FOLLOW_PAGE_SIZE}
loading={loadingPage} loading={isLoadingPage}
useLoadingSpinner useLoadingSpinner
/> />
</> </>