import React, { useMemo, useContext, useState, useRef } from 'react'; import { useStore } from 'App/mstore'; import TimeTracker from 'Components/Session_/Player/Controls/TimeTracker'; import stl from 'Components/Session_/Player/Controls/timeline.module.css'; import DraggableCircle from 'Components/Session_/Player/Controls/components/DraggableCircle'; import CustomDragLayer, { OnDragCallback, } from 'Components/Session_/Player/Controls/components/CustomDragLayer'; import { debounce } from 'App/utils'; import TooltipContainer from 'Components/Session_/Player/Controls/components/TooltipContainer'; import { PlayerContext, ILivePlayerContext, } from 'App/components/Session/playerContext'; import { observer } from 'mobx-react-lite'; import { Duration } from 'luxon'; function Timeline() { const { sessionStore } = useStore(); const startedAt = sessionStore.current.startedAt ?? 0; const tooltipVisible = sessionStore.timeLineTooltip.isVisible; const setTimelineHoverTime = sessionStore.setTimelineTooltip; // @ts-ignore const { player, store } = useContext(PlayerContext); const [wasPlaying, setWasPlaying] = useState(false); const { playing, time, ready, endTime, liveTimeTravel } = store.get(); const timelineRef = useRef(null); const progressRef = useRef(null); const scale = 100 / endTime; const debouncedJump = useMemo(() => debounce(player.jump, 500), []); const debouncedTooltipChange = useMemo( () => debounce(setTimelineHoverTime, 50), [], ); const onDragEnd = () => { if (!liveTimeTravel) return; if (wasPlaying) { player.togglePlay(); } }; const onDrag: OnDragCallback = (offset: { x: number }) => { if (!liveTimeTravel || !progressRef.current) return; const p = offset.x / progressRef.current.offsetWidth; const time = Math.max(Math.round(p * endTime), 0); debouncedJump(time); hideTimeTooltip(); if (playing) { setWasPlaying(true); player.pause(); } }; const getLiveTime = (e: React.MouseEvent) => { const duration = new Date().getTime() - startedAt; // @ts-ignore type mismatch from react? const p = e.nativeEvent.offsetX / e.target.offsetWidth; const time = Math.max(Math.round(p * duration), 0); return [time, duration]; }; const showTimeTooltip = (e: React.MouseEvent) => { if (e.target !== progressRef.current && e.target !== timelineRef.current) { return tooltipVisible && hideTimeTooltip(); } const [time, duration] = getLiveTime(e); const timeLineTooltip = { time: Duration.fromMillis(duration - time).toFormat('-mm:ss'), offset: e.nativeEvent.offsetX, isVisible: true, }; debouncedTooltipChange(timeLineTooltip); }; const hideTimeTooltip = () => { const timeLineTooltip = { isVisible: false }; debouncedTooltipChange(timeLineTooltip); }; const seekProgress = (e: React.MouseEvent) => { const time = getTime(e); player.jump(time); hideTimeTooltip(); }; const loadAndSeek = async (e: React.MouseEvent) => { e.persist(); const result = await player.toggleTimetravel(); if (result) { seekProgress(e); } }; const jumpToTime = (e: React.MouseEvent) => { if (!liveTimeTravel) { void loadAndSeek(e); } else { seekProgress(e); } }; const getTime = ( e: React.MouseEvent, customEndTime?: number, ) => { // @ts-ignore type mismatch from react? const p = e.nativeEvent.offsetX / e.target.offsetWidth; const targetTime = customEndTime || endTime; return Math.max(Math.round(p * targetTime), 0); }; return (
); } export default observer(Timeline);