aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/RecordingBox/ProgressBar.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')
-rw-r--r--src/client/views/nodes/RecordingBox/ProgressBar.tsx425
1 files changed, 212 insertions, 213 deletions
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 7da3f45be..81f753e44 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -1,51 +1,47 @@
-import { disable } from 'colors';
-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 } from "react"
import "./ProgressBar.scss"
import { MediaSegment } from './RecordingView';
interface ProgressBarProps {
- videos: MediaSegment[],
- setVideos: React.Dispatch<React.SetStateAction<MediaSegment[]>>,
- orderVideos: boolean,
- progress: number,
+ videos: MediaSegment[],
+ setVideos: React.Dispatch<React.SetStateAction<MediaSegment[]>>,
+ orderVideos: boolean,
+ progress: number,
recording: boolean,
doUndo: boolean,
}
interface SegmentBox {
- endTime: number,
- startTime: number,
- order: number,
+ endTime: number,
+ startTime: number,
+ order: number,
}
interface CurrentHover {
- index: number,
- minX: number,
- maxX: number
+ index: number,
+ minX: number,
+ maxX: number
}
export function ProgressBar(props: ProgressBarProps) {
- const progressBarRef = useRef<HTMLDivElement | null>(null)
+ const progressBarRef = useRef<HTMLDivElement | null>(null)
- // array for the order of video segments
- const [segments, setSegments] = useState<JSX.Element[]>([]);
+ // array for the order of video segments
+ const [segments, setSegments] = useState<JSX.Element[]>([]);
const [ordered, setOrdered] = useState<SegmentBox[]>([]);
-
+
const [undoStack, setUndoStack] = useState<SegmentBox[]>([]);
- const [dragged, setDragged] = useState<number>(-1);
+ const [dragged, setDragged] = useState<number>(-1);
+
+ // length of the time removed from the video, in seconds*100
+ const [totalRemovedTime, setTotalRemovedTime] = useState<number>(0);
+
+ // this holds the index of the videoc segment to be removed
+ const [removed, setRemoved] = useState<number>(-1);
- // length of the time removed from the video, in seconds*100
- const [totalRemovedTime, setTotalRemovedTime] = useState<number>(0);
- // this holds the index of the videoc segment to be removed
- const [removed, setRemoved] = useState<number>(-1);
-
-
// abstracted for other uses - brings back the most recently deleted segment
const handleUndo = () => {
// get the last element from the undo if it exists
@@ -60,27 +56,32 @@ export function ProgressBar(props: ProgressBarProps) {
}
useEffect(() => handleUndo(), [props.doUndo])
- useEffect(() => {
- // get segments segment's html using it's id -> make them appeared disabled (or enabled)
- segments.forEach((seg) => document.getElementById(seg.props.id)?.classList.toggle('segment-disabled', props.recording));
- progressBarRef.current?.classList.toggle('progressbar-disabled', props.recording);
-
- if (props.recording)
- setSegments(prevSegments => [...prevSegments, <div key='segment-expanding' id='segment-expanding' className='segment segment-expanding blink' style={{ width: 'min-content' }}>{props.videos.length + 1}</div>]);
- }, [props.recording])
-
-
- useEffect(() => {
- const totalTime = props.progress * 1000 - totalRemovedTime;
- const segmentsJSX = ordered.map((seg, i) =>
- <div key={`segment-${i}`} id={`segment-${i}`} className={dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime) * 100}%` }}>{seg.order + 1}</div>);
-
- setSegments(segmentsJSX)
- }, [dragged, ordered]);
-
- // to imporve performance, we only want to update the width, not re-render the whole thing
useEffect(() => {
- if (!props.recording) return
+ // get segments segment's html using it's id -> make them appeared disabled (or enabled)
+ segments.forEach((seg) => document.getElementById(seg.props.id)?.classList.toggle('segment-disabled', props.recording));
+ progressBarRef.current?.classList.toggle('progressbar-disabled', props.recording);
+
+ if (props.recording)
+ setSegments(prevSegments => [...prevSegments, <div key='segment-expanding' id='segment-expanding' className='segment segment-expanding blink' style={{ width: 'min-content' }}>{props.videos.length + 1}</div>]);
+ }, [props.recording])
+
+
+ useEffect(() => {
+ const totalTime = props.progress * 1000 - totalRemovedTime;
+ const segmentsJSX = ordered.map((seg, i) =>
+ <div key={`segment-${i}`} id={`segment-${i}`} className={dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime) * 100}%` }}>{seg.order + 1}</div>);
+
+ setSegments(segmentsJSX)
+ }, [dragged, ordered]);
+
+ // update the cursor to be dragging while moving the floating segment
+ useEffect(() => {
+ progressBarRef.current?.classList.toggle('progressbar-dragging', dragged !== -1);
+ }, [dragged]);
+
+ // to imporve performance, we only want to update the width, not re-render the whole thing
+ useEffect(() => {
+ if (!props.recording) return
const totalTime = props.progress * 1000 - totalRemovedTime;
let remainingTime = totalTime;
segments.forEach((seg, i) => {
@@ -89,197 +90,195 @@ export function ProgressBar(props: ProgressBarProps) {
// update remaining time
remainingTime -= (ordered[i].endTime - ordered[i].startTime);
- // update the width for this segment
+ // update the width for this segment
const htmlId = seg.props.id;
const segmentHtml = document.getElementById(htmlId);
if (segmentHtml) segmentHtml.style.width = `${((ordered[i].endTime - ordered[i].startTime) / totalTime) * 100}%`;
- });
+ });
// update the width of the expanding segment using the remaining time
const segExapandHtml = document.getElementById('segment-expanding');
if (segExapandHtml)
segExapandHtml.style.width = ordered.length === 0 ? '100%' : `${(remainingTime / totalTime) * 100}%`;
-
- }, [props.progress]);
-
-
-
- useEffect(() => {
- // this useEffect fired when the videos are being rearragned to the order
- // in this case, do nothing.
- if (props.orderVideos) return;
-
- const order = props.videos.length - 1;
- // in this case, a new video is added -> push it onto ordered
- if (order >= ordered.length) {
- const { endTime, startTime } = props.videos.lastElement();
- setOrdered(prevOrdered => {
- return [...prevOrdered, { endTime, startTime , order }];
- });
- }
- // in this case, a video is removed
- else if (order < ordered.length) {
- console.error('warning: video removed from parent');
- }
- }, [props.videos]);
-
- useEffect(() => {
- props.setVideos(vids => ordered.map((seg) => vids[seg.order]));
- }, [props.orderVideos]);
-
- useEffect(() => {
- if (removed === -1) return;
- // update total removed time
- setTotalRemovedTime(prevRemoved => prevRemoved + (ordered[removed].endTime - ordered[removed].startTime));
-
- // put the element on the undo stack
- setUndoStack(prevUndo => [...prevUndo, ordered[removed]]);
- // remove the segment from the array
- setOrdered(prevOrdered => prevOrdered.filter((seg, i) => i !== removed));
- // reset to default/nullish state
- setRemoved(-1);
- }, [removed]);
-
- 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<HTMLDivElement>) => {
- // don't move the videobox element
- e.stopPropagation()
-
- // if recording, do nothing
- if (props.recording) return;
+ }, [props.progress]);
- // 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
- // if holding shift key, let's remove that segment
- // TODO: think of a way to accomodate touch -> maybe if pointerup isn't fired after x secs? or if dragged into a trash bin or smthn
- if (e.shiftKey) {
- const segId = parseInt(clickedSegment.id.split('-')[1]);
- setRemoved(segId);
- return
- }
-
- if (e.ctrlKey) {
- handleUndo();
- return;
- // todo: implement undo stack
- }
+ useEffect(() => {
+ // this useEffect fired when the videos are being rearragned to the order
+ // in this case, do nothing.
+ if (props.orderVideos) return;
+
+ const order = props.videos.length - 1;
+ // in this case, a new video is added -> push it onto ordered
+ if (order >= ordered.length) {
+ const { endTime, startTime } = props.videos.lastElement();
+ setOrdered(prevOrdered => {
+ return [...prevOrdered, { endTime, startTime, order }];
+ });
+ }
+
+ // in this case, a video is removed
+ else if (order < ordered.length) {
+ console.warn('warning: video removed from parent');
+ }
+ }, [props.videos]);
- 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,
- }
+ useEffect(() => {
+ props.setVideos(vids => ordered.map((seg) => vids[seg.order]));
+ }, [props.orderVideos]);
- // 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
- 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);
+ useEffect(() => {
+ if (removed === -1) return;
+ // update total removed time
+ setTotalRemovedTime(prevRemoved => prevRemoved + (ordered[removed].endTime - ordered[removed].startTime));
+
+ // put the element on the undo stack
+ setUndoStack(prevUndo => [...prevUndo, ordered[removed]]);
+ // remove the segment from the array
+ setOrdered(prevOrdered => prevOrdered.filter((seg, i) => i !== removed));
+ // reset to default/nullish state
+ setRemoved(-1);
+ }, [removed]);
+
+ 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<HTMLDivElement>) => {
+ // don't move the videobox element
+ e.stopPropagation()
- progressBar.addEventListener('pointermove', updateSegmentOrder)
- progressBar.addEventListener('pointerup', placeSegmentandCleanup, { once: true })
- }
+ // if recording, do nothing
+ if (props.recording) return;
- 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)
+ // 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
+
+ // if holding shift key, let's remove that segment
+ // TODO: think of a way to accomodate touch -> maybe if pointerup isn't fired after x secs? or if dragged into a trash bin or smthn
+ if (e.shiftKey) {
+ const segId = parseInt(clickedSegment.id.split('-')[1]);
+ setRemoved(segId);
+ return
+ }
+
+ if (e.ctrlKey) {
+ handleUndo();
+ 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
+ if (!progressBar.hasPointerCapture(ptrId)) {
+ placeSegmentandCleanup();
+ return;
+ }
+
+ followCursor(event, detchedSegment);
+
+ 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);
+ // 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)
+ }
- // functions for the floating segment that tracks the cursor while grabbing it
- 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 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`;
- }
+
+ // functions for the floating segment that tracks the cursor while grabbing it
+ 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 followCursor = (event: PointerEvent, dot: HTMLDivElement): void => {
+ // event.stopPropagation()
+ const { width, height } = dot.getBoundingClientRect();
+ dot.style.left = `${event.clientX - width / 2}px`;
+ dot.style.top = `${event.clientY - height / 2}px`;
+ }
return (
- <div className="progressbar" id="progressbar" onPointerDown={onPointerDown} ref={progressBarRef}>
- {segments}
- </div>
+ <div className="progressbar" id="progressbar" onPointerDown={onPointerDown} ref={progressBarRef}>
+ {segments}
+ </div>
)
} \ No newline at end of file