aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-03 07:42:24 -0700
committerAdam Soltys <[email protected]>2026-08-03 07:42:24 -0700
commit161adb250e4273d0bcaa160516bc950f5f46310f (patch)
treea845b83adb75e33847f709c0d7d3e5ec3ee2ca95 /src
parent848958e450a5b5aac95a2fcd981fd229da1d6190 (diff)
downloadcoinosv3-161adb250e4273d0bcaa160516bc950f5f46310f.tar.gz
coinosv3-161adb250e4273d0bcaa160516bc950f5f46310f.zip
Self-host the punks; skipping avatar still publishes your punk
coinos.io serves 502 for a third of the punk set (both the numbered and hash endpoints), which is why a default avatar could come up blank. The 64 webps now ship with the app (53 salvaged, 11 gaps filled with donors) and every reference is same-origin. Skipping the avatar step publishes the deterministic punk as the kind-0 picture — your face is the same on every nostr client. Success screen: checkmark now sits below the title, bigger. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
Diffstat (limited to 'src')
-rw-r--r--src/app.js13
-rw-r--r--src/recipient-search.js4
-rw-r--r--src/style.css1
3 files changed, 12 insertions, 6 deletions
diff --git a/src/app.js b/src/app.js
index 562ca99..36fb0d2 100644
--- a/src/app.js
+++ b/src/app.js
@@ -6,7 +6,7 @@
import { Wallet, newMnemonic, isValidMnemonic, accountXpubFor, cacheKeyFor, utxoId, parseExtendedKey, xpubToZpub, encryptVault, decryptVault } from './wallet.js';
import { qrSvg } from './qr.js';
-import { makeSearcher, resultRows, searchable } from './recipient-search.js';
+import { makeSearcher, resultRows, searchable, punkUrl } from './recipient-search.js';
import { npubOf } from './nostr.js';
import { scanQr } from './scan.js';
import { dataSources, getSource, setSource, getNetwork, setNetwork, NETWORKS } from './api.js';
@@ -2007,7 +2007,7 @@ function shouldOnboard() {
}
const PUNK_PICKS = [7, 14, 21, 3, 33, 40, 47, 36, 61, 26, 12, 50];
-const punkImg = (n) => `https://coinos.io/api/public/punks/${n}.webp`;
+const punkImg = (n) => `punks/${n}.webp`;
async function onbUpload(file) {
const fd = new FormData();
@@ -2110,7 +2110,10 @@ function onboardScreen() {
ui.onbBusy = true; ui.onbError = ''; render();
try {
const addr = featureHook('namesAddress');
- const fields = { name: addr ? addr.split('@')[0] : undefined, picture: o.avatar || undefined };
+ // keeping the default still publishes it — their punk should be
+ // their face on every nostr client, not just here
+ const picture = o.avatar || (me ? 'https://v3.coinos.io/' + punkUrl(me) : undefined);
+ const fields = { name: addr ? addr.split('@')[0] : undefined, picture };
if (fields.name || fields.picture) await featureHook('publishProfile', fields);
o.step = 'success';
} catch (e) { ui.onbError = e.message; }
@@ -2121,8 +2124,8 @@ function onboardScreen() {
// success
return page([
h('div', { class: 'onb-confetti' }, ...Array.from({ length: 18 }, () => h('i'))),
- h('div', { class: 'check-badge', style: 'align-self:center' }, '✓'),
- title(t('onbDoneTitle')),
+ h('h2', { class: 'onb-title', style: 'text-align:center' }, t('onbDoneTitle')),
+ h('div', { class: 'check-badge onb-check' }, '✓'),
h('p', { class: 'muted', style: 'margin:0;text-align:center' }, t('onbDoneBody')),
h('button', { class: 'btn-primary btn-block', style: 'padding:14px', onClick: () => {
try { localStorage.setItem(ONBOARDED_KEY, '1'); } catch {}
diff --git a/src/recipient-search.js b/src/recipient-search.js
index 2594488..553673e 100644
--- a/src/recipient-search.js
+++ b/src/recipient-search.js
@@ -13,8 +13,10 @@ import { queryOn, parseNostrPubkey, npubOf, PROFILE_RELAYS } from './nostr.js';
// coinos punks: the deterministic default avatar — same formula the coinos
// app uses (last byte of the pubkey picks one of 64). Initials show through
// if the image can't load (offline).
+// Self-hosted (dist/punks) — coinos.io's own copies 502 for a third of the
+// set. Same deterministic formula the coinos app uses.
export const punkUrl = (pk) =>
- `https://coinos.io/api/public/punks/${Math.floor((parseInt(pk.slice(-2), 16) / 256) * 64) + 1}.webp`;
+ `punks/${Math.floor((parseInt(pk.slice(-2), 16) / 256) * 64) + 1}.webp`;
export const fallbackAvatar = (h, pk, name, cls) =>
h('div', { class: cls + ' fallback' },
diff --git a/src/style.css b/src/style.css
index 3dbeaaa..562db92 100644
--- a/src/style.css
+++ b/src/style.css
@@ -506,3 +506,4 @@ body::after {
.tour-overlay { background: transparent; }
.tour-card { position: fixed; left: 50%; transform: translateX(-50%); width: min(90vw, 360px); background: var(--paper); color: var(--ink); border: 1px solid var(--line-strong); border-radius: 14px; padding: 14px 16px; box-shadow: var(--shadow); z-index: 81; }
@media (prefers-reduced-motion: reduce) { .onb-confetti i { animation: none; display: none; } }
+.onb-check { width: 96px; height: 96px; font-size: 52px; align-self: center; margin: 6px 0; }