Skip to content

feat(settings): add toggle to disable in-app toast notifications#3088

Merged
pauldambra merged 2 commits into
mainfrom
posthog-code/disable-toast-notifications
Jul 2, 2026
Merged

feat(settings): add toggle to disable in-app toast notifications#3088
pauldambra merged 2 commits into
mainfrom
posthog-code/disable-toast-notifications

Conversation

@pauldambra

Copy link
Copy Markdown
Member

Summary

  • Adds a toastNotifications setting (default on) alongside the existing push/dock notification toggles
  • Gates all non-error toasts on it in the shared toast wrapper's emit() — a single choke point every toast.* call in the app goes through, so no per-feature changes were needed
  • Error toasts are exempt and always show
  • Adds an "In-app toasts" toggle to the Notifications settings page, wired into "Reset to defaults"

Test plan

  • pnpm --filter @posthog/ui typecheck
  • pnpm --filter @posthog/ui test (28/28 passing, including new parameterized suppression cases)
  • biome check on changed files

Created with PostHog Code

Toasts always showed unconditionally; users had no way to turn off
in-app notifications the way they can for push/dock notifications.
Gate is centralized in the shared toast wrapper's emit() so every
call site is covered without per-feature changes, and error toasts
are exempt so users never lose failure feedback.

Generated-By: PostHog Code
Task-Id: 6ea6d7f0-144f-4c02-9b23-f975bcd246f4
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit b039f86.

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(settings): add toggle to disable in..." | Re-trigger Greptile

Comment thread packages/ui/src/primitives/toast.tsx
@pauldambra pauldambra added the Stamphog This will request an autostamp by stamphog on small changes label Jul 2, 2026

@pauldambra pauldambra left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Swarm review complete. See inline comments.

Comment thread packages/ui/src/primitives/toast.tsx
Comment thread packages/ui/src/primitives/toast.tsx
Comment thread packages/ui/src/features/settings/settingsStore.ts
Comment thread packages/ui/src/primitives/toast.tsx
@pauldambra

Copy link
Copy Markdown
Member Author

Note

🤖 Automated comment by QA Swarm — not written by a human

Multi-perspective review: qa-team (specialists + generalists), paul-reviewer, xp-reviewer, security-audit

Verdict: 💬 APPROVE WITH NITS

Small, well-scoped change. The centralized gate in toast.tsx's emit() is the right call — one choke point instead of sprinkling guards across every feature. No CRITICAL/HIGH findings; one MEDIUM UX papercut plus several LOW/NIT items, none blocking.

Key findings

  • 🟡 MEDIUM (convergent: qa-team/frontend + qa-team/generalist-b + qa-team/copy) — The settings-page "Test" harness ("In-app toast" / "Deep-link toast" Send buttons, NotificationsSettings.tsx:503 & :512) silently does nothing when the new toggle is off, since it routes through the now-gated NotificationBus. Couldn't post this inline since those lines aren't part of this PR's diff (pre-existing code). Suggest disabling those buttons with a hint, or having the harness bypass the gate.
  • 🟢 LOW (convergent: paul + xp) — toast.tsx (a primitive) now imports the settings feature store — a mild layering inversion within @posthog/ui. Works today since getState() is read lazily, not a real cycle risk, but worth a thought.
  • 🟢 LOW (convergent: qa-team/generalist-a + qa-team/generalist-b + paul + xp) — The suppressed-emit early return (toast.tsx:51) drew independent attention from all four reviewers: it returns a fabricated/unregistered id, gives no observability into suppression, can strand a stale toast if the setting is toggled mid-upsert, and raises the question of whether suppressing loading toasts is deliberate. None of these bite today.
  • 🟢 LOW (qa-team/generalist-a) — notifications.ts:49's comment ("the in-app toast always shows") is now stale post-PR. Not inline-commentable (file untouched by this PR).
  • 🟢 LOW (qa-team/frontend) — useArchiveTask.ts:200's "Task archived" toast carries the only Undo action; disabling toasts silently drops undo-archive. Worth a deliberate call (exempt action-bearing toasts, or accept the tradeoff). Not inline-commentable (file untouched by this PR).
  • 🟢 LOW (qa-team/generalist-b) — Async store hydration means a brand-new session briefly serves the default (true) before the persisted value loads. Benign direction (over-shows, never under-shows).
  • NIT (xp) — toast.tsx:49's gate condition could be an extracted isSuppressed(level) predicate instead of a comment.

