fix(rules): show friendly loading state string for board rules

Replace hardcoded loading text in BoardRulesDisplay with useStateString and LoadingEllipsis so rules loading matches other views like board downloads from peers.
This commit is contained in:
Tommaso Casaburi
2026-05-27 17:39:06 +07:00
parent 029939089f
commit 30dde0944d
2 changed files with 30 additions and 22 deletions
+25
View File
@@ -35,6 +35,9 @@ vi.mock('react-router-dom', async () => {
});
vi.mock('@bitsocial/bitsocial-react-hooks', () => ({
useClientsStates: () => ({
states: {},
}),
useCommunity: (options?: { communityAddress?: string; community?: { name?: string; publicKey?: string } }) => {
const communityAddress = options?.communityAddress ?? options?.community?.name ?? options?.community?.publicKey;
return communityAddress ? testState.communities[communityAddress] : undefined;
@@ -58,6 +61,14 @@ vi.mock('../../../components/markdown', () => ({
default: ({ content }: { content: string }) => createElement('div', { 'data-testid': 'markdown' }, content),
}));
vi.mock('lodash/debounce', () => ({
default: <T extends (...args: any[]) => unknown>(fn: T) => {
const wrapped = ((...args: Parameters<T>) => fn(...args)) as T & { cancel: () => void };
wrapped.cancel = () => undefined;
return wrapped;
},
}));
let container: HTMLDivElement;
let root: Root;
@@ -124,4 +135,18 @@ describe('Rules', () => {
expect(Array.from(select?.options ?? []).map((option) => option.value)).toEqual(['', 'anime-posting.eth', 'random-posting.eth']);
expect(container.textContent).toContain('Rules for: /a/ - Anime & Manga');
});
it('shows a friendly loading state string while board rules are downloading', async () => {
testState.boardIdentifier = 'a';
testState.communities = {
'anime-posting.eth': {
state: 'fetching-ipns',
},
};
await renderRules();
expect(container.textContent).toContain('Downloading board from peers');
expect(container.textContent).not.toContain('loading...');
});
});
+5 -22
View File
@@ -6,6 +6,8 @@ import { useDirectories, DirectoryCommunity, findDirectoryByAddress } from '../.
import { useCommunityIdentifier } from '../../hooks/use-community-identifiers';
import { getCommunityAddress, getBoardPath } from '../../lib/utils/route-utils';
import Markdown from '../../components/markdown';
import LoadingEllipsis from '../../components/loading-ellipsis';
import useStateString from '../../hooks/use-state-string';
import styles from './rules.module.css';
import { useTranslation } from 'react-i18next';
import lowerCase from 'lodash/lowerCase';
@@ -23,30 +25,11 @@ const getBoardName = (title?: string): string => {
};
const BoardRulesDisplay = ({ communityAddress, directories }: { communityAddress: string; directories: DirectoryCommunity[] }) => {
const { t } = useTranslation();
const communityIdentifier = useCommunityIdentifier(communityAddress);
const community = useCommunity(communityIdentifier ? { community: communityIdentifier } : undefined);
const { rules, state, title, shortAddress } = community || {};
let loadingText: string | null = null;
if (!community) {
loadingText = 'connecting...';
} else {
switch (state) {
case 'fetching-ipns':
case 'fetching-ipfs':
loadingText = 'loading...';
break;
case 'failed':
loadingText = 'failed to load';
break;
case 'succeeded':
loadingText = null;
break;
default:
loadingText = state ? `${state}...` : 'loading...';
}
}
const stateString = useStateString(community) || t('downloading_board');
const isLoaded = state === 'succeeded';
const defaultSub = directories.find((sub) => sub.address === communityAddress);
@@ -75,7 +58,7 @@ const BoardRulesDisplay = ({ communityAddress, directories }: { communityAddress
<div className={styles.boxContent}>
{!isLoaded ? (
<p>
<em>{loadingText}</em>
<em>{state === 'failed' ? t('failed') : <LoadingEllipsis string={stateString} />}</em>
</p>
) : rules && rules.length > 0 ? (
<ol>