Files
immich/web/src/lib/components/asset-viewer/immich-time-range.ts
T
2026-05-28 18:14:35 -04:00

55 lines
1.8 KiB
TypeScript

import { MediaUIEvents } from 'media-chrome/constants';
import MediaTimeRange from 'media-chrome/media-time-range';
const COMMIT_DELAY_MS = 750;
/** Custom MediaTimeRange that only seeks after pointer release to avoid hammering the server.
* Keyboard input uses timed debouncing instead since there's no release event. */
class ImmichTimeRange extends MediaTimeRange {
private seeking = false;
private pending: number | undefined;
private idleTimer: ReturnType<typeof setTimeout> | undefined;
override connectedCallback() {
super.connectedCallback();
this.addEventListener('pointerdown', this.hold);
this.addEventListener('keydown', this.hold);
this.addEventListener('pointerup', this.release);
this.addEventListener('pointercancel', this.release);
this.addEventListener(MediaUIEvents.MEDIA_SEEK_REQUEST, this.intercept, { capture: true });
}
private hold(event: Event) {
if (event instanceof KeyboardEvent) {
if (!this.keysUsed.includes(event.key)) {
return;
}
clearTimeout(this.idleTimer);
this.idleTimer = setTimeout(this.release, COMMIT_DELAY_MS);
}
this.seeking = true;
}
private intercept(event: Event) {
if (!this.seeking) {
return; // not mid-scrub, or this is the request we replay in release()
}
this.pending = (event as CustomEvent<number>).detail;
event.stopImmediatePropagation();
}
private release() {
clearTimeout(this.idleTimer);
this.seeking = false;
if (this.pending !== undefined) {
const detail = this.pending;
this.pending = undefined;
this.dispatchEvent(new CustomEvent(MediaUIEvents.MEDIA_SEEK_REQUEST, { bubbles: true, composed: true, detail }));
}
}
}
if (!globalThis.customElements.get('immich-time-range')) {
globalThis.customElements.define('immich-time-range', ImmichTimeRange);
}