react-native-youtube-bridge
Version:
🎥 Easy-to-use YouTube player for React Native with cross-platform support
149 lines (127 loc) • 4.76 kB
text/typescript
import { useEffect, useRef, useState } from 'react';
import type { EventCallback, YoutubePlayerEvents } from '@react-native-youtube-bridge/core';
import type YoutubePlayer from '../modules/YoutubePlayer';
import { INTERNAL_SET_PROGRESS_INTERVAL } from '../modules/YoutubePlayer';
const DEFAULT_PROGRESS_INTERVAL = 1000;
/**
* @param player - The Youtube player instance.
* @param eventType - The type of event to subscribe to. `progress` event is not supported.
* @param defaultValue - The default value to return if the event is not triggered.
* @returns The event data.
* @example
* ```ts
* const player = useYouTubePlayer('AbZH7XWDW_k');
* const playbackRate = useYouTubeEvent(player, 'playbackRateChange', 1);
* ```
*/
function useYouTubeEvent<T extends Exclude<keyof YoutubePlayerEvents, 'progress'>>(
player: YoutubePlayer,
eventType: T,
defaultValue?: YoutubePlayerEvents[T],
): YoutubePlayerEvents[T] | null;
/**
* @param player - The Youtube player instance.
* @param eventType - The type of event to subscribe to.
* @param callback - The callback to call when the event is triggered.
* @param deps - The dependencies to watch for changes.
* @returns void
* @example
* ```ts
* const player = useYouTubePlayer('AbZH7XWDW_k');
* useYouTubeEvent(player, 'ready', (playerInfo) => {
* console.log('Player is ready!');
* console.log('rates', playerInfo.availablePlaybackRates);
* console.log('vol', playerInfo.volume);
* console.log('muted', playerInfo.muted);
* });
* ```
*/
function useYouTubeEvent<T extends keyof YoutubePlayerEvents>(
player: YoutubePlayer,
eventType: T,
callback: EventCallback<YoutubePlayerEvents[T]>,
deps?: React.DependencyList,
): void;
/**
* @param player - The Youtube player instance.
* @param eventType - `progress` event only.
* @param throttleMs - The throttle time in milliseconds (default 1000ms).
* @returns The event data.
* @example
* ```ts
* const player = useYouTubePlayer('AbZH7XWDW_k');
* const progress = useYouTubeEvent(player, 'progress', 1000);
* ```
*/
function useYouTubeEvent(
player: YoutubePlayer,
eventType: 'progress',
throttleMs?: number,
): YoutubePlayerEvents['progress'] | null;
/**
* @param player - The Youtube player instance.
* @param eventType - The type of event to subscribe to.
* @param callbackOrThrottleOrDefaultValue - The callback to call when the event is triggered. If it is a number, it will be used as the throttle time in milliseconds for `progress` event.
* @param deps - The dependencies to watch for changes.
* @returns The event data. If it is a callback, it will return void.
*/
function useYouTubeEvent<T extends keyof YoutubePlayerEvents>(
player: YoutubePlayer,
eventType: T,
callbackOrThrottleOrDefaultValue?: EventCallback<YoutubePlayerEvents[T]> | YoutubePlayerEvents[T] | null,
deps?: React.DependencyList,
): YoutubePlayerEvents[T] | null | undefined {
const isProgress = eventType === 'progress';
const isCallback = typeof callbackOrThrottleOrDefaultValue === 'function';
const getThrottleMs = (): number | undefined => {
if (!isProgress) {
return undefined;
}
return typeof callbackOrThrottleOrDefaultValue === 'number'
? callbackOrThrottleOrDefaultValue
: DEFAULT_PROGRESS_INTERVAL;
};
const getDefaultValue = () => {
if (isCallback || isProgress) {
return null;
}
return callbackOrThrottleOrDefaultValue ?? null;
};
const throttleMs = getThrottleMs();
const defaultValue = getDefaultValue();
const callbackRef = useRef<EventCallback<YoutubePlayerEvents[T]> | null>(
isCallback ? callbackOrThrottleOrDefaultValue : null,
);
const [data, setData] = useState<YoutubePlayerEvents[T] | null>(defaultValue);
useEffect(() => {
if (isCallback) {
callbackRef.current = callbackOrThrottleOrDefaultValue;
}
}, [callbackOrThrottleOrDefaultValue, isCallback, ...(deps ?? [])]);
useEffect(() => {
if (typeof throttleMs === 'number' && player) {
player[INTERNAL_SET_PROGRESS_INTERVAL](throttleMs);
}
}, [throttleMs, player]);
// biome-ignore lint/correctness/useExhaustiveDependencies: defaultValue is intentionally excluded to prevent unnecessary re-subscriptions
useEffect(() => {
if (!player) {
return;
}
const unsubscribe = player.subscribe(eventType, (eventData) => {
if (isCallback && callbackRef.current) {
callbackRef.current(eventData);
return;
}
if (!isCallback) {
setData(eventData);
}
});
return () => {
setData(defaultValue ?? null);
unsubscribe();
};
}, [player, eventType, isCallback]);
return isCallback ? undefined : data;
}
export default useYouTubeEvent;