Skip to content

feat: add muteChange event with subscription-based muted state tracking - #85

Merged
saseungmin merged 5 commits into
mainfrom
feat/mute-change-event-sync
Mar 13, 2026
Merged

feat: add muteChange event with subscription-based muted state tracking#85
saseungmin merged 5 commits into
mainfrom
feat/mute-change-event-sync

Conversation

@saseungmin

@saseungmin saseungmin commented Mar 13, 2026

Copy link
Copy Markdown
Member
  • Add muteChange event for real-time muted state updates.
  • Forward muteChange through core/web/webview/react-native bridge layers.
  • Enable muted-state tracking only while muteChange is subscribed (performance optimization).
  • Keep replay mute-preservation behavior intact.
  • Update the example app to use useYouTubeEvent(player, 'muteChange', false) for muted state.
  • Update README docs (EN/KO, root + package) to document muteChange usage and tracking behavior.
import { YoutubeView, useYouTubeEvent, useYouTubePlayer } from 'react-native-youtube-bridge';

function App() {
  const player = useYouTubePlayer(videoIdOrUrl);

  // 1. State-based event listening
  const isMuted = useYouTubeEvent(player, 'muteChange', false);

  // 2. Callback-based event listening
  useYouTubeEvent(player, 'muteChange', (muted) => {
    console.log('Player is muted:', muted);
  });

  return <YoutubeView player={player} />;
}

Summary by CodeRabbit

  • New Features

    • Added a muteChange event to receive real-time muted state updates (UI button and programmatic mute/unMute).
    • useYouTubeEvent supports both state-based (e.g., isMuted via useYouTubeEvent(player, 'muteChange', false)) and callback-based subscriptions.
  • Behavior

    • Muted-state tracking is enabled only while muteChange is subscribed to (performance optimization).
  • Documentation

    • Examples and READMEs updated to show muteChange usage and migration to event-driven mute state.

@changeset-bot

changeset-bot Bot commented Mar 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f877593

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
react-native-youtube-bridge Minor
@react-native-youtube-bridge/core Minor
@react-native-youtube-bridge/web Minor
@react-native-youtube-bridge/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Mar 13, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22700279-2a36-476d-a0cf-b63c874bcd2b

📥 Commits

Reviewing files that changed from the base of the PR and between 40be6b4 and f877593.

📒 Files selected for processing (3)
  • example/src/App.tsx
  • packages/core/src/WebYoutubePlayerController.ts
  • packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts

📝 Walkthrough

Walkthrough

Adds a new muteChange event and end-to-end muted-state tracking: event emission from the iframe/player, propagation through core/web/bridge layers, a polling-based sync subsystem that activates only when there are active subscriptions, API hooks to enable/disable tracking, and docs/examples updated.

Changes

Cohort / File(s) Summary
Documentation & Examples
README.md, README-ko_kr.md, packages/react-native-youtube-bridge/README.md, packages/react-native-youtube-bridge/README-ko_kr.md, example/src/App.tsx
Documented muteChange usage (state- and callback-based), added example isMuted via useYouTubeEvent(player, 'muteChange', false), refactored example app to use event-driven mute state.
Core Types & Message Contracts
packages/core/src/types/index.ts, packages/core/src/types/webview.ts, packages/react-native-youtube-bridge/src/types/message.ts, packages/web/src/types/message.ts, packages/react-native-youtube-bridge/src/types/message.ts
Added onMuteChange and muteChange fields, introduced MuteChangeMessageData, extended MessageType/CommandType with muteChange and setMutedTrackingEnabled, and added setMutedTrackingEnabled to receive controls.
Core Player Controller
packages/core/src/WebYoutubePlayerController.ts
Implemented muted-state tracking (poll interval constant, tracking fields), setMutedTrackingEnabled public method, polling/sync helpers, updated ready/state handlers and cleanup to propagate onMuteChange and manage tracking lifecycle.
Web Player Bridge
packages/web/src/YoutubePlayer.tsx, packages/web/src/types/message.ts
Added onMuteChange callback to emit muteChange messages and updated receive controls typing for setMutedTrackingEnabled.
React Native Bridge - Player Management
packages/react-native-youtube-bridge/src/modules/YoutubePlayer.ts, packages/react-native-youtube-bridge/src/modules/WebviewYoutubePlayerController.ts
Added INTERNAL_SET_MUTE_TRACKING symbol and muteChangeSubscriptionCount, implemented internal handler to toggle tracking and wire setMutedTrackingEnabled forwarding via controller command.
React Native Bridge - UI & WebView
packages/react-native-youtube-bridge/src/YoutubeView.tsx, packages/react-native-youtube-bridge/src/YoutubeView.web.tsx
Added handling for incoming muteChange messages and wired onMuteChange to emit muteChange events to the RN side.
React Native Bridge - Hooks & Iframe Scripts
packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts, packages/react-native-youtube-bridge/src/hooks/youtubeIframeScripts.ts, packages/react-native-youtube-bridge/src/hooks/useYouTubeEvent.ts, packages/react-native-youtube-bridge/src/hooks/youtubeIframeScripts.ts
Added comprehensive mute-tracking subsystem in iframe scripts and local player HTML (sync/update/emit, start/stop polling, setMutedTrackingEnabled in playerCommands); useYouTubeEvent now toggles tracking when subscribing to muteChange.
Release Metadata
.changeset/flat-bats-greet.md
Added changeset documenting muteChange event, tracking behavior, and usage examples.

