aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
Diffstat (limited to 'src/features')
-rw-r--r--src/features/messages.js34
1 files changed, 29 insertions, 5 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 8f5adcb..a8815b0 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -14743,10 +14743,33 @@ export function messagesFeature(ctx) {
(ev) => { if (mergeNotifs([ev])) scheduleRepaint(); });
}
function stopNotifWatch() { if (notifUnsub) { try { notifUnsub(); } catch {} notifUnsub = null; } }
- // Shown as a notification: not from someone hidden, and not something your
- // follows flagged — the thread folds that post, so the tap would land on a
- // fold instead of the reply it promised (or the author, flagged enough).
+ // Counted as a notification: not from someone hidden, and not something
+ // your follows flagged — the page folds that row (notifFoldRow) rather
+ // than count it as news.
const notifShown = (x) => !hiddenPk(x.actor) && !reportsOn({ id: x.id, pubkey: x.actor });
+ // Who among your follows flagged this row's author or post, unless you
+ // asked to see it: the row is folded, not dropped — a reply from someone
+ // two follows reported as spam vanished from the page without a trace
+ // (Adam's phone, 2026-10-14), while the thread would have shown a fold.
+ const notifFlagged = (x) => (ui.revealed && ui.revealed.has(x.id)) ? null : reportsOn({ id: x.id, pubkey: x.actor });
+ function notifFoldRow(x, by) {
+ profileOf(x.actor);
+ for (const pk of by.slice(0, 3)) profileOf(pk);
+ const who = by.length <= 3 ? t('foldReported1', { name: by.map((pk) => displayName(pk)).join(', ') }) : t('foldReportedN', { n: by.length });
+ return h('div', { class: 'row alert-row note-folded', style: 'gap:10px;align-items:center;padding:10px 0;opacity:.75' },
+ avatar(x.actor, 'chat-avatar note-avatar', false),
+ h('div', { class: 'col grow', style: 'min-width:0;gap:3px' },
+ h('div', { class: 'row', style: 'gap:7px;align-items:baseline;min-width:0' },
+ h('span', { style: 'font-weight:600;font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis' }, displayName(x.actor)),
+ h('span', { class: 'small', style: 'min-width:0' }, notifLabel(x)),
+ h('span', { class: 'small faint', style: 'white-space:nowrap;margin-left:auto' }, timeLabel(x.ts * 1000))),
+ h('div', { class: 'small muted' }, '\u26a0 ' + who)),
+ // seen here, the reply is seen in its thread too (the same id)
+ h('button', {
+ class: 'btn-sm', type: 'button', style: 'flex-shrink:0',
+ onClick: (e) => { e.stopPropagation(); (ui.revealed ||= new Set()).add(x.id); render(); },
+ }, t('foldShow')));
+ }
const notifUnread = () => notifNow().items.filter((x) => x.ts > notifSeen() && notifShown(x)).length;
function markNotifsSeen() {
const newest = Math.max(0, ...notifNow().items.map((x) => x.ts));
@@ -14854,9 +14877,10 @@ export function messagesFeature(ctx) {
}
function notifView() {
const c = notifNow();
+ syncReports().catch(() => {}); // the folds below follow your follows' flags (throttled inside)
markNotifsSeen(); // anything that lands while you look is seen too
- const items = c.items.filter(notifShown); // a mute or report made later applies to what was kept
- const rows = items.flatMap((x, i) => [i ? noteSep() : null, notifRow(x)]);
+ const items = c.items.filter((x) => !hiddenPk(x.actor)); // a mute made later applies to what was kept
+ const rows = items.flatMap((x, i) => { const by = notifFlagged(x); return [i ? noteSep() : null, by ? notifFoldRow(x, by) : notifRow(x)]; });
return h('div', { class: 'card col chat-page', style: 'gap:10px' },
h('div', { class: 'row gap6', style: 'align-items:center' },
backBtn(() => { ui.msgView = 'home'; stopNotifWatch(); render(); }),