diff options
| author | Adam Soltys <[email protected]> | 2026-08-02 23:48:31 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-02 23:48:31 -0700 |
| commit | 4b191f4eb8ad519e982f6667f2b540cdd77e539a (patch) | |
| tree | 07c409c93e0e0d99df249ad40bf95da67ed99eb6 /src/features | |
| parent | 02d60eb824cad0b8c09cb5685a6c2dbe02e16c4f (diff) | |
| download | coinosv3-4b191f4eb8ad519e982f6667f2b540cdd77e539a.tar.gz coinosv3-4b191f4eb8ad519e982f6667f2b540cdd77e539a.zip | |
First-run onboarding: a five-step wizard and a coach-mark tour
Fresh devices land in a full-screen flow instead of the unlock screen:
welcome → "do you have a nostr account?" (sign in brings wallet, name,
and profile along; No mints a wallet on the spot) → claim a username
(the background npub-default only surfaces a Keep button; a custom
claim — or a restored login's recovered name — advances by itself) →
pick a punk or upload a photo (nostr.build), published as the kind-0
name+picture → confetti. "Enter coinos" drops into a three-stop tour:
the avatar menu, the Spending/Savings toggle, and the tabs with the
swipe tip — each highlighted by a measured gold ring over a dimmed
page. One time only (localStorage flag), with an "I already have a
wallet" escape to the classic screen.
New seams: names.namesClaimForm, messages.publishProfile.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
Diffstat (limited to 'src/features')
| -rw-r--r-- | src/features/messages.js | 22 | ||||
| -rw-r--r-- | src/features/names.js | 2 |
2 files changed, 24 insertions, 0 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 6ff94a4..46f7399 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -1356,6 +1356,28 @@ export function messagesFeature(ctx) { // Anyone (ark's history, other features) can open a profile or render a // small clickable identity chip. showProfile(pk) { openProfile(pk); return true; }, + // Publish (merge) kind-0 fields for the current identity — the onboarding + // wizard sets name + picture through this. + async publishProfile(fields) { + const id = await identity(); + if (!id) throw new Error(t('msgNoIdentity')); + const evs = await queryOn([...new Set([...PROFILE_RELAYS, ...DM_RELAYS])], { kinds: [0], authors: [id.pubkey] }, 3000); + const newest = evs.sort((a, b) => b.created_at - a.created_at)[0]; + let base = {}; + try { base = newest ? JSON.parse(newest.content) : {}; } catch {} + const merged = { ...base }; + for (const [k, v] of Object.entries(fields || {})) { + if (v) merged[k] = v; + } + if (merged.name) merged.display_name = merged.name; + const partial = { kind: 0, content: JSON.stringify(merged), tags: [], created_at: Math.floor(Date.now() / 1000) }; + const evt = id.signer instanceof Uint8Array ? finalizeEvent(partial, id.signer) : await id.signer.signEvent(partial); + const ok = await publishOn([...new Set([...PROFILE_RELAYS, ...DM_RELAYS])], evt); + if (!ok) throw new Error(t('msgSendFailed')); + fullProfiles.set(id.pubkey, merged); + profiles.set(id.pubkey, { name: merged.name || null, picture: merged.picture || null, t: Date.now() }); + return true; + }, profileChip(pk) { return h('span', { class: 'zap-chip', diff --git a/src/features/names.js b/src/features/names.js index 3952214..a30038e 100644 --- a/src/features/names.js +++ b/src/features/names.js @@ -310,6 +310,8 @@ export function namesFeature(ctx) { namesAdoptIdentity(signer, npub) { return adoptIdentity(signer, npub); }, // the claimed payment address, for anyone prefilling a lightning address namesAddress() { const st = load(); return st.name ? `${st.name}@${st.domain || DOMAIN}` : null; }, + // the onboarding wizard renders the same claim form on its username step + namesClaimForm() { return claimForm(true); }, stop() { clearTimeout(retryTimer); clearTimeout(deadline); deadline = null; }, receiveModes() { if (!available()) return []; |
