From fc2fe931617033465477b02c336f4de1663fcf09 Mon Sep 17 00:00:00 2001 From: Avi Date: Fri, 25 Sep 2026 16:30:18 -0500 Subject: [PATCH] =?UTF-8?q?feat(ui):=20drop=20the=20pairing=20label=20step?= =?UTF-8?q?=20=E2=80=94=20profile=20names=20come=20from=20the=20account?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sep 25 feedback: the prefilled 'Amber' name had to be cleared letter by letter before the QR would appear (the input fight), and naming should be automatic anyway. One click on 'Sign in with a signer app (Amber)' now goes straight to the QR with the seed label 'Amber'; the existing adopt_identity background enrichment fetches the account's kind-0 after the handshake and upgrades the profile row to the account's REAL display name whenever it still carries our seed (a manual rename in Profiles always wins and is never overwritten). frontend: 125 tests green (pairing test asserts the one-click path and the 'Amber' seed label), typecheck/lint/format/electron:build/build green. Rust untouched — the auto-naming enrichment already shipped at a76d8df. --- frontend/src/screens/CreateProfileModal.tsx | 63 +++---------------- frontend/src/test/CreateProfileModal.test.tsx | 12 ++-- 2 files changed, 13 insertions(+), 62 deletions(-) diff --git a/frontend/src/screens/CreateProfileModal.tsx b/frontend/src/screens/CreateProfileModal.tsx index 30347d7..7c259e5 100644 --- a/frontend/src/screens/CreateProfileModal.tsx +++ b/frontend/src/screens/CreateProfileModal.tsx @@ -12,7 +12,7 @@ interface CreateProfileModalProps { onClose: () => void; } -type Phase = 'choice' | 'pair-label' | 'pairing' | 'paired' | 'local' | 'creating' | 'success'; +type Phase = 'choice' | 'pairing' | 'paired' | 'local' | 'creating' | 'success'; export function CreateProfileModal({ open, onClose }: CreateProfileModalProps) { const { state, createProfile, nip46PairStart, nip46Status, nip46Disconnect, refresh } = useApp(); @@ -39,7 +39,7 @@ export function CreateProfileModal({ open, onClose }: CreateProfileModalProps) { // Let the local-form input take focus once that step mounts. useEffect(() => { - if (phase === 'local' || phase === 'pair-label') { + if (phase === 'local') { const frame = requestAnimationFrame(() => inputRef.current?.focus()); return () => cancelAnimationFrame(frame); } @@ -133,6 +133,12 @@ export function CreateProfileModal({ open, onClose }: CreateProfileModalProps) { const startPairing = async () => { setError(null); try { + // No user-typed label: the profile is named automatically. The + // backend fetches the account's kind-0 after the handshake and + // upgrades this seed label to the account's real display name + // (adopt_identity background enrichment); a nameless account keeps + // 'Amber', which beats a manual step the user must fight with a + // backspace key (Sep 25 feedback). const status = await nip46PairStart(label.trim() || 'Amber'); if (status.pairing_uri) setLivePairingUri(status.pairing_uri); setPhase('pairing'); @@ -170,13 +176,7 @@ export function CreateProfileModal({ open, onClose }: CreateProfileModalProps) { {error && {error}}
- @@ -196,51 +196,6 @@ export function CreateProfileModal({ open, onClose }: CreateProfileModalProps) { ); } - if (phase === 'pair-label') { - return ( - -
{ - event.preventDefault(); - void startPairing(); - }} - noValidate - > -
-

- Name this connection so you can tell it apart later — the profile it creates gets this - name, and Amber will show it when asking you to approve signatures. -

-
-
- - setLabel(event.target.value)} - placeholder="e.g. Amber" - maxLength={60} - aria-describedby={error ? errorId : undefined} - aria-invalid={error ? true : undefined} - autoComplete="off" - /> - {error && {error}} -
-
- - -
-
-
- ); - } - if (phase === 'pairing') { return ( diff --git a/frontend/src/test/CreateProfileModal.test.tsx b/frontend/src/test/CreateProfileModal.test.tsx index 98b11f6..da456cb 100644 --- a/frontend/src/test/CreateProfileModal.test.tsx +++ b/frontend/src/test/CreateProfileModal.test.tsx @@ -11,7 +11,6 @@ vi.mock('qrcode', () => ({ async function startPairingFlow(user: ReturnType) { await user.click(screen.getByRole('button', { name: /Sign in with a signer app \(Amber\)/ })); - await user.click(screen.getByRole('button', { name: 'Show QR code' })); } describe('CreateProfileModal', () => { @@ -93,17 +92,14 @@ describe('CreateProfileModal', () => { renderWithApp(); const user = userEvent.setup(); + // No label step (Sep 25 feedback: typing/fighting a prefilled name was + // friction). One click mints the QR with the 'Amber' seed label; the + // backend upgrades it to the account's real kind-0 display name. await user.click(screen.getByRole('button', { name: /Sign in with a signer app \(Amber\)/ })); - // The label step comes first and is prefilled so Enter just works. - const nameInput = await screen.findByLabelText('Connection name'); - expect(nameInput).toHaveValue('Amber'); - await user.clear(nameInput); - await user.type(nameInput, 'My Phone'); - await user.click(screen.getByRole('button', { name: 'Show QR code' })); expect(await screen.findByText(/Waiting for the signer to scan/i)).toBeInTheDocument(); const start = backend.requests.find((r) => r.method === 'nip46_pair_start'); - expect(start?.params.label).toBe('My Phone'); + expect(start?.params.label).toBe('Amber'); expect(await screen.findByAltText('Pairing QR code')).toBeInTheDocument(); });