aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-28 21:31:16 -0700
committerAdam Soltys <[email protected]>2026-09-28 21:31:16 -0700
commit63e7746f98d7910053cd0b9e1a4a2ed3e6612af3 (patch)
treea261d3ded1dfcf980e9170b758c41342ffda696b
parent292d5527cf36c119d74486061db3a44a608f5138 (diff)
downloadcoinosv3-63e7746f98d7910053cd0b9e1a4a2ed3e6612af3.tar.gz
coinosv3-63e7746f98d7910053cd0b9e1a4a2ed3e6612af3.zip
The bottom nav spans the window; the chat list is just conversations.
Feed and Notifications leave the chat list (the nav reaches both; the bell carries the notifications dot now). On a wide screen the bar runs edge to edge with its buttons over the page column; quiet muted tabs, a pill for the current one. Feed gets a newspaper icon instead of a house. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--src/features/messages.js50
-rw-r--r--src/style.css13
2 files changed, 17 insertions, 46 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 7be5f26..64754d0 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -7537,15 +7537,22 @@ export function messagesFeature(ctx) {
'pos', 'lockedGift', 'viewGift', 'claimChoose', 'signinAsk']) ui[key] = null;
if (!visitor) { ui.screen = 'wallet'; ui.pubProf = null; }
};
+ // The chat list used to carry the notifications row and its count; the
+ // nav's bell carries the dot now.
+ const notifsWaiting = () => {
+ if (!myPubkeys().length) return 0;
+ refreshNotifs(); // throttled inside; keeps the count honest
+ return notifUnread();
+ };
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 = (id, label, paths, onClick) => h('button', {
- type: 'button', class: 'app-nav-button' + (id === 'messages' && !visitor && hook('unreadMessages') ? ' unread' : ''),
+ type: 'button', class: 'app-nav-button' + (!visitor && (id === 'messages' ? hook('unreadMessages') : id === 'notifs' && notifsWaiting()) ? ' unread' : ''),
'aria-label': label, 'aria-current': active === id ? 'page' : undefined, onClick,
}, h('span', { html: icon(paths) }), h('span', {}, label));
return h('nav', {
class: 'app-bottom-nav', 'data-key': 'app-bottom-nav',
},
- 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"/>', () => {
+ button('feed', t('feedTitle'), '<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-2 2m0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><path d="M18 14h-8M15 18h-5M10 6h8v4h-8z"/>', () => {
if (active === 'feed' && ui.msgView === 'feed' && !ui.profilePk && !ui.noteThread && !ui.feedEdit) { jumpToNew(); return; }
leavePage();
if (visitor) { ui.pubProf = true; openFirehose(); } else showFeed(curFeedId);
@@ -8202,45 +8209,6 @@ export function messagesFeature(ctx) {
if (st().noPushService && !st().push)
kids.push(h('div', { class: 'notice info small' }, pushAdvice()));
- // ---- the feed, above the conversations: it's the thing you read, they're
- // the things you answer
- kids.push(h('div', { class: 'list' },
- h('div', {
- class: 'item chat-thread-row',
- onClick: () => { ui.msgView = 'feed'; switchFeed(FOLLOWING); },
- },
- h('div', { class: 'chat-avatar fallback' }, '\u2605'),
- h('div', { class: 'col grow', style: 'min-width:0;gap:1px' },
- h('span', { class: 'chat-name' }, t('feedTitle')),
- h('div', { class: 'muted small' },
- followsNow().set.size === 1 ? t('feedFollowing1')
- : followsNow().set.size ? t('feedFollowingN', { n: followsNow().set.size })
- : t('feedNoFollowsShort')))),
- // ...the feeds you made, each its own row under it
- ...st().feeds.map((f) => h('div', {
- class: 'item chat-thread-row',
- onClick: () => { ui.msgView = 'feed'; switchFeed(f.id); },
- },
- h('div', { class: 'chat-avatar fallback' }, feedTopics(f).length && !f.follows && !(f.authors || []).length && !(f.packs || []).length ? '#' : '\u2605'),
- h('div', { class: 'col grow', style: 'min-width:0;gap:1px' },
- h('span', { class: 'chat-name' }, f.name),
- h('div', { class: 'muted small chat-preview' }, feedSummary(f))))),
- // ...and what happened to what you posted
- (() => {
- const n = myPubkeys().length ? notifUnread() : 0;
- if (myPubkeys().length) refreshNotifs(); // throttled inside; keeps the count honest
- return h('div', {
- class: 'item chat-thread-row' + (n ? ' unread' : ''),
- onClick: openNotifs,
- },
- h('div', { class: 'chat-avatar fallback' }, '\ud83d\udd14'),
- h('div', { class: 'col grow', style: 'min-width:0;gap:1px' },
- h('span', { class: 'chat-name' }, t('alertsTitle')),
- h('div', { class: 'muted small' }, n ? t('alertsNew', { n }) : t('alertsSub'))),
- // like a group row: the dot sits mid-height, padded off the edge
- n ? h('i', { class: 'thread-dot' }) : null);
- })()));
-
// ---- DMs
kids.push(h('div', { class: 'row between', style: 'align-items:baseline' },
h('h3', { style: 'margin:0' }, t('msgDmsTitle')),
diff --git a/src/style.css b/src/style.css
index 9696e7b..c873ccb 100644
--- a/src/style.css
+++ b/src/style.css
@@ -772,19 +772,22 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
sticky element inside the card would scroll away with the card. */
#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));
+ /* Edge to edge like a system bar; the buttons stay over the page column. */
+ position: fixed; bottom: 0; left: 0; right: 0;
+ z-index: 30; display: flex; justify-content: center; box-sizing: border-box;
+ padding: 6px max(4px, calc((100% - 560px) / 2)) 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);
}
.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;
+ border: 0; border-radius: 12px; background: transparent; color: var(--muted); font-size: 11px;
+ cursor: pointer; transition: color .15s ease, background-color .15s ease;
}
+@media (hover: hover) { .app-nav-button:hover { color: var(--ink); } }
.app-nav-button > span:first-child { display: flex; position: relative; }
-.app-nav-button[aria-current="page"] { background: var(--accent-soft); }
+.app-nav-button[aria-current="page"] { background: var(--accent-soft); color: var(--ink); font-weight: 600; }
.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);