aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-02 19:57:46 -0700
committerAdam Soltys <[email protected]>2026-09-02 19:57:57 -0700
commit52b411947bd6c61cf50c60a27249abeedc26c102 (patch)
tree413434542222d4f1af2443b45c461604340823e4 /src/features
parent1326188e0ffe08398943da2ef051b5553427d1a9 (diff)
downloadcoinosv3-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.js3
-rw-r--r--src/features/gifts.js5
-rw-r--r--src/features/messages.js16
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();