mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(home): keep stats loading until directories resolve
This commit is contained in:
@@ -49,6 +49,10 @@ vi.mock('../../../hooks/use-communities-stats', () => ({
|
|||||||
useCommunitiesStatsStore: (selector: (state: { communityStats: typeof testState.communityStats }) => unknown) => selector({ communityStats: testState.communityStats }),
|
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', () => ({
|
vi.mock('../../../stores/use-directory-modal-store', () => ({
|
||||||
default: () => ({
|
default: () => ({
|
||||||
closeDirectoryModal: testState.closeDirectoryModalMock,
|
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="boards-list"]')?.textContent).toBe('boards:2');
|
||||||
expect(container.querySelector('[data-testid="popular-threads-box"]')?.textContent).toBe('popular:2: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="stats-collector"]')).toHaveLength(2);
|
||||||
|
expect(container.querySelectorAll('[data-testid="loading-ellipsis"]')).toHaveLength(0);
|
||||||
expect(container.textContent).toContain('total_posts 12');
|
expect(container.textContent).toContain('total_posts 12');
|
||||||
expect(container.textContent).toContain('current_users 7');
|
expect(container.textContent).toContain('current_users 7');
|
||||||
expect(container.textContent).toContain('boards_tracked 2');
|
expect(container.textContent).toContain('boards_tracked 2');
|
||||||
@@ -131,6 +136,35 @@ describe('Home', () => {
|
|||||||
expect(container.querySelector<HTMLAnchorElement>('a[href="/pass"]')?.textContent).toBe('support_5chan');
|
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 () => {
|
it('navigates to the canonical board path when the search form is submitted', async () => {
|
||||||
renderHome();
|
renderHome();
|
||||||
|
|
||||||
|
|||||||
+28
-12
@@ -7,6 +7,7 @@ import { CommunityStatsCollector, useCommunitiesStatsStore } from '../../hooks/u
|
|||||||
import PopularThreadsBox from './popular-threads-box';
|
import PopularThreadsBox from './popular-threads-box';
|
||||||
import BoardsList from './boards-list';
|
import BoardsList from './boards-list';
|
||||||
import SiteLegalMeta from '../../components/site-legal-meta';
|
import SiteLegalMeta from '../../components/site-legal-meta';
|
||||||
|
import LoadingEllipsis from '../../components/loading-ellipsis';
|
||||||
import useDirectoryModalStore from '../../stores/use-directory-modal-store';
|
import useDirectoryModalStore from '../../stores/use-directory-modal-store';
|
||||||
import DisclaimerModal from '../../components/disclaimer-modal';
|
import DisclaimerModal from '../../components/disclaimer-modal';
|
||||||
import DirectoryModal from '../../components/directory-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 Stats = ({ directoryAddresses }: { directoryAddresses: string[] }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const communitiesStats = useCommunitiesStatsStore((state) => state.communityStats);
|
const communitiesStats = useCommunitiesStatsStore((state) => state.communityStats);
|
||||||
|
|
||||||
const { totalPosts, currentUsers, boardsTracked } = useMemo(() => {
|
const { totalPosts, currentUsers, boardsTracked, allDirectoryStatsLoaded } = useMemo(() => {
|
||||||
let totalPosts = 0;
|
let totalPosts = 0;
|
||||||
let currentUsers = 0;
|
let currentUsers = 0;
|
||||||
let boardsTracked = 0;
|
let boardsTracked = 0;
|
||||||
|
let allDirectoryStatsLoaded = directoryAddresses.length > 0;
|
||||||
|
|
||||||
directoryAddresses.forEach((address) => {
|
for (const address of directoryAddresses) {
|
||||||
const stat = communitiesStats[address];
|
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]);
|
}, [communitiesStats, directoryAddresses]);
|
||||||
|
|
||||||
|
const loadingLabel = t('loading');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Render collectors to fetch stats for each community */}
|
{/* Render collectors to fetch stats for each community */}
|
||||||
@@ -117,13 +133,13 @@ const Stats = ({ directoryAddresses }: { directoryAddresses: string[] }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className={`${styles.boxContent} ${styles.stats}`}>
|
<div className={`${styles.boxContent} ${styles.stats}`}>
|
||||||
<div className={styles.stat}>
|
<div className={styles.stat}>
|
||||||
<b>{t('total_posts')}</b> {totalPosts}
|
<b>{t('total_posts')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={totalPosts} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.stat}>
|
<div className={styles.stat}>
|
||||||
<b>{t('current_users')}</b> {currentUsers}
|
<b>{t('current_users')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={currentUsers} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.stat}>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user