Merge branch 'codex/feature/add-q-feedback-directory'

This commit is contained in:
Tommaso Casaburi
2026-06-26 18:12:11 +07:00
8 changed files with 71 additions and 9 deletions
@@ -216,6 +216,16 @@ describe('BoardsBar', () => {
expect(testState.openDirectoryModalMock).toHaveBeenCalledTimes(1); 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 () => { it('opens the desktop search bar and submits entered board addresses', async () => {
await renderBoardsBar('/mu'); await renderBoardsBar('/mu');
@@ -1,5 +1,6 @@
import Version from '../version'; import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Version from '../version';
type SiteLegalMetaOrder = 'version-first' | 'license-first'; type SiteLegalMetaOrder = 'version-first' | 'license-first';
@@ -16,11 +17,7 @@ const LicenseText = () => {
const VersionFeedbackContact = () => ( const VersionFeedbackContact = () => (
<> <>
<Version /> {' '} <Version /> <Link to='/q'>Feedback</Link> {' '}
<a href='https://github.com/bitsocialnet/5chan/issues/new' target='_blank' rel='noopener noreferrer'>
Feedback
</a>{' '}
{' '}
<a href='https://github.com/bitsocialnet/5chan/graphs/contributors' target='_blank' rel='noopener noreferrer'> <a href='https://github.com/bitsocialnet/5chan/graphs/contributors' target='_blank' rel='noopener noreferrer'>
Contact Contact
</a> </a>
+2 -1
View File
@@ -11,7 +11,7 @@ export const BOARD_CODE_GROUPS: string[][] = [
['r9k', 's5s', 'vip'], ['r9k', 's5s', 'vip'],
// Group 4: [cm / hm / lgbt / y] // Group 4: [cm / hm / lgbt / y]
['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', '3',
'aco', 'aco',
@@ -39,6 +39,7 @@ export const BOARD_CODE_GROUPS: string[][] = [
'po', 'po',
'pol', 'pol',
'pw', 'pw',
'q',
'qst', 'qst',
'sci', 'sci',
'soc', 'soc',
@@ -2,7 +2,7 @@
"title": "5chan Directory Defaults", "title": "5chan Directory Defaults",
"description": "Expected 5chan UX defaults by directory code. These defaults apply to the directory, not to individual candidate boards.", "description": "Expected 5chan UX defaults by directory code. These defaults apply to the directory, not to individual candidate boards.",
"createdAt": 1779262179, "createdAt": 1779262179,
"updatedAt": 1780129468, "updatedAt": 1782467500,
"directories": { "directories": {
"3": { "3": {
"directoryCode": "3", "directoryCode": "3",
@@ -692,6 +692,29 @@
"All images and discussion should pertain to professional wrestling." "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": { "qst": {
"directoryCode": "qst", "directoryCode": "qst",
"title": "/qst/ - Quests", "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])); const directoriesByCode = new Map(snapshot.directories.map((directory) => [directory.directoryCode, directory]));
expect(directoriesByCode.get('mu')?.address).toBe('music-remote.bso'); expect(directoriesByCode.get('mu')?.address).toBe('music-remote.bso');
expect(directoriesByCode.get('biz')?.address).toBe('business-and-finance.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(directoriesByCode.get('tv')?.address).toBe('television-and-film.bso');
expect(snapshot.directory?.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); 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; return fallbackDirectoryDefaults;
}; };
const mergeDirectoryDefaults = (remote: DirectoryDefaultsData, fallback: DirectoryDefaultsData): DirectoryDefaultsData => ({
...fallback,
...remote,
directories: {
...fallback.directories,
...remote.directories,
},
});
export const getFallbackDirectoriesData = (): DirectoriesData => { export const getFallbackDirectoriesData = (): DirectoriesData => {
if (fallbackDirectoriesData) return fallbackDirectoriesData; if (fallbackDirectoriesData) return fallbackDirectoriesData;
const normalized = normalizeDirectoriesData(directoryListsData as unknown); const normalized = normalizeDirectoriesData(directoryListsData as unknown);
@@ -364,7 +373,7 @@ const fetchDirectoryListFromGitHub = async (code: string, defaults: DirectoryDef
}; };
const fetchDirectoriesFromGitHub = async (): Promise<DirectoriesData> => { const fetchDirectoriesFromGitHub = async (): Promise<DirectoriesData> => {
const defaults = await fetchDirectoryDefaultsFromGitHub(); const defaults = mergeDirectoryDefaults(await fetchDirectoryDefaultsFromGitHub(), getFallbackDirectoryDefaults());
const fallbackLists = getFallbackDirectoryLists(defaults); const fallbackLists = getFallbackDirectoryLists(defaults);
const fallbackListsByCode = new Map(fallbackLists.map((list) => [list.directoryCode, list])); const fallbackListsByCode = new Map(fallbackLists.map((list) => [list.directoryCode, list]));
const codes = [...new Set([...Object.keys(defaults.directories), ...fallbackLists.map((list) => list.directoryCode)])]; 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} address={boardAddressesByName['Very Important Posts'] ?? null}
{...boardLinkProps} {...boardLinkProps}
/> />
<BoardLink
key={boardAddressesByName['5chan Feedback'] ?? '5chan Feedback'}
boardName='5chan Feedback'
address={boardAddressesByName['5chan Feedback'] ?? null}
{...boardLinkProps}
/>
</ul> </ul>
</> </>
)} )}