import { useSyncExternalStore, useRef, useCallback } from 'react';
import { useAviationPlayer } from '../AviationContext';
import { IDLE_POSITION, samePosition } from '../snapshots';
import type { MediaPosition } from '../specs/types.nitro';
import type { PlaybackItemRef } from './usePlaybackFor';
import type { AviationPlayer } from '../Aviation';
/**
* Scoped variant of {@link useMediaPosition} that returns idle (zeroed)
* position when the given item is not the engine's currently active
* content.
*
* Gates position reads on URI ownership so a UI for one item does not
* render another item's playhead.
*
* @param item - The media item to scope to. Accepts a `MediaItem`
* HybridObject, a `MediaItemConfig`, or any object with a `uri`
* field. Pass `undefined` to get idle position.
*
* @example
* ```tsx
* function AudioProgressBar({ track }: { track: MediaItemConfig }) {
* const { progress, currentMs, durationMs } = useMediaPositionFor(track);
* // Stays at 0 when video is playing; reflects audio progress
* // only when this track is the active one.
* return ;
* }
* ```
*
* @remarks
* Ownership matches when the passed item shares either JS reference
* identity OR `uri` with `store.currentItem` — same logic as
* {@link usePlaybackFor}.
*/
export function useMediaPositionFor(
item: PlaybackItemRef | undefined,
playerArg?: AviationPlayer
): MediaPosition {
const player = useAviationPlayer(playerArg);
const store = player.store;
const ref = useRef(IDLE_POSITION);
const subscribePosition = useCallback(
(cb: () => void): (() => void) => {
const unsubs = [
store.subscribePosition(cb),
store.subscribeCurrentItem(cb),
];
return () => unsubs.forEach((u) => u());
},
[store]
);
const getSnapshot = () => {
const currentItem = store.currentItem;
const itemUri =
item !== undefined ? (item as { uri?: string }).uri : undefined;
const isOwner =
item !== undefined &&
currentItem !== undefined &&
(currentItem === (item as unknown as typeof currentItem) ||
(itemUri !== undefined && currentItem.uri === itemUri));
if (!isOwner) {
// Not the active item — return stable zeroed position.
if (ref.current === IDLE_POSITION) return ref.current;
ref.current = IDLE_POSITION;
return ref.current;
}
const pos = store.mediaPosition;
const prev = ref.current;
if (samePosition(prev, pos)) return prev;
ref.current = pos;
return ref.current;
};
return useSyncExternalStore(subscribePosition, getSnapshot);
}