import { useSyncExternalStore, useRef } from 'react';
import { useAviationPlayer } from '../AviationContext';
import type { AdBreakInfo, AdInfo } from '../specs/types.nitro';
import type { AviationPlayer } from '../Aviation';
/**
* Snapshot of the current ad playback state.
*/
export interface AdStateInfo {
/** Whether an ad is currently playing. */
isPlayingAd: boolean;
/** Info about the current ad break, or undefined if not in a break. */
currentAdBreak: AdBreakInfo | undefined;
/** Info about the current individual ad, or undefined. */
currentAd: AdInfo | undefined;
}
const IDLE_AD_STATE: AdStateInfo = {
isPlayingAd: false,
currentAdBreak: undefined,
currentAd: undefined,
};
/**
* React hook that subscribes to the ads controller's state.
*
* Uses `useSyncExternalStore` for tear-free reads. Re-renders only when
* ad state actually changes.
*
* Returns idle state if no ads controller is registered.
*
* @example
* ```tsx
* function AdOverlay() {
* const { isPlayingAd, currentAd } = useAdState();
* if (!isPlayingAd || !currentAd) return null;
* return Ad {currentAd.adIndexInBreak + 1}/{currentAd.totalAdsInBreak};
* }
* ```
*/
export function useAdState(playerArg?: AviationPlayer): AdStateInfo {
const player = useAviationPlayer(playerArg);
const store = player.store;
const ref = useRef(IDLE_AD_STATE);
const getSnapshot = () => {
const isPlayingAd = store.isPlayingAd;
const currentAdBreak = store.currentAdBreak;
const currentAd = store.currentAd;
const prev = ref.current;
if (
prev.isPlayingAd === isPlayingAd &&
prev.currentAdBreak === currentAdBreak &&
prev.currentAd === currentAd
) {
return prev;
}
ref.current = {
isPlayingAd,
currentAdBreak,
currentAd,
};
return ref.current;
};
return useSyncExternalStore(store.subscribeAdState, getSnapshot);
}