import { resolveTxt } from 'dns'; import { videointelligence } from 'googleapis/build/src/apis/videointelligence'; import { isInteger } from 'lodash'; import * as React from 'react'; import { useEffect, useState, useCallback, useRef, useMemo } from "react" import "./ProgressBar.scss" import { MediaSegment } from './RecordingView'; interface ProgressBarProps { videos: MediaSegment[], setVideos: React.Dispatch>, orderVideos: boolean, } interface SegmentBox { endTime: number, startTime: number, order: number, } interface CurrentHover { index: number, minX: number, maxX: number } export function ProgressBar(props: ProgressBarProps) { const progressBarRef = useRef(null) // array for the order of video segments const [segments, setSegments] = useState([]); const [ordered, setOrdered] = useState([]); const [dragged, setDragged] = useState(-1); // const totalTime = useMemo(() => props.videos.lastElement().endTime, [props.videos]) const totalTime = () => props.videos.lastElement().endTime // const memoTotal = useMemo(totalTime, [props.videos]) useEffect(() => { const segmentsJSX = ordered.map((seg, i) =>
{seg.order}
); setSegments(segmentsJSX) }, [dragged, ordered]) useEffect(() => { const order = props.videos.length if (order && !props.orderVideos) { const { endTime, startTime } = props.videos.lastElement(); setOrdered(prevOrdered => { return [...prevOrdered, { endTime, startTime , order }]; }); } // }props.videos.map((vid, order) => { // //const { endTime, startTime } = vid // // TODO: not tranfer the blobs around // return { ...vid, order }; // })) }, [props.videos]); useEffect(() => { props.setVideos(vids => ordered.map((seg) => vids[seg.order - 1])); }, [props.orderVideos]); // const handleClick = (e: React.MouseEvent) => { // let progressbar = document.getElementById('progressbar')! // let bounds = progressbar!.getBoundingClientRect(); // let x = e.clientX - bounds.left; // let percent = x / progressbar.clientWidth * 100 // for (let i = 0; i < props.marks.length; i++) { // let start = i == 0 ? 0 : props.marks[i-1]; // if (percent > start && percent < props.marks[i]) { // props.playSegment(i) // // console.log(i) // // console.log(percent) // // console.log(props.marks[i]) // break // } // } // } const updateLastHover = (segId: number): CurrentHover | null => { // get the segId of the segment that will become the new bounding area const rect = progressBarRef.current?.children[segId].getBoundingClientRect() if (rect == null) return null return { index: segId, minX: rect.x, maxX: rect.x + rect.width, } } const onPointerDown = (e: React.PointerEvent) => { console.log('pointer down') // don't move the videobox element e.stopPropagation() // get the segment the user clicked on to be dragged const clickedSegment = e.target as HTMLDivElement & EventTarget // get the profess bar ro add event listeners // don't do anything if null const progressBar = progressBarRef.current if (progressBar == null || clickedSegment.id === progressBar.id) return const ptrId = e.pointerId; progressBar.setPointerCapture(ptrId) const rect = clickedSegment.getBoundingClientRect() // id for segment is like 'segment-1' or 'segment-10', // so this works to get the id const segId = parseInt(clickedSegment.id.split('-')[1]) // set the selected segment to be the one dragged setDragged(segId) // this is the logic for storing the lower X bound and upper X bound // to know whether a swap is needed between two segments let lastHover: CurrentHover = { index: segId, minX: rect.x, maxX: rect.x + rect.width, } // create the div element that tracks the cursor const detchedSegment = document.createElement("div") initDeatchSegment(detchedSegment, rect); const updateSegmentOrder = (event: PointerEvent): void => { event.stopPropagation(); event.preventDefault(); // this fixes a bug where pointerup doesn't fire while cursor is upped while being dragged console.log('update cursor', progressBar.hasPointerCapture(ptrId), ptrId) if (!progressBar.hasPointerCapture(ptrId)) { placeSegmentandCleanup(); return; } followCursor(event, detchedSegment, rect) const curX = event.clientX; if (curX < lastHover.minX && lastHover.index > 0) { swapSegments(lastHover.index, lastHover.index - 1) lastHover = updateLastHover(lastHover.index - 1) ?? lastHover } else if (curX > lastHover.maxX && lastHover.index < segments.length - 1) { swapSegments(lastHover.index, lastHover.index + 1) lastHover = updateLastHover(lastHover.index + 1) ?? lastHover } } const placeSegmentandCleanup = (event?: PointerEvent): void => { event?.stopPropagation(); event?.preventDefault(); // remove the update event listener for pointermove progressBar.removeEventListener('pointermove', updateSegmentOrder), { once: true } // remove the floating segment from the DOM detchedSegment.remove() // dragged is -1 is equiv to nothing being dragged, so the normal state // so this will place the segment in it's location and update the segment bar setDragged(-1); } progressBar.addEventListener('pointermove', updateSegmentOrder) progressBar.addEventListener('pointerup', placeSegmentandCleanup, { once: true }) } const swapSegments = (oldIndex: number, newIndex: number) => { if (newIndex == null) return setOrdered(prevOrdered => { const cpy = [...prevOrdered] cpy[oldIndex] = cpy[newIndex] cpy[newIndex] = prevOrdered[oldIndex] return cpy }) setDragged(newIndex) } const initDeatchSegment = (dot: HTMLDivElement, rect: DOMRect) => { dot.classList.add("segment-selected") dot.style.transitionDuration = '0s'; dot.style.position = 'absolute'; dot.style.zIndex = '999'; dot.style.width = `${rect.width}px`; dot.style.height = `${rect.height}px`; dot.style.left = `${rect.x}px`; dot.style.top = `${rect.y}px`; dot.draggable = false; document.body.append(dot) } const cleanupDetachSegment = (dot: HTMLDivElement) => { dot.remove() } const followCursor = (event: PointerEvent, dot: HTMLDivElement, rect: DOMRect): void => { // event.stopPropagation() // const { width, height } = dot.getBoundingClientRect() const { width, height } = rect; dot.style.left = `${event.clientX - width/2}px`; dot.style.top = `${event.clientY - height/2}px`; } return (
{/*
*/} {segments}
) }