diff options
| author | Adam Soltys <[email protected]> | 2026-09-02 19:57:46 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-02 19:57:57 -0700 |
| commit | 52b411947bd6c61cf50c60a27249abeedc26c102 (patch) | |
| tree | 413434542222d4f1af2443b45c461604340823e4 /src/features | |
| parent | 1326188e0ffe08398943da2ef051b5553427d1a9 (diff) | |
| download | coinosv3-52b411947bd6c61cf50c60a27249abeedc26c102.tar.gz coinosv3-52b411947bd6c61cf50c60a27249abeedc26c102.zip | |
History becomes home: the tab bar slims to Receive | Send (tapping the lit one puts it away), every landing — login, account creation, an account snapped into the carousel — is the account's own timeline, and that timeline is now SCOPED: Savings shows the on-chain ledger and gift reservations, Spending shows the ark side. The carousel meanwhile learns mouse and mind-reading: desktop gets a real grab-and-pull drag (snap lifted mid-drag, restored on the glide), the peeking neighbor switches on a single click, and the strip wears grab cursors. And a thread's avatars and author names finally go somewhere: profiles stack on top of the conversation and back returns to it
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
Diffstat (limited to 'src/features')
| -rw-r--r-- | src/features/ark.js | 3 | ||||
| -rw-r--r-- | src/features/gifts.js | 5 | ||||
| -rw-r--r-- | src/features/messages.js | 16 |
3 files changed, 20 insertions, 4 deletions
diff --git a/src/features/ark.js b/src/features/ark.js index c3a8a8c..1145983 100644 --- a/src/features/ark.js +++ b/src/features/ark.js @@ -2920,7 +2920,8 @@ export function arkFeature(ctx) { render(); } }, t('arkExitInFlightLink', { n: fmtAmount(acts.reduce((s2, a) => s2 + a.amountSat, 0)) })); }, - historyEntries() { + historyEntries(_txs, sel) { + if (sel && sel !== 'spending') return []; // ark movements are the Spending timeline const s = arkStateNow(); if (!s) return []; // Memoized briefly: this rebuilds, sorts, and dedupes the whole diff --git a/src/features/gifts.js b/src/features/gifts.js index a73bfe9..12dd735 100644 --- a/src/features/gifts.js +++ b/src/features/gifts.js @@ -895,7 +895,10 @@ export function giftsFeature(ctx) { const locked = wallet.giftLockedValue(); return locked > 0 ? [{ label: t('lockedInGifts'), sat: locked }] : []; }, - historyEntries(txs) { + historyEntries(txs, sel) { + // These are ON-CHAIN gift reservations — Savings timeline. Ark gifts show + // up as ark send movements on the Spending side. + if (sel === 'spending') return []; // Outstanding sent gifts (reserved/reclaimed but unclaimed) sit above the // on-chain history; they aren't transactions until claimed or revoked. // A claimed gift merges into its claim transaction's row once we know which diff --git a/src/features/messages.js b/src/features/messages.js index 1daa5bc..ac43e40 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -1444,6 +1444,11 @@ export function messagesFeature(ctx) { function openProfile(pk) { ui.profilePk = pk; + // Opened from inside a thread (an author's avatar/name), the profile + // stacks ON TOP of it — back returns to the conversation. The screen + // router otherwise keeps a thread above the profile, which is the order + // for the other direction (a note row tapped on a profile page). + ui.profOverThread = !!ui.noteThread; ui.profEdit = null; ui.profEditFilled = false; ui.logoutConfirm = null; render(); fetchFullProfile(pk); @@ -1599,7 +1604,11 @@ export function messagesFeature(ctx) { h('div', { class: 'col grow', style: 'min-width:0;gap:3px' }, h('div', { class: 'row between', style: 'align-items:center;gap:8px' }, h('div', { class: 'row', style: 'gap:7px;align-items:baseline;min-width:0' }, - h('span', { style: 'font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis' }, name), + h('span', { + // the name, like the avatar, is its own tap-target (profile) + style: 'font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer', + onClick: (e) => { e.stopPropagation(); openProfile(pk); }, + }, name), h('span', { class: 'small faint', style: 'white-space:nowrap' }, (isReply ? '↩ ' + t('profReplyTag') + ' · ' : '') + timeLabel(ev.created_at * 1000))), canZap ? h('button', { class: 'btn-sm', title: t('zapTitle'), onClick: (e) => { e.stopPropagation(); zapNote(pk, ev); } }, '⚡') : null), @@ -1643,6 +1652,7 @@ export function messagesFeature(ctx) { } function openNoteThread(ev) { ui.noteThread = { rootId: rootIdOf(ev), focusId: ev.id, seed: ev }; + ui.profOverThread = false; // a freshly opened thread goes on top render(); } // A nostr:nevent / nostr:note reference: the thread loader needs the real @@ -2042,7 +2052,7 @@ export function messagesFeature(ctx) { noteRow(pk, ev, name), ]))); })(), - h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.profilePk = null; ui.pubProf = null; ui.profEdit = null; ui.profEditFilled = false; render(); } }, t('back')), + h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.profilePk = null; ui.profOverThread = false; ui.pubProf = null; ui.profEdit = null; ui.profEditFilled = false; render(); } }, t('back')), mine ? logoutPop() : null); } @@ -2756,12 +2766,14 @@ export function messagesFeature(ctx) { // threads reachable from it, are public nostr content — shown // without an account. Back falls through to the app's own screens. if (ui.pubProf) { + if (ui.profOverThread && ui.profilePk) return profileScreen(); if (ui.noteThread) return threadScreen(); if (ui.profilePk) return profileScreen(); } return null; } if (ui.zapSetup) return zapSetupScreen(); + if (ui.profOverThread && ui.profilePk) return profileScreen(); if (ui.noteThread) return threadScreen(); if (ui.profilePk) return profileScreen(); if (ui.userSearch && !ui.chatOpen) return userSearchScreen(); |
