Repository navigation
feat(moderation): ✨ report any content record from public pages - #888
aXenDeveloper wants to merge 2 commits into
Conversation
- Add `contentTarget` and `ContentTarget` to `@vitnode/core/content` so a
public action names its record from the content type definition.
- `ReportButton` takes `target`, gets an accessible name ("Report {type}"),
`aria-haspopup`, an optional `describedBy`, and a non-focusable skeleton
while its messages load.
- Export `ReportDialog` (controlled) and `REPORT_NAMESPACES`.
- An expired session (401) now shows a "sign in again" toast.
- Forum: enable reports on topics and comments, render the Report action for
everyone but the author, and label the generated permissions.
- Blog: switch to `target`.
- Docs: list-item reporting, `ReportButton`/`ReportDialog` reference, and
fixes in threads, approvals, public write and rate limit pages.
BREAKING CHANGE: `ReportButton` takes `target` instead of `pluginId`,
`targetType` and `targetId`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CE6w3pVi8mgjZMhax7f6tu
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
- ReportButton: accessible name, aria-haspopup, aria-describedby, opening the dialog, and the guest sign-in link with returnTo. - ReportDialog: renders the dialog only when open. - POST /reports: 401 for guests, 400 for a short "other" description, and 429 rate_limited until the hour passes. - contentReportTarget capture: snapshot of a published record, nothing for hidden, draft or missing ones. - createReport: maps 401 to "unauthenticated" alongside the other failures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0127YWdeYvCW1buERsEUUMbu
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5a0a2758a0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| publication: true, | ||
| visibility: true, | ||
| reports: true, |
There was a problem hiding this comment.
Scope topic reports to their forum
Configure the topic model's report target with the forum column and forumContainers.id as its moderation scope. topicContent is currently created without moderationScope, so enabling reports here generates unscoped can_view_reports, can_manage_reports, can_view_hidden, and can_hide permissions: a moderator intended to handle one forum can consequently view and act on topic reports from every forum, unlike the existing container-scoped topic edit and approval permissions.
Useful? React with 👍 / 👎.
| definition: { id: string }, | ||
| id: ContentId, |
There was a problem hiding this comment.
Tie target IDs to the definition's ID strategy
Make this helper generic over the content definition and accept ContentIdOf<TDefinition> rather than the unconstrained ContentId. As written, a UUID definition accepts a number and a bigint definition accepts a JavaScript number; the former produces a target the server rejects, while a bigint beyond Number.MAX_SAFE_INTEGER can be rounded before stringification and report the wrong record. The rest of the Content Engine already models bigint and UUID IDs through ContentIdOf specifically to prevent this mismatch.
Useful? React with 👍 / 👎.
Improving Documentation
pnpm lint:fixpasses. The one remaining warning, insrc/tanstack/layout/route-error.tsx, is already oncanary.Description
What?
This is the first part of the Content Engine public-UI plan: reporting, from turning it on for a content type to rendering the button, with docs.
Core (
@vitnode/core)contentTarget(pluginId, definition, id)and theContentTargettype in@vitnode/core/content. They build the{ pluginId, type, id }a public action points at, straight from the content type definition. Content definitions are safe to import in the browser.ReportButtontakestargetinstead ofpluginId,targetTypeandtargetId. Accessibility changes:aria-haspopup="dialog".describedBylets a list of buttons be told apart, for example by the comment's author.ReportDialog: the same dialog, controlled by the caller, for a custom trigger or for one dialog shared by a long list.REPORT_NAMESPACESis now exported.401while reporting, for example when the session expired, shows a "sign in again" toast instead of the generic error.Plugins
reportsis enabled onforum.topicandforum.comment. Comment snapshots keep thebody.target.Docs
dev/moderation/enable-reports.mdx:dev/moderation/reference.mdx:contentTarget,ReportButtonandReportDialog.threads.mdx: relations are{ id }, and the snippet now matches the realfetchThreadPage.approvals.mdx: removed a reference to a non-existent file.public-write-api.mdx: the content-write rate limit counts per user when signed in.public-write-frontend.mdx: new section on approvals and edit windows.content-engine/reference.mdx: links the report UI.Breaking (canary):
ReportButton'spluginId,targetTypeandtargetIdprops are replaced bytarget.Why?
The backend and
ReportButtonalready existed, but:This slice sets the pattern the later phases will reuse: one target identifier, a ready-made component plus a controlled primitive, real plugin usage, and a task-focused doc.
Verification
returnTo.targetType = forum.commentand a body snapshot.AGENTS.mdrequires, and will land on this branch.Known limits, left for later
:tree-aware report filtering.Generated by Claude Code