aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-28 21:03:31 -0700
committerAdam Soltys <[email protected]>2026-09-28 21:03:31 -0700
commit59e6028b6e62820fcde93068347c44637b67c93d (patch)
tree659dca49a5b25ad05b419d5fbc7fd017889736b7
parentf95093a90637abfa9f0adbb3d7d28127ecd20569 (diff)
downloadcoinosv3-59e6028b6e62820fcde93068347c44637b67c93d.tar.gz
coinosv3-59e6028b6e62820fcde93068347c44637b67c93d.zip
Keep feed navigation visible and add a wallet shortcut
-rw-r--r--src/features/messages.js10
-rw-r--r--src/style.css7
-rw-r--r--tools/feed-bottom-nav-test.js19
3 files changed, 23 insertions, 13 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 3af968f..0b40a5a 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -3472,9 +3472,6 @@ export function messagesFeature(ctx) {
// only runs once a wallet opens.
if (typeof window !== 'undefined') {
window.addEventListener('scroll', () => {
- // Update just the navigation visibility; scrolling must not repaint posts.
- const nav = document.querySelector('.feed-bottom-nav');
- if (nav) nav.hidden = window.scrollY < 160;
if (!ui.profilePk && (!(ui.chatOpen && ui.msgView === 'feed') || ui.noteThread)) return;
windowScrolled(ui.profilePk ? notesCache.get(ui.profilePk) : feed);
// back at the top by yourself: the new posts are under your eyes, so
@@ -7534,7 +7531,6 @@ export function messagesFeature(ctx) {
}, h('span', { html: icon(paths) }), h('span', {}, label));
return h('nav', {
class: 'feed-bottom-nav', 'data-key': 'feed-bottom-nav', 'aria-label': t('feedTitle'),
- hidden: typeof window === 'undefined' || window.scrollY < 160,
},
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"/>', () => {
@@ -7546,6 +7542,12 @@ export function messagesFeature(ctx) {
if (signinAsk()) return;
stopFeedWatch(); 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"/>', () => {
+ if (isVisitor()) { toFrontDoor(); return; }
+ stopFeedWatch();
+ ui.chatOpen = false; ui.msgView = null; ui.tab = 'receive';
+ render(); window.scrollTo({ top: 0 });
}));
}
function feedView() {
diff --git a/src/style.css b/src/style.css
index d2ad443..3a8c036 100644
--- a/src/style.css
+++ b/src/style.css
@@ -774,18 +774,17 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
.feed-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 12px calc(6px + env(safe-area-inset-bottom, 0px));
+ 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-bottom-nav[hidden] { display: none; }
.feed-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: 12px;
+ border: 0; background: transparent; color: var(--ink); font-size: 11px;
}
.feed-nav-button > span:first-child { display: flex; }
-.feed-page:has(.feed-bottom-nav:not([hidden])) .feed-new-pill {
+.feed-page .feed-new-pill {
bottom: calc(env(safe-area-inset-bottom, 0px) + 82px);
}
.feed-new-pill {
diff --git a/tools/feed-bottom-nav-test.js b/tools/feed-bottom-nav-test.js
index f277c4a..53b7529 100644
--- a/tools/feed-bottom-nav-test.js
+++ b/tools/feed-bottom-nav-test.js
@@ -13,7 +13,7 @@ const wallet = { xpub: 'test-wallet', loaded: true, nostrRelays: () => [],
loadFeatureState: (k, d) => k === 'follows' ? { tags: [['p', author]], at: Date.now()/1000 } : d,
saveFeatureState() {}, registerCacheExtension() {} };
let feature;
-function render() { if (feature) morphChildren(document.querySelector('#app'), [feature.screenView()]); }
+function render() { if (feature) morphChildren(document.querySelector('#app'), [feature.screenView() || h('div', { class: 'wallet-screen' }, 'Wallet')]); }
feature = messagesFeature({ h, ui, wallet, render, toast() {}, hook() {}, brandHeader: () => h('div', { style: 'height:60px' }, 'Coinos') });
window.test = { ui, wallet, render, async feed() {
ui.chatOpen = true; ui.msgView = 'feed'; ui.noteThread = null;
@@ -46,15 +46,15 @@ try {
await page.emulateMediaFeatures([{name:'prefers-reduced-motion',value:'reduce'}]);
await page.goto(server.url.href);
await page.waitForSelector('.note-post', {timeout:5000});
- assert(await page.$eval('.feed-bottom-nav',e=>e.hidden),'hidden at top');
+ assert(await page.$eval('.feed-bottom-nav',e=>!e.hidden && e.getBoundingClientRect().height>0),'visible at top without scrolling');
await scroll();
- assert.deepEqual(await page.$$eval('.feed-nav-button',els=>els.map(e=>e.textContent)),['Home','Messages','Notifications']);
+ assert.deepEqual(await page.$$eval('.feed-nav-button',els=>els.map(e=>e.textContent)),['Home','Messages','Notifications','Wallet']);
assert(await page.$eval('.feed-bottom-nav',e=>Math.abs(e.getBoundingClientRect().bottom-innerHeight)<1),'fixed at viewport bottom');
await page.evaluate(()=>test.unseen());
assert(await page.evaluate(()=>document.querySelector('.feed-new-pill').getBoundingClientRect().bottom < document.querySelector('.feed-bottom-nav').getBoundingClientRect().top),'new posts pill stays above navigation');
await page.screenshot({path:'/tmp/feed-bottom-nav-mobile.png'});
await page.tap('.feed-nav-button:nth-child(1)');
- await page.waitForFunction(()=>scrollY===0 && document.querySelector('.feed-bottom-nav').hidden);
+ await page.waitForFunction(()=>scrollY===0 && !document.querySelector('.feed-bottom-nav').hidden);
assert.equal(await page.evaluate(()=>test.ui.msgView),'feed','Home stays in feed');
await scroll(); await page.tap('.feed-nav-button:nth-child(2)');
await page.waitForFunction(()=>test.ui.msgView==='home' && !document.querySelector('.feed-bottom-nav'));
@@ -65,9 +65,18 @@ try {
await page.evaluate(()=>test.feed()); await scroll(); await page.tap('.note-text');
await page.waitForFunction(()=>!!test.ui.noteThread && !document.querySelector('.feed-bottom-nav'));
await page.evaluate(()=>test.feed());
+ assert(await page.$eval('.feed-bottom-nav',e=>!e.hidden && e.getBoundingClientRect().height>0),'returning to feed restores nav immediately without a scroll');
+ await page.evaluate(()=>{test.ui.tab='settings';});
+ await page.tap('.feed-nav-button:nth-child(4)');
+ await page.waitForSelector('.wallet-screen');
+ assert(await page.evaluate(()=>!test.ui.chatOpen && test.ui.tab==='receive' && !document.querySelector('.feed-bottom-nav')),'Wallet opens main wallet instead of old settings tab');
+ await page.evaluate(()=>test.feed());
+ await page.setViewport({width:320,height:844,isMobile:true,hasTouch:true});
+ assert(await page.$eval('.feed-bottom-nav',e=>e.scrollWidth<=e.clientWidth),'four buttons fit a narrow phone');
+ await page.screenshot({path:'/tmp/feed-bottom-nav-always-mobile.png'});
await page.evaluate(()=>{test.wallet.xpub=null;test.render();}); await scroll();
await page.tap('.feed-nav-button:nth-child(2)');
assert(await page.evaluate(()=>test.ui.signinAsk && test.ui.msgView==='feed'),'visitor gets sign-in prompt');
assert.deepEqual(errors,[]);
- console.log('✓ Feed nav appears on scroll; Home returns to top; Messages and Notifications open their views; nav leaves threads; visitor sign-in and new-post pill work');
+ console.log('✓ Feed nav stays visible at top and on return; Home, Messages, Notifications and Wallet work; four buttons fit a narrow phone; threads, visitor sign-in and new-post pill work');
} finally { await page.close(); if (remote) await browser.disconnect(); else await browser.close(); server.stop(true); }