Skip to content

feat(web): sending-access status line in Settings - #1057

Merged
jiashuoz merged 1 commit into
mainfrom
feat/settings-sending-access-status
Sep 28, 2026
Merged

jiashuoz merged 1 commit into
mainfrom
feat/settings-sending-access-status

Conversation

@jiashuoz

Copy link
Copy Markdown
Member

Summary

Since v1.13.0 (#1052), a restricted account only learns about /sending-access from the Inboxes banner or a blocked send's recovery_url. Once an operator approves the account (or a request is declined), that banner disappears and there's no navigation back to the page. This adds a one-line "External sending" status to the Settings page's account facts (dt/dd list) that always links back to /sending-access, so the state is discoverable regardless of banner visibility.

What

  • web/src/lib/sendingAccess.ts: two new pure helpers, reusing the existing isSendingRestricted rule and the same route precedence as sendingAccessEnabledRoute.
    • sendingAccessEnabledReason(status) — short reason ("operator approved" / "paid plan") for the enabled case. A verified custom domain unlocks sending per-message (internal/sendingpolicy's RouteCustomIdentity), never as an account-wide grant, so it has no field on sending_access and never produces a reason.
    • sendingAccessSettingsSummary(status, request) — the full one-line value: Restricted / Restricted — request under review / Restricted — request declined / Enabled / Enabled — <reason>. Returns null when the deployment has no sending_access object at all (self-host, feature disabled), so Settings renders no row rather than a "not restricted" line.
  • web/src/app/(app)/settings/page.tsx: new SendingAccessRow component in the Profile section's existing dl, matching the row style of the neighboring Email/User ID/Member since facts. Reads the account's sending_access (via the existing useSendingAccess hook) and the latest request (getSendingAccessRequest, same SWR key the /sending-access page uses), and waits for both to settle before rendering — no flash of "Restricted" before the request state (pending vs. declined) is known, and no row shown at all if the object is absent or a read errors.

States rendered

  • Deployment has no sending_access object (self-host, feature off): no row at all.
  • Restricted, no request on file: "Restricted".
  • Restricted, pending request: "Restricted — request under review".
  • Restricted, latest request declined: "Restricted — request declined".
  • Not restricted via operator grant: "Enabled — operator approved".
  • Not restricted via the paid entitlement: "Enabled — paid plan".

Each value is a link to /sending-access.

Test plan

  • Added describe("Settings — Sending access row", ...) to web/src/app/(app)/settings/page.test.tsx covering all 6 states above, mocking GET /v1/account and GET /v1/account/sending-access/request the same way sending-access/page.test.tsx does.
  • Added sendingAccessEnabledReason / sendingAccessSettingsSummary cases to web/src/lib/sendingAccess.test.ts.
  • Updated page.profile-edit.test.tsx and an existing delete-account call-count assertion in page.test.tsx for the row's two new background reads on mount (switched both to the SWR-aware test-utils/swr render helper).
  • cd web && npm run lint — clean
  • cd web && npx tsc --noEmit — clean
  • cd web && npm test -- --runTestsByPath src/app/\(app\)/settings/page.test.tsx src/app/\(app\)/settings/page.profile-edit.test.tsx src/lib/sendingAccess.test.ts src/app/\(app\)/sending-access/page.test.tsx — 4 suites, 87 tests passed
  • cd web && npm test — full suite, 123 suites / 1094 tests passed
  • cd web && npm run build — static export succeeds, /settings and /sending-access both prerender

No OpenAPI, SDK, MCP, or Go changes — this only consumes the existing GET /v1/account and GET /v1/account/sending-access/request endpoints, so the "Client surface checklist" doesn't apply.

🤖 Generated with Claude Code

https://claude.ai/code/session_018tVLxUHk3fqQuq8C3wqyHW

Once an account is approved for external sending, the /sending-access
banner on Inboxes disappears, and a declined account had no navigation
back to the page. Add a one-line "External sending" status to Settings
(reusing the account's sending_access status + latest request, and the
existing lib/sendingAccess.ts helpers) that always links back to
/sending-access: Restricted, Restricted — request under review,
Restricted — request declined, Enabled — operator approved, or
Enabled — paid plan. Renders no row on deployments without the
sending_access object (self-host, feature disabled), and waits for
both reads to settle before rendering to avoid a flash of stale state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tVLxUHk3fqQuq8C3wqyHW
@jiashuoz
jiashuoz merged commit 782d14d into main Sep 28, 2026
29 checks passed
@jiashuoz
jiashuoz deleted the feat/settings-sending-access-status branch September 28, 2026 03:25
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