Skip to content

feat(settings): add random notification sound options#3094

Merged
pauldambra merged 5 commits into
mainfrom
posthog-code/random-notification-sounds
Jul 2, 2026
Merged

feat(settings): add random notification sound options#3094
pauldambra merged 5 commits into
mainfrom
posthog-code/random-notification-sounds

Conversation

@pauldambra

Copy link
Copy Markdown
Member

Summary

Adds two options to the notification "Sound effect" dropdown in Settings > Notifications:

  • Random (all) — picks a fresh sound per notification from the built-in sounds plus any installed custom sounds
  • Random (custom) — picks from the user's custom sounds only; the option is shown once at least one custom sound is installed (same condition as the existing Custom group)

Details

  • New CompletionSound values random-all / random-custom live alongside the custom:<id> sentinel rather than in BuiltInCompletionSound, which keys the SOUND_URLS asset record
  • Resolution happens in resolveSoundUrl, which is called per notification (and per press of the "Test sound" button), so each firing draws a fresh random pick with no extra plumbing
  • random-custom with an empty pool resolves to null and plays nothing, matching the existing deleted-custom-sound behaviour; removing the last custom sound while random-custom is active falls back to "None", extending the existing fallback in removeCustomSound

Testing

  • Extended sounds.test.ts: pool membership for both modes, null for random-custom with nothing installed, and a Math.random spy at both extremes proving custom sounds are reachable in the random-all pool
  • Extended the parameterised removeCustomSound test in settingsStore.test.ts with the random-custom fallback, plus a case verifying it stays active while other custom sounds remain

Created with PostHog Code

Add "Random (all)" and "Random (custom)" to the completion sound
dropdown. Random-all picks a fresh sound per notification from the
built-ins plus any installed custom sounds; random-custom draws from
custom sounds only and is offered once at least one is installed.
Removing the last custom sound while random-custom is active falls
back to silence, matching the existing deleted-sound behaviour.

Generated-By: PostHog Code
Task-Id: 2e7e511c-6b31-4480-8ac1-37863ddd7c3b
@trunk-io

trunk-io Bot commented Jul 2, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@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 729b5ea.

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(settings): add random notification ..." | Re-trigger Greptile

@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/utils/sounds.ts
Comment thread packages/ui/src/utils/sounds.ts
Comment thread packages/ui/src/features/settings/settingsStore.ts
@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

Small, well-tested feature with zero critical/high/medium findings across all four review perspectives. Everything flagged is a low-severity polish item or nit; nothing blocks merge.

Key findings

🟢 Low

  • random-custom can persist with an empty custom-sound library outside the guarded removeCustomSound path (rehydrate / downgrade window), leaving a blank Select trigger with orphaned volume/scale rows — normalize on rehydrate (convergent: 3 reviewers)
  • resolveSoundUrl is now nondeterministic and NotificationBus.notify resolves twice per native notification; only null-ness is consumed today so behaviour is correct, but the played and checked URLs are different rolls — a latent trap

⚪ Nits

  • customUrls is mapped on every call including paths that never use it (negligible)
  • Silent fallback to "None" when the last custom sound is removed could use a toast
  • customSounds.length > 0 guard now appears twice in the JSX — small local hasCustomSounds const

Convergence

  • The orphaned random-custom + empty-library state was flagged independently by qa-team/frontend, qa-team/generalist-b, and paul-reviewer — highest confidence finding
  • The per-call customUrls allocation was flagged independently by qa-team/performance and xp-reviewer

Reviewer summaries

Reviewer Assessment
🔍 qa-team Overall LOW: correct branch ordering, safe empty pools, solid tests; one convergent low-severity ghost-state gap
👤 paul "nice little feature, and honestly the tests carry it... perfetto. ship as you see fit"
📐 xp Clean work at "make it right"; strong parameterized tests, only minor duplication and incidental allocation
🛡 security-audit No exploitable vulnerabilities: pool is build-time assets + user's own local data URLs into a media-fetch sink; no trust boundary crossed

Automated by QA Swarm — not a human review

Adds a guard in the persist merge function so that if completionSound is
"random-custom" but customSounds is empty, it falls back to "none". Prevents
the Select trigger rendering a ghost value after a partial rehydrate or
app-version transition that leaves the library empty.

Generated-By: PostHog Code
Task-Id: 2e7e511c-6b31-4480-8ac1-37863ddd7c3b
Generated-By: PostHog Code
Task-Id: 2e7e511c-6b31-4480-8ac1-37863ddd7c3b

@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 follow-up review of the new commits (81b8e58, fca242c). See inline comments.

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

Copy link
Copy Markdown
Member Author

Note

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

Follow-up review of commits 81b8e58 + fca242c (merge() normalization fix, comment removal)

Verdict: ✅ APPROVE

The delta is a correct, well-targeted fix for the prior convergent finding — it handles missing-key, null, and empty-array shapes, correctly leaves random-all alone, and security review confirms it strictly narrows reachable state. Two low-severity follow-ups flagged, nothing blocking.

Key findings

🟢 Low

  • The new merge() normalization has no test, despite the test file having the exact rehydrate pattern needed (convergent: 4 reviewers)
  • The fix closes the random-custom ghost but not the equally-reachable orphaned custom:<id> ghost (pre-existing, convergent: 2 reviewers)

Reviewer summaries

Reviewer Assessment
🔍 qa-team LOW — fix verified correct across all persisted-state permutations; missing test is the one gap
👤 paul "nice fix... no correctness bugs in the delta. ship as you see fit"
📐 xp Clean, minimal, in the right place; two-place guard logic is acceptable divergence, not a OnceAndOnlyOnce violation
🛡 security-audit Zero findings — writes a hardcoded literal, strictly reduces reachable state

Automated by QA Swarm — not a human review

…ormalization

Pins the merge() guard that downgrades completionSound from
random-custom to none when the custom-sounds library is empty or absent
on rehydrate.

Generated-By: PostHog Code
Task-Id: 2e7e511c-6b31-4480-8ac1-37863ddd7c3b
Generated-By: PostHog Code
Task-Id: 2e7e511c-6b31-4480-8ac1-37863ddd7c3b

@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 follow-up review of the rehydrate-test commits (d9f0155, 729b5ea). One optional nit; verdict unchanged (APPROVE).

Comment thread packages/ui/src/features/settings/settingsStore.test.ts
@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.

Additive UI/settings feature with no production risk — new sound type values, proper edge-case guards at both runtime and rehydration, and good test coverage. All review comments are resolved.

@pauldambra pauldambra merged commit ea246be into main Jul 2, 2026
26 checks passed
@pauldambra pauldambra deleted the posthog-code/random-notification-sounds branch July 2, 2026 14:00
Gilbert09 added a commit that referenced this pull request Jul 3, 2026
Ports the desktop "Random" completion-sound option (#3094) to the mobile app.
Mobile has no custom sounds, so only the `random-all` equivalent is ported: a
new "Random" mode that picks a fresh built-in sound each time a task completes.

- Add "random" to the CompletionSound union and a "Random" entry to the sound picker.
- Resolve a random built-in asset per play via a shared pickRandom helper.
- Promote pickRandom to src/lib and reuse it in getRandomThinkingActivity.

Generated-By: PostHog Code
Task-Id: 2e74262a-f351-45b8-b98e-3cb0dd11df76
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.

1 participant