Sequence Diagram(s)

sequenceDiagram
    participant UI as User/UI
    participant Iframe as YouTube Iframe Player
    participant Core as WebYoutubePlayerController
    participant Bridge as Message Bridge (WebView)
    participant Hook as useYouTubeEvent Hook
    participant App as React Component

    Note over Hook: App subscribes to 'muteChange'
    Hook->>Bridge: INTERNAL_SET_MUTE_TRACKING(true)
    Bridge->>Core: setMutedTrackingEnabled(true)
    Core->>Core: Start polling (250ms)

    UI->>Iframe: click mute/unmute or call player.mute()/unMute()
    Iframe->>Core: onMuteChange(muted)

    Core->>Bridge: emit message { type: 'muteChange', muted }
    Bridge->>Hook: deliver 'muteChange' event
    Hook->>App: update isMuted state

    Note over Core: Polling: syncMutedStateFromPlayer()
    Core->>Iframe: query muted state
    Iframe-->>Core: current muted
    alt changed
        Core->>Bridge: emit 'muteChange'
        Bridge->>Hook: deliver event
        Hook->>App: update state
    end

    App->>Hook: unsubscribe 'muteChange'
    Hook->>Bridge: INTERNAL_SET_MUTE_TRACKING(false)
    Bridge->>Core: setMutedTrackingEnabled(false)
    Core->>Core: Stop polling
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 I nibble on code where the mute lights gleam,
I hop through events—quiet as a dream,
Polling the player in soft, timely beats,
Only when asked do I listen to seats,
A tiny rabbit, reporting muted streams.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main feature being added: a new muteChange event with subscription-based muted state tracking, which directly corresponds to the changeset's primary objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/mute-change-event-sync
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Mar 13, 2026

Copy link
Copy Markdown

Deploying react-native-youtube-bridge-example with  Cloudflare Pages  Cloudflare Pages

Latest commit: f877593
Status: ✅  Deploy successful!
Preview URL: https://9a2587ee.react-native-youtube-bridge-example.pages.dev
Branch Preview URL: https://feat-mute-change-event-sync.react-native-youtube-bridge-example.pages.dev

View logs

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying react-native-youtube-bridge with  Cloudflare Pages  Cloudflare Pages

Latest commit: 40be6b4
Status: ✅  Deploy successful!
Preview URL: https://a2b8bbf6.react-native-youtube-bridge.pages.dev
Branch Preview URL: https://feat-mute-change-event-sync.react-native-youtube-bridge.pages.dev

View logs

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/WebYoutubePlayerController.ts (1)

122-138: ⚠️ Potential issue | 🟡 Minor

Use the normalized ready value in the onReady payload too.

readyMuted already falls back to this.desiredMuted, but Line 130 still forwards playerInfo.muted. If that fallback path is hit, onReady and muteChange will disagree about the initial muted state.

