diff --git a/packages/react-native-video/src/core/VideoPlayer.web.ts b/packages/react-native-video/src/core/VideoPlayer.web.ts index a931b83c..29e4cbac 100644 --- a/packages/react-native-video/src/core/VideoPlayer.web.ts +++ b/packages/react-native-video/src/core/VideoPlayer.web.ts @@ -104,13 +104,11 @@ class VideoPlayer extends VideoPlayerEvents implements WebVideoPlayer { video.playsInline = true; const media = new WebMediaProxy(video); - super(new WebEventEmitter(media)); + const emitter = new WebEventEmitter(media); + // WebEventEmitter uses generic dispatch, cast to satisfy base class + super(emitter as any); this._media = media; - (this.eventEmitter as WebEventEmitter).addOnErrorListener((error) => { - this.triggerJSEvent('onError', error); - }); - this.replaceSourceAsync(source); } @@ -135,6 +133,11 @@ class VideoPlayer extends VideoPlayerEvents implements WebVideoPlayer { this._media.setStore(null); } + /** @internal */ + __getEmitter(): WebEventEmitter { + return this.eventEmitter as WebEventEmitter; + } + /** @internal */ __getMedia(): HTMLVideoElement { return this._media.video; diff --git a/packages/react-native-video/src/core/events/VideoPlayerEvents.native.ts b/packages/react-native-video/src/core/events/VideoPlayerEvents.native.ts index ad71751f..ae55862e 100644 --- a/packages/react-native-video/src/core/events/VideoPlayerEvents.native.ts +++ b/packages/react-native-video/src/core/events/VideoPlayerEvents.native.ts @@ -1,4 +1,7 @@ -import type { AllPlayerEvents as PlayerEvents } from '../types/Events'; +import type { + JSVideoPlayerEvents, + AllPlayerEvents as PlayerEvents, +} from '../types/Events'; import type { ListenerSubscription } from '../types/EventEmitter'; import { VideoPlayerEventsBase } from './VideoPlayerEventsBase'; @@ -8,7 +11,68 @@ export class VideoPlayerEvents extends VideoPlayerEventsBase { callback: PlayerEvents[Event] ): ListenerSubscription { switch (event) { - // ----------------- Native-only Events ----------------- + // --- JS-only events --- + case 'onError': + this.jsEventListeners.onError ??= new Set(); + this.jsEventListeners.onError.add( + callback as JSVideoPlayerEvents['onError'] + ); + return { + remove: () => + this.jsEventListeners.onError?.delete( + callback as JSVideoPlayerEvents['onError'] + ), + }; + // --- Shared events --- + case 'onBuffer': + return this.eventEmitter.addOnBufferListener( + callback as PlayerEvents['onBuffer'] + ); + case 'onEnd': + return this.eventEmitter.addOnEndListener( + callback as PlayerEvents['onEnd'] + ); + case 'onLoad': + return this.eventEmitter.addOnLoadListener( + callback as PlayerEvents['onLoad'] + ); + case 'onLoadStart': + return this.eventEmitter.addOnLoadStartListener( + callback as PlayerEvents['onLoadStart'] + ); + case 'onPlaybackStateChange': + return this.eventEmitter.addOnPlaybackStateChangeListener( + callback as PlayerEvents['onPlaybackStateChange'] + ); + case 'onPlaybackRateChange': + return this.eventEmitter.addOnPlaybackRateChangeListener( + callback as PlayerEvents['onPlaybackRateChange'] + ); + case 'onProgress': + return this.eventEmitter.addOnProgressListener( + callback as PlayerEvents['onProgress'] + ); + case 'onReadyToDisplay': + return this.eventEmitter.addOnReadyToDisplayListener( + callback as PlayerEvents['onReadyToDisplay'] + ); + case 'onSeek': + return this.eventEmitter.addOnSeekListener( + callback as PlayerEvents['onSeek'] + ); + case 'onTrackChange': + return this.eventEmitter.addOnTrackChangeListener( + callback as PlayerEvents['onTrackChange'] + ); + case 'onVolumeChange': + return this.eventEmitter.addOnVolumeChangeListener( + callback as PlayerEvents['onVolumeChange'] + ); + case 'onStatusChange': + return this.eventEmitter.addOnStatusChangeListener( + callback as PlayerEvents['onStatusChange'] + ); + // --- Native-only events --- case 'onAudioBecomingNoisy': return this.eventEmitter.addOnAudioBecomingNoisyListener( callback as PlayerEvents['onAudioBecomingNoisy'] @@ -38,7 +102,7 @@ export class VideoPlayerEvents extends VideoPlayerEventsBase { callback as PlayerEvents['onTextTrackDataChanged'] ); default: - return super.addEventListener(event, callback); + throw new Error(`[React Native Video] Unsupported event: ${event}`); } } } diff --git a/packages/react-native-video/src/core/events/VideoPlayerEvents.web.ts b/packages/react-native-video/src/core/events/VideoPlayerEvents.web.ts index 00d0e473..1f8b1ee3 100644 --- a/packages/react-native-video/src/core/events/VideoPlayerEvents.web.ts +++ b/packages/react-native-video/src/core/events/VideoPlayerEvents.web.ts @@ -1,16 +1,23 @@ import type { AllPlayerEvents as PlayerEvents } from '../types/Events'; import type { ListenerSubscription } from '../types/EventEmitter'; import { VideoPlayerEventsBase } from './VideoPlayerEventsBase'; +import type { WebEventEmitter } from '../web/WebEventEmitter'; +/** + * Web event dispatch — all events (including onError) go through + * WebEventEmitter.addListener(). No switch, no special cases. + * + * This file must exist so the bundler doesn't fall back to + * VideoPlayerEvents.ts which re-exports the native version. + */ export class VideoPlayerEvents extends VideoPlayerEventsBase { addEventListener( event: Event, callback: PlayerEvents[Event] ): ListenerSubscription { - switch (event) { - // Web-only events will be added here - default: - return super.addEventListener(event, callback); - } + return (this.eventEmitter as unknown as WebEventEmitter).addListener( + event, + callback as (...args: any[]) => void + ); } } diff --git a/packages/react-native-video/src/core/events/VideoPlayerEventsBase.ts b/packages/react-native-video/src/core/events/VideoPlayerEventsBase.ts index 5172e828..92f31309 100644 --- a/packages/react-native-video/src/core/events/VideoPlayerEventsBase.ts +++ b/packages/react-native-video/src/core/events/VideoPlayerEventsBase.ts @@ -31,74 +31,12 @@ export class VideoPlayerEventsBase { } addEventListener( - event: Event, - callback: PlayerEvents[Event] + _event: Event, + _callback: PlayerEvents[Event] ): ListenerSubscription { - switch (event) { - // ----------------- JS Events ----------------- - case 'onError': - this.jsEventListeners.onError ??= new Set(); - this.jsEventListeners.onError.add( - callback as JSVideoPlayerEvents['onError'] - ); - return { - remove: () => - this.jsEventListeners.onError?.delete( - callback as JSVideoPlayerEvents['onError'] - ), - }; - // ----------------- Shared Events ----------------- - case 'onBuffer': - return this.eventEmitter.addOnBufferListener( - callback as PlayerEvents['onBuffer'] - ); - case 'onEnd': - return this.eventEmitter.addOnEndListener( - callback as PlayerEvents['onEnd'] - ); - case 'onLoad': - return this.eventEmitter.addOnLoadListener( - callback as PlayerEvents['onLoad'] - ); - case 'onLoadStart': - return this.eventEmitter.addOnLoadStartListener( - callback as PlayerEvents['onLoadStart'] - ); - case 'onPlaybackStateChange': - return this.eventEmitter.addOnPlaybackStateChangeListener( - callback as PlayerEvents['onPlaybackStateChange'] - ); - case 'onPlaybackRateChange': - return this.eventEmitter.addOnPlaybackRateChangeListener( - callback as PlayerEvents['onPlaybackRateChange'] - ); - case 'onProgress': - return this.eventEmitter.addOnProgressListener( - callback as PlayerEvents['onProgress'] - ); - case 'onReadyToDisplay': - return this.eventEmitter.addOnReadyToDisplayListener( - callback as PlayerEvents['onReadyToDisplay'] - ); - case 'onSeek': - return this.eventEmitter.addOnSeekListener( - callback as PlayerEvents['onSeek'] - ); - case 'onTrackChange': - return this.eventEmitter.addOnTrackChangeListener( - callback as PlayerEvents['onTrackChange'] - ); - case 'onVolumeChange': - return this.eventEmitter.addOnVolumeChangeListener( - callback as PlayerEvents['onVolumeChange'] - ); - case 'onStatusChange': - return this.eventEmitter.addOnStatusChangeListener( - callback as PlayerEvents['onStatusChange'] - ); - default: - throw new Error(`[React Native Video] Unsupported event: ${event}`); - } + throw new Error( + '[React Native Video] addEventListener must be implemented by platform' + ); } clearAllEvents() { diff --git a/packages/react-native-video/src/core/video-view/VideoView.web.tsx b/packages/react-native-video/src/core/video-view/VideoView.web.tsx index 330b402b..8f2f314a 100644 --- a/packages/react-native-video/src/core/video-view/VideoView.web.tsx +++ b/packages/react-native-video/src/core/video-view/VideoView.web.tsx @@ -21,13 +21,17 @@ import { import { VideoSkin } from '@videojs/react/video'; import '@videojs/react/video/skin.css'; import type { VideoStore } from '../web/VideoStore'; +import { useViewEvents, addViewEventListener } from './useViewEvents.web'; const Player = createPlayer({ features: videoFeatures }); -/** - * Attaches the adapter's pre-existing