aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.js24
-rw-r--r--src/features/messages.js47
-rw-r--r--src/style.css13
3 files changed, 47 insertions, 37 deletions
diff --git a/src/app.js b/src/app.js
index 7dde883..5e3fba4 100644
--- a/src/app.js
+++ b/src/app.js
@@ -681,7 +681,11 @@ function renderInner() {
ui.navAnimSkip = false;
}
applyAnim(screen, 'anim-page', (performance.now() - _navAt) < 340 ? performance.now() - _navAt : -1);
- morphChildren(root, [screen, footer(), ...(ui.lightbox ? [imageViewer()] : [])]);
+ // The bottom nav rides every page of a signed-in wallet and a visitor's
+ // public pages — never over sign-in, onboarding or the lock prompt.
+ const navPage = ui.pubProf || (activeAccount() && ['wallet', 'accounts', 'accountSettings'].includes(ui.screen) && !(ui.onb || onbInProgress()));
+ const bottomNav = navPage && !lockAsk && !_bootDeciding ? featureHook('bottomNav') : null;
+ morphChildren(root, [screen, footer(), ...(bottomNav ? [bottomNav] : []), ...(ui.lightbox ? [imageViewer()] : [])]);
promoteLazySrc(root); // sources on the nodes that actually made it into the page
try { document.documentElement.classList.toggle('no-scroll', !!ui.lightbox); } catch {}
if (fpath) {
@@ -2488,23 +2492,6 @@ function lockBtn() {
}, h('span', { style: 'font-size:15px;line-height:1;margin-right:6px' }, locked ? '\u{1F512}' : '\u{1F513}'), locked ? t('unlock') : t('lockWallet'));
}
-// Messages sit one tap away, left of the wallet selector. The dot is presence,
-// not a count — it says "someone's waiting", and the chat list says who.
-function messagesBtn() {
- const me = (featureHook('nostrLoginIdentity') || {}).pubkey || (wallet.nostrPubkey && wallet.nostrPubkey());
- if (!me) return null;
- const unread = featureHook('unreadMessages') || 0;
- return h('button', {
- class: 'header-msgs' + (unread ? ' unread' : ''),
- title: t('msgDmsTitle'),
- 'aria-label': t('msgDmsTitle'),
- onClick: () => { clearFeatureNav(); ui.screen = 'wallet'; ui.chatOpen = true; ui.msgView = 'home'; render(); },
- }, h('span', {
- class: 'hm-ico',
- html: '<svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>',
- }));
-}
-
// Find anyone on nostr from the header — results open their profile.
function searchBtn() {
if (!featureHook('userSearchAvailable')) return null;
@@ -2539,7 +2526,6 @@ function brandHeader(withLock) {
withLock
? h('div', { class: 'row gap6', style: 'align-items:center' },
searchBtn(),
- messagesBtn(),
settingsBtn(),
avatarMenu())
: ui.pubProf && !acc ? visitorButtons() : null
diff --git a/src/features/messages.js b/src/features/messages.js
index 0b40a5a..7be5f26 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -7524,28 +7524,47 @@ export function messagesFeature(ctx) {
} catch {}
}, 0);
}
- function feedBottomNav() {
+ function appBottomNav() {
+ const visitor = isVisitor();
+ const active = ui.noteThread || ui.profilePk ? 'feed' : ui.chatOpen
+ ? ui.msgView === 'feed' ? 'feed' : ui.msgView === 'notifs' ? 'notifs' : 'messages' : 'wallet';
+ const leavePage = () => {
+ stopFeedWatch(); stopNotifWatch();
+ // Top-level destinations must leave any feature page covering the shell.
+ for (const key of ['profilePk', 'profEdit', 'profEditFilled', 'profOverThread', 'noteThread', 'userSearch',
+ 'zapSetup', 'hatShop', 'feedEdit', 'feedMenu', 'feedRelayEdit', 'noteSheet', 'reactPick', 'reportSheet',
+ 'msgReplyTo', 'msgSheet', 'arkCoinsPage', 'arkExitPage', 'nameEditOpen', 'nostrReconnect', 'nostrLoginOpen',
+ 'pos', 'lockedGift', 'viewGift', 'claimChoose', 'signinAsk']) ui[key] = null;
+ if (!visitor) { ui.screen = 'wallet'; ui.pubProf = null; }
+ };
const icon = (paths) => '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + paths + '</svg>';
- const button = (label, paths, onClick) => h('button', {
- type: 'button', class: 'feed-nav-button', 'aria-label': label, onClick,
+ const button = (id, label, paths, onClick) => h('button', {
+ type: 'button', class: 'app-nav-button' + (id === 'messages' && !visitor && hook('unreadMessages') ? ' unread' : ''),
+ 'aria-label': label, 'aria-current': active === id ? 'page' : undefined, onClick,
}, h('span', { html: icon(paths) }), h('span', {}, label));
return h('nav', {
- class: 'feed-bottom-nav', 'data-key': 'feed-bottom-nav', 'aria-label': t('feedTitle'),
+ class: 'app-bottom-nav', 'data-key': 'app-bottom-nav',
},
- button(t('home'), '<path d="m3 10 9-7 9 7v10a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1z"/>', jumpToNew),
- button(t('msgDmsTitle'), '<path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.4 8.4 0 0 1 3.8-.9h.5a8.5 8.5 0 0 1 8 8z"/>', () => {
- if (signinAsk()) return;
- stopFeedWatch(); ui.msgView = 'home'; render();
+ button('feed', t('feedTitle'), '<path d="m3 10 9-7 9 7v10a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1z"/>', () => {
+ if (active === 'feed' && ui.msgView === 'feed' && !ui.profilePk && !ui.noteThread && !ui.feedEdit) { jumpToNew(); return; }
+ leavePage();
+ if (visitor) { ui.pubProf = true; openFirehose(); } else showFeed(curFeedId);
+ window.scrollTo({ top: 0 });
+ }),
+ button('messages', t('msgDmsTitle'), '<path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.4 8.4 0 0 1 3.8-.9h.5a8.5 8.5 0 0 1 8 8z"/>', () => {
+ if (visitor) { toFrontDoor(); return; }
+ leavePage(); ui.chatOpen = true; ui.msgView = 'home'; render();
window.scrollTo({ top: 0 });
}),
- button(t('alertsTitle'), '<path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10 21h4"/>', () => {
- if (signinAsk()) return;
- stopFeedWatch(); openNotifs();
+ button('notifs', t('alertsTitle'), '<path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10 21h4"/>', () => {
+ if (visitor) { toFrontDoor(); return; }
+ leavePage(); openNotifs();
window.scrollTo({ top: 0 });
}),
- button(t('settingsWallet'), '<path d="M20 8V5H5a2 2 0 0 0 0 4h16v11H5a2 2 0 0 1-2-2V7"/><path d="M21 12h-5v5h5"/><path d="M17 14.5h.01"/>', () => {
+ button('wallet', t('settingsWallet'), '<path d="M20 8V5H5a2 2 0 0 0 0 4h16v11H5a2 2 0 0 1-2-2V7"/><path d="M21 12h-5v5h5"/><path d="M17 14.5h.01"/>', () => {
if (isVisitor()) { toFrontDoor(); return; }
- stopFeedWatch();
+ leavePage();
+ ui.txDetail = null; ui.arkMoveDetail = null; ui.arkReconDetail = null; ui.arkExitDetail = null; ui.giftDetail = null;
ui.chatOpen = false; ui.msgView = null; ui.tab = 'receive';
render(); window.scrollTo({ top: 0 });
}));
@@ -7644,7 +7663,6 @@ export function messagesFeature(ctx) {
// the reader's language, right where a curated feed is judged by it
ctx.languagePicker ? h('div', { class: 'row gap6 feed-lang', 'data-key': 'feed-lang', style: 'justify-content:center;align-items:center;padding:6px 0 2px' },
h('span', { class: 'small muted' }, t('feedLangLabel')), ctx.languagePicker()) : null,
- feedBottomNav(),
...noteOverlays());
}
@@ -9024,6 +9042,7 @@ export function messagesFeature(ctx) {
if (!Object.keys(s.read || {}).length && !anyMsgs) return 1;
return unreadCount();
},
+ bottomNav: appBottomNav,
notifySettingsCards() { return [notifyCard()]; },
screenView() {
// A profile deep link mid-resolution holds the frame over EVERY screen
diff --git a/src/style.css b/src/style.css
index 3a8c036..9696e7b 100644
--- a/src/style.css
+++ b/src/style.css
@@ -770,20 +770,25 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
feed near the top, out of the way of the thumb, and takes you up to them
when tapped. Fixed, not sticky: the feed is the page scroller, and a
sticky element inside the card would scroll away with the card. */
-.feed-page { padding-bottom: calc(82px + env(safe-area-inset-bottom, 0px)); }
-.feed-bottom-nav {
+#app:has(> .app-bottom-nav) { padding-bottom: calc(82px + env(safe-area-inset-bottom, 0px)); }
+.app-bottom-nav {
position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
z-index: 30; display: flex; width: min(100%, 560px); box-sizing: border-box;
padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
background: var(--paper); border-top: 1px solid var(--line);
box-shadow: 0 -4px 18px rgba(0, 0, 0, .06);
}
-.feed-nav-button {
+.app-nav-button {
display: flex; flex: 1; min-width: 0; flex-direction: column; align-items: center;
justify-content: center; gap: 3px; min-height: 52px; padding: 4px;
border: 0; background: transparent; color: var(--ink); font-size: 11px;
}
-.feed-nav-button > span:first-child { display: flex; }
+.app-nav-button > span:first-child { display: flex; position: relative; }
+.app-nav-button[aria-current="page"] { background: var(--accent-soft); }
+.app-nav-button.unread > span:first-child::after {
+ content: ''; position: absolute; top: -2px; right: -4px; width: 7px; height: 7px;
+ border-radius: 50%; background: var(--green); border: 1px solid var(--paper);
+}
.feed-page .feed-new-pill {
bottom: calc(env(safe-area-inset-bottom, 0px) + 82px);
}