diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/features/messages.js | 34 |
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(); }), |
