Skip to content

Commit

Permalink
fix(player): chapters menu should update on cue changes
Browse files Browse the repository at this point in the history
closes #1043
  • Loading branch information
mihar-22 committed Dec 10, 2023
1 parent 48b8324 commit 574a0a3
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 12 deletions.
3 changes: 2 additions & 1 deletion packages/react/mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -619,5 +619,6 @@
"_watchVolume": "Gd",
"_watchWaiting": "Bk",
"_whenQueryList": "ne",
"_whenSmQueryList": "Mc"
"_whenSmQueryList": "Mc",
"_watchTrack": "Hk"
}
8 changes: 5 additions & 3 deletions packages/react/src/hooks/options/use-chapter-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@ import { formatSpokenTime, formatTime, mediaContext } from 'vidstack';

import { useActiveTextCues } from '../use-active-text-cues';
import { useActiveTextTrack } from '../use-active-text-track';
import { useTextCues } from '../use-text-cues';

/**
* @docs {@link https://www.vidstack.io/docs/player/api/hooks/use-chapter-options}
*/
export function useChapterOptions(): ChapterOptions {
const media = useReactContext(mediaContext)!,
track = useActiveTextTrack('chapters');
track = useActiveTextTrack('chapters'),
cues = useTextCues(track);

useActiveTextCues(track);

return React.useMemo(() => {
const options = track
? track.cues.map<ChapterOption>((cue, i) => {
? cues.map<ChapterOption>((cue, i) => {
let currentRef: HTMLElement | null = null,
stopProgressEffect: StopEffect | undefined;
return {
Expand Down Expand Up @@ -67,7 +69,7 @@ export function useChapterOptions(): ChapterOptions {
});

return options as ChapterOptions;
}, [track]);
}, [cues]);
}

export type ChapterOptions = ChapterOption[] & {
Expand Down
35 changes: 35 additions & 0 deletions packages/react/src/hooks/use-text-cues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';

import { createDisposalBin, listenEvent } from 'maverick.js/std';
import type { VTTCue } from 'media-captions';
import type { TextTrack } from 'vidstack';

/**
* @docs {@link https://www.vidstack.io/docs/player/api/hooks/use-text-cues}
*/
export function useTextCues(track: TextTrack | null): VTTCue[] {
const [cues, setCues] = React.useState<VTTCue[]>([]);

React.useEffect(() => {
if (!track) return;

function onCuesChange(track: TextTrack) {
setCues([...track.cues]);
}

const disposal = createDisposalBin();
disposal.add(
listenEvent(track, 'add-cue', () => onCuesChange(track)),
listenEvent(track, 'remove-cue', () => onCuesChange(track)),
);

onCuesChange(track);

return () => {
disposal.empty();
setCues([]);
};
}, [track]);

return cues;
}
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export * from './hooks/use-media-state';
export * from './hooks/use-thumbnails';
export * from './hooks/use-slider-state';
export * from './hooks/use-slider-preview';
export * from './hooks/use-text-cues';
export * from './hooks/use-active-text-cues';
export * from './hooks/use-active-text-track';
export * from './hooks/create-text-track';
Expand Down
3 changes: 2 additions & 1 deletion packages/vidstack/mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -610,5 +610,6 @@
"_watchViewType": "Yf",
"_watchVolume": "Fd",
"_whenQueryList": "me",
"_whenSmQueryList": "Lc"
"_whenSmQueryList": "Lc",
"_watchTrack": "tk"
}
10 changes: 9 additions & 1 deletion packages/vidstack/src/components/ui/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
useContext,
} from 'maverick.js';
import {
animationFrameThrottle,
ariaBool,
DOMEvent,
isKeyboardEvent,
Expand Down Expand Up @@ -159,6 +160,7 @@ export class Menu extends Component<MenuProps, {}, MenuEvents> {
protected override onConnect(el: HTMLElement) {
effect(this._watchExpanded.bind(this));
if (this.isSubmenu) this._parentMenu?._addSubmenu(this);

requestAnimationFrame(() => {
this._onResize();
});
Expand Down Expand Up @@ -248,7 +250,13 @@ export class Menu extends Component<MenuProps, {}, MenuEvents> {
this._updateMenuItemsHidden(false);

if (!__SERVER__) {
requestAnimationFrame(this._onResize.bind(this));
const onResize = animationFrameThrottle(this._onResize.bind(this)),
mutations = new MutationObserver(onResize);

onResize();

mutations.observe(el, { childList: true, subtree: true });
onDispose(() => mutations.disconnect());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
signal,
useContext,
} from 'maverick.js';
import { DOMEvent, isNumber, setStyle } from 'maverick.js/std';
import { DOMEvent, isNumber, listenEvent, setStyle } from 'maverick.js/std';
import type { VTTCue } from 'media-captions';

import { useMediaContext, type MediaContext } from '../../../../core/api/media-context';
Expand Down Expand Up @@ -41,6 +41,7 @@ export class ChaptersRadioGroup extends Component<

private _index = signal(0);
private _track = signal<TextTrack | null>(null);
private _cues = signal<readonly VTTCue[]>([]);

@prop
get value() {
Expand All @@ -49,8 +50,7 @@ export class ChaptersRadioGroup extends Component<

@prop
get disabled() {
const track = this._track();
return !track || !track.cues.length;
return !this._cues()?.length;
}

constructor() {
Expand Down Expand Up @@ -80,9 +80,7 @@ export class ChaptersRadioGroup extends Component<

@method
getOptions(): ChaptersRadioOption[] {
const track = this._track();
if (!track) return [];
return track.cues.map((cue, i) => ({
return this._cues().map((cue, i) => ({
cue,
value: i + '',
label: cue.text,
Expand All @@ -99,9 +97,29 @@ export class ChaptersRadioGroup extends Component<
effect(this._watchValue.bind(this));
effect(this._watchCurrentTime.bind(this));
effect(this._watchControllerDisabled.bind(this));
effect(this._watchTrack.bind(this));
observeActiveTextTrack(this._media.textTracks, 'chapters', this._track.set);
}

protected _watchTrack() {
const track = this._track();
if (!track) return;

const onCuesChange = this._onCuesChange.bind(this, track);

onCuesChange();
listenEvent(track, 'add-cue', onCuesChange);
listenEvent(track, 'remove-cue', onCuesChange);

return () => {
this._cues.set([]);
};
}

protected _onCuesChange(track: TextTrack) {
this._cues.set([...track.cues]);
}

private _watchValue() {
this._controller.value = this._getValue();
}
Expand Down

0 comments on commit 574a0a3

Please sign in to comment.