Skip to content

[loop cycle 4] fix(notifications): de-duplicate burst-repeated native toasts#12

Merged
karem505 merged 1 commit into
masterfrom
whatrust-loop/cycle-4-notification-dedupe
Jun 28, 2026
Merged

[loop cycle 4] fix(notifications): de-duplicate burst-repeated native toasts#12
karem505 merged 1 commit into
masterfrom
whatrust-loop/cycle-4-notification-dedupe

Conversation

@karem505

Copy link
Copy Markdown
Owner

What

Stops the same notification showing up two or three times. WhatsApp can raise the same alert more than once in a quick burst (a React re-render, or one message surfacing through two notification code paths). A browser collapses those by tag, but a native OS toast stacks every call as a separate visible popup.

How

Add a nativeNotify(title, body) helper that suppresses an identical title+body seen within a 3500ms window (pruning stale keys each call so the map stays bounded), and route the window.Notification shim through it.

  • Distinct messages — including different messages from the same chat — are never suppressed.
  • An identical alert is allowed again once the window expires.
  • Title/body coercion is unchanged; lock/settings suppression still happens downstream in the Rust notify command.
  • Map is Object.create(null) (no prototype-key hazards).

Verification

Gate 1 — cargo build --locked + cargo test: PASS (51 tests; bridge embedded via include_str!).

Gate 2 — real WebKitGTK engine harness (origin spoofed, invoke recorder): PASS

  • 5 fires, 2 identical immediate duplicates → 3 native notifies
  • distinct title/body messages all delivered
  • the same alert re-fires after the 3500ms window expires (4 total)

Gate 3 — generation-blind code review: APPROVE, severity none, no must-fix. Confirmed dedup correctness, Object.create(null) key safety, safe for..in+delete prune, strict-mode legality, behavior parity.

🐞 A latent crash was caught and fixed during this cycle. An earlier iteration used a NUL byte as the dedup-key separator. JS string literals can contain NUL, so node --check passed — but the script is handed to WebKit as a C string, so the NUL truncated the entire injected bridge.js at that line (which would have silently broken drag-drop, unread counts, and notifications in the real app). The real-engine harness surfaced it; the separator is now a plain space and the file is pure ASCII.


🤖 PR-ONLY — do not auto-merge. Releasing whatRust is manual via a v* tag; this loop never merges, bumps the version, or tags a release. Opened by the whatrust-fix-loop (cycle 4/6).

WhatsApp can raise the same alert more than once in a quick burst (a React re-render,
or one message surfacing through two notification code paths). A browser collapses
those by tag, but a native OS toast stacks each call as a separate visible popup, so
the user sees the same notification two or three times.

Add a nativeNotify(title, body) helper that suppresses an identical title+body seen
within a 3500ms window (pruning stale keys each call so the map stays bounded), and
route the window.Notification shim through it. Distinct messages — including different
messages from the same chat — are never suppressed, and an identical alert is allowed
again once the window expires. The title/body coercion is unchanged, and lock/settings
suppression still happens downstream in the Rust notify command.

Verified in a real WebKitGTK engine (origin spoofed to web.whatsapp.com): 5 fires with
2 identical immediate duplicates -> 3 native notifies; distinct title/body messages all
delivered; the same alert re-fires after the window expires.

Note: the dedup key uses a single-space separator. An earlier iteration used a NUL byte,
which (being passed to the engine as a C string) truncated the whole injected script;
that was caught by the engine harness and fixed before this commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016o9cWBaPy4zU4BAurUVoTp
karem505 added a commit that referenced this pull request Jun 28, 2026
Bundles six loop-shipped fixes:
- perf(dnd): stream dropped files as base64 to cut peak memory on large videos (#9)
- fix(notifications): forward service-worker showNotification to the native toast (#10)
- feat(calls): expose a minimal window.chrome so WhatsApp enables call buttons (#11)
- fix(notifications): de-duplicate burst-repeated native toasts (#12)
- fix(dnd): route AVIF/HEIF photos as photos; broaden MIME labels (#13)
- fix(calls/capability): return a complete Chrome high-entropy client-hints set (#14)
Plus integration: SW notifications share the dedup window; base64_encode is test-only now.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016o9cWBaPy4zU4BAurUVoTp
@karem505 karem505 merged commit 09cfffc into master Jun 28, 2026
6 checks passed
@karem505 karem505 deleted the whatrust-loop/cycle-4-notification-dedupe branch June 28, 2026 01:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant