Skip to content

feat(flags): add comment flags#1140

Merged
tomcasaburi merged 3 commits into
masterfrom
codex/feature/comment-flags
May 24, 2026
Merged

feat(flags): add comment flags#1140
tomcasaburi merged 3 commits into
masterfrom
codex/feature/comment-flags

Conversation

@tomcasaburi
Copy link
Copy Markdown
Member

@tomcasaburi tomcasaburi commented May 24, 2026

Summary

  • add 5chan flag selectors for boards with hasFlags and forward selected flags into publish options
  • render signed 5chan country assertions and board flag flairs in post metadata
  • update to pkc-js 0.0.37 and handle multi-step iframe challenges

Verification

  • corepack yarn vitest run src/lib/__tests__/comment-flags.test.ts src/lib/__tests__/comment-flag-selection.test.ts src/stores/__tests__/publish-stores.test.ts src/components/post-form/__tests__/post-form.test.tsx src/components/reply-modal/__tests__/reply-modal.test.tsx src/components/challenge-modal/__tests__/challenge-modal.test.tsx
  • corepack yarn lint
  • corepack yarn type-check
  • corepack yarn build
  • Chrome live check on No.298 rendered the Vietnam flag from comment["5chan"].flag

Notes

  • Fresh isolated Playwright could not load the live thread without the contributor browser RPC state.

Note

Medium Risk
Changes the publish and challenge-completion paths plus a protocol dependency bump; mistakes could break posting or flag verification, but scope is feature/UI rather than core auth.

Overview
This PR adds 4chan-style author flags end to end: boards with features.hasFlags get a flag dropdown on the post form and reply modal, selections map to comment flairs and bitsocial-flags:5chan:… challenge answers, and desktop/mobile posts render sprite flags from signed comment["5chan"].flag (with fallbacks). New helpers cover country / political / pony flag catalogs, parsing, and publish options; publish stores/hooks now carry flairs and challengeRequest and clear stale flag fields when posting without a flag.

The challenge modal no longer publishes after the first iframe—it steps through multiple iframe challenges before submitting the full answer array (e.g. spam blocker then flags host).

@pkcprotocol/pkc-js is bumped to 0.0.37 (with yarn resolutions for gossipsub). Locale files gain a flag string. findDirectoryByAddress returns undefined when nothing matches (no silent fuzzy match after normalized lookup).

Reviewed by Cursor Bugbot for commit d612dcd. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Per-author flag display on posts/comments
    • Flag selector when creating posts and replies
    • Improved multi-step challenge/modal progression
  • Localization

    • Added "flag" translation strings across many locales
  • Style

    • New/updated styles for flag display and flag-select controls
  • Tests

    • New and expanded tests covering flag handling and challenge flows
  • Chores

    • Bumped PKC dependency and added libp2p gossipsub resolution

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
5chan Ready Ready Preview, Comment May 24, 2026 4:02pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 421b8718-7078-4aa2-9b14-4fd676c4d163

📥 Commits

Reviewing files that changed from the base of the PR and between acf28a8 and d612dcd.

📒 Files selected for processing (4)
  • src/hooks/__tests__/use-publish-post.test.tsx
  • src/hooks/__tests__/use-publish-reply.test.tsx
  • src/lib/__tests__/comment-flag-selection.test.ts
  • src/lib/comment-flag-selection.ts

📝 Walkthrough

Walkthrough

This PR implements per-board flag/flair support end-to-end: flag metadata and lookups, selection UI in PostForm/ReplyModal, conversion to challenge answers and publish payloads, parsing of flair sources into renderable view models, UI rendering in posts, store/hook plumbing to persist flags through publishing, challenge modal sequencing, and i18n additions for the "flag" label.

Changes

Board Flags & Flairs System

