aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-27 23:11:01 -0700
committerAdam Soltys <[email protected]>2026-08-27 23:11:01 -0700
commit3a0f02b7624d72e73a4f92b635f5335ffd29f972 (patch)
tree436fa37b5cee769507b703de2352c52c834472fe /src
parentff9b87bb48130f638249412848c69b2d4a2a770a (diff)
downloadcoinosv3-3a0f02b7624d72e73a4f92b635f5335ffd29f972.tar.gz
coinosv3-3a0f02b7624d72e73a4f92b635f5335ffd29f972.zip
The face arrives with the key: sign-in warms the identity's profile and preloads the avatar bytes in parallel with the seed work, so the home screen's first paint already wears the picture instead of a blank circle
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
Diffstat (limited to 'src')
-rw-r--r--src/features/messages.js11
-rw-r--r--src/features/nostrlogin.js3
2 files changed, 14 insertions, 0 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index c3ca466..3c03ce0 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -202,6 +202,11 @@ export function messagesFeature(ctx) {
}
wallet.saveFeatureState('profiles', s);
}
+ // Pull the picture bytes into the HTTP cache the moment we learn the URL —
+ // an avatar div then paints instantly instead of holding its quiet circle
+ // while the image downloads.
+ const preloadPicture = (p) => { try { if (p && p.picture) new Image().src = p.picture; } catch {} };
+
function profileOf(pk) {
warmProfiles();
const cur = profiles.get(pk);
@@ -211,6 +216,7 @@ export function messagesFeature(ctx) {
const entry = { ...(p || {}), t: Date.now() };
profiles.set(pk, entry);
persistProfile(pk, entry);
+ preloadPicture(entry);
scheduleRepaint();
}).catch(() => profiles.set(pk, { t: Date.now() }));
return cur || null;
@@ -1207,6 +1213,7 @@ export function messagesFeature(ctx) {
};
profiles.set(pk, entry);
persistProfile(pk, entry);
+ preloadPicture(entry);
if (ui.profilePk === pk) render();
else scheduleRepaint();
}).catch(() => { if (!fullProfiles.has(pk)) fullProfiles.set(pk, {}); });
@@ -2445,6 +2452,10 @@ export function messagesFeature(ctx) {
// The light profile cache, read-only — lets the onboarding wizard skip
// asks (like the avatar picker) that a loaded identity already answered.
cachedProfile(pk) { return profileOf(pk); },
+ // Start fetching a profile (and its picture bytes) NOW — the login flow
+ // calls this the moment it knows the identity, so the avatar is already
+ // in cache by the time the home screen first paints.
+ warmProfile(pk) { try { prefetchProfilePage(pk); profileOf(pk); } catch {} return true; },
// Publish (merge) kind-0 fields for the current identity — the onboarding
// wizard sets name + picture through this.
publishProfile(fields, opts = {}) { return publishProfileFields(fields, opts); },
diff --git a/src/features/nostrlogin.js b/src/features/nostrlogin.js
index a084dd1..687a389 100644
--- a/src/features/nostrlogin.js
+++ b/src/features/nostrlogin.js
@@ -85,6 +85,9 @@ export function nostrLoginFeature(ctx) {
let signer;
try {
signer = await makeSigner();
+ // warm the identity's profile + avatar in parallel with the seed work,
+ // so the home screen's first paint already has the picture
+ ctx.hook('warmProfile', signer.pubkey);
const res = await walletForSigner(signer);
if (res.mode === 'new') {
// No wallet on this identity yet — make one and walk straight in.