Convergence

  • Layering inversion (toast.tsx importing settingsStore) — paul + xp, independently.
  • Suppressed-emit return-value cluster (toast.tsx:51) — qa-team/generalist-a + qa-team/generalist-b + paul + xp, four independent angles on the same line.
  • Test-harness silent-no-op — qa-team/frontend + qa-team/generalist-b + qa-team/copy, independently within the qa-team pass.

Reviewer summaries

Reviewer Assessment
🔍 qa-team APPROVE WITH NITS — one MEDIUM UX papercut (test harness), rest LOW/NIT; security and performance sub-agents found nothing.
👤 paul "stamp anyway" — clean, well-scoped, loves the parameterized tests; flags the primitive/feature layering and silent-swallow observability as thoughts, not blockers.
📐 xp Textbook OnceAndOnlyOnce — one choke point instead of ShotgunSurgery across call sites; minor extract-method nit on the gate condition.
🛡 security-audit 0 findings — purely client-side local preference, no auth/network/data boundary crossed.

Automated by QA Swarm — not a human review

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The greptile bot raised a valid, unresolved concern on the current head: when toasts are suppressed and no caller-supplied id exists, emit returns "". A caller doing const id = toast.loading("Saving"); toast.dismiss(id) then passes "" to dismiss, which doesn't hit the undefined guard and forwards quillToast.dismiss("") — potentially dismissing unrelated toasts or behaving unexpectedly. The diff doesn't address this, and bot comments with valid concerns that are ignored require escalation.

@stamphog stamphog Bot removed the Stamphog This will request an autostamp by stamphog on small changes label Jul 2, 2026
…when suppressed

Suppressed toasts without a caller-supplied id previously returned "" from
emit(), an implicit sentinel that dismiss() would forward to quill. Return
undefined so the contract matches dismiss()'s existing undefined guard.

@pauldambra pauldambra left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Swarm round 2 review complete (follow-up fix commit). See inline comment.

Comment thread packages/ui/src/primitives/toast.test.ts
@pauldambra

Copy link
Copy Markdown
Member Author

Note

🤖 Automated comment by QA Swarm — not written by a human

Round 2 — reviewing follow-up commit b039f86 (fixes the round-1 fabricated-id finding)

Verdict: ✅ APPROVE

All six review passes converged: emit()'s string | undefined return type change is correct and complete, verified against every call site in the repo that captures a toast return value (connectivityToast.ts, ConfigureAgentsSection.tsx:295, useInboxCloudTaskRunner.ts:158 — all safe, two are toast.loading/exempt levels that never hit the suppressed branch). Tests pass (13/13 including the new regression case), typecheck clean.

Key findings

  • NIT (convergent: qa-team/generalist-a + xp) — the new parameterized test's expected tuple element always equals id; could collapse to one parameter. Optional, posted inline.
  • 🟢 LOW (qa-team/frontend, re-surfaced) — the pre-existing suppression-before-upsert-lookup edge case (a live stable-id toast created before the setting is toggled off won't receive its upsert/dismiss) was already flagged and resolved as informational in round 1's review — not re-posting as a new thread, just noting it's the same known, deferred item.
  • NIT (qa-team/generalist-b) — toast.error's signature now advertises string | undefined even though it can never actually return undefined (error is exempt from suppression); a narrower per-level overload would encode the invariant, but for one call site the current shape is a defensible simplicity trade-off.

Nothing blocking. Ready from qa-swarm's side.


Automated by QA Swarm — not a human review

@pauldambra pauldambra added the Stamphog This will request an autostamp by stamphog on small changes label Jul 2, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean feature addition with tests. The original empty-string ID bug (flagged by bots on an older commit) was fixed — suppressed emit() now returns undefined or the caller-supplied stable id, and connectivityToast.ts was updated to match. All inline comments are resolved. The soft layering concern (primitives importing a feature store) is acknowledged and non-blocking given the lazy getState() call.

@pauldambra pauldambra merged commit ed85464 into main Jul 2, 2026
26 checks passed
@pauldambra pauldambra deleted the posthog-code/disable-toast-notifications branch July 2, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stamphog This will request an autostamp by stamphog on small changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants