feat(audio): sync background notification volume with video player#1429
feat(audio): sync background notification volume with video player#1429theglove44 wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="app/components/App/BackgroundNotifier.jsx" line_range="60-66" />
<code_context>
// Handle sound enable/disable and statement focus changes
useEffect(() => {
+ const playSound = (audioFile) => {
+ audioFile.volume = volume
+ audioFile.play()
+ }
+
</code_context>
<issue_to_address>
**suggestion:** Guard against play failures and ensure `Audio` state is safe for rapid successive calls.
Shared `Audio` instances can throw or return rejected promises on `play()` (e.g., autoplay policies, rapid preference toggles), and overlapping plays may share state. Consider wrapping `audioFile.play()` in a minimal guard/try-catch (ignoring failures if acceptable) and resetting `audioFile.currentTime = 0` before `play()` so repeated notifications consistently start from the beginning.
```suggestion
// Handle sound enable/disable and statement focus changes
useEffect(() => {
+ const playSound = (audioFile) => {
+ if (!audioFile) return
+
+ audioFile.volume = volume
+
+ try {
+ // Ensure rapid successive calls always start from the beginning
+ audioFile.currentTime = 0
+
+ const playPromise = audioFile.play()
+
+ // Guard against browsers returning a rejected play() promise
+ if (playPromise && typeof playPromise.then === 'function') {
+ playPromise.catch(() => {
+ // Intentionally ignore playback failures (e.g., autoplay restrictions)
+ })
+ }
+ } catch {
+ // Intentionally ignore synchronous playback errors
+ }
+ }
+
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d58c261a2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| useEffect(() => { | ||
| return () => { | ||
| internalPlayerRef.current?.removeEventListener?.('volumechange', syncVolume) | ||
| } | ||
| }, [syncVolume]) |
There was a problem hiding this comment.
Reattach volumechange listener after first volume update
This cleanup effect depends on syncVolume, so whenever syncVolume gets a new identity (which happens after setVolume updates context state), React runs this cleanup and removes the native volumechange listener—but nothing adds a new listener at that point because listener registration only happens in onReady. In practice, paused-volume tracking works once, then stops after the first volume change, so background notification volume can become stale until playback resumes.
Useful? React with 👍 / 👎.
Fixes CaptainFact/captain-fact#93
What changed
Three small, focused changes:
VideoPlaybackContext.jsx— addsvolume(default1) to the playback state, aSET_VOLUMEreducer case, and asetVolumeaction that only dispatches when the value actually changes.VideoDebatePlayer.jsx— addssyncVolume, which reads the current volume from the internal player on eachonProgressevent and ononReady. Supports HTML5<video>(player.volume, 0–1) and YouTube iframe API (player.getVolume(), 0–100). For HTML5 video it also attaches a nativevolumechangeDOM listener so volume changes while paused are captured immediately — no polling needed.BackgroundNotifier.jsx— readsvolumefromuseVideoPlayback()and applies it (audioFile.volume = volume) to every notification sound before.play().Why this approach
setIntervalpolling — volume is sampled on existing player events plus a native DOM listener.setVolumeis a no-op when the value has not changed.