aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
Diffstat (limited to 'src/features')
-rw-r--r--src/features/names.js106
1 files changed, 73 insertions, 33 deletions
diff --git a/src/features/names.js b/src/features/names.js
index 45b0b36..d57bb19 100644
--- a/src/features/names.js
+++ b/src/features/names.js
@@ -455,29 +455,35 @@ export function namesFeature(ctx) {
// said as it's typed, not after a round trip (the registrar is the judge:
// a name claimed before the rule can still be retaken by its owner)
nameRuleHint(ui.nameClaim) ? h('div', { class: 'small', style: 'color:var(--red,#c0392b)' }, t('namesRule')) : null,
- h('button', { class: 'btn-primary btn-block', disabled: ui.busy, onClick: async () => {
- const name = (ui.nameClaim || '').toLowerCase().trim();
- if (!name) return;
- ui.busy = true; ui.nameClaimError = null; ui.nameClaimMigration = null; render();
- try {
- // Sign with the login identity when one can be (re)attached: the
- // registrar lets a migrated coinos user's own identity take their
- // name back, which the wallet key alone could never prove.
- const signer = await Promise.resolve(hook('nostrLoginResume')).catch(() => null);
- await claimRequestedName(name, signer ? { signer, manager: wallet.nostrPubkey() } : {});
- // the wizard's Spending step tells a name claimed here from one
- // the registrar handed back (an account set up before)
- if (ui.onb && ui.onb.step === 'spend') ui.onb.claimedHere = name;
- ui.nameClaim = '';
- ui.nameEditOpen = false;
- toast(t('namesClaimed', { name: `${name}@${DOMAIN()}` }));
- } catch (e) {
- ui.nameClaimError = e.message;
- if (ui.nameClaim === name) ui.nameClaimMigration = e.migrationName || null;
- }
- ui.busy = false; render();
- } }, ui.busy ? h('span', { class: 'spinner' }) : t('namesClaim')),
- ui.nameClaimMigration && ui.nameClaimMigration === ui.nameClaim && DOMAIN() === 'coinos.io'
+ h('button', { class: 'btn-primary btn-block', disabled: ui.busy, onClick: () => claimTyped(ui.nameClaim) }, ui.busy ? h('span', { class: 'spinner' }) : t('namesClaim')),
+ migrateLink());
+ }
+ async function claimTyped(typed) {
+ const name = (typed || '').toLowerCase().trim();
+ if (!name) return;
+ ui.busy = true; ui.nameClaimError = null; ui.nameClaimMigration = null; render();
+ try {
+ // Sign with the login identity when one can be (re)attached: the
+ // registrar lets a migrated coinos user's own identity take their
+ // name back, which the wallet key alone could never prove.
+ const signer = await Promise.resolve(hook('nostrLoginResume')).catch(() => null);
+ await claimRequestedName(name, signer ? { signer, manager: wallet.nostrPubkey() } : {});
+ // the wizard's Spending step tells a name claimed here from one
+ // the registrar handed back (an account set up before)
+ if (ui.onb && ui.onb.step === 'spend') ui.onb.claimedHere = name;
+ ui.nameClaim = '';
+ ui.nameEdit = null;
+ ui.nameEditOpen = false;
+ toast(t('namesClaimed', { name: `${name}@${DOMAIN()}` }));
+ } catch (e) {
+ ui.nameClaimError = e.message;
+ if ((ui.nameEdit ?? ui.nameClaim) === name) ui.nameClaimMigration = e.migrationName || null;
+ }
+ ui.busy = false; render();
+ }
+ // "I have a coinos account": a name the registrar holds for a legacy user
+ function migrateLink() {
+ return ui.nameClaimMigration && ui.nameClaimMigration === (ui.nameEdit ?? ui.nameClaim) && DOMAIN() === 'coinos.io'
? h('button', {
type: 'button', class: 'linklike small',
onClick: () => {
@@ -490,7 +496,48 @@ export function namesFeature(ctx) {
location.href = `https://coinos.io/migrate?to=${encodeURIComponent(addr)}&back=${encodeURIComponent(location.origin + '/')}`;
}
},
- }, t('onbHaveCoinos')) : null);
+ }, t('onbHaveCoinos')) : null;
+ }
+
+ // Settings: the address and changing it are one card — the name sits in
+ // its own field; typing a different one offers the change (and says the
+ // old address stops working), otherwise Copy and Release.
+ function addressCard(st) {
+ const domain = st.domain || DOMAIN();
+ const addr = `${st.name}@${domain}`;
+ const typed = ui.nameEdit ?? st.name;
+ const changed = !!typed && typed !== st.name;
+ return h('div', { class: 'card col', 'data-key': 'names' },
+ h('h3', {}, t('namesTitle')),
+ ui.nameClaimError ? h('div', { class: 'notice err' }, ui.nameClaimError) : null,
+ h('div', { class: 'row gap6 name-field' },
+ h('input', {
+ type: 'text', class: 'mono-input', placeholder: t('namesPlaceholder'), style: 'flex:1',
+ autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', 'aria-label': t('namesCustom'),
+ value: typed,
+ onInput: (e) => {
+ const was = changed;
+ ui.nameEdit = e.target.value.toLowerCase().trim();
+ ui.nameClaimError = null;
+ if (ui.nameClaimMigration) ui.nameClaimMigration = null;
+ if ((!!ui.nameEdit && ui.nameEdit !== st.name) !== was || nameRuleHint(ui.nameEdit)) render();
+ },
+ }),
+ h('span', { class: 'muted', style: 'align-self:center' }, '@' + domain)),
+ changed && nameRuleHint(typed) ? h('div', { class: 'small', style: 'color:var(--red,#c0392b)' }, t('namesRule')) : null,
+ changed
+ ? h('div', { class: 'col', style: 'gap:8px' },
+ h('p', { class: 'small muted', style: 'margin:0' }, t('namesCustomHow')),
+ h('div', { class: 'row gap6' },
+ h('button', { class: 'btn-ghost btn-sm', disabled: ui.busy, onClick: () => { ui.nameEdit = null; ui.nameClaimError = null; ui.nameClaimMigration = null; render(); } }, t('cancel')),
+ h('button', { class: 'btn-primary btn-sm grow', disabled: ui.busy, onClick: () => claimTyped(typed) },
+ ui.busy ? h('span', { class: 'spinner sm' }) : t('namesChangeTo', { addr: typed }))),
+ migrateLink())
+ : h('div', { class: 'row gap6' },
+ copyBtn(addr, t('namesCopy')),
+ h('button', { class: 'btn-ghost btn-sm', onClick: async () => {
+ await release(); toast(t('namesReleased')); render();
+ } }, t('namesRelease'))));
}
// Settings: one card per concern, all laid out (no collapsed sections —
@@ -519,14 +566,7 @@ export function namesFeature(ctx) {
h('div', { class: 'addr-box break', style: 'font-size:11px' },
`${st.name}.user._bitcoin-payment.yourdomain.com. CNAME ${st.name}.user._bitcoin-payment.${domain}.`)),
card('ownnode', t('namesOwnNode'), t('namesOwnNodeMenuHow'), ownNodeSection()),
- card('custom', t('namesCustom'), t('namesCustomHow'), claimForm(false)),
- card('names', t('namesTitle'), null,
- h('div', { class: 'addr-box break', style: 'font-size:14px' }, addr),
- h('div', { class: 'row gap6' },
- copyBtn(addr, t('namesCopy')),
- h('button', { class: 'btn-ghost btn-sm', onClick: async () => {
- await release(); toast(t('namesReleased')); render();
- } }, t('namesRelease')))),
+ addressCard(st),
].filter(Boolean);
}
// The terminal's cards live on the Point of sale settings page now — no
@@ -884,7 +924,7 @@ export function namesFeature(ctx) {
checked = false; lastError = null;
// The claim field is a draft for THIS account — a name typed before a
// logout used to greet the next account that opened on this device.
- ui.nameClaim = null; ui.nameClaimError = null; ui.nameClaimMigration = null; suggestedFor = null;
+ ui.nameClaim = null; ui.nameEdit = null; ui.nameClaimError = null; ui.nameClaimMigration = null; suggestedFor = null;
refresh();
},
// The pencil by the address opens this: choosing a name gets a page of