Merge branch 'codex/fix/directory-loading-center'

This commit is contained in:
Tommaso Casaburi
2026-05-29 17:47:53 +07:00
4 changed files with 48 additions and 22 deletions
+9 -1
View File
@@ -145,8 +145,16 @@
word-break: break-all;
}
.ownerCell {
.ownerCell,
.statusCell {
text-align: center;
vertical-align: middle;
}
.statusLoading {
display: block;
width: fit-content;
margin-inline: auto;
}
.ownerName {
+3 -1
View File
@@ -135,7 +135,9 @@ const DirectoryRow = ({ board, nowSeconds, rank, onVote }: DirectoryRowProps) =>
</Tooltip>
</span>
) : status === 'loading' ? (
<LoadingEllipsis string={t('loading')} />
<span className={styles.statusLoading}>
<LoadingEllipsis string={t('loading')} />
</span>
) : status === 'unknown' ? (
<span className={styles.statusUnavailable}>{DIRECTORY_STATUS_UNAVAILABLE_MARKER}</span>
) : (
+14 -2
View File
@@ -3,6 +3,7 @@ import { createElement } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { MemoryRouter } from 'react-router-dom';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { useFeedStateString } from '../../../hooks/use-state-string';
import Home from '../home';
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
@@ -19,6 +20,7 @@ const testState = vi.hoisted(() => ({
navigateMock: vi.fn(),
communities: {} as Record<string, unknown>,
communityStats: {} as Record<string, { allPostCount?: number; weekActiveUserCount?: number; state?: string }>,
feedStateString: 'Downloading boards',
}));
vi.mock('react-i18next', () => ({
@@ -76,6 +78,10 @@ vi.mock('../../../hooks/use-now-seconds', () => ({
useNowSeconds: () => testState.nowSeconds,
}));
vi.mock('../../../hooks/use-state-string', () => ({
useFeedStateString: vi.fn(() => testState.feedStateString),
}));
vi.mock('../../../components/loading-ellipsis', () => ({
default: ({ string }: { string: string }) => createElement('span', { 'data-testid': 'loading-ellipsis' }, string),
}));
@@ -139,6 +145,7 @@ describe('Home', () => {
'music-posting.eth': { allPostCount: 5, weekActiveUserCount: 2 },
'tech-posting.eth': { allPostCount: 7, weekActiveUserCount: 5 },
};
testState.feedStateString = 'Downloading boards';
container = document.createElement('div');
document.body.appendChild(container);
@@ -153,6 +160,7 @@ describe('Home', () => {
it('renders the home view chrome, child sections, collectors, and aggregated stats', () => {
renderHome();
expect(vi.mocked(useFeedStateString)).not.toHaveBeenCalled();
expect(document.title).toBe('5chan');
expect(container.querySelector('[data-testid="disclaimer-modal"]')?.textContent).toBe('disclaimer-modal');
expect(container.querySelector('[data-testid="directory-modal"]')?.textContent).toBe('directory-modal');
@@ -175,8 +183,12 @@ describe('Home', () => {
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(loadingValues).toHaveLength(1);
expect(loadingValues[0]?.textContent).toBe('Downloading boards');
expect(vi.mocked(useFeedStateString)).toHaveBeenCalledWith(['music-posting.eth', 'tech-posting.eth']);
expect(container.textContent).not.toContain('total_posts');
expect(container.textContent).not.toContain('current_users');
expect(container.textContent).not.toContain('boards_tracked');
expect(container.textContent).not.toContain('total_posts 5');
expect(container.textContent).not.toContain('current_users 2');
expect(container.textContent).not.toContain('boards_tracked 1');
+22 -18
View File
@@ -9,6 +9,7 @@ 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 { useFeedStateString } from '../../hooks/use-state-string';
import useDirectoryModalStore from '../../stores/use-directory-modal-store';
import DisclaimerModal from '../../components/disclaimer-modal';
import DirectoryModal from '../../components/directory-modal';
@@ -99,19 +100,18 @@ const InfoBox = () => {
);
};
interface StatValueProps {
isLoaded: boolean;
loadingLabel: string;
value: number;
}
type HomepageStats = {
allPostCount?: number;
weekActiveUserCount?: number;
state?: string;
};
const StatValue = ({ isLoaded, loadingLabel, value }: StatValueProps) => (isLoaded ? <>{value}</> : <LoadingEllipsis string={loadingLabel} />);
const StatsLoading = ({ communityAddresses }: { communityAddresses: string[] }) => {
const { t } = useTranslation();
const loadingStateString = useFeedStateString(communityAddresses) || t('loading');
return <LoadingEllipsis string={loadingStateString} />;
};
const STATS_DIRECTORY_FALLBACK_DELAY_SECONDS = 30;
@@ -204,8 +204,6 @@ const Stats = ({ directories }: { directories: DirectoryCommunity[] }) => {
};
}, [communitiesStats, defaultDirectoryAddresses, directories, listsByCode]);
const loadingLabel = t('loading');
return (
<>
{/* Render collectors to fetch stats for each community */}
@@ -217,15 +215,21 @@ const Stats = ({ directories }: { directories: DirectoryCommunity[] }) => {
<h2 className='capitalize'>{t('stats')}</h2>
</div>
<div className={`${styles.boxContent} ${styles.stats}`}>
<div className={styles.stat}>
<b>{t('total_posts')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={totalPosts} />
</div>
<div className={styles.stat}>
<b>{t('current_users')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={currentUsers} />
</div>
<div className={styles.stat}>
<b>{t('boards_tracked')}</b> <StatValue isLoaded={allDirectoryStatsLoaded} loadingLabel={loadingLabel} value={boardsTracked} />
</div>
{allDirectoryStatsLoaded ? (
<>
<div className={styles.stat}>
<b>{t('total_posts')}</b> {totalPosts}
</div>
<div className={styles.stat}>
<b>{t('current_users')}</b> {currentUsers}
</div>
<div className={styles.stat}>
<b>{t('boards_tracked')}</b> {boardsTracked}
</div>
</>
) : (
<StatsLoading communityAddresses={collectorAddresses} />
)}
</div>
</div>
</>