fix(home): keep stats loading until directories resolve

This commit is contained in:
Tommaso Casaburi
2026-05-17 17:49:45 +07:00
parent 95f3faf31a
commit c3d53202a3
2 changed files with 62 additions and 12 deletions
+34
View File
@@ -49,6 +49,10 @@ vi.mock('../../../hooks/use-communities-stats', () => ({
useCommunitiesStatsStore: (selector: (state: { communityStats: typeof testState.communityStats }) => unknown) => selector({ communityStats: testState.communityStats }),
}));
vi.mock('../../../components/loading-ellipsis', () => ({
default: ({ string }: { string: string }) => createElement('span', { 'data-testid': 'loading-ellipsis' }, string),
}));
vi.mock('../../../stores/use-directory-modal-store', () => ({
default: () => ({
closeDirectoryModal: testState.closeDirectoryModalMock,
@@ -124,6 +128,7 @@ describe('Home', () => {
expect(container.querySelector('[data-testid="boards-list"]')?.textContent).toBe('boards:2');
expect(container.querySelector('[data-testid="popular-threads-box"]')?.textContent).toBe('popular:2:2');
expect(container.querySelectorAll('[data-testid="stats-collector"]')).toHaveLength(2);
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
expect(container.textContent).toContain('total_posts 12');
expect(container.textContent).toContain('current_users 7');
expect(container.textContent).toContain('boards_tracked 2');
@@ -131,6 +136,35 @@ describe('Home', () => {
expect(container.querySelector<HTMLAnchorElement>('a[href="/pass"]')?.textContent).toBe('support_5chan');
});
it('keeps the stats values loading until every directory has loaded stats', () => {
testState.communityStats = {
'music-posting.eth': { allPostCount: 5, weekActiveUserCount: 2 },
};
renderHome();
const loadingValues = Array.from(container.querySelectorAll('[data-testid="loading-ellipsis"]'));
expect(loadingValues).toHaveLength(3);
expect(loadingValues.map((value) => value.textContent)).toEqual(['loading', 'loading', 'loading']);
expect(container.textContent).not.toContain('total_posts 5');
expect(container.textContent).not.toContain('current_users 2');
expect(container.textContent).not.toContain('boards_tracked 1');
});
it('shows zero totals after every directory has loaded zero-count stats', () => {
testState.communityStats = {
'music-posting.eth': { allPostCount: 0, weekActiveUserCount: 0 },
'tech-posting.eth': { allPostCount: 0, weekActiveUserCount: 0 },
};
renderHome();
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
expect(container.textContent).toContain('total_posts 0');
expect(container.textContent).toContain('current_users 0');
expect(container.textContent).toContain('boards_tracked 2');
});
it('navigates to the canonical board path when the search form is submitted', async () => {
renderHome();
+28 -12
View File
@@ -7,6 +7,7 @@ import { CommunityStatsCollector, useCommunitiesStatsStore } from '../../hooks/u
import PopularThreadsBox from './popular-threads-box';
import BoardsList from './boards-list';
import SiteLegalMeta from '../../components/site-legal-meta';
import LoadingEllipsis from '../../components/loading-ellipsis';
import useDirectoryModalStore from '../../stores/use-directory-modal-store';
import DisclaimerModal from '../../components/disclaimer-modal';
import DirectoryModal from '../../components/directory-modal';
@@ -84,27 +85,42 @@ const InfoBox = () => {
);
};
interface StatValueProps {
isLoaded: boolean;
loadingLabel: string;
value: number;
}
const StatValue = ({ isLoaded, loadingLabel, value }: StatValueProps) => (isLoaded ? <>{value}</> : <LoadingEllipsis string={loadingLabel} />);
const Stats = ({ directoryAddresses }: { directoryAddresses: string[] }) => {
const { t } = useTranslation();
const communitiesStats = useCommunitiesStatsStore((state) => state.communityStats);
const { totalPosts, currentUsers, boardsTracked } = useMemo(() => {
const { totalPosts, currentUsers, boardsTracked, allDirectoryStatsLoaded } = useMemo(() => {
let totalPosts = 0;
let currentUsers = 0;
let boardsTracked = 0;
let allDirectoryStatsLoaded = directoryAddresses.length > 0;
directoryAddresses.forEach((address) => {
for (const address of directoryAddresses) {
const stat = communitiesStats[address];
if (stat) {
totalPosts += stat.allPostCount || 0;
currentUsers += stat.weekActiveUserCount || 0;
boardsTracked++;
}
});
return { totalPosts, currentUsers, boardsTracked };
if (!stat || stat.allPostCount === undefined) {
allDirectoryStatsLoaded = false;
continue;
}
totalPosts += stat.allPostCount || 0;
currentUsers += stat.weekActiveUserCount || 0;
boardsTracked++;
}
return { totalPosts, currentUsers, boardsTracked, allDirectoryStatsLoaded };
}, [communitiesStats, directoryAddresses]);
const loadingLabel = t('loading');
return (
<>
{/* Render collectors to fetch stats for each community */}
@@ -117,13 +133,13 @@ const Stats = ({ directoryAddresses }: { directoryAddresses: string[] }) => {
</div>
<div className={`${styles.boxContent} ${styles.stats}`}>
<div className={styles.stat}>
<b>{t('total_posts')}</b> {totalPosts}
<b>{t('total_posts')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={totalPosts} />
</div>
<div className={styles.stat}>
<b>{t('current_users')}</b> {currentUsers}
<b>{t('current_users')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={currentUsers} />
</div>
<div className={styles.stat}>
<b>{t('boards_tracked')}</b> {boardsTracked}
<b>{t('boards_tracked')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={boardsTracked} />
</div>
</div>
</div>