aboutsummaryrefslogtreecommitdiffstats
path: root/tools/feed-custom-test.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-16 17:02:00 -0700
committerAdam Soltys <[email protected]>2026-10-16 17:02:00 -0700
commit885bc2647a3558554a53a794f1d56e9dbd7657e6 (patch)
tree6f22f27cc12d452f383cee6ef28b643288e2987b /tools/feed-custom-test.js
parent73bffd93caefa53af3a5d28e70059a3cfbe68939 (diff)
downloadcoinosv3-885bc2647a3558554a53a794f1d56e9dbd7657e6.tar.gz
coinosv3-885bc2647a3558554a53a794f1d56e9dbd7657e6.zip
Conversations: a feed of the replies the people you follow write, each under the post it answers.
Amethyst's Conversations beside its New Threads (Adam, 2026-10-16: "a replies mode or Conversations mode like Amethyst has"). The Following feed showed their posts and dropped every reply, so what people were saying TO each other only turned up by opening threads. Now a built-in Conversations chip sits beside Following (id 'replies'; its own notes, clock, disk cache and relay choice like the other built-ins): the same people, asked for notes and NIP-22 comments, keeping the replies — a reply as NIP-10 marks it, a comment, never a quote or a repost (feedKinds/feedKeeps; the merge, seeding, borrowing, the live watch and the media index all go by them). Each row shows what it answers above it — "↩ Replying to" and the quote card of the parent (the e tag marked reply, else root, else the last; a comment's e or E), tapping the card opens that post, tapping the reply its thread at the reply — and the header drops its "reply" tag there. The parent is usually in hand already (the Following feed, a thread, someone's page: noteInHand, which quotedNote checks before any relay — nostr:nevent quotes gain the same), and a thread opened from such a row paints its root at once instead of fetching it. In a conversations feed the parent counts as part of the row, so it arrives whole with the first screen (noteReady's parent option, per feed); a profile's Replies tab shows the same context and fills its cards in. Not a shareable URL (it is nobody's timeline), so no Share in its menu. Any custom feed can be one: "Replies instead of posts" in the editor (def.replies, synced with the definition), named in its summary. The disk cache now keeps comments. tools/feed-conversations-test.js: Following keeps posts only; the chip; the replies newest first with the parent card from hand (no relay asked) and a missing parent said so; the menu without Share; the reply's own tap opens its thread at the reply, with the root painted from hand; the card's tap opens the parent; Back lands on Conversations; the profile's Replies tab shows the context, Posts none; a custom conversations feed via the editor borrows the replies from the feeds in hand, not the posts. feed-custom-test lists the new chip. Pre-existing failures not touched: feed-custom-test's "a tagged post lands in it"/"lands in Everything" and feed-streaming-test's pill position fail on clean HEAD the same way; feed-follow, feed-cache, own-post-feeds, feed-list, feed-media-views, feed-switch-seed, profile-pager, thread-pager, feed-repost, feed-live, feed-new-posts, feed-window, feed-choice-per-wallet, feed-nav-destination, feed-wallet-back, moderation, feed-mention-tap and feed-reboost pass. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
Diffstat (limited to 'tools/feed-custom-test.js')
-rw-r--r--tools/feed-custom-test.js10
1 files changed, 5 insertions, 5 deletions
diff --git a/tools/feed-custom-test.js b/tools/feed-custom-test.js
index 122cb0a..6f345f1 100644
--- a/tools/feed-custom-test.js
+++ b/tools/feed-custom-test.js
@@ -108,7 +108,7 @@ try {
await waitText('receive', 20000);
await openFeed();
await sleep(1200);
- check('the feed opens on Following, with a + chip', JSON.stringify(await chips()) === JSON.stringify(['*Following', 'Popular', '#gardenstr', '+']), JSON.stringify(await chips()));
+ check('the feed opens on Following, with a + chip', JSON.stringify(await chips()) === JSON.stringify(['*Following', 'Conversations', 'Popular', '#gardenstr', '+']), JSON.stringify(await chips()));
console.log('\n[a new feed: everyone I follow, on one topic]');
await page.click('.feed-chip.add'); await sleep(300);
@@ -117,7 +117,7 @@ try {
await page.click('input[type=checkbox]:not(.feed-all-toggle)'); // everyone I follow (the first box is the relays' every-post one)
await type('input[placeholder="#bitcoin #nostr"]', '#Bitcoin, lightning');
await click('save'); await sleep(1500);
- check('the new feed is on screen and selected', JSON.stringify(await chips()) === JSON.stringify(['Following', 'Popular', '#gardenstr', '*Coin talk', '+']), JSON.stringify(await chips()));
+ check('the new feed is on screen and selected', JSON.stringify(await chips()) === JSON.stringify(['Following', 'Conversations', 'Popular', '#gardenstr', '*Coin talk', '+']), JSON.stringify(await chips()));
const req = (await page.evaluate(() => window.__reqs)).find((f) => f['#t'] && f.authors);
check('it asks relays for the follows AND the topics', !!req && req.authors?.includes(AUTHOR) && JSON.stringify(req['#t']) === JSON.stringify(['bitcoin', 'lightning']), JSON.stringify(req || null));
const tagged = signed('fresh #bitcoin post', 0, [['t', 'bitcoin']]);
@@ -132,7 +132,7 @@ try {
await page.evaluate(() => [...document.querySelectorAll('.feed-chip')].find((c) => c.textContent.trim() === 'Following').click()); await sleep(1200);
check('Following again, with its own posts', (await chips()).includes('*Following') && await waitText('plain post', 4000));
await page.evaluate(() => { const a = [...document.querySelectorAll('.notes-feed a')].find((x) => x.textContent === '#bitcoin'); if (a) a.click(); }); await sleep(1200);
- check('a #tag opens a topic feed of its own', JSON.stringify(await chips()) === JSON.stringify(['Following', 'Popular', '#gardenstr', 'Coin talk', '*#bitcoin', '+']) && await page.evaluate(() => { const m = document.querySelector('[aria-label="Feed options"]'); if (!m) return false; m.click(); return true; }) && await waitText('save feed', 2000) && await page.evaluate(() => { const b = [...document.querySelectorAll('.msg-sheet button')].find((x) => /back/i.test(x.textContent)); if (b) b.click(); return true; }), JSON.stringify(await chips()));
+ check('a #tag opens a topic feed of its own', JSON.stringify(await chips()) === JSON.stringify(['Following', 'Conversations', 'Popular', '#gardenstr', 'Coin talk', '*#bitcoin', '+']) && await page.evaluate(() => { const m = document.querySelector('[aria-label="Feed options"]'); if (!m) return false; m.click(); return true; }) && await waitText('save feed', 2000) && await page.evaluate(() => { const b = [...document.querySelectorAll('.msg-sheet button')].find((x) => /back/i.test(x.textContent)); if (b) b.click(); return true; }), JSON.stringify(await chips()));
const treq = (await page.evaluate(() => window.__reqs)).filter((f) => f['#t'] && !f.authors).pop();
check('...asked for by topic alone', !!treq && JSON.stringify(treq['#t']) === JSON.stringify(['bitcoin']), JSON.stringify(treq || null));
@@ -183,7 +183,7 @@ try {
await waitText('receive', 20000);
await openFeed(); await sleep(1200);
// (the feed you were on is remembered: Follows + lists, saved last)
- check('the saved feeds are still there; the session topic is not', JSON.stringify(await chips()) === JSON.stringify(['Following', 'Popular', '#gardenstr', 'Coin talk', 'Pack feed', 'Everything', '*Follows + lists', '+']), JSON.stringify(await chips()));
+ check('the saved feeds are still there; the session topic is not', JSON.stringify(await chips()) === JSON.stringify(['Following', 'Conversations', 'Popular', '#gardenstr', 'Coin talk', 'Pack feed', 'Everything', '*Follows + lists', '+']), JSON.stringify(await chips()));
// (the chat home no longer lists the feed with its summary: its editor shows the pack)
await chip('Pack feed'); await sleep(800);
await page.click('[aria-label="Feed options"]'); await sleep(300);
@@ -199,7 +199,7 @@ try {
await page.click('[aria-label="Feed options"]'); await sleep(300);
check('the menu offers Delete feed beside Edit', await page.evaluate(() => { const b = [...document.querySelectorAll('.msg-sheet button')].map((x) => x.textContent); return b.some((x) => /edit feed/i.test(x)) && b.some((x) => /delete feed/i.test(x)); }));
await page.evaluate(() => [...document.querySelectorAll('.msg-sheet button')].find((b) => /delete feed/i.test(b.textContent)).click()); await sleep(1000);
- check('deleting it from the menu lands back on Following', JSON.stringify(await chips()) === JSON.stringify(['*Following', 'Popular', '#gardenstr', 'Pack feed', 'Everything', 'Follows + lists', '+']), JSON.stringify(await chips()));
+ check('deleting it from the menu lands back on Following', JSON.stringify(await chips()) === JSON.stringify(['*Following', 'Conversations', 'Popular', '#gardenstr', 'Pack feed', 'Everything', 'Follows + lists', '+']), JSON.stringify(await chips()));
check('no page errors', errs.length === 0, errs.slice(0, 2).join(' | '));
} finally { await browser.close(); server.stop(true); }
console.log(ok ? '\n✅ feeds: Following is one of many' : '\n❌ failed');