Repository navigation
Conversation
# Conflicts: # resources/js/components/user-search-dialog.tsx # routes/web.php
…sh#243) Co-authored-by: brendt <brent.roose@gmail.com>
…ev-null-sh#247) Co-authored-by: Houssem Eddine BENABDALLAH <h.benabdallah@jacquetmetals.com>
nunomaduro
force-pushed
the
production
branch
from
October 16, 2026 15:09
ad2288c to
9894cf6
Compare
xHeaven
marked this pull request as draft
October 17, 2026 03:09
Member
Author
|
Pretty much superseded by AI moderation. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The admin moderation queue had approve/reject and a free-text reason, nothing else:
What changes
Rejection reason templates. A new Reasons dialog in the queue header lets admins create, edit and delete shared templates (title of up to 40 characters, body of up to 280). Under each commit's actions, the templates show as a single row of chips that scrolls sideways when it doesn't fit. Clicking a chip, or pressing
1–9on the focused commit, prefills the reason, which stays editable. The digits work like the existinga/xshortcuts: they are buttons witharia-keyshortcutsinside the focused commit.⌘↵in the reason field rejects. The keyboard shortcuts dialog lists both.Claims. Focusing or acting on a commit claims it for 5 minutes (
PUT moderation/{commit}/claim, debounced). Each admin holds one claim at a time, so moving to the next commit releases the previous one. Claim behaviour:claims.Decision log.
UpdateCommitStatusesnow takes the acting admin and writes acommit_decisionsrow (admin, status, reason, time) for every commit it changes. Admins see the log in three places:/moderation/decisions): newest first, with infinite scroll.Polish
A pass over this PR's UI using the standards from #156 (no layout jumps, no reload flashes, skeletons sized like the content, no clipping). The measurements were taken in Chrome with a
PerformanceObserverfor layout shifts. "Largest jump" is the biggest distance any row moved in a single frame.Review queue
j/kgrew and shrank every row. They are now one muted row of chips that scrolls sideways and snaps to whole chips. A chip picked by keyboard scrolls into view if it was cut off.Dialogs
$ ls reasons/and "No reasons yet. Add the ones you keep typing out."Decision log
Before and after: reviewing with the keyboard (
j,j,3,⌘↵,a)MP4: before · after
Before and after: two admins, @jonas (left) and @mara (right)
@mara claims a commit. @jonas's next poll shows the claim, and @jonas takes it over. @mara's stale approve is refused. @jonas then rejects with reason
1.MP4: before · after
Before and after: the Reasons dialog on a phone (12 reasons)
MP4: before · after
Implementation
RejectionReason,CommitClaim(uniquecommit_idand uniqueuser_id, plusexpires_at), andCommitDecision(the admin is nulled if their account is deleted; the row is deleted with the commit).RejectionReasonController@store/update/destroy→Create/Update/DeleteRejectionReason[Request]CommitClaimController@update→UpdateCommitClaimRequest→UpdateCommitClaimCommitDecisionController@index→ListCommitDecisionsRequest+CommitDecisionsQueryApp\Rules\Unclaimedrule backs the approval, rejection, bulk-approval and claim requests (the claim request skips it whentake_overis true). Only pending commits can be claimed.UpdateCommitStatus(es)::handle()now takesUser $moderatorfirst, and every caller passes the current admin.preserveState: trueand also reloadclaims.CommitFeedtakesremovingIds, andCommitCardtakesisRemoving, to collapse rows that are leaving.Overlap check
This doesn't reimplement #93 (reports), #104 (risk scoring), #125 (Jev rules) or #142 (trusted authors). #104 and #125 also edit
moderation/show.tsxandModerationController, and #125 editsUpdateCommitStatusesand addstypes/moderation.ts. Expect textual conflicts with whichever lands first. Jev's automated decisions in #125 would need to pass a moderator, or make it nullable, once both are merged.The polish touches
commit-card.tsx,commit-feed.tsxandapp-nav.tsx, which #156 also edits; expect small rebases there.Media
Captured against a local SQLite database with seeded admins
@maraand@jonas, twelve rejection reasons (some long), and a 37-character GitHub username.Picking a reason from the keyboard (
j,j,3, edit,⌘↵)MP4
Approving three selected commits
MP4
Managing reasons (add, edit, delete)
MP4
Decision log: approve, open Decisions, load more, open the commit
MP4
Reason picked on the focused commit
Commit claimed by another admin
Reasons dialog
Decisions page
Decisions on a commit's page (admins only)
Empty states: queue, reasons, decisions
Validation
Feature:
pest --coverage --exactly=100.0: 987 passed, 100.0% coverage (run with Xdebug).pest --type-coverage --min=100: 100%.phpstan,rector --dry-runandpint --testpass.bun run test:lintandbun run test:typespass.Polish (front end only, no behaviour change on the server, so no new tests):
peston the moderation, claim, reason, decision, approval, rejection and commit controller tests plusArchTest: 137 passed.pest --type-coverage --min=100: 100%.phpstan,rector --dry-run,pint --test,bun run test:lint,bun run test:typesandbun run buildpass.Limitations