aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-10 08:04:59 -0700
committerAdam Soltys <[email protected]>2026-10-10 08:04:59 -0700
commit89b16d6363920129744665ceeda5218f6786aaaa (patch)
treefea7e76644233d51db10fe61d6052c15bd817a26
parent9abd654e8ef57de59a4f4bd5cbfbba560ed7e523 (diff)
downloadcoinosv3-89b16d6363920129744665ceeda5218f6786aaaa.tar.gz
coinosv3-89b16d6363920129744665ceeda5218f6786aaaa.zip
Feed: your own posts are in Following. The follows feed read only the people you follow, so your own post was there just after you sent it (the optimistic row) and gone after a reload — Adam didn't see his latest. Like every other client, a follows feed now includes you. Someone who follows nobody still gets the "find people" invitation, with their own posts under it. feed-live-test's catch-up queue now goes only to an ask naming the post's author (the feed also asks for yours); tools/feed-repost-test.js checks your own post shows.
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
-rw-r--r--src/features/messages.js11
-rw-r--r--tools/feed-live-test.js6
-rw-r--r--tools/feed-repost-test.js9
3 files changed, 20 insertions, 6 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 99b5c48..b271bc9 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -5605,6 +5605,9 @@ export function messagesFeature(ctx) {
const feedAuthors = (def = feedDef()) => {
if (!def) return [];
const set = new Set(def.follows ? followsNow().set : []);
+ // your own posts belong in the feed of the people you follow, as in
+ // every other client — not only for the moment after posting
+ if (def.follows && mePk()) set.add(mePk());
for (const pk of def.authors || []) if (pk) set.add(pk);
for (const pk of def.priv || []) if (pk) set.add(pk); // a list's private members
for (const pk of packAuthors(def)) set.add(pk);
@@ -11772,6 +11775,8 @@ export function messagesFeature(ctx) {
});
const authors = feedAuthors(def);
const hasQuery = feedHasQuery(def);
+ // the follows feed of someone who follows nobody: only their own posts
+ const noFollows = !!def.follows && !followsNow().set.size && !(def.authors || []).length && !(def.priv || []).length && !feedTopics(def).length;
prepareFeedAhead(c);
// Never a bare spinner over posts we already hold: while the opening
// page's faces and pictures are still being decoded, the cached rows
@@ -11851,11 +11856,13 @@ export function messagesFeature(ctx) {
h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, t('feedOfLoading')))
: def.of && !hasQuery
? h('div', { class: 'small muted', style: 'text-align:center;padding:12px 0' }, t('feedNoFollowsOf'))
- : !hasQuery
+ : !hasQuery || noFollows
? def.builtin
? h('div', { class: 'col', style: 'gap:8px' },
h('div', { class: 'small muted' }, t('feedNoFollows')),
- h('button', { class: 'btn-sm', onClick: () => { stopFeedWatch(); hook('openUserSearch'); render(); } }, t('feedFindPeople')))
+ h('button', { class: 'btn-sm', onClick: () => { stopFeedWatch(); hook('openUserSearch'); render(); } }, t('feedFindPeople')),
+ // your own posts, under the invitation
+ noFollows && visible.length && mediaMode === 'all' ? h('div', { class: 'card col notes-feed', style: 'gap:0' }, ...rows) : null)
: h('div', { class: 'col', style: 'gap:8px' },
h('div', { class: 'small muted' }, t('feedNoQuery')),
h('button', { class: 'btn-sm', onClick: () => openFeedEditor(def) }, t('feedEdit')))
diff --git a/tools/feed-live-test.js b/tools/feed-live-test.js
index b9879d0..2bee19f 100644
--- a/tools/feed-live-test.js
+++ b/tools/feed-live-test.js
@@ -58,8 +58,10 @@ await page.evaluateOnNewDocument(() => {
subs.push([ws, m[1]]);
// A catch-up query (no since, no until) is answered from the queue
// the test filled, ahead of the real relay's end-of-stream.
- if (!m[2].since && !m[2].until && (window.__catchup || []).length) {
- const evs = window.__catchup; window.__catchup = [];
+ // (only to an ask that names their author: the feed also asks for your own posts)
+ const wants = (ev) => !m[2].authors || m[2].authors.includes(ev.pubkey);
+ if (!m[2].since && !m[2].until && (window.__catchup || []).some(wants)) {
+ const evs = window.__catchup.filter(wants); window.__catchup = window.__catchup.filter((ev) => !wants(ev));
setTimeout(() => { for (const ev of evs) ws.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(['EVENT', m[1], ev]) })); }, 0);
}
}
diff --git a/tools/feed-repost-test.js b/tools/feed-repost-test.js
index 7bc43aa..4936dd6 100644
--- a/tools/feed-repost-test.js
+++ b/tools/feed-repost-test.js
@@ -7,6 +7,7 @@
import puppeteer from './puppeteer.js';
import { cacheKeyFor } from '../src/wallet.js';
import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure';
+import { privateKeyFromSeedWords } from 'nostr-tools/nip06';
import { buildHtml } from '../build.js';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
let ok = true;
@@ -33,7 +34,7 @@ const page = await browser.newPage();
await page.evaluateOnNewDocument((fx) => {
const Real = window.WebSocket;
const fits = (ev, f) => (!f.kinds || f.kinds.includes(ev.kind)) && (!f.authors || f.authors.includes(ev.pubkey)) && (!f.ids || f.ids.includes(ev.id));
- window.WebSocket = function (...a) { const ws = new Real(...a); const send = ws.send.bind(ws); ws.send = (d) => { try { const m = JSON.parse(d); if (m[0] === 'REQ') { const fs = m.slice(2); setTimeout(() => { if (window.__live) for (const ev of fx.filter((e) => fs.some((f) => fits(e, f)))) ws.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(['EVENT', m[1], ev]) })); ws.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(['EOSE', m[1]]) })); }, 0); return; } if (m[0] === 'EVENT') return; } catch {} return send(d); }; return ws; };
+ window.WebSocket = function (...a) { const ws = new Real(...a); const send = ws.send.bind(ws); ws.send = (d) => { try { const m = JSON.parse(d); if (m[0] === 'REQ') { const fs = m.slice(2); setTimeout(() => { if (window.__live) for (const ev of fx.concat(window.__extra || []).filter((e) => fs.some((f) => fits(e, f)))) ws.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(['EVENT', m[1], ev]) })); ws.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(['EOSE', m[1]]) })); }, 0); return; } if (m[0] === 'EVENT') return; } catch {} return send(d); }; return ws; };
window.WebSocket.prototype = Real.prototype; Object.assign(window.WebSocket, Real);
}, POSTS);
const errs = [];
@@ -57,7 +58,9 @@ try {
localStorage.setItem(k + ':profiles', JSON.stringify({ [z]: { name: 'Zapstore', t: Date.now() }, [l]: { name: 'LWB', t: Date.now() }, [o]: { name: 'Other', t: Date.now() } }));
localStorage.setItem('btc-wallet-feed:' + k, 'following');
}, [base, ZAPSTORE.pk, LWB.pk, OTHER.pk]);
- await page.evaluateOnNewDocument(() => { window.__live = true; });
+ // and a post of my own, which I don't follow myself
+ const MINE = finalizeEvent({ kind: 1, created_at: now - 50, tags: [], content: 'my own latest post' }, privateKeyFromSeedWords(mn));
+ await page.evaluateOnNewDocument((e) => { window.__live = true; window.__extra = [e]; }, MINE);
await page.reload({ waitUntil: 'domcontentloaded' });
await waitText('receive', 20000);
await page.evaluate(() => document.querySelector('.app-nav-button[aria-label="Feed"]')?.click());
@@ -70,6 +73,8 @@ try {
check('a note already in the feed isn\'t shown again for its repost', (t.match(/zapstore says hello itself/g) || []).length === 1);
await page.evaluate(() => [...document.querySelectorAll('[data-zap-post]')].find((n) => n.textContent.includes('Tankful')).querySelector('.note-text, .note-content').click());
check('a tap opens the reposted note\'s thread', await page.evaluate(async () => { for (let i = 0; i < 20; i++) { if (location.pathname.includes('nevent') || document.querySelector('.thread-reply-send, .thread-view')) return true; await new Promise((r) => setTimeout(r, 200)); } return false; }));
+ await page.evaluate(() => history.back()); await sleep(800);
+ check('my own post is in my Following feed', await waitText('my own latest post', 10000));
check('no page errors', errs.length === 0, errs.slice(0, 2).join(' | '));
} finally { await browser.close(); server.stop(true); }
console.log(ok ? '\n✅ reposts show in the feed' : '\n❌ failed');