fix(notifications): timed mutes are lifted by any Lotus device, not just the one that set them (#333) #393

Merged
jared merged 1 commits from fix/mute-timers-account-data into lotus 2026-10-18 02:22:22 -04:00
Owner

Fixes #333.

A timed mute ("Mute for 1 hour") writes a server-side push rule, so the room is muted on every device. The unmute timer, though, lived only in one browser's localStorage:

  • Close that laptop, and the room stayed muted on the phone until the laptop was opened again.
  • Log out there, and it stayed muted forever.

Change

  • Timers move to account data io.lotus.mute_timers, as { timers: { [roomId]: unmuteAt } }.
  • MuteTimerRestore runs on every Lotus device. It schedules from that account data and reschedules whenever it changes, so the first device online after expiry lifts the mute. unmuteRoom keeps its guard: it doesn't reset a mode the user changed by hand.
  • An indefinite mute now clears a pending timer. Before, it could be lifted by an old timer.
  • Timers from before this change, still in localStorage, are migrated into account data once.

Verified (two browser sessions of one account, local Synapse):

  1. Device A mutes Zebra Lounge for 1 hour from the sidebar menu: push rule set, timer in account data.
  2. Device A closes, and the timer's end is moved to 8 s from now.
  3. Device B opens. It was never involved, yet at expiry the room is unmuted and the timer is removed.

Unit tests for the account-data format. tsc clean · unit tests pass · ESLint clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Fixes #333. A timed mute ("Mute for 1 hour") writes a **server-side** push rule, so the room is muted on every device. The unmute timer, though, lived only in **one browser's localStorage**: - Close that laptop, and the room stayed muted on the phone until the laptop was opened again. - Log out there, and it stayed muted forever. **Change** - Timers move to account data `io.lotus.mute_timers`, as `{ timers: { [roomId]: unmuteAt } }`. - `MuteTimerRestore` runs on every Lotus device. It schedules from that account data and reschedules whenever it changes, so the first device online after expiry lifts the mute. `unmuteRoom` keeps its guard: it doesn't reset a mode the user changed by hand. - An **indefinite** mute now clears a pending timer. Before, it could be lifted by an old timer. - Timers from before this change, still in localStorage, are migrated into account data once. **Verified** (two browser sessions of one account, local Synapse): 1. Device A mutes Zebra Lounge for 1 hour from the sidebar menu: push rule set, timer in account data. 2. Device A closes, and the timer's end is moved to 8 s from now. 3. Device B opens. It was never involved, yet at expiry the room is **unmuted** and the timer is **removed**. Unit tests for the account-data format. `tsc` clean · unit tests pass · ESLint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 1 commit 2026-10-17 20:33:52 -04:00
fix(notifications): timed mutes are lifted by any Lotus device, not just the one that set them (#333)
CI / Build & Quality Checks (pull_request) Successful in 3m3s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 8s
CI / Playwright smoke (e2e) (pull_request) Successful in 16m45s
69e05bbf8d
A timed mute writes a server-side push rule (muted everywhere), but the
unmute timer lived in one browser's localStorage: close that laptop and
the room stayed muted on the phone until it was reopened, and forever
after a logout there. Timers now live in account data
(io.lotus.mute_timers: { timers: { [roomId]: unmuteAt } }). Every
client schedules from it, reschedules when it changes, and the first one
online after expiry unmutes. An indefinite mute clears a pending timer.
Local timers from before are migrated once.

Fixes #333

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared merged commit 667925733e into lotus 2026-10-18 02:22:22 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#393