aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-02 10:20:57 -0700
committerAdam Soltys <[email protected]>2026-10-02 10:20:57 -0700
commitaf19e72e54b1b3bac210d8c9d74317f662160c18 (patch)
tree4899e195ab1f9d2850d51b30e6b5f89ba88a6412 /src
parentef6729ceaf49d2d85bc8920c2c7756086ee7450d (diff)
downloadcoinosv3-af19e72e54b1b3bac210d8c9d74317f662160c18.tar.gz
coinosv3-af19e72e54b1b3bac210d8c9d74317f662160c18.zip
Feed form: relays and an every-post firehose; its Back works; a stronger new-post tint
- The new/edit feed form gets a Relays field (read this feed from these relays only) and "Every post on these relays", which saves a firehose feed (optionally curated) named after its relay and hides the people, packs and topics it doesn't need. It used to take a hand-typed /feed/all?r= link. - The form's Back did nothing visible: opening it made no history entry, so Back went to the page behind the feed and left the form up. The editor (and the gear page's Spending/Savings view) are now part of the navigation state. - New-post tint 9% -> 16% green in light mode; dark mode unchanged. - tools/feed-editor-relays-test.js. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
Diffstat (limited to 'src')
-rw-r--r--src/app.js9
-rw-r--r--src/features/messages.js40
-rw-r--r--src/i18n.js4
-rw-r--r--src/locales/ar.json6
-rw-r--r--src/locales/bn.json6
-rw-r--r--src/locales/de.json6
-rw-r--r--src/locales/es.json6
-rw-r--r--src/locales/fr.json6
-rw-r--r--src/locales/hi.json6
-rw-r--r--src/locales/id.json6
-rw-r--r--src/locales/ja.json6
-rw-r--r--src/locales/ko.json6
-rw-r--r--src/locales/mr.json6
-rw-r--r--src/locales/pt.json6
-rw-r--r--src/locales/ru.json6
-rw-r--r--src/locales/sw.json6
-rw-r--r--src/locales/ta.json6
-rw-r--r--src/locales/te.json6
-rw-r--r--src/locales/tr.json6
-rw-r--r--src/locales/ur.json6
-rw-r--r--src/locales/vi.json6
-rw-r--r--src/locales/zh.json6
-rw-r--r--src/style.css5
23 files changed, 139 insertions, 33 deletions
diff --git a/src/app.js b/src/app.js
index 4ce3386..a50808b 100644
--- a/src/app.js
+++ b/src/app.js
@@ -795,7 +795,7 @@ function renderInner() {
// ui field that decides which page is on screen.
const navKey = [ui.screen, ui.tab === 'settings', ui.chatOpen, ui.msgView, ui.msgPeer, ui.msgCommunity,
ui.profilePk, ui.settingsPage, ui.addrScan, ui.arkExitPage, ui.txDetail, ui.giftMode, ui.claimStep, ui.nameEditOpen,
- ui.noteThread && ui.noteThread.focusId, !!ui.userSearch, !!ui.zapSetup, !!ui.hatShop].join('|');
+ ui.noteThread && ui.noteThread.focusId, !!ui.userSearch, !!ui.zapSetup, !!ui.hatShop, !!ui.feedEdit, ui.settingsView].join('|');
// A deep-linked profile landing is ONE page settling, not a chain of
// navigations: boot screens shuffle beneath the shell (unlock → restored
// wallet), then the resolved profile replaces the shell in place. The key
@@ -868,7 +868,7 @@ wallet.subscribe(scheduleRender);
// lightbox: a full-screen photo is a place too — the phone's Back must close
// it, not pop the screen under it (which once walked a viewer straight back
// to the start page, so the photo's × then looked like a logout).
-const NAV_FIELDS = ['screen', 'tab', 'txDetail', 'arkMoveDetail', 'arkReconDetail', 'arkExitDetail', 'giftDetail', 'bump', 'giftMode', 'claimStep', 'chatOpen', 'msgView', 'feedId', 'pubProf', 'arkCoinsPage', 'arkExitPage', 'msgCommunity', 'msgPeer', 'profilePk', 'profEdit', 'profEditFilled', 'profOverThread', 'settingsPage', 'nameEditOpen', 'noteThread', 'userSearch', 'zapSetup', 'hatShop', 'lightbox'];
+const NAV_FIELDS = ['screen', 'tab', 'txDetail', 'arkMoveDetail', 'arkReconDetail', 'arkExitDetail', 'giftDetail', 'bump', 'giftMode', 'claimStep', 'chatOpen', 'msgView', 'feedId', 'pubProf', 'arkCoinsPage', 'arkExitPage', 'msgCommunity', 'msgPeer', 'profilePk', 'profEdit', 'profEditFilled', 'profOverThread', 'settingsPage', 'nameEditOpen', 'noteThread', 'userSearch', 'zapSetup', 'hatShop', 'lightbox', 'feedEdit', 'settingsView'];
function navSnapshot() {
const s = {};
for (const f of NAV_FIELDS) s[f] = ui[f] ?? null;
@@ -877,6 +877,7 @@ function navSnapshot() {
// keystroke must never mint a history entry.
if (ui.noteThread) s.noteThread = { rootId: ui.noteThread.rootId, focusId: ui.noteThread.focusId, seed: ui.noteThread.seed };
if (ui.userSearch) s.userSearch = { q: '', rows: null };
+ if (ui.feedEdit) s.feedEdit = { ...ui.feedEdit, q: '', rows: null, pq: '', packRows: null, pt: null };
if (ui.zapSetup) s.zapSetup = { pk: ui.zapSetup.pk, npub: ui.zapSetup.npub, eventId: ui.zapSetup.eventId, amount: '21' };
// Keep the editable fields in this history entry so a refresh restores the
// draft. Copy them: later keystrokes must not mutate earlier snapshots.
@@ -970,7 +971,7 @@ function restoreNavFromHistory(renderNow = true) {
// the vault prompt); with an account open now, those screens are stale.
// Restoring one painted the start page over a perfectly good wallet.
if (['unlock', 'vault'].includes(nav.screen) && activeAccount()) nav.screen = 'wallet';
- for (const f of NAV_FIELDS) if (f in nav) ui[f] = f === 'profEdit' && nav[f] ? structuredClone(nav[f]) : nav[f];
+ for (const f of NAV_FIELDS) if (f in nav) ui[f] = (f === 'profEdit' || f === 'feedEdit') && nav[f] ? structuredClone(nav[f]) : nav[f];
ui.lightbox = null; nav.lightbox = null; // an object URL doesn't survive a reload
navStack = [nav];
navIndex = 0;
@@ -997,7 +998,7 @@ window.addEventListener('popstate', (e) => {
}
restoringHistory = true;
try {
- for (const f of NAV_FIELDS) ui[f] = f === 'profEdit' && snap[f] ? structuredClone(snap[f]) : f in snap ? snap[f] : null;
+ for (const f of NAV_FIELDS) ui[f] = (f === 'profEdit' || f === 'feedEdit') && snap[f] ? structuredClone(snap[f]) : f in snap ? snap[f] : null;
if (st && typeof st.i === 'number') navIndex = st.i;
else { const i = navStack.findIndex((s) => navSig(s) === navSig(snap)); if (i >= 0) navIndex = i; }
render();
diff --git a/src/features/messages.js b/src/features/messages.js
index 176c779..68eefda 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -8894,8 +8894,10 @@ export function messagesFeature(ctx) {
const isNew = !def || adhocFeeds.has(def.id);
ui.feedEdit = def
? { id: def.id, name: def.name || '', follows: !!def.follows, authors: [...(def.authors || [])], priv: [...(def.priv || [])], packs: [...(def.packs || [])],
- topics: feedTopics(def).map((x) => '#' + x).join(' '), q: '', rows: null, pq: '', packRows: null, isNew, d: def.d || null, publish: isNew || !!def.d }
- : { id: null, name: '', follows: false, authors: [], priv: [], packs: [], topics: '', q: '', rows: null, pq: '', packRows: null, isNew: true, d: null, publish: true };
+ topics: feedTopics(def).map((x) => '#' + x).join(' '), q: '', rows: null, pq: '', packRows: null, isNew, d: def.d || null, publish: isNew || !!def.d,
+ relays: (def.relays || []).map((r) => r.replace(/^wss:\/\//, '')).join(' '), all: !!def.all && !def.builtin, curated: !!def.curated }
+ : { id: null, name: '', follows: false, authors: [], priv: [], packs: [], topics: '', q: '', rows: null, pq: '', packRows: null, isNew: true, d: null, publish: true,
+ relays: '', all: false, curated: false };
render();
}
const feedPeopleSearcher = makeSearcher((q, rows) => {
@@ -8907,12 +8909,20 @@ export function messagesFeature(ctx) {
const e = ui.feedEdit;
if (!e) return;
const topics = [...new Set(e.topics.split(/[\s,]+/).map(normTopic).filter(Boolean))].slice(0, 20);
- if (!e.follows && !e.authors.length && !e.packs.length && !topics.length) { toast(t('feedNoQuery')); return; }
- const name = e.name.trim().slice(0, 40) || (topics.length ? '#' + topics[0] : t('feedNewName'));
+ const words = String(e.relays || '').split(/[\s,]+/).filter(Boolean);
+ const relays = [...new Set(words.map(normRelay).filter(Boolean))].slice(0, 12);
+ if (relays.length < words.length) { toast(t('feedRelayBad')); return; }
+ // every post on its relays: a firehose needs the relays, nothing else
+ if (e.all && !relays.length) { toast(t('feedAllNeedsRelays')); return; }
+ if (!e.all && !e.follows && !e.authors.length && !e.packs.length && !topics.length) { toast(t('feedNoQuery')); return; }
+ const name = e.name.trim().slice(0, 40) || (e.all ? relays[0].replace(/^wss:\/\//, '') : topics.length ? '#' + topics[0] : t('feedNewName'));
const s = st();
const keep = e.id && !adhocFeeds.has(e.id) && s.feeds.some((f) => f.id === e.id);
const id = keep ? e.id : 'f' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
- const def = { id, name, follows: e.follows, authors: e.authors.slice(0, FEED_AUTHORS_MAX), packs: e.packs.slice(0, 20), topics, at: Date.now() };
+ const def = e.all
+ ? { id, name, follows: false, authors: [], packs: [], topics: [], all: true, curated: !!e.curated, relays, at: Date.now() }
+ : { id, name, follows: e.follows, authors: e.authors.slice(0, FEED_AUTHORS_MAX), packs: e.packs.slice(0, 20), topics, relays, all: false, curated: false, at: Date.now() };
+ if (!relays.length) delete def.relays;
const prev = keep ? s.feeds.find((f) => f.id === id) : null;
// d, listAt, listContent and priv ride along from the previous copy
if (keep) s.feeds = s.feeds.map((f) => (f.id === id ? { ...f, ...def } : f)); else s.feeds.push(def);
@@ -9008,7 +9018,19 @@ export function messagesFeature(ctx) {
h('label', { class: 'col', style: 'gap:4px' },
h('span', { class: 'small muted' }, t('feedName')),
h('input', { type: 'text', value: e.name, placeholder: t('feedNameHint'), maxlength: '40', onInput: (ev) => { e.name = ev.target.value; } })),
- h('div', { class: 'col', style: 'gap:8px' },
+ // where the feed reads, and whether it takes everything there
+ h('div', { class: 'col', style: 'gap:6px' },
+ h('span', { class: 'small muted' }, t('feedRelays')),
+ h('input', { type: 'text', class: 'feed-relays-input', value: e.relays || '', placeholder: 'nos.lol relay.damus.io',
+ autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', onInput: (ev) => { e.relays = ev.target.value; } }),
+ h('div', { class: 'small faint' }, t('feedRelaysFormHelp')),
+ h('label', { class: 'row gap6', style: 'align-items:center;cursor:pointer' },
+ h('input', { type: 'checkbox', class: 'feed-all-toggle', checked: !!e.all, style: 'width:18px;height:18px;accent-color:var(--accent);margin:0', onChange: (ev) => { e.all = ev.target.checked; render(); } }),
+ h('span', {}, t('feedAllToggle'))),
+ e.all ? h('label', { class: 'row gap6', style: 'align-items:center;cursor:pointer;margin-left:26px' },
+ h('input', { type: 'checkbox', checked: !!e.curated, style: 'width:18px;height:18px;accent-color:var(--accent);margin:0', onChange: (ev) => { e.curated = ev.target.checked; } }),
+ h('span', { class: 'small' }, t('feedAllCurated'))) : null),
+ e.all ? null : h('div', { class: 'col', style: 'gap:8px' },
h('span', { class: 'small muted' }, t('feedPeople')),
h('label', { class: 'row gap6', style: 'align-items:center;cursor:pointer' },
h('input', { type: 'checkbox', checked: e.follows, style: 'width:18px;height:18px;accent-color:var(--accent);margin:0', onChange: (ev) => { e.follows = ev.target.checked; render(); } }),
@@ -9030,7 +9052,7 @@ export function messagesFeature(ctx) {
e.authors.push(r.pk); e.q = ''; e.rows = null; render();
}, (pk, node) => hook('wrapAvatar', pk, node)))
: null),
- h('div', { class: 'col', style: 'gap:8px' },
+ e.all ? null : h('div', { class: 'col', style: 'gap:8px' },
h('span', { class: 'small muted' }, t('feedPacks')),
...e.packs.map((p) => packRow(p, null,
h('button', { class: 'btn-sm', type: 'button', 'aria-label': t('remove'), onClick: () => { e.packs = e.packs.filter((x) => packKey(x) !== packKey(p)); render(); } }, '\u00d7'))),
@@ -9045,12 +9067,12 @@ export function messagesFeature(ctx) {
e.pq = ''; e.packRows = null; render();
})),
e.packRows && !e.packRows.length ? h('div', { class: 'small faint' }, t('searchNoResults')) : null),
- h('label', { class: 'col', style: 'gap:4px' },
+ e.all ? null : h('label', { class: 'col', style: 'gap:4px' },
h('span', { class: 'small muted' }, t('feedTopics')),
h('input', { type: 'text', value: e.topics, placeholder: t('feedTopicsHint'), autocapitalize: 'none', autocomplete: 'off', onInput: (ev) => { e.topics = ev.target.value; } }),
h('div', { class: 'small faint' }, t('feedTopicsHelp'))),
// a feed of people is a nostr list (NIP-51 follow set) unless told not to be
- e.authors.length || (e.priv || []).length || e.d
+ !e.all && (e.authors.length || (e.priv || []).length || e.d)
? h('div', { class: 'col', style: 'gap:4px' },
h('label', { class: 'row gap6', style: 'align-items:center;cursor:pointer' },
h('input', { type: 'checkbox', class: 'feed-list-toggle', checked: !!e.publish, style: 'width:18px;height:18px;accent-color:var(--accent);margin:0', onChange: (ev) => { e.publish = ev.target.checked; } }),
diff --git a/src/i18n.js b/src/i18n.js
index ac538ed..906ff51 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -282,6 +282,10 @@ const STR = {
askNotNow: 'Not now',
feedLangLabel: 'Language',
feedRelays: 'Relays',
+ feedRelaysFormHelp: 'Read this feed from these relays only. Leave empty for the usual ones.',
+ feedAllToggle: 'Every post on these relays (a firehose)',
+ feedAllCurated: 'Only popular posts: hide bots and posts nobody reacted to',
+ feedAllNeedsRelays: 'Name the relays to read everything from.',
feedRelaysHint: 'Posts are read from these relays. What the feed asks for stays the same.',
feedRelayUsual: 'usual',
feedRelaysOutbox: 'Right now each person is read from the relays they publish to.',
diff --git a/src/locales/ar.json b/src/locales/ar.json
index 75971cf..dd81a34 100644
--- a/src/locales/ar.json
+++ b/src/locales/ar.json
@@ -881,5 +881,9 @@
"followHistRestored": "تمت استعادة {n} متابَعًا.",
"securityTitle": "الأمان",
"arkCoinsNone": "لا توجد عملات في الإنفاق بعد.",
- "profKeyLink": "المفتاح العام"
+ "profKeyLink": "المفتاح العام",
+ "feedRelaysFormHelp": "اقرأ هذه الخلاصة من هذه المرحّلات فقط. اتركها فارغة لاستخدام المعتاد.",
+ "feedAllToggle": "كل منشور على هذه المرحّلات (تدفق كامل)",
+ "feedAllCurated": "المنشورات الرائجة فقط: إخفاء الروبوتات والمنشورات بلا تفاعل",
+ "feedAllNeedsRelays": "اذكر المرحّلات التي تقرأ منها كل شيء."
}
diff --git a/src/locales/bn.json b/src/locales/bn.json
index 98bb71c..d93622d 100644
--- a/src/locales/bn.json
+++ b/src/locales/bn.json
@@ -881,5 +881,9 @@
"followHistRestored": "{n} জনকে আবার ফলো করা হয়েছে।",
"securityTitle": "নিরাপত্তা",
"arkCoinsNone": "স্পেন্ডিং-এ এখনও কোনো কয়েন নেই।",
- "profKeyLink": "পাবলিক কী"
+ "profKeyLink": "পাবলিক কী",
+ "feedRelaysFormHelp": "শুধু এই রিলেগুলো থেকে এই ফিড পড়ুন। সাধারণগুলোর জন্য খালি রাখুন।",
+ "feedAllToggle": "এই রিলেগুলোর প্রতিটি পোস্ট (ফায়ারহোস)",
+ "feedAllCurated": "শুধু জনপ্রিয় পোস্ট: বট ও প্রতিক্রিয়াহীন পোস্ট লুকান",
+ "feedAllNeedsRelays": "কোন রিলে থেকে সব পড়বেন তা লিখুন।"
}
diff --git a/src/locales/de.json b/src/locales/de.json
index ce61392..33e1f56 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -881,5 +881,9 @@
"followHistRestored": "{n} Follows wiederhergestellt.",
"securityTitle": "Sicherheit",
"arkCoinsNone": "Noch keine Coins in Ausgaben.",
- "profKeyLink": "Öffentlicher Schlüssel"
+ "profKeyLink": "Öffentlicher Schlüssel",
+ "feedRelaysFormHelp": "Diesen Feed nur von diesen Relays lesen. Leer lassen für die üblichen.",
+ "feedAllToggle": "Jeder Beitrag auf diesen Relays (Firehose)",
+ "feedAllCurated": "Nur beliebte Beiträge: Bots und Beiträge ohne Reaktionen ausblenden",
+ "feedAllNeedsRelays": "Nenne die Relays, von denen alles gelesen werden soll."
}
diff --git a/src/locales/es.json b/src/locales/es.json
index c66feac..291da3d 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -881,5 +881,9 @@
"followHistRestored": "Se restauraron {n} seguidos.",
"securityTitle": "Seguridad",
"arkCoinsNone": "Aún no hay monedas en Gastos.",
- "profKeyLink": "Clave pública"
+ "profKeyLink": "Clave pública",
+ "feedRelaysFormHelp": "Lee este feed solo de estos relays. Déjalo vacío para usar los habituales.",
+ "feedAllToggle": "Todas las publicaciones de estos relays (firehose)",
+ "feedAllCurated": "Solo publicaciones populares: oculta bots y publicaciones sin reacciones",
+ "feedAllNeedsRelays": "Indica los relays de los que leerlo todo."
}
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 970004e..31d07ba 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -881,5 +881,9 @@
"followHistRestored": "{n} abonnements restaurés.",
"securityTitle": "Sécurité",
"arkCoinsNone": "Aucune pièce dans Dépenses pour l’instant.",
- "profKeyLink": "Clé publique"
+ "profKeyLink": "Clé publique",
+ "feedRelaysFormHelp": "Lire ce fil uniquement depuis ces relais. Laisser vide pour les relais habituels.",
+ "feedAllToggle": "Toutes les publications de ces relais (firehose)",
+ "feedAllCurated": "Seulement les publications populaires : masquer les bots et les publications sans réaction",
+ "feedAllNeedsRelays": "Indiquez les relais dont tout lire."
}
diff --git a/src/locales/hi.json b/src/locales/hi.json
index 686bd6b..0d410e4 100644
--- a/src/locales/hi.json
+++ b/src/locales/hi.json
@@ -881,5 +881,9 @@
"followHistRestored": "{n} फ़ॉलो बहाल किए गए।",
"securityTitle": "सुरक्षा",
"arkCoinsNone": "स्पेंडिंग में अभी कोई कॉइन नहीं।",
- "profKeyLink": "पब्लिक की"
+ "profKeyLink": "पब्लिक की",
+ "feedRelaysFormHelp": "यह फ़ीड सिर्फ़ इन रिले से पढ़ें। सामान्य रिले के लिए खाली छोड़ें।",
+ "feedAllToggle": "इन रिले की हर पोस्ट (फ़ायरहोज़)",
+ "feedAllCurated": "सिर्फ़ लोकप्रिय पोस्ट: बॉट और बिना प्रतिक्रिया वाली पोस्ट छिपाएँ",
+ "feedAllNeedsRelays": "वे रिले बताएँ जिनसे सब कुछ पढ़ना है।"
}
diff --git a/src/locales/id.json b/src/locales/id.json
index a5b4f32..82c8e30 100644
--- a/src/locales/id.json
+++ b/src/locales/id.json
@@ -217,5 +217,9 @@
"followHistRestored": "{n} ikutan dipulihkan.",
"securityTitle": "Keamanan",
"arkCoinsNone": "Belum ada koin di Belanja.",
- "profKeyLink": "Kunci publik"
+ "profKeyLink": "Kunci publik",
+ "feedRelaysFormHelp": "Baca feed ini hanya dari relay ini. Kosongkan untuk relay biasa.",
+ "feedAllToggle": "Setiap postingan di relay ini (firehose)",
+ "feedAllCurated": "Hanya postingan populer: sembunyikan bot dan postingan tanpa reaksi",
+ "feedAllNeedsRelays": "Sebutkan relay untuk membaca semuanya."
}
diff --git a/src/locales/ja.json b/src/locales/ja.json
index b809144..c95f5d0 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -881,5 +881,9 @@
"followHistRestored": "{n}人のフォローを復元しました。",
"securityTitle": "セキュリティ",
"arkCoinsNone": "支出にはまだコインがありません。",
- "profKeyLink": "公開鍵"
+ "profKeyLink": "公開鍵",
+ "feedRelaysFormHelp": "このフィードはこれらのリレーからだけ読み込みます。空欄ならいつものリレーです。",
+ "feedAllToggle": "これらのリレーのすべての投稿(ファイアホース)",
+ "feedAllCurated": "人気の投稿のみ:ボットと反応のない投稿を隠す",
+ "feedAllNeedsRelays": "すべてを読み込むリレーを指定してください。"
}
diff --git a/src/locales/ko.json b/src/locales/ko.json
index e2e2500..40a9a40 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -880,5 +880,9 @@
"followHistRestored": "팔로우 {n}명을 복원했습니다.",
"securityTitle": "보안",
"arkCoinsNone": "지출에 아직 코인이 없습니다.",
- "profKeyLink": "공개 키"
+ "profKeyLink": "공개 키",
+ "feedRelaysFormHelp": "이 피드를 이 릴레이에서만 읽습니다. 비워 두면 평소 릴레이를 씁니다.",
+ "feedAllToggle": "이 릴레이의 모든 게시물 (파이어호스)",
+ "feedAllCurated": "인기 게시물만: 봇과 반응 없는 게시물 숨기기",
+ "feedAllNeedsRelays": "모두 읽어 올 릴레이를 지정하세요."
}
diff --git a/src/locales/mr.json b/src/locales/mr.json
index 6d443bb..8fdb6db 100644
--- a/src/locales/mr.json
+++ b/src/locales/mr.json
@@ -217,5 +217,9 @@
"followHistRestored": "{n} फॉलो पुनर्संचयित केले.",
"securityTitle": "सुरक्षा",
"arkCoinsNone": "स्पेंडिंगमध्ये अद्याप कोणतेही कॉइन नाहीत.",
- "profKeyLink": "सार्वजनिक की"
+ "profKeyLink": "सार्वजनिक की",
+ "feedRelaysFormHelp": "ही फीड फक्त या रिलेवरून वाचा. नेहमीच्या रिलेसाठी रिकामे ठेवा.",
+ "feedAllToggle": "या रिलेवरील प्रत्येक पोस्ट (फायरहोज)",
+ "feedAllCurated": "फक्त लोकप्रिय पोस्ट: बॉट्स आणि प्रतिसाद नसलेल्या पोस्ट लपवा",
+ "feedAllNeedsRelays": "सर्व काही कोणत्या रिलेवरून वाचायचे ते सांगा."
}
diff --git a/src/locales/pt.json b/src/locales/pt.json
index 63d15cf..3c832a2 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -881,5 +881,9 @@
"followHistRestored": "{n} seguidos restaurados.",
"securityTitle": "Segurança",
"arkCoinsNone": "Ainda não há moedas em Gastos.",
- "profKeyLink": "Chave pública"
+ "profKeyLink": "Chave pública",
+ "feedRelaysFormHelp": "Ler este feed só destes relays. Deixe em branco para os habituais.",
+ "feedAllToggle": "Todas as publicações destes relays (firehose)",
+ "feedAllCurated": "Só publicações populares: ocultar bots e publicações sem reações",
+ "feedAllNeedsRelays": "Indique os relays de onde ler tudo."
}
diff --git a/src/locales/ru.json b/src/locales/ru.json
index 98da4e2..6766b0e 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -881,5 +881,9 @@
"followHistRestored": "Восстановлено подписок: {n}.",
"securityTitle": "Безопасность",
"arkCoinsNone": "В расходах пока нет монет.",
- "profKeyLink": "Открытый ключ"
+ "profKeyLink": "Открытый ключ",
+ "feedRelaysFormHelp": "Читать эту ленту только с этих реле. Оставьте пустым для обычных.",
+ "feedAllToggle": "Все посты на этих реле (поток)",
+ "feedAllCurated": "Только популярные: скрыть ботов и посты без реакций",
+ "feedAllNeedsRelays": "Укажите реле, с которых читать всё."
}
diff --git a/src/locales/sw.json b/src/locales/sw.json
index efaad77..57ff264 100644
--- a/src/locales/sw.json
+++ b/src/locales/sw.json
@@ -217,5 +217,9 @@
"followHistRestored": "Umerejesha unaowafuata {n}.",
"securityTitle": "Usalama",
"arkCoinsNone": "Bado hakuna sarafu kwenye Matumizi.",
- "profKeyLink": "Ufunguo wa umma"
+ "profKeyLink": "Ufunguo wa umma",
+ "feedRelaysFormHelp": "Soma mlisho huu kutoka relay hizi tu. Acha tupu kwa zile za kawaida.",
+ "feedAllToggle": "Kila chapisho kwenye relay hizi (firehose)",
+ "feedAllCurated": "Machapisho maarufu tu: ficha boti na machapisho yasiyo na majibu",
+ "feedAllNeedsRelays": "Taja relay za kusoma kila kitu."
}
diff --git a/src/locales/ta.json b/src/locales/ta.json
index bc210a3..0cb5244 100644
--- a/src/locales/ta.json
+++ b/src/locales/ta.json
@@ -217,5 +217,9 @@
"followHistRestored": "{n} பின்தொடர்வுகள் மீட்டெடுக்கப்பட்டன.",
"securityTitle": "பாதுகாப்பு",
"arkCoinsNone": "செலவுக் கணக்கில் இன்னும் நாணயங்கள் இல்லை.",
- "profKeyLink": "பொது விசை"
+ "profKeyLink": "பொது விசை",
+ "feedRelaysFormHelp": "இந்த ஊட்டத்தை இந்த ரிலேக்களிலிருந்து மட்டும் படிக்கவும். வழக்கமானவற்றுக்கு காலியாக விடவும்.",
+ "feedAllToggle": "இந்த ரிலேக்களில் உள்ள ஒவ்வொரு பதிவும் (ஃபயர்ஹோஸ்)",
+ "feedAllCurated": "பிரபல பதிவுகள் மட்டும்: பாட்களையும் எதிர்வினையற்ற பதிவுகளையும் மறை",
+ "feedAllNeedsRelays": "அனைத்தையும் படிக்க வேண்டிய ரிலேக்களைக் குறிப்பிடவும்."
}
diff --git a/src/locales/te.json b/src/locales/te.json
index 44d7017..9f468e6 100644
--- a/src/locales/te.json
+++ b/src/locales/te.json
@@ -215,5 +215,9 @@
"followHistRestored": "{n} ఫాలోలు పునరుద్ధరించబడ్డాయి.",
"securityTitle": "భద్రత",
"arkCoinsNone": "ఖర్చు ఖాతాలో ఇంకా నాణేలు లేవు.",
- "profKeyLink": "పబ్లిక్ కీ"
+ "profKeyLink": "పబ్లిక్ కీ",
+ "feedRelaysFormHelp": "ఈ ఫీడ్‌ను ఈ రిలేల నుంచి మాత్రమే చదవండి. సాధారణ వాటికి ఖాళీగా ఉంచండి.",
+ "feedAllToggle": "ఈ రిలేలలోని ప్రతి పోస్ట్ (ఫైర్‌హోస్)",
+ "feedAllCurated": "జనాదరణ పొందిన పోస్ట్‌లు మాత్రమే: బాట్‌లు, స్పందన లేని పోస్ట్‌లను దాచు",
+ "feedAllNeedsRelays": "అన్నీ చదవాల్సిన రిలేలను పేర్కొనండి."
}
diff --git a/src/locales/tr.json b/src/locales/tr.json
index f22cb0e..6c441b2 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -880,5 +880,9 @@
"followHistRestored": "{n} takip geri yüklendi.",
"securityTitle": "Güvenlik",
"arkCoinsNone": "Harcama’da henüz coin yok.",
- "profKeyLink": "Açık anahtar"
+ "profKeyLink": "Açık anahtar",
+ "feedRelaysFormHelp": "Bu akışı yalnızca bu rölelerden oku. Her zamankiler için boş bırak.",
+ "feedAllToggle": "Bu rölelerdeki her gönderi (firehose)",
+ "feedAllCurated": "Yalnızca popüler gönderiler: botları ve tepki almamış gönderileri gizle",
+ "feedAllNeedsRelays": "Her şeyin okunacağı röleleri yaz."
}
diff --git a/src/locales/ur.json b/src/locales/ur.json
index a018085..c6a6fe4 100644
--- a/src/locales/ur.json
+++ b/src/locales/ur.json
@@ -217,5 +217,9 @@
"followHistRestored": "{n} فالو بحال ہو گئے۔",
"securityTitle": "سیکیورٹی",
"arkCoinsNone": "خرچ میں ابھی کوئی کوائن نہیں۔",
- "profKeyLink": "پبلک کی"
+ "profKeyLink": "پبلک کی",
+ "feedRelaysFormHelp": "یہ فیڈ صرف ان ریلے سے پڑھیں۔ معمول کے ریلے کے لیے خالی چھوڑ دیں۔",
+ "feedAllToggle": "ان ریلے پر ہر پوسٹ (فائر ہوز)",
+ "feedAllCurated": "صرف مقبول پوسٹس: بوٹس اور بغیر ردعمل والی پوسٹس چھپائیں",
+ "feedAllNeedsRelays": "وہ ریلے بتائیں جن سے سب کچھ پڑھنا ہے۔"
}
diff --git a/src/locales/vi.json b/src/locales/vi.json
index fefac38..963aa21 100644
--- a/src/locales/vi.json
+++ b/src/locales/vi.json
@@ -217,5 +217,9 @@
"followHistRestored": "Đã khôi phục {n} người theo dõi.",
"securityTitle": "Bảo mật",
"arkCoinsNone": "Chưa có coin nào trong Chi tiêu.",
- "profKeyLink": "Khóa công khai"
+ "profKeyLink": "Khóa công khai",
+ "feedRelaysFormHelp": "Chỉ đọc bảng tin này từ các relay này. Để trống để dùng relay thường dùng.",
+ "feedAllToggle": "Mọi bài đăng trên các relay này (firehose)",
+ "feedAllCurated": "Chỉ bài phổ biến: ẩn bot và bài không ai tương tác",
+ "feedAllNeedsRelays": "Hãy nêu các relay để đọc mọi thứ."
}
diff --git a/src/locales/zh.json b/src/locales/zh.json
index e09b385..c73d88e 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -881,5 +881,9 @@
"followHistRestored": "已恢复 {n} 个关注。",
"securityTitle": "安全",
"arkCoinsNone": "支出账户中还没有币。",
- "profKeyLink": "公钥"
+ "profKeyLink": "公钥",
+ "feedRelaysFormHelp": "仅从这些中继读取此动态。留空则使用常用中继。",
+ "feedAllToggle": "这些中继上的每一条帖子(全量流)",
+ "feedAllCurated": "仅热门帖子:隐藏机器人和无人互动的帖子",
+ "feedAllNeedsRelays": "请填写要读取全部内容的中继。"
}
diff --git a/src/style.css b/src/style.css
index 7ef428c..5bb171a 100644
--- a/src/style.css
+++ b/src/style.css
@@ -964,12 +964,15 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; }
side shadows carry the tint through the list's padding to its edges. */
.notes-feed > .row { transition: background-color 3s ease-out, box-shadow 3s ease-out; }
.notes-feed > .row.note-fresh {
- --fresh-bg: color-mix(in srgb, var(--green) 9%, var(--paper));
+ /* 9% was a whisper on white; dark mode keeps it (the green carries more there) */
+ --fresh-bg: color-mix(in srgb, var(--green) 16%, var(--paper));
background-color: var(--fresh-bg);
box-shadow: -14px 0 0 var(--fresh-bg), 14px 0 0 var(--fresh-bg);
}
+:root[data-theme="dark"] .notes-feed > .row.note-fresh { --fresh-bg: color-mix(in srgb, var(--green) 9%, var(--paper)); }
+
/* Inline move-money panel on the balance card. */
.balance-move { gap: 10px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); text-align: left; }
/* balance carousel: the other face peeks at the card's edge and drags into