Proposed fix
          this.callbacks.onReady?.({
            availablePlaybackRates: playerInfo.availablePlaybackRates,
            availableQualityLevels: playerInfo.availableQualityLevels,
            currentTime: playerInfo.currentTime,
            duration: playerInfo.duration,
-           muted: playerInfo.muted,
+           muted: readyMuted,
            playbackQuality: playerInfo.playbackQuality,
            playbackRate: playerInfo.playbackRate,
            playerState: playerInfo.playerState,
            size: playerInfo.size,
            volume: playerInfo.volume,
          });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/WebYoutubePlayerController.ts` around lines 122 - 138, The
onReady payload is still using playerInfo.muted instead of the normalized
readyMuted, which can cause a mismatch between the initial onReady state and the
later update via updateMutedState; change the muted field sent to
this.callbacks.onReady in WebYoutubePlayerController (the onReady call that
currently references playerInfo.muted) to use the computed readyMuted (which
falls back to this.desiredMuted) so the initial event and the
updateMutedState(readyMuted, true) call agree.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@example/src/App.tsx`:
- Line 44: The default value for the isMuted state from useYouTubeEvent does not
match the player's initial config (player initialized with muted: true), causing
a brief UI inconsistency; update the call to useYouTubeEvent(player,
'muteChange', true) so isMuted starts as true (referencing isMuted,
useYouTubeEvent, and the player initialization with muted: true) ensuring the
mute button reflects the real initial state until the first muteChange event
arrives.

In `@packages/core/src/WebYoutubePlayerController.ts`:
- Around line 297-306: The mute() and unMute() methods set desiredMuted and then
call updateMutedState even when no player exists, causing onMuteChange to fire
before createPlayer() attaches a player; change both methods (mute() and
unMute()) to set this.desiredMuted as before but bail out early if this.player
is null (i.e., only call this.player.mute()/unMute() and
this.updateMutedState(...) when this.player is present) so the event is emitted
only after an actual player is attached.

In `@packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts`:
- Around line 263-269: The mute/unMute handlers call updateMutedState before
checking for a real player, causing a muteChange to emit even when the
iframe/player isn't ready; adjust mute and unMute so they still record the
desired muted value (keep desiredMuted or the equivalent setter) immediately but
only call updateMutedState(...) and emit the change after confirming player
exists (i.e., only when player is truthy), then invoke
player.mute()/player.unMute(); reference the mute, unMute, player,
updateMutedState, and desiredMuted symbols when making the change.

---

Outside diff comments:
In `@packages/core/src/WebYoutubePlayerController.ts`:
- Around line 122-138: The onReady payload is still using playerInfo.muted
instead of the normalized readyMuted, which can cause a mismatch between the
initial onReady state and the later update via updateMutedState; change the
muted field sent to this.callbacks.onReady in WebYoutubePlayerController (the
onReady call that currently references playerInfo.muted) to use the computed
readyMuted (which falls back to this.desiredMuted) so the initial event and the
updateMutedState(readyMuted, true) call agree.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 154ff4b9-5ef4-47e4-a9c7-a7826e13bc7e

📥 Commits

Reviewing files that changed from the base of the PR and between 3cad8a0 and 40be6b4.

📒 Files selected for processing (19)
  • .changeset/flat-bats-greet.md
  • README-ko_kr.md
  • README.md
  • example/src/App.tsx
  • packages/core/src/WebYoutubePlayerController.ts
  • packages/core/src/types/index.ts
  • packages/core/src/types/webview.ts
  • packages/react-native-youtube-bridge/README-ko_kr.md
  • packages/react-native-youtube-bridge/README.md
  • packages/react-native-youtube-bridge/src/YoutubeView.tsx
  • packages/react-native-youtube-bridge/src/YoutubeView.web.tsx
  • packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts
  • packages/react-native-youtube-bridge/src/hooks/useYouTubeEvent.ts
  • packages/react-native-youtube-bridge/src/hooks/youtubeIframeScripts.ts
  • packages/react-native-youtube-bridge/src/modules/WebviewYoutubePlayerController.ts
  • packages/react-native-youtube-bridge/src/modules/YoutubePlayer.ts
  • packages/react-native-youtube-bridge/src/types/message.ts
  • packages/web/src/YoutubePlayer.tsx
  • packages/web/src/types/message.ts

Comment thread example/src/App.tsx Outdated
Comment thread packages/core/src/WebYoutubePlayerController.ts
Comment thread packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts Outdated
@saseungmin
saseungmin merged commit 65fa568 into main Mar 13, 2026
4 of 5 checks passed
@saseungmin
saseungmin deleted the feat/mute-change-event-sync branch March 13, 2026 11:19
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