block / block/buzz

feat: Slack-parity per-channel notification settings — levels, temporary mute, mute-and-hide, broadcast opt-out

Open
#3,160 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

> 🤖 Filed by kray's AI agent on his behalf.

## Problem

The only per-channel notification control in Buzz is a binary mute (#838). That single bit can't express the states people actually live in:

- **"This channel is busy but I can't leave it."** A high-traffic channel bolds, dots, and dock-bounces on every message. The only relief is full mute — which also kills the signal you *do* want. There is no "just tell me when I'm mentioned."
- **"Quiet this for the afternoon."** A launch thread is blowing up, you're heads-down for an hour. Muting works, but nothing reminds you to unmute — the channel stays silent forever, and Buzz has no temporary mute at all.
- **"I'm done with this channel but can't leave it."** Muted channels still occupy sidebar rows (dimmed, but present). There's no way to hide a channel and trust it to resurface when someone actually mentions you.
- **"@channel is too much for *this* channel."** #3146 adds `@channel`/`@here`. Slack learned that channel-wide mentions need a per-channel escape valve ("Get broadcast messages"); without one, the only defense against an @channel-happy channel is full mute.

Slack users reach for all four by muscle memory: a three-level notification setting, "Mute until…", "Mute and hide", and per-channel broadcast opt-out. Buzz has none of them — and issue #100 (the original notification-system issue) closed without any follow-up for per-channel granularity, so this is greenfield in the tracker.

## Proposal

Slack-parity per-channel notification settings, desktop first:

- **Notification level** per channel — **All new posts** (default; today's exact behavior) / **Just mentions** (channel still shows unread, but no dock bounce or alerts for ordinary posts) / **Mute and hide** (today's mute semantics, plus the channel leaves the sidebar — resurfacing while active or when it holds an unread mention, Slack-style; still reachable from the channel browser).
- **Temporary mute** — "For 1 hour" / "Until tomorrow" (9am), stored as an absolute timestamp, evaluated lazily so it syncs correctly across devices and survives restarts. Expiry restores the previous level automatically. Reuses the reminders time-preset machinery (#1093).
- **Per-channel advanced settings** (member-scoped, in the channel sheet): **Desktop notifications** on/off, **Follow every thread** (every thread in this channel behaves as followed — the per-channel inverse of #761's per-thread follows), **Get broadcast messages** (opt out of `@channel`/`@here` from #3146 for this channel), and an **Edit default preferences** link to Settings → Notifications.

### Storage: a new synced prefs blob, `channel-notify-prefs`

A new kind-30078 d-tag (NIP-78, NIP-44 self-encrypted, per-channel `updatedAt` LWW), following the shipped `channel-mutes`/`channel-stars`/`channel-sort` pattern (#838/#860/#1556) — documented as a Buzz-local NIP (`docs/nips/NIP-CN.md`). A **separate document rather than extending `channel-mutes`**, so older clients can't strip the new fields on their next LWW round-trip (#2947's rationale). The legacy mute blob is dual-written for the mute dimension, so existing clients (including mobile) keep working unchanged. Entries are sparse — only channels that diverge from the default are stored (the NIP-44 blob-size wall from #1309). Zero relay changes: kind 30078 is already generic.

Nothing in the wider Nostr ecosystem covers this — NIP-51 mute lists have no channel entry type and no expiry, no NIP or draft defines notification preferences, and no NIP-29 client syncs any per-channel notification state (survey: Chachi, 0xchat, Flotilla, notepush). Matrix's `m.push_rules` and MSC3952 are the closest well-specified analogs; like them, the design states an explicit precedence chain (direct mention → @channel/@here gated by level + broadcast opt-out → level → thread follows) instead of ad-hoc flags.

### Semantics worth calling out

- **Direct @name mentions pierce everything**, including Mute and hide (badge + alert) — unchanged from today, and the Slack behavior.
- **Channel mutes suppress `@channel`/`@here`** — the rule #3146 already established; the per-channel "Get broadcast messages" toggle extends it to non-muted channels.
- **NIP-CW `["broadcast","1"]` thread replies stop piercing mutes** — today they short-circuit ahead of the mute check, which contradicts both #3146's rule and the workspace rail (which already excludes muted channels, #1738). They become ordinary posts under the level. Intentional behavior change, flagged for review.
- **Level "All new posts" adds no per-post OS banners.** VISION.md: "Zero is the default. You opt in to noise, not out." The default level reproduces today's behavior exactly (#1505's default-preserving rollout rule); a per-post banner class would be a separate opt-in follow-up.

## Out of scope / follow-ups

- Mobile UI + push (mobile has no notification settings surface or push registration yet; the schema reserves a `mobile` toggle, and per-channel push suppression is a client-authored NIP-PL lease projection per NIP-PL's own non-goals) — follow-up issue.
- Synced global default preferences ("Edit default preferences" links to the existing device-local settings card).
- Admin-side policy ("only admins may @channel") — belongs with #2497.
- Per-channel sounds / per-post banner class.

## Prior art

#838 (binary channel mute — the store this extends), #761 (thread follow/mute + the notify predicate), #968 (override-inherits-default settings pane), #802 (two-tier badge classifier), #1093 (snooze/time presets), #1738/#1747 (rail-observer mute divergence bugs this consolidates behind one resolver), #157/#857 (DM hide — the only existing hide mechanism), #3146 (@channel/@here), #2947 (separate-document forward-compat precedent), #100 (original notification system issue, closed without per-channel follow-up).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.