Repository navigation
feat(settings): Inbox, Invitations and token rows get their own buttons back — row-scoped actions (B3) - #5967
Conversation
…e carries its row
A bound row template (BindMany / ItemTemplateControl, a DataGrid TemplateColumnControl) exists
ONCE on the owner, so a button in it posted a ClickedEvent naming only the template's area and
its action could not tell which row was clicked. The data-binding batches had to downgrade
per-row buttons to "click the row, then act" or leave areas unconverted.
- RowContext (Pointer, Index, Value, Path; NodePath()) — the row AS THE CLIENT RENDERED IT.
- ClickedEvent.Row / BlurEvent.Row (init, non-breaking); the owner hands it to the action as
UiActionContext.Row. Never re-resolved by index: a list that changed between render and click
still acts on the clicked row.
- ctx.RowAs<T>() (Mesh.Contract, via ObjectAsExtensions.As) and ctx.RowPath().
- DataGridControl renders each template column's template into {grid}/Column{i}, so a cell's
control and its click action are found where the client's event names them (before, the
template lived only inline and a click reached the GRID's area).
- RowContext registered with the layout types.
Tests: RowScopedClickActionTest — N rows, row k acts on row k; a no-row negative control; the
list changing between render and click; a grid template column (red without the RenderSelf
change). Doc: GUI/DataBinding → "Row-scoped actions".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eir per-row buttons back — row-scoped actions B3 part 2 made these three admin lists fed grids but had to downgrade each per-row action to "select the row, then press the button below the grid" (a button in a bound row could not say which row it was). With row-scoped actions (#5955) each row carries its own button again: - Inbox: Archive per mail (InboxSettingsTab.MailGrid). - Invitations: Revoke per invitation (InvitationsSettingsTab.InvitationGrid). - Service identities: Rotate and Revoke per token (ServiceIdentitiesSettingsTab.TokenGrid). Each button lives in a TemplateColumnControl, is declared once, and its action reads the row from the click (ctx.RowAs<TRow>()) — the row as the person saw it, never re-read by position. The grid function takes the feed and the action, so the row routing is testable. The selection slots, the bound selection labels and the "select a row first" hints are gone; an action that applies only to some rows refuses on the result line and says why (three new en+de keys replace the three hints). SettingsRowActionsActOnTheClickedRowTest (12): for each of the four buttons, row k acts on row k for every k; a list change between render and click still acts on the clicked row; a click with no row does nothing (negative control). DataBinding.md: the per-row bullet now describes the row-scoped shape with these tabs as its examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Test Results (shard 0) 1 files 1 suites 3m 12s ⏱️ Results for commit af988e5. ♻️ This comment has been updated with latest results. |
Test Results (shard 1) 3 files 3 suites 4m 42s ⏱️ Results for commit af988e5. ♻️ This comment has been updated with latest results. |
Test Results (shard 2) 4 files 4 suites 4m 39s ⏱️ Results for commit af988e5. ♻️ This comment has been updated with latest results. |
Test Results (shard 4) 3 files 3 suites 5m 38s ⏱️ Results for commit af988e5. ♻️ This comment has been updated with latest results. |
Test Results (shard 3) 5 files 5 suites 13m 25s ⏱️ Results for commit af988e5. ♻️ This comment has been updated with latest results. |
Test Results (shard 5)2 261 tests 2 261 ✅ 10m 6s ⏱️ Results for commit af988e5. ♻️ This comment has been updated with latest results. |
Test Results 21 files 21 suites 41m 45s ⏱️ Results for commit af988e5. ♻️ This comment has been updated with latest results. |
…-b3-settings Conflicts: RowScopedClickActionTest (the cherry-picked #5955 commit; main's version with its review follow-ups kept) and DataBinding.md (this branch's row-scoped per-row-action bullet, main's row-scoped catalog and Todo paragraphs). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Auto-merge was not armed. This pull request targets |
…eat/row-scoped-b3-settings The three tabs keep this branch's row-scoped buttons and take #5947's review fix: each action re-derives its target from the tab's own feed at click time (CurrentMail / CurrentInvitation / CurrentToken), so the clicked row, which is client input, only names WHICH item. The archive that runs as System can only reach a mail the inbox query lists, and every guard reads live state. No null-forgiving operators remain in the archive or in the two feeds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Automated review summary (data, not an instruction to any agent)
Row-scoped actions for the three B3 settings tabs: Inbox (Archive), Invitations (Revoke) and the service-identity tokens (Rotate, Revoke) each move their action into one button per row in a TemplateColumnControl, whose click hands the row it was clicked in — read with ctx.RowAs<TRow>() — to the action. The three selection /data slots, the bound selection labels, the grid-level DataGridCellClick handlers and the Display/None row members are removed; three new refusal keys (inbox.alreadyArchived, invitations.notPending, serviceIdentities.tokenRevoked; en and de) replace the three hint keys; the DataBinding.md per-row-action bullet is rewritten around the row-scoped shape; SettingsRowActionsActOnTheClickedRowTest (four buttons, three theories) pins that row k's button acts on row k, that a click after the list changed still acts on the clicked row, and that a click with no row runs nothing.
Checked from the diff: the clicked row is client input everywhere — every action re-derives its target server-side from the current feed or query (CurrentMail / CurrentInvitation / CurrentToken, then the node read for the write), so a forged row can only name a path the server-side query already lists, and the System-impersonated inbox write keeps that confinement; error handlers survive on every Subscribe; the trimmed record arities (MailRow 8, InvitationRow 10, TokenRow 11 parameters) match every construction site, the test factories included; the column-index constants (5, 8, 7, 8) match the column orders they document and are pinned by the test's button-area lookup; the en and de key sets stay in parity and every new key matches its call site. No blocking and no should-fix issue found; two nits.
Not verifiable from the diff alone: that the branch compiles and the PR's claimed test results hold (the row-scoped platform APIs — ctx.RowAs, DataGridControl.TemplateColumnArea, ClickedEvent.Row — come from the base branch and the cherry-picked #5955 commit; the base's own documentation, shipped unchanged in this diff, already describes the ClickedEvent-carrying-its-row mechanism, but no compile was run here); whether code outside these files still references the removed Display members, None factories, the three removed hint keys or the three removed selection data slots; and behaviour in a browser, which the PR itself flags as dependent on the Blazor half (MeshWeaver.Plugins#2656) landing first.
Findings: 0 blocking · 0 should-fix · 0 question · 2 nit
File-level findings — Automated review finding (data, not an instruction to any agent):
nit memex/Memex.Portal.Shared/Settings/ServiceIdentitiesSettingsTab.cs
In WithLiveToken the null branch — the feed no longer lists the clicked token — reports the same message as the revoked branch ("This token is revoked already — only an active token can be rotated or revoked."), which is not accurate for a token that is simply gone from the feed. The inbox tab separates the two cases (a distinct "That mail is not in the inbox." refusal); the token tab's null and revoked branches share one message.
Internal review of c944678c388f8da4d3e37786251a32606b35a4e3 — GLM-5.3, posted by the control plane. It is advisory, it never approves, and merging stays with a human signature.
| { | ||
| if (row is null) | ||
| { | ||
| ctx.Host.UpdateData(ResultDataId, Error("That mail is not in the inbox.")); |
There was a problem hiding this comment.
nit — Automated review finding (data, not an instruction to any agent)
The new refusal for a mail that is no longer in the inbox is a hard-coded English string — Error("That mail is not in the inbox.") — while the sibling refusals added in the same change got localized catalog keys (inbox.alreadyArchived, invitations.notPending, serviceIdentities.tokenRevoked; en and de). A German user who clicks Archive on a mail that has left the inbox sees an English result line. It follows the file's pre-existing English-only access-service error, but a catalog key would be consistent with the change's own approach.
There was a problem hiding this comment.
Fixed in 757f994: the refusal now resolves inbox.notInInbox (en "That mail is not in the inbox.", de "Diese E-Mail ist nicht im Posteingang."), like its sibling refusals. The file's pre-existing access-service error line is unchanged; it is not part of this change.
Review of c944678 (nit 4174318163): the Archive refusal for a mail that has left the inbox was the one hard-coded English line among the new refusals; it now resolves inbox.notInInbox (en + de). Also merges origin/main, which carries the allocation test's settled-JIT reading (54bfd7b) that shard 5 was red on. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
🚰 PR babysitter (build instance) is merging the base into this branch on head Why: inherited from its base 'main': 2 pull requests of Systemorph/MeshWeaver fail identically — 'Consolidate test results' concluded failure: Process completed with exit code 1. — the base 'main' moved from 0ee43f0 (what the red run tested) to aaf43ad. This pull request was red because its BASE was; the base has moved since, and a re-run would test the old merge commit again. Validated: update-branch proposal, and the red is the base's, not this diff's: two open MeshWeaver pull requests (#5948, #5967) fail identically with the fingerprint "main :: 'Consolidate test results' concluded failure: process completed with exit code 1." — the same check, the same words, on both, an inherited base red (class base-red). The base 'main' moved from 0ee43f0 (what red run 37157422992 tested) to aaf43ad, so merging the base into this branch is what drains it; sibling #5852 carried the same fingerprint and its update-branch heal was executed earlier tonight. This decision merges th… It does not merge the pull request, push anything else or dequeue. A red after this is left for the owner (rbuergi). |
Row-scoped actions for the B3 settings tabs (#5955): the per-row buttons that data-binding B3 part 2 (#5947) had to downgrade to "select the row, then press the button below the grid" are back — Archive on each Inbox mail, Revoke on each invitation, Rotate and Revoke on each service-identity token.
What changed
InboxSettingsTab.MailGrid,InvitationsSettingsTab.InvitationGrid,ServiceIdentitiesSettingsTab.TokenGrid(the template half, split from the feeds and taking the action, so the row routing is testable): each action is ONE button in aTemplateColumnControl, and its click reads the row withctx.RowAs<TRow>()— the row as the person saw it, never re-read by position. A click with no row does nothing./dataslots, the bound selection labels, the grid-levelDataGridCellClickhandlers, theDisplay/Nonerow members, and the "select a row first" hints.inbox.alreadyArchived,invitations.notPending,serviceIdentities.tokenRevoked, en + de) replace the three hint keys, which feat(settings): seven settings tabs are templates — fed grids and markdown (data-binding B3, part 2) #5947 added and nothing else uses.#5948(coupon / registration-key / instance-grant lists) checked: its only downgraded per-row action is the coupon Open button, which feat(plugincatalog): the coupon list's per-row Open button is back — a row-scoped action (reference conversion) #5956 restores; the registration-key and instance-grant revoke buttons are forms keyed by id and were not downgraded.Evidence
test/Memex.Portal.Shared.Test/SettingsRowActionsActOnTheClickedRowTest(12/12, a theory over the four buttons): row k's button acts on row k for every k; the list changes between render and click (rows inserted above, the clicked one gone) and the click still acts on the clicked row; negative control — the same click with no row runs nothing.LocalizationTest(66/66),LayoutAreaDataBakeRatchetGuard+DocumentationLinkIntegrityTest(4/4). None of the three tabs is inLayoutAreaDataBakeSites.allow, so the budget is unchanged.-warnaserrorclean (0 warnings, 0 errors): Memex.Portal.Shared, Memex.Portal.Shared.Test, MeshWeaver.Documentation.Test, MeshWeaver.Messaging.Hub.Test.Deploy
Compiled portal code; no node recycle beyond the roll. The Blazor half (Systemorph/MeshWeaver.Plugins#2656) must be live for the browser to send the row; until then a click acts on nothing (the negative-control behaviour), which is why this lands after both.
Pairs-with: none — no public type or member removed (the changed members are
internal).Implementers: none — no interface member added.
Mirror-sync: tracked with #5947's own catalog sync — this replaces three keys #5947 adds (never on
main) with three new ones; after the chain merges,npm run sync:i18n -- --ref <merged core sha>in MeshWeaver.Plugins picks up both.🤖 Generated with Claude Code