Skip to content

fix: notification sound settings sync#2092

Merged
arnestrickmann merged 2 commits into
mainfrom
emdash/claude-plays-notification-sound-even-though-off-ilbih
May 19, 2026
Merged

fix: notification sound settings sync#2092
arnestrickmann merged 2 commits into
mainfrom
emdash/claude-plays-notification-sound-even-though-off-ilbih

Conversation

@janburzinski
Copy link
Copy Markdown
Collaborator

summary

  • fixes notifcaiton settings sync

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 18, 2026

Greptile Summary

This PR replaces the old direct IPC read-and-assign pattern in soundPlayer.ts with a React Query cache subscription, so notification settings stay in sync with the rest of the app's query layer rather than being read once at startup.

  • initSoundPlayer now subscribes to the query cache for the ['appSettings', 'notifications', 'meta'] key and re-applies settings on every cache update; the module-level unsubscribeSettingsCache reference ensures only one active subscription exists at a time.
  • The settings object is pre-initialised with safe defaults so sounds work correctly during the async settings-load window, and play() now guards on both settings.enabled and settings.sound separately.

Confidence Score: 5/5

Safe to merge; the settings sync logic is straightforward and correctly handles re-initialisation through the module-level subscription reference.

The change is well-scoped to a single utility file. The default settings object prevents any regression in the startup sound window, and the subscription cleanup pattern is sound. The only notes are a defensive null-check in applyMeta and the discarded return value at the call site, neither of which affects current runtime behaviour.

No files require special attention beyond the minor guard in applyMeta noted in the review.

Important Files Changed

Filename Overview
src/renderer/utils/soundPlayer.ts Refactors notification settings sync to use the React Query cache subscription pattern instead of direct IPC reads; adds proper cleanup tracking via a module-level unsubscribe reference and sensible default settings to avoid a silent-sound window at startup.

Sequence Diagram

sequenceDiagram
    participant Main as main.tsx
    participant SP as soundPlayer.ts
    participant IPC as rpc.appSettings
    participant QC as QueryClient cache

    Main->>SP: initSoundPlayer()
    SP->>IPC: getWithMeta('notifications') [async]
    SP->>QC: getQueryCache().subscribe(handler)
    SP-->>Main: returns cleanup fn (discarded)

    IPC-->>SP: .then(meta)
    SP->>QC: setQueryData(['appSettings','notifications','meta'], meta)
    QC->>SP: cache event fired
    SP->>SP: applyMeta(event.query.state.data)
    SP->>SP: "settings = meta.value"

    Note over QC,SP: Any future settings change in the cache triggers applyMeta via subscription

    SP->>SP: play(event, appFocused)
    SP->>SP: guard: settings.enabled, settings.sound, soundFocusMode
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
src/renderer/utils/soundPlayer.ts:17-20
If `applyMeta` receives a meta object where `value` is `null` or `undefined` (e.g. the IPC returns `{ value: null }`), the `'value' in meta` guard passes but `settings` is assigned `null`. Every subsequent `play()` call then throws a `TypeError` on `settings.enabled`. Adding a nullish check on the value itself prevents this.

```suggestion
function applyMeta(meta: unknown): void {
  if (!meta || typeof meta !== 'object' || !('value' in meta)) return;
  const value = (meta as { value: NotificationSettings }).value;
  if (!value) return;
  settings = value;
}
```

### Issue 2 of 2
src/renderer/utils/soundPlayer.ts:30-31
`initSoundPlayer()` now returns a cleanup function, but the call site in `main.tsx` discards it (`initSoundPlayer();`). The module-level `unsubscribeSettingsCache` reference handles re-initialisation correctly, but the returned function is the intended external tear-down handle. Capturing it at the call site would make the contract explicit and keep the API usable if cleanup is ever needed (e.g., during hot-module replacement or testing).

```suggestion
  // NOTE: callers should store and invoke the returned cleanup function.
  unsubscribeSettingsCache?.();
  const unsubscribe = queryClient.getQueryCache().subscribe((event) => {
```

Reviews (2): Last reviewed commit: "Fix sound player settings subscription" | Re-trigger Greptile

Comment thread src/renderer/utils/soundPlayer.ts Outdated
Comment thread src/renderer/utils/soundPlayer.ts
Comment thread src/renderer/utils/soundPlayer.ts Outdated
@janburzinski
Copy link
Copy Markdown
Collaborator Author

@greptileai

Copy link
Copy Markdown
Contributor

@arnestrickmann arnestrickmann left a comment

Choose a reason for hiding this comment

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

LGTM — thanks! Updating the user who reported this!

@arnestrickmann arnestrickmann merged commit f899643 into main May 19, 2026
1 check passed
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.

2 participants