fix(board): prevent transient no threads state (#1151)

* fix(board): prevent transient no threads state

* fix(board): scope raw thread fallback by sort

* fix(board): keep flash table loading during feed sync
This commit is contained in:
Tommaso Casaburi
2026-06-04 13:43:52 +07:00
committed by GitHub
parent 49d6723225
commit 608402b5c8
5 changed files with 210 additions and 75 deletions
@@ -0,0 +1,63 @@
import { describe, expect, it } from 'vitest';
import type { Comment, CommunitiesPages, Community } from '@bitsocial/bitsocial-react-hooks';
import { getRawBoardThreadState } from '../raw-board-thread-state';
const rootThread = (cid: string): Comment =>
({
cid,
postCid: cid,
}) as Comment;
describe('getRawBoardThreadState', () => {
it('keeps the preloaded fallback scoped to the requested sort type', () => {
const community = {
posts: {
pageCids: {
new: 'new-page-1',
},
pages: {
active: {
comments: [],
},
new: {
comments: [rootThread('new-thread')],
nextCid: 'new-page-2',
},
},
},
} as Community;
expect(
getRawBoardThreadState({
accountId: undefined,
communitiesPages: {} as CommunitiesPages,
community,
sortType: 'active',
}),
).toMatchObject({
isFullyLoaded: true,
rootThreadCids: new Set<string>(),
});
});
it('treats an empty preloaded requested-sort page as a fully loaded empty board', () => {
const community = {
posts: {
pages: {
active: {
comments: [],
},
},
},
} as Community;
expect(
getRawBoardThreadState({
accountId: undefined,
communitiesPages: {} as CommunitiesPages,
community,
sortType: 'active',
}).isFullyLoaded,
).toBe(true);
});
});
+68
View File
@@ -0,0 +1,68 @@
import type { Comment, CommunitiesPages, Community } from '@bitsocial/bitsocial-react-hooks';
import { getCommunityFirstPageCid, getCommunityPages } from '@bitsocial/bitsocial-react-hooks/dist/stores/communities-pages';
export type RawBoardThreadState = {
isFullyLoaded: boolean;
rootThreadCids: Set<string>;
};
const EMPTY_RAW_BOARD_THREAD_STATE: RawBoardThreadState = {
isFullyLoaded: false,
rootThreadCids: new Set<string>(),
};
const addRootThreadCids = (cids: Set<string>, comments: readonly Comment[] | undefined) => {
for (const comment of comments || []) {
if (comment?.cid && !comment.parentCid) {
cids.add(comment.cid);
}
}
};
export const getRawBoardThreadState = ({
accountId,
communitiesPages,
community,
sortType,
}: {
accountId: string | undefined;
communitiesPages: CommunitiesPages;
community: Community | undefined;
sortType: 'active' | 'new';
}): RawBoardThreadState => {
if (!community) {
return EMPTY_RAW_BOARD_THREAD_STATE;
}
const rootThreadCids = new Set<string>();
const preloadedSortPage = community.posts?.pages?.[sortType];
addRootThreadCids(rootThreadCids, preloadedSortPage?.comments);
const firstPageCid = getCommunityFirstPageCid(community, sortType, 'posts');
const pages = firstPageCid ? getCommunityPages(community, sortType, communitiesPages, 'posts', accountId) : [];
for (const page of pages) {
addRootThreadCids(rootThreadCids, page?.comments);
}
if (pages.length > 0) {
return {
isFullyLoaded: !pages[pages.length - 1]?.nextCid,
rootThreadCids,
};
}
const hasPageCid = Boolean(community.posts?.pageCids?.[sortType]);
const preloadedPages = (preloadedSortPage ? [preloadedSortPage] : []) as Array<{ comments?: Comment[]; nextCid?: string }>;
const hasCompletePreloadedPage = !hasPageCid && preloadedPages.some((page) => Array.isArray(page?.comments)) && preloadedPages.every((page) => !page?.nextCid);
if (hasCompletePreloadedPage) {
for (const page of preloadedPages) {
addRootThreadCids(rootThreadCids, page?.comments);
}
}
return {
isFullyLoaded: hasCompletePreloadedPage,
rootThreadCids,
};
};