feat(directories): add /q/ feedback directory and link site footer

Register /q/ in directory defaults and board codes, ship the feedback
directory list, merge remote defaults with bundled fallbacks, and route
footer Feedback to /q/ instead of GitHub issues.
This commit is contained in:
Tommaso Casaburi
2026-06-26 18:12:05 +07:00
parent 802aa2a447
commit 51c0a16249
8 changed files with 71 additions and 9 deletions
@@ -216,6 +216,16 @@ describe('BoardsBar', () => {
expect(testState.openDirectoryModalMock).toHaveBeenCalledTimes(1);
});
it('links the feedback directory when /q/ is visible', async () => {
testState.directories = [{ address: '5chan-feedback.bso', title: '/q/ - 5chan Feedback' }];
testState.visibleDirectories = new Set(['q']);
testState.resolvedCommunityAddress = '5chan-feedback.bso';
await renderBoardsBar('/q');
expect(getLinkHref('q')).toBe('/q');
});
it('opens the desktop search bar and submits entered board addresses', async () => {
await renderBoardsBar('/mu');
@@ -1,5 +1,6 @@
import Version from '../version';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import Version from '../version';
type SiteLegalMetaOrder = 'version-first' | 'license-first';
@@ -16,11 +17,7 @@ const LicenseText = () => {
const VersionFeedbackContact = () => (
<>
<Version /> {' '}
<a href='https://github.com/bitsocialnet/5chan/issues/new' target='_blank' rel='noopener noreferrer'>
Feedback
</a>{' '}
{' '}
<Version /> <Link to='/q'>Feedback</Link> {' '}
<a href='https://github.com/bitsocialnet/5chan/graphs/contributors' target='_blank' rel='noopener noreferrer'>
Contact
</a>
+2 -1
View File
@@ -11,7 +11,7 @@ export const BOARD_CODE_GROUPS: string[][] = [
['r9k', 's5s', 'vip'],
// Group 4: [cm / hm / lgbt / y]
['cm', 'hm', 'lgbt', 'y'],
// Group 5: [3 / aco / adv / an / bant / biz / cgl / ck / co / diy / fa / fit / gd / hc / his / int / jp / lit / mlp / mu / n / news / out / po / pol / pw / qst / sci / soc / sp / tg / toy / trv / tv / vp / vt / wsg / wsr / x / xs]
// Group 5: [3 / aco / adv / an / bant / biz / cgl / ck / co / diy / fa / fit / gd / hc / his / int / jp / lit / mlp / mu / n / news / out / po / pol / pw / q / qst / sci / soc / sp / tg / toy / trv / tv / vp / vt / wsg / wsr / x / xs]
[
'3',
'aco',
@@ -39,6 +39,7 @@ export const BOARD_CODE_GROUPS: string[][] = [
'po',
'pol',
'pw',
'q',
'qst',
'sci',
'soc',
@@ -2,7 +2,7 @@
"title": "5chan Directory Defaults",
"description": "Expected 5chan UX defaults by directory code. These defaults apply to the directory, not to individual candidate boards.",
"createdAt": 1779262179,
"updatedAt": 1780129468,
"updatedAt": 1782467500,
"directories": {
"3": {
"directoryCode": "3",
@@ -692,6 +692,29 @@
"All images and discussion should pertain to professional wrestling."
]
},
"q": {
"directoryCode": "q",
"title": "/q/ - 5chan Feedback",
"features": {
"pseudonymityMode": "per-reply",
"safeForWork": true,
"hasFlags": false,
"requirePostLink": false,
"requirePostLinkIsMedia": false,
"bumpLimit": 300,
"noSpoilers": false,
"noSpoilerReplies": false,
"postsPerPage": 15
},
"rules": [
"This board is for feedback, bug reports, feature requests, board proposals, governance, and other discussion about 5chan itself.",
"Keep threads focused on 5chan, Bitsocial, directory listings, board ownership, moderation policy, or site behavior.",
"Do not use this board for personal callouts, harassment, or drama about ordinary posters.",
"Do not post private information, credentials, node secrets, exploit details, or other security-sensitive material.",
"Ban appeals and moderation disputes belong on the relevant board unless they concern directory-wide policy.",
"Board proposals should explain the intended audience, rules, and why the board helps 5chan grow."
]
},
"qst": {
"directoryCode": "qst",
"title": "/qst/ - Quests",
@@ -0,0 +1,13 @@
{
"description": "Boards competing to host the /q/ directory on 5chan. The highest-scoring board resolves the directory code; if it goes offline, 5chan rotates to the next-highest. Anyone can open a PR on this file to add their board.\n\nhttps://github.com/bitsocialnet/lists/blob/master/5chan-directories/5chan-q-directory.json",
"createdAt": 1782467500,
"updatedAt": 1782467500,
"boards": [
{
"address": "5chan-feedback.bso",
"publicKey": "12D3KooWNmuwmBDAgxBNrPRvzvCLYD9U3etN9idguXZLTpRRgqVf",
"addedAt": 1782467500,
"owner": "plebeius.bso"
}
]
}
@@ -346,8 +346,11 @@ describe('use-directories', () => {
const directoriesByCode = new Map(snapshot.directories.map((directory) => [directory.directoryCode, directory]));
expect(directoriesByCode.get('mu')?.address).toBe('music-remote.bso');
expect(directoriesByCode.get('biz')?.address).toBe('business-and-finance.bso');
expect(directoriesByCode.get('q')?.address).toBe('5chan-feedback.bso');
expect(directoriesByCode.get('tv')?.address).toBe('television-and-film.bso');
expect(snapshot.directory?.address).toBe('television-and-film.bso');
expect(snapshot.directoryDefaults.directories.q.title).toBe('/q/ - 5chan Feedback');
expect(snapshot.directoryDefaults.directories.q.rules).toContain('Board proposals should explain the intended audience, rules, and why the board helps 5chan grow.');
expect(warnSpy.mock.calls.some((call: ConsoleWarnCall) => String(call[0]).includes('Failed to fetch directory list "biz"'))).toBe(true);
});
+10 -1
View File
@@ -224,6 +224,15 @@ export const getFallbackDirectoryDefaults = (): DirectoryDefaultsData => {
return fallbackDirectoryDefaults;
};
const mergeDirectoryDefaults = (remote: DirectoryDefaultsData, fallback: DirectoryDefaultsData): DirectoryDefaultsData => ({
...fallback,
...remote,
directories: {
...fallback.directories,
...remote.directories,
},
});
export const getFallbackDirectoriesData = (): DirectoriesData => {
if (fallbackDirectoriesData) return fallbackDirectoriesData;
const normalized = normalizeDirectoriesData(directoryListsData as unknown);
@@ -364,7 +373,7 @@ const fetchDirectoryListFromGitHub = async (code: string, defaults: DirectoryDef
};
const fetchDirectoriesFromGitHub = async (): Promise<DirectoriesData> => {
const defaults = await fetchDirectoryDefaultsFromGitHub();
const defaults = mergeDirectoryDefaults(await fetchDirectoryDefaultsFromGitHub(), getFallbackDirectoryDefaults());
const fallbackLists = getFallbackDirectoryLists(defaults);
const fallbackListsByCode = new Map(fallbackLists.map((list) => [list.directoryCode, list]));
const codes = [...new Set([...Object.keys(defaults.directories), ...fallbackLists.map((list) => list.directoryCode)])];
@@ -486,6 +486,12 @@ const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => {
address={boardAddressesByName['Very Important Posts'] ?? null}
{...boardLinkProps}
/>
<BoardLink
key={boardAddressesByName['5chan Feedback'] ?? '5chan Feedback'}
boardName='5chan Feedback'
address={boardAddressesByName['5chan Feedback'] ?? null}
{...boardLinkProps}
/>
</ul>
</>
)}