mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-05 05:36:17 +00:00
Cleanup useEvent on web
This commit is contained in:
+73
-111
@@ -1,126 +1,88 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { OmniEvents } from "./types/events";
|
||||
import type { OmniPlayerState, PlayerStatus } from "./types/player";
|
||||
import {
|
||||
selectPlayback,
|
||||
selectTime,
|
||||
selectBuffer,
|
||||
selectPlaybackRate,
|
||||
selectVolume,
|
||||
selectSource,
|
||||
usePlayer,
|
||||
type Selector,
|
||||
selectBuffer,
|
||||
selectError,
|
||||
selectPlayback,
|
||||
selectPlaybackRate,
|
||||
selectSource,
|
||||
selectTextTrack,
|
||||
selectTime,
|
||||
selectVolume,
|
||||
usePlayer,
|
||||
} from "@videojs/react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import type { OmniEvents } from "./types/events";
|
||||
import type { OmniPlayerState } from "./types/player";
|
||||
|
||||
type EventMapperConfig = {
|
||||
[Key in keyof OmniEvents]?: {
|
||||
selector: Selector<any, any>;
|
||||
handler: (cb: OmniEvents[Key], value: any, prev: any) => void;
|
||||
};
|
||||
};
|
||||
|
||||
function createEventMapper<Key extends keyof OmniEvents, Result>(
|
||||
key: Key,
|
||||
selector: Selector<any, Result>,
|
||||
handler: (
|
||||
cb: OmniEvents[Key],
|
||||
value: Result,
|
||||
prev: Result | undefined,
|
||||
) => void,
|
||||
) {
|
||||
return { [key]: { selector, handler } };
|
||||
}
|
||||
|
||||
const eventMapper: EventMapperConfig = {
|
||||
...createEventMapper("end", selectPlayback, (cb, value, prev) => {
|
||||
if (value?.ended && prev && !prev.ended) cb();
|
||||
}),
|
||||
...createEventMapper("error", selectError, (cb, value, prev) => {
|
||||
if (value?.error && value.error !== prev?.error) {
|
||||
cb(
|
||||
value.error.message ?? "Unknown error",
|
||||
value.error.message ?? "Unknown error",
|
||||
);
|
||||
}
|
||||
}),
|
||||
...createEventMapper("subtitleChange", selectTextTrack, (cb, value, prev) => {
|
||||
if (value === prev) return;
|
||||
const tracks = value?.textTrackList;
|
||||
if (tracks?.length) {
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
const track = tracks[i];
|
||||
if (track?.mode === "showing") {
|
||||
cb({
|
||||
id: `text-${i}`,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
cb(undefined);
|
||||
}),
|
||||
};
|
||||
|
||||
export const useEvent = <Event extends keyof OmniEvents>(
|
||||
event: Event,
|
||||
callback: OmniEvents[Event],
|
||||
) => {
|
||||
const player = usePlayer();
|
||||
const config = eventMapper[event];
|
||||
const callbackRef = useRef(callback);
|
||||
callbackRef.current = callback;
|
||||
const prevRef = useRef<any>(undefined);
|
||||
|
||||
const value = usePlayer(config?.selector ?? (() => ({})));
|
||||
|
||||
useEffect(() => {
|
||||
const store = player.store;
|
||||
const media = store.state.media as any;
|
||||
|
||||
if (!media) return;
|
||||
|
||||
switch (event) {
|
||||
case "end": {
|
||||
const handler = () => (callbackRef.current as OmniEvents["end"])();
|
||||
media.addEventListener("ended", handler);
|
||||
return () => media.removeEventListener("ended", handler);
|
||||
}
|
||||
case "error": {
|
||||
const handler = () => {
|
||||
const error = store.state.error;
|
||||
(callbackRef.current as OmniEvents["error"])(
|
||||
error?.message ?? "Unknown error",
|
||||
error?.message ?? "Unknown error",
|
||||
);
|
||||
};
|
||||
media.addEventListener("error", handler);
|
||||
return () => media.removeEventListener("error", handler);
|
||||
}
|
||||
case "prev": {
|
||||
const handler = () => (callbackRef.current as OmniEvents["prev"])();
|
||||
store.state.onPrev = handler;
|
||||
return () => {
|
||||
if (store.state.onPrev === handler) store.state.onPrev = undefined;
|
||||
};
|
||||
}
|
||||
case "next": {
|
||||
const handler = () => (callbackRef.current as OmniEvents["next"])();
|
||||
store.state.onNext = handler;
|
||||
return () => {
|
||||
if (store.state.onNext === handler) store.state.onNext = undefined;
|
||||
};
|
||||
}
|
||||
case "videoTrackChange": {
|
||||
const handler = () => {
|
||||
if (!media.videoTracks?.length) return;
|
||||
for (let i = 0; i < media.videoTracks.length; i++) {
|
||||
const track = media.videoTracks[i];
|
||||
if (track?.enabled) {
|
||||
(callbackRef.current as OmniEvents["videoTrackChange"])({
|
||||
id: track.id || `video-${i}`,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: true,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
media.videoTracks?.addEventListener("change", handler);
|
||||
return () => media.videoTracks?.removeEventListener("change", handler);
|
||||
}
|
||||
case "audioTrackChange": {
|
||||
const handler = () => {
|
||||
if (!media.audioTracks?.length) return;
|
||||
for (let i = 0; i < media.audioTracks.length; i++) {
|
||||
const track = media.audioTracks[i];
|
||||
if (track?.enabled) {
|
||||
(callbackRef.current as OmniEvents["audioTrackChange"])({
|
||||
id: track.id || `audio-${i}`,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: true,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
media.audioTracks?.addEventListener("change", handler);
|
||||
return () => media.audioTracks?.removeEventListener("change", handler);
|
||||
}
|
||||
case "subtitleChange": {
|
||||
const handler = () => {
|
||||
if (!media.textTracks?.length) return;
|
||||
for (let i = 0; i < media.textTracks.length; i++) {
|
||||
const track = media.textTracks[i];
|
||||
if (track?.mode === "showing") {
|
||||
(callbackRef.current as OmniEvents["subtitleChange"])({
|
||||
id: track.id || `text-${i}`,
|
||||
label: track.label,
|
||||
language: track.language,
|
||||
selected: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
(callbackRef.current as OmniEvents["subtitleChange"])(undefined);
|
||||
};
|
||||
media.textTracks?.addEventListener("cuechange", handler);
|
||||
return () =>
|
||||
media.textTracks?.removeEventListener("cuechange", handler);
|
||||
}
|
||||
case "audioFocusChange":
|
||||
case "renditionChange":
|
||||
return;
|
||||
}
|
||||
}, [player, event]);
|
||||
if (!config) return;
|
||||
const prev = prevRef.current;
|
||||
config.handler(callbackRef.current as any, value, prev);
|
||||
prevRef.current = value;
|
||||
}, [value, config]);
|
||||
};
|
||||
|
||||
type MapperConfig = {
|
||||
|
||||
Reference in New Issue
Block a user