diff --git a/portal/app/account/ChangeHandleModal.tsx b/portal/app/account/ChangeHandleModal.tsx index d149426..743b8fb 100644 --- a/portal/app/account/ChangeHandleModal.tsx +++ b/portal/app/account/ChangeHandleModal.tsx @@ -36,8 +36,8 @@ export default function ChangeHandleModal({ currentHandle, token, onClose, onSuc try { await updateHandle(trimmed, token); } catch (err) { - if (err instanceof ApiError && err.status === 402) { - setError("Custom handles require Pro or above."); + if (err instanceof ApiError && err.status === 403) { + setError("Verify your email first, then choose your handle."); } else if (err instanceof ApiError && err.status === 409) { setError("That handle is taken."); } else if (err instanceof ApiError && err.status === 422) { diff --git a/portal/app/account/page.tsx b/portal/app/account/page.tsx index 2908d6e..ff55510 100644 --- a/portal/app/account/page.tsx +++ b/portal/app/account/page.tsx @@ -194,6 +194,10 @@ export default function AccountPage() { setAccountId(me.account_id); setAccountEmail(me.email); setAccountHandle(me.handle); + // Authoritative over the JWT claim above: the token predates a + // verification the user may have completed since it was issued, and + // this value now decides whether the handle control is offered. + setEmailVerified(me.email_verified); } catch { /* non-critical */ } // Trial expired modal @@ -442,12 +446,13 @@ export default function AccountPage() {

Handle

{accountHandle ? `@${accountHandle}` : "—"}

- {displayTier === "free" && ( -
-

Custom handles are a Pro feature.

-

Others can still reach you at this handle.

-
- )} +
+

A custom handle makes you findable by people outside your teams.

+

Your current handle already works for anyone you give it to.

+ {emailVerified === false && ( +

Verify your email to choose a custom handle.

+ )} +
{accountHandle && ( @@ -458,14 +463,15 @@ export default function AccountPage() { {handleCopied ? "Copied" : "Copy"} )} - {displayTier != null && displayTier !== "free" && ( - - )} + {/* Disabled with the reason rather than hidden — hiding it is + what made the old tier gate unreadable. */} +
{/* Email + password row */}