From c124a845ea1ed2d755032eb4de09eccace88cd12 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 4 Aug 2026 02:06:58 -0700 Subject: The wizard offers a profile, it doesn't overwrite one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finishing the avatar step published name = the payment-address name and the default punk under the user's real nostr identity — renaming (and re-facing) profiles that already existed. Wizard-suggested fields are fill-only now: they land only where the profile has nothing. Co-Authored-By: Claude Fable 5 --- src/app.js | 5 ++++- src/features/messages.js | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/app.js b/src/app.js index 1d08ac3..24cc56c 100644 --- a/src/app.js +++ b/src/app.js @@ -2311,8 +2311,11 @@ function onboardScreen() { const fields = { name: addr ? addr.split('@')[0] : undefined, picture }; // Publishing the profile is a nicety — a signer that can't sign // right now (a sleeping phone bunker) must not trap the wizard. + // And it's an OFFER: the address-derived name (and, unless they + // actually picked one, the default punk) only fills gaps — it + // never renames an identity that already has a profile. if (fields.name || fields.picture) { - try { await featureHook('publishProfile', fields); } + try { await featureHook('publishProfile', fields, { fillOnly: o.avatar ? ['name'] : ['name', 'picture'] }); } catch (e) { toast(t('onbProfileSkipped')); console.warn('onboarding: profile publish failed —', e.message); } } o.step = 'success'; diff --git a/src/features/messages.js b/src/features/messages.js index 65a71a8..2782b4a 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -1705,7 +1705,7 @@ export function messagesFeature(ctx) { 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) { + async publishProfile(fields, opts = {}) { 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); @@ -1713,10 +1713,18 @@ export function messagesFeature(ctx) { let base = {}; try { base = newest ? JSON.parse(newest.content) : {}; } catch {} const merged = { ...base }; + // fillOnly fields are offers, not orders: the onboarding wizard suggests + // a name and a punk for profiles that have none — it must never RENAME + // (or re-face) an identity that already has one. + const fill = new Set(opts.fillOnly || []); + const taken = (k) => (k === 'name' ? !!(base.name || base.display_name) : !!base[k]); for (const [k, v] of Object.entries(fields || {})) { - if (v) merged[k] = v; + if (!v) continue; + if (fill.has(k) && taken(k)) continue; + merged[k] = v; } - if (merged.name) merged.display_name = merged.name; + if (merged.name && !(fill.has('name') && taken('name'))) merged.display_name = merged.name; + if (JSON.stringify(merged) === JSON.stringify(base)) return true; // nothing to say 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); -- cgit v1.2.3