Layer / File(s) Summary
Board Flag Metadata Definitions
src/lib/board-flags.ts, src/lib/__tests__/board-flags.test.ts
BoardFlagKind/BoardFlagDefinition and helpers were added; sprite-sheet coords and per-flag definitions for pol and pony are provided.
Flag Selection & Challenge Mapping
src/lib/comment-flag-selection.ts, src/lib/__tests__/comment-flag-selection.test.ts
Directory-aware select options and conversion helpers map a selected flag into CommentFlagRequest and CommentFlagPublishOptions (challenge answers + flairs).
Flag Parsing & Display Models
src/lib/comment-flags.ts, src/lib/__tests__/comment-flags.test.ts
Parses comment/author flair syntaxes and structured flair fields into AuthorFlagViewModel items with sprite metadata and deduplication.
Directory Feature & Lookup Updates
src/hooks/use-directories.ts, src/hooks/__tests__/use-directories.test.ts
Directory lookup normalized-match refactor and test coverage; directory entries support directoryCode used by flag option derivation.
Publish Stores & Hooks
src/stores/use-publish-post-store.ts, src/stores/use-publish-reply-store.ts, src/stores/__tests__/publish-stores.test.ts, src/hooks/use-publish-post.ts, src/hooks/use-publish-reply.ts
Zustand stores extended to persist flairs and challengeRequest; publish hooks include flairs and conditional challengeRequest into constructed publish payloads and clear stale flag data on one-shot publishes.
Post Display Components
src/components/post-author-flags.tsx, src/components/post-desktop/post-desktop.tsx, src/components/post-mobile/post-mobile.tsx, src/views/post/post.module.css
New PostAuthorFlags component renders sprite-backed flags (accessibility attrs) and is integrated into desktop/mobile post headers, gated by directory hasFlags and post state.
PostForm & ReplyModal Flag UI
src/components/post-form/post-form.tsx, src/components/post-form/post-form.module.css, src/components/post-form/__tests__/post-form.test.tsx, src/components/reply-modal/reply-modal.tsx, src/components/reply-modal/reply-modal.module.css, src/components/reply-modal/__tests__/reply-modal.test.tsx
Forms and modals render conditional flag <select> controls, reset behavior, CSS for selects, and spread derived flagPublishOptions (challenge answers + flairs) into publish calls; tests validate /pol flows.
Challenge Modal Multi-Iframe
src/components/challenge-modal/challenge-modal.tsx, src/components/challenge-modal/__tests__/challenge-modal.test.tsx
Centralized completeCurrentChallenge merges iframe answers across sequential challenges and publishes when the sequence completes; tests added for multi-iframe progression.
Localization
public/translations/*/default.json
Adds flag translation key across locales to support the new UI label.
Dependency Updates
package.json
Bumps @pkcprotocol/pkc-js to 0.0.37 and pins @libp2p/gossipsub to 15.0.23 in resolutions.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Complexity: multiple new modules and cross-cutting UI/store changes, but well-scoped and heavily covered by new tests.

Possibly related PRs

  • bitsocialnet/5chan#1134: Related work touching directory resolution and directoryCode handling used by flag option derivation.

Poem

🐇
I hopped through sprites and tests today,
Flags stitched into posts in bright array,
Select, challenge, flair — then send,
Tiny ribbons flutter to every friend.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(flags): add comment flags' accurately summarizes the main objective of this PR: adding comment flag functionality to the application.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/feature/comment-flags

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/hooks/use-directories.ts Outdated
Comment thread src/components/reply-modal/reply-modal.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/__tests__/comment-flags.test.ts (1)

1-104: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Run yarn test successfully (use Yarn 4 via Corepack) and share the results

  • yarn test currently fails due to packageManager: yarn@4.13.0 vs global Yarn 1.22.22
  • Enable Corepack (corepack enable) and rerun yarn test, then include the output/exit status in the PR
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/__tests__/comment-flags.test.ts` around lines 1 - 104, The test suite
fails locally because global Yarn is v1 while the repo specifies yarn@4; enable
Corepack and run the tests with the right Yarn, then paste results and exit code
into the PR: run corepack enable (so Corepack can shim Yarn 4), then run yarn
test (which will use the declared packageManager and execute the tests covering
getAuthorFlagViewModels, getCommentFlagFlairs, and getAuthorFlagFlairs); copy
the full terminal output and the final exit status into the PR so CI reviewers
can verify the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/post-form/post-form.tsx`:
- Around line 302-307: The table header and aria-label currently hardcode "Flag"
instead of using the i18n helper; update the hardcoded strings to use the
translation function (e.g., t('flag') or an appropriate key) so the header text
and the select's aria-label go through i18n. Locate the fragment around the
PostForm component where the <td>Flag</td> and the select with aria-label='Flag'
(near symbols flagOptions and styles.flagSelect) are rendered and replace both
occurrences with the translated string via the existing t(...) call used
elsewhere in this component.

In `@src/components/reply-modal/reply-modal.tsx`:
- Line 522: The select element currently hardcodes aria-label='Flag'; replace
that literal with a localized string using your app's i18n utility (e.g.,
t('reply.flag') or intl.formatMessage({id:'reply.flag', defaultMessage:'Flag'}))
so the accessibility label is translated; update the select at the element where
key={flagOptions.map((option) => option.value).join('|')} ref={flagRef}
defaultValue={...} to use the localized value and add the corresponding
translation key/entry (reply.flag) to your translation files.

In `@src/lib/comment-flag-selection.ts`:
- Around line 38-40: The getDirectoryCode helper incorrectly uses the nullish
coalescing operator so an empty trimmed directoryCode ('') prevents falling back
to parsing the title; update getDirectoryCode to treat an empty/whitespace-only
directoryCode as absent by checking the trimmed value's truthiness (e.g.,
compute const code = directory?.directoryCode?.trim().toLowerCase(); return code
? code : directory?.title?.match(/^\/([^/]+)\//)?.[1]?.toLowerCase()) so the
title parsing is used when directoryCode is empty; reference getDirectoryCode,
directory?.directoryCode?.trim().toLowerCase(), and directory?.title?.match(...)
when locating the change.

In `@src/lib/comment-flags.ts`:
- Around line 117-121: Normalize the parsed token before doing literal
comparisons: call normalizeBoardFlagKind(kindValue) (or otherwise
lowercase/normalize kindValue) into a local variable (e.g., normalizedKind) and
use that when checking for 'country' or 'geo' and when returning the kind;
update both the block that currently checks kindValue === 'country' || kindValue
=== 'geo' (which returns { type: 'country', code }) and the later similar block
at lines 127-131 to use the normalizedKind variable instead of the raw kindValue
so mixed/upper-case inputs are handled correctly.

---

Outside diff comments:
In `@src/lib/__tests__/comment-flags.test.ts`:
- Around line 1-104: The test suite fails locally because global Yarn is v1
while the repo specifies yarn@4; enable Corepack and run the tests with the
right Yarn, then paste results and exit code into the PR: run corepack enable
(so Corepack can shim Yarn 4), then run yarn test (which will use the declared
packageManager and execute the tests covering getAuthorFlagViewModels,
getCommentFlagFlairs, and getAuthorFlagFlairs); copy the full terminal output
and the final exit status into the PR so CI reviewers can verify the fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7782c241-099a-47bc-859f-0d3a5fe09436

📥 Commits

Reviewing files that changed from the base of the PR and between 804c14f and f63beb1.

⛔ Files ignored due to path filters (3)
  • public/assets/icons/flags-2.png is excluded by !**/*.png
  • public/assets/icons/flags-pony.png is excluded by !**/*.png
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (26)
  • package.json
  • src/components/challenge-modal/__tests__/challenge-modal.test.tsx
  • src/components/challenge-modal/challenge-modal.tsx
  • src/components/post-author-flags.tsx
  • src/components/post-desktop/post-desktop.tsx
  • src/components/post-form/__tests__/post-form.test.tsx
  • src/components/post-form/post-form.module.css
  • src/components/post-form/post-form.tsx
  • src/components/post-mobile/post-mobile.tsx
  • src/components/reply-modal/__tests__/reply-modal.test.tsx
  • src/components/reply-modal/reply-modal.module.css
  • src/components/reply-modal/reply-modal.tsx
  • src/hooks/__tests__/use-directories.test.tsx
  • src/hooks/use-directories.ts
  • src/hooks/use-publish-post.ts
  • src/hooks/use-publish-reply.ts
  • src/lib/__tests__/board-flags.test.ts
  • src/lib/__tests__/comment-flag-selection.test.ts
  • src/lib/__tests__/comment-flags.test.ts
  • src/lib/board-flags.ts
  • src/lib/comment-flag-selection.ts
  • src/lib/comment-flags.ts
  • src/stores/__tests__/publish-stores.test.ts
  • src/stores/use-publish-post-store.ts
  • src/stores/use-publish-reply-store.ts
  • src/views/post/post.module.css

Comment thread src/components/post-form/post-form.tsx Outdated
Comment thread src/components/reply-modal/reply-modal.tsx Outdated
Comment thread src/lib/comment-flag-selection.ts Outdated
Comment thread src/lib/comment-flags.ts Outdated
@tomcasaburi
Copy link
Copy Markdown
Member Author

Addressed the valid review findings in commit acf28a8: removed the temporary flags test community fallback, localized the flag labels, fixed blank directoryCode fallback behavior, normalized text flag kind parsing, and removed the stale reply-modal CSS-module reference.\n\nLocal verification after the changes:\n- corepack yarn vitest run src/hooks/tests/use-directories.test.tsx src/lib/tests/comment-flag-selection.test.ts src/lib/tests/comment-flags.test.ts src/components/post-form/tests/post-form.test.tsx src/components/reply-modal/tests/reply-modal.test.tsx\n- corepack yarn test --run\n- corepack yarn lint\n- corepack yarn type-check\n- corepack yarn doctor\n- corepack yarn build\n\nReact Doctor still reports existing repo warnings outside this diff; I did not treat those as blockers for this PR.

Comment thread src/hooks/use-publish-post.ts
Comment thread src/lib/comment-flag-selection.ts Outdated
@tomcasaburi
Copy link
Copy Markdown
Member Author

Addressed the latest valid review findings in d612dcd: non-flag publishes now explicitly clear stale flag flairs/challenge requests from one-shot publish options, and the unused standalone challenge-request helper was removed. Local verification: corepack yarn install --immutable, corepack yarn knip, corepack yarn lint, corepack yarn type-check, corepack yarn test --run, corepack yarn doctor, and corepack yarn build. One full test run briefly hit a settings-modal assertion that passed in isolation and on rerun; the final full-suite rerun passed.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d612dcd. Configure here.

link: string | undefined;
flairs: Comment['flairs'] | undefined;
spoiler: boolean | undefined;
challengeRequest: Record<string, unknown> | undefined;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant challengeRequest top-level state field never read

Low Severity

The challengeRequest field is added as a top-level state property in both use-publish-post-store and use-publish-reply-store, but no hook ever reads it from the top level. Both use-publish-post.ts and use-publish-reply.ts access the challenge request exclusively via publishCommentOptions.challengeRequest. This creates dead state that's written on every store update and reset but never consumed, unlike flairs which is read from both the top-level field and publishCommentOptions. The asymmetry risks confusing future contributors into thinking it's a meaningful separate field.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d612dcd. Configure here.

@tomcasaburi
Copy link
Copy Markdown
Member Author

Latest Cursor Bugbot low-severity note about the top-level store challengeRequest field is valid cleanup, but not a merge blocker: publishing reads from publishCommentOptions.challengeRequest, and the shipped correctness bug was stale flag data leaking into non-flag publishes, which is fixed and covered by the new one-shot post/reply tests. I’m treating that store-shape cleanup as follow-up rather than reopening CI again for a non-behavioral simplification.

@tomcasaburi tomcasaburi merged commit f74b33f into master May 24, 2026
11 checks passed
@tomcasaburi tomcasaburi deleted the codex/feature/comment-flags branch May 24, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant