mirror of
https://github.com/zoriya/react-native-video.git
synced 2025-12-05 23:06:14 +00:00
Rework event handler to use add/remove styyle
This commit is contained in:
@@ -87,17 +87,17 @@ import { VideoPlayer } from 'react-native-video';
|
||||
|
||||
const player = new VideoPlayer('https://example.com/video.mp4');
|
||||
|
||||
player.onLoad = (data) => {
|
||||
player.addEventListener('onLoad', (data) => {
|
||||
console.log('Video loaded! Duration:', data.duration);
|
||||
};
|
||||
});
|
||||
|
||||
player.onProgress = (data) => {
|
||||
player.addEventListener('onProgress', (data) => {
|
||||
console.log('Current time:', data.currentTime);
|
||||
};
|
||||
});
|
||||
|
||||
player.onError = (error) => {
|
||||
player.addEventListener('onError', (error) => {
|
||||
console.error('Player Error:', error.code, error.message);
|
||||
};
|
||||
});
|
||||
|
||||
player.play();
|
||||
```
|
||||
@@ -105,4 +105,4 @@ player.play();
|
||||
## Clearing Events
|
||||
|
||||
- The `player.clearEvent(eventName)` method can be used to clear a specific native event handler.
|
||||
- When a player instance is no longer needed and `player.release()` is called, all event listeners are automatically cleared
|
||||
- When a player instance is no longer needed and `player.release()` is called, all event listeners are automatically cleared
|
||||
|
||||
@@ -20,8 +20,6 @@ import { VideoPlayerEvents } from './VideoPlayerEvents';
|
||||
class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
|
||||
protected player: VideoPlayerImpl;
|
||||
|
||||
public onError?: (error: VideoRuntimeError) => void = undefined;
|
||||
|
||||
constructor(source: VideoSource | VideoConfig | VideoPlayerSource) {
|
||||
const hybridSource = createSource(source);
|
||||
const player = createPlayer(hybridSource);
|
||||
@@ -57,8 +55,10 @@ class VideoPlayer extends VideoPlayerEvents implements VideoPlayerBase {
|
||||
private throwError(error: unknown) {
|
||||
const parsedError = tryParseNativeVideoError(error);
|
||||
|
||||
if (parsedError instanceof VideoRuntimeError && this.onError) {
|
||||
this.onError(parsedError);
|
||||
if (
|
||||
parsedError instanceof VideoRuntimeError
|
||||
&& this.triggerEvent('onError', parsedError)
|
||||
) {
|
||||
// We don't throw errors if onError is provided
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { VideoPlayerEventEmitter } from '../spec/nitro/VideoPlayerEventEmitter.nitro';
|
||||
import type { VideoPlayerEvents as VideoPlayerEventsInterface } from './types/Events';
|
||||
import type { AllPlayerEvents as PlayerEvents } from './types/Events';
|
||||
|
||||
export class VideoPlayerEvents implements VideoPlayerEventsInterface {
|
||||
export class VideoPlayerEvents {
|
||||
protected eventEmitter: VideoPlayerEventEmitter;
|
||||
protected eventListeners: Partial<Record<keyof PlayerEvents, Set<(...params: any[]) => void>>> = {};
|
||||
|
||||
protected readonly supportedEvents: (keyof VideoPlayerEventsInterface)[] = [
|
||||
protected readonly supportedEvents: (keyof PlayerEvents)[] = [
|
||||
'onAudioBecomingNoisy',
|
||||
'onAudioFocusChange',
|
||||
'onBandwidthUpdate',
|
||||
@@ -28,6 +29,37 @@ export class VideoPlayerEvents implements VideoPlayerEventsInterface {
|
||||
|
||||
constructor(eventEmitter: VideoPlayerEventEmitter) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
for (let event of this.supportedEvents){
|
||||
// @ts-expect-error we narrow the type of the event
|
||||
this.eventEmitter[event] = this.triggerEvent.bind(this, event);
|
||||
}
|
||||
}
|
||||
|
||||
protected triggerEvent<Event extends keyof PlayerEvents>(
|
||||
event: Event,
|
||||
...params: Parameters<PlayerEvents[Event]>
|
||||
): boolean {
|
||||
if (!this.eventListeners[event]?.size)
|
||||
return false;
|
||||
for (let fn of this.eventListeners[event]) {
|
||||
fn(...params);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
addEventListener<Event extends keyof PlayerEvents>(
|
||||
event: Event,
|
||||
callback: PlayerEvents[Event]
|
||||
) {
|
||||
this.eventListeners[event] ??= new Set<PlayerEvents[Event]>();
|
||||
this.eventListeners[event].add(callback);
|
||||
}
|
||||
|
||||
removeEventListener<Event extends keyof PlayerEvents>(
|
||||
event: Event,
|
||||
callback: PlayerEvents[Event]
|
||||
) {
|
||||
this.eventListeners[event]!.delete(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,177 +75,7 @@ export class VideoPlayerEvents implements VideoPlayerEventsInterface {
|
||||
* Clears a specific event from the event emitter.
|
||||
* @param event - The name of the event to clear.
|
||||
*/
|
||||
clearEvent(event: keyof VideoPlayerEventsInterface) {
|
||||
this.eventEmitter[event] = VideoPlayerEvents.NOOP;
|
||||
}
|
||||
|
||||
static NOOP = () => {};
|
||||
|
||||
set onAudioBecomingNoisy(
|
||||
value: VideoPlayerEventsInterface['onAudioBecomingNoisy']
|
||||
) {
|
||||
this.eventEmitter.onAudioBecomingNoisy = value;
|
||||
}
|
||||
|
||||
get onAudioBecomingNoisy(): VideoPlayerEventsInterface['onAudioBecomingNoisy'] {
|
||||
return this.eventEmitter.onAudioBecomingNoisy;
|
||||
}
|
||||
|
||||
set onAudioFocusChange(
|
||||
value: VideoPlayerEventsInterface['onAudioFocusChange']
|
||||
) {
|
||||
this.eventEmitter.onAudioFocusChange = value;
|
||||
}
|
||||
|
||||
get onAudioFocusChange(): VideoPlayerEventsInterface['onAudioFocusChange'] {
|
||||
return this.eventEmitter.onAudioFocusChange;
|
||||
}
|
||||
|
||||
set onBandwidthUpdate(
|
||||
value: VideoPlayerEventsInterface['onBandwidthUpdate']
|
||||
) {
|
||||
this.eventEmitter.onBandwidthUpdate = value;
|
||||
}
|
||||
|
||||
get onBandwidthUpdate(): VideoPlayerEventsInterface['onBandwidthUpdate'] {
|
||||
return this.eventEmitter.onBandwidthUpdate;
|
||||
}
|
||||
|
||||
set onBuffer(value: VideoPlayerEventsInterface['onBuffer']) {
|
||||
this.eventEmitter.onBuffer = value;
|
||||
}
|
||||
|
||||
get onBuffer(): VideoPlayerEventsInterface['onBuffer'] {
|
||||
return this.eventEmitter.onBuffer;
|
||||
}
|
||||
|
||||
set onControlsVisibleChange(
|
||||
value: VideoPlayerEventsInterface['onControlsVisibleChange']
|
||||
) {
|
||||
this.eventEmitter.onControlsVisibleChange = value;
|
||||
}
|
||||
|
||||
get onControlsVisibleChange(): VideoPlayerEventsInterface['onControlsVisibleChange'] {
|
||||
return this.eventEmitter.onControlsVisibleChange;
|
||||
}
|
||||
|
||||
set onEnd(value: VideoPlayerEventsInterface['onEnd']) {
|
||||
this.eventEmitter.onEnd = value;
|
||||
}
|
||||
|
||||
get onEnd(): VideoPlayerEventsInterface['onEnd'] {
|
||||
return this.eventEmitter.onEnd;
|
||||
}
|
||||
|
||||
set onExternalPlaybackChange(
|
||||
value: VideoPlayerEventsInterface['onExternalPlaybackChange']
|
||||
) {
|
||||
this.eventEmitter.onExternalPlaybackChange = value;
|
||||
}
|
||||
|
||||
get onExternalPlaybackChange(): VideoPlayerEventsInterface['onExternalPlaybackChange'] {
|
||||
return this.eventEmitter.onExternalPlaybackChange;
|
||||
}
|
||||
|
||||
set onLoad(value: VideoPlayerEventsInterface['onLoad']) {
|
||||
this.eventEmitter.onLoad = value;
|
||||
}
|
||||
|
||||
get onLoad(): VideoPlayerEventsInterface['onLoad'] {
|
||||
return this.eventEmitter.onLoad;
|
||||
}
|
||||
|
||||
set onLoadStart(value: VideoPlayerEventsInterface['onLoadStart']) {
|
||||
this.eventEmitter.onLoadStart = value;
|
||||
}
|
||||
|
||||
get onLoadStart(): VideoPlayerEventsInterface['onLoadStart'] {
|
||||
return this.eventEmitter.onLoadStart;
|
||||
}
|
||||
|
||||
set onPlaybackStateChange(
|
||||
value: VideoPlayerEventsInterface['onPlaybackStateChange']
|
||||
) {
|
||||
this.eventEmitter.onPlaybackStateChange = value;
|
||||
}
|
||||
|
||||
get onPlaybackStateChange(): VideoPlayerEventsInterface['onPlaybackStateChange'] {
|
||||
return this.eventEmitter.onPlaybackStateChange;
|
||||
}
|
||||
|
||||
set onPlaybackRateChange(
|
||||
value: VideoPlayerEventsInterface['onPlaybackRateChange']
|
||||
) {
|
||||
this.eventEmitter.onPlaybackRateChange = value;
|
||||
}
|
||||
|
||||
get onPlaybackRateChange(): VideoPlayerEventsInterface['onPlaybackRateChange'] {
|
||||
return this.eventEmitter.onPlaybackRateChange;
|
||||
}
|
||||
|
||||
set onProgress(value: VideoPlayerEventsInterface['onProgress']) {
|
||||
this.eventEmitter.onProgress = value;
|
||||
}
|
||||
|
||||
get onProgress(): VideoPlayerEventsInterface['onProgress'] {
|
||||
return this.eventEmitter.onProgress;
|
||||
}
|
||||
|
||||
set onReadyToDisplay(value: VideoPlayerEventsInterface['onReadyToDisplay']) {
|
||||
this.eventEmitter.onReadyToDisplay = value;
|
||||
}
|
||||
|
||||
get onReadyToDisplay(): VideoPlayerEventsInterface['onReadyToDisplay'] {
|
||||
return this.eventEmitter.onReadyToDisplay;
|
||||
}
|
||||
|
||||
set onSeek(value: VideoPlayerEventsInterface['onSeek']) {
|
||||
this.eventEmitter.onSeek = value;
|
||||
}
|
||||
|
||||
get onSeek(): VideoPlayerEventsInterface['onSeek'] {
|
||||
return this.eventEmitter.onSeek;
|
||||
}
|
||||
|
||||
set onStatusChange(value: VideoPlayerEventsInterface['onStatusChange']) {
|
||||
this.eventEmitter.onStatusChange = value;
|
||||
}
|
||||
|
||||
get onStatusChange(): VideoPlayerEventsInterface['onStatusChange'] {
|
||||
return this.eventEmitter.onStatusChange;
|
||||
}
|
||||
|
||||
set onTimedMetadata(value: VideoPlayerEventsInterface['onTimedMetadata']) {
|
||||
this.eventEmitter.onTimedMetadata = value;
|
||||
}
|
||||
|
||||
get onTimedMetadata(): VideoPlayerEventsInterface['onTimedMetadata'] {
|
||||
return this.eventEmitter.onTimedMetadata;
|
||||
}
|
||||
|
||||
set onTextTrackDataChanged(
|
||||
value: VideoPlayerEventsInterface['onTextTrackDataChanged']
|
||||
) {
|
||||
this.eventEmitter.onTextTrackDataChanged = value;
|
||||
}
|
||||
|
||||
get onTextTrackDataChanged(): VideoPlayerEventsInterface['onTextTrackDataChanged'] {
|
||||
return this.eventEmitter.onTextTrackDataChanged;
|
||||
}
|
||||
|
||||
set onTrackChange(value: VideoPlayerEventsInterface['onTrackChange']) {
|
||||
this.eventEmitter.onTrackChange = value;
|
||||
}
|
||||
|
||||
get onTrackChange(): VideoPlayerEventsInterface['onTrackChange'] {
|
||||
return this.eventEmitter.onTrackChange;
|
||||
}
|
||||
|
||||
set onVolumeChange(value: VideoPlayerEventsInterface['onVolumeChange']) {
|
||||
this.eventEmitter.onVolumeChange = value;
|
||||
}
|
||||
|
||||
get onVolumeChange(): VideoPlayerEventsInterface['onVolumeChange'] {
|
||||
return this.eventEmitter.onVolumeChange;
|
||||
clearEvent(event: keyof PlayerEvents) {
|
||||
this.eventListeners[event]?.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { VideoPlayer } from '../VideoPlayer';
|
||||
import { type VideoPlayerEvents } from '../types/Events';
|
||||
|
||||
// Omit undefined from events
|
||||
type NonUndefined<T> = T extends undefined ? never : T;
|
||||
|
||||
// Valid events names
|
||||
type Events = keyof VideoPlayerEvents | 'onError';
|
||||
|
||||
// Valid events params
|
||||
type EventsParams<T extends Events> = T extends keyof VideoPlayerEvents
|
||||
? // (Native) Events from VideoPlayerEvents
|
||||
Parameters<VideoPlayerEvents[T]>
|
||||
: // (JS) Events from Video Player
|
||||
Parameters<NonUndefined<VideoPlayer[T]>>;
|
||||
import { type AllPlayerEvents } from '../types/Events';
|
||||
|
||||
/**
|
||||
* Attaches an event listener to a `VideoPlayer` instance for a specified event.
|
||||
@@ -22,22 +9,15 @@ type EventsParams<T extends Events> = T extends keyof VideoPlayerEvents
|
||||
* @param event - The name of the event to attach the callback to
|
||||
* @param callback - The callback for the event
|
||||
*/
|
||||
export const useEvent = <T extends Events>(
|
||||
export const useEvent = <T extends keyof AllPlayerEvents>(
|
||||
player: VideoPlayer,
|
||||
event: T,
|
||||
callback: (...args: EventsParams<T>) => void
|
||||
callback: AllPlayerEvents[T]
|
||||
) => {
|
||||
useEffect(() => {
|
||||
// @ts-expect-error we narrow the type of the event
|
||||
player[event] = callback;
|
||||
player.addEventListener(event, callback);
|
||||
|
||||
return () => {
|
||||
if (event === 'onError') {
|
||||
// onError is not native event, so we can set it to undefined
|
||||
player.onError = undefined;
|
||||
} else {
|
||||
player.clearEvent(event);
|
||||
}
|
||||
};
|
||||
return () => player.removeEventListener(event, callback);
|
||||
;
|
||||
}, [player, event, callback]);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { VideoPlayerSource } from '../../spec/nitro/VideoPlayerSource.nitro';
|
||||
import type { TextTrack } from './TextTrack';
|
||||
import type { VideoRuntimeError } from './VideoError';
|
||||
import type { VideoOrientation } from './VideoOrientation';
|
||||
import type { VideoPlayerStatus } from './VideoPlayerStatus';
|
||||
|
||||
@@ -92,6 +93,11 @@ export interface VideoPlayerEvents {
|
||||
onStatusChange: (status: VideoPlayerStatus) => void;
|
||||
}
|
||||
|
||||
export interface AllPlayerEvents extends VideoPlayerEvents {
|
||||
onError: (error: VideoRuntimeError) => void;
|
||||
}
|
||||
|
||||
|
||||
export interface VideoViewEvents {
|
||||
/**
|
||||
* Called when the video view's picture in picture state changes.
|
||||
|
||||
Reference in New Issue
Block a user