From f4a384b194f1267d1a5a22cedf2a06edcd6e1b26 Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Tue, 7 Jun 2022 17:26:53 -0400
Subject: started working with the pointer events to drag - very laggy, may try
 something more simple for now

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 80 ++++++++++++++++++----
 1 file changed, 67 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 82d5e1f04..3e71239ce 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -1,14 +1,26 @@
+import { isInteger } from 'lodash';
 import * as React from 'react';
-import { useEffect } from "react"
+import { useEffect, useState, useCallback, useRef } from "react"
 import "./ProgressBar.scss"
+import { MediaSegment } from './RecordingView';
 
 interface ProgressBarProps {
-      progress: number,
-      marks: number[],
+  videos: MediaSegment[],
 }
 
 export function ProgressBar(props: ProgressBarProps) {
 
+  const progressBarRef = useRef<HTMLDivElement | null>(null)
+
+  // array for the order of video segments
+  const [segments, setSegments] = useState([]);
+
+  // const totalTime = useMemo(() => props.videos.lastElement().endTime, [props.videos])
+
+  const totalTime = () => props.videos.lastElement().endTime
+
+
+
       // const handleClick = (e: React.MouseEvent) => {
       //       let progressbar = document.getElementById('progressbar')!
       //       let bounds = progressbar!.getBoundingClientRect();
@@ -26,20 +38,62 @@ export function ProgressBar(props: ProgressBarProps) {
       //             }
       //       }
       // }
+  
+  const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) =>{
+    // e.persist()
+    // console.log('event', e)
+    // don't move the videobox element
+    e.stopPropagation()
+
+    const progressBar = progressBarRef.current
+    if (progressBar == null) return
+    console.log('progressbar', progressBar)
+
+    const updateSegmentOrder = (event: PointerEvent): void => {
+      // event.stopPropagation()
+
+      // const hoveredSegment = event.target as HTMLDivElement & EventTarget
+      // console.log('updateSegmentOrder called', hoveredSegment.id === clickedSegment.id)
+      // if (hoveredSegment.id === clickedSegment.id) return;
+
+      console.log('updateSegmentOrder : target', event.target.id)
+     }
+    
+    // progressBar.addEventListener('pointermove', updateSegmentOrder)
+
+
+    const clickedSegment = e.target as HTMLDivElement & EventTarget
+    console.log('segment', clickedSegment.getBoundingClientRect())
+
+    const rect = clickedSegment.getBoundingClientRect()
+
+    const dragSegment = (event: PointerEvent): void => {
+      // event.stopPropagation()
+      clickedSegment.style.position = 'absolute';
+      clickedSegment.style.zIndex = '999';
+      clickedSegment.style.width = `${rect.width}px`;
+      clickedSegment.style.height = `${rect.height}px`;
+      clickedSegment.style.left = `${event.offsetX - rect.width/2}px`;
+      clickedSegment.style.top = `${event.offsetY - rect.height/2}px`;
+     }
+
+    progressBar.setPointerCapture(e.pointerId)
+    progressBar.addEventListener('pointermove', dragSegment)
+    progressBar.addEventListener('pointerup', () => {
+      progressBar.removeEventListener('pointermove', dragSegment), { once: true }
+      clickedSegment.style.position = clickedSegment.style.zIndex = 'inherit';
+      // progressBar.removeEventListener('pointermove', updateSegmentOrder), { once: true }
+    })
+  }
 
       return (
-            <div className="progressbar" id="progressbar">
-                  <div
+        <div className="progressbar" id="progressbar" onPointerDown={onPointerDown} ref={progressBarRef}>
+                  {/* <div
                         className="progressbar done"
                         style={{ width: `${props.progress}%` }}
                   // onClick={handleClick}
-                  ></div>
-                  {props.marks.map((mark) => {
-                        return <div
-                              className="progressbar mark"
-                              style={{ width: `${mark}%` }}
-                        ></div>
-                  })}
-            </div>
+                  ></div> */}
+          {props.videos.map((vid, i) => <div id={`segment-${i.toString()}`} className='segment' style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%` }}></div>)}
+        </div>
       )
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From ac76c4836c61cc6564367f35e14014bb9489257b Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Tue, 7 Jun 2022 19:22:37 -0400
Subject: got basic ui for segmentation to work - W overall

---
 .../views/nodes/RecordingBox/ProgressBar.scss      |   2 +-
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 113 ++++++++++++++++-----
 .../views/nodes/RecordingBox/RecordingView.tsx     |   1 +
 3 files changed, 91 insertions(+), 25 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index 5d27c8a7d..67f96033a 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -8,7 +8,7 @@
       justify-content: flex-start;
       bottom: 10px;
       width: 80%;
-      height: 20px;
+      height: 30px;
       background-color: gray;
 
       &.done {
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 3e71239ce..b17d27d2e 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -1,3 +1,5 @@
+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"
@@ -6,6 +8,7 @@ import { MediaSegment } from './RecordingView';
 
 interface ProgressBarProps {
   videos: MediaSegment[],
+  setVideos,
 }
 
 export function ProgressBar(props: ProgressBarProps) {
@@ -13,12 +16,30 @@ export function ProgressBar(props: ProgressBarProps) {
   const progressBarRef = useRef<HTMLDivElement | null>(null)
 
   // array for the order of video segments
-  const [segments, setSegments] = useState([]);
+  const [segments, setSegments] = useState<JSX.Element[]>([]);
+  const [ordered, setOrdered] = useState<Object[]>([]);
+
+  const [clicked, setClicked] = useState<number>(-1);
 
   // const totalTime = useMemo(() => props.videos.lastElement().endTime, [props.videos])
 
   const totalTime = () => props.videos.lastElement().endTime
 
+  useEffect(() => {
+    const segmentsJSX = ordered.map((vid, i) =>
+      <div id={`segment-${i.toString()}`} className='segment' style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%`, backgroundColor: clicked === i ? 'inherit' : 'red', }}>{vid.order}</div>);
+      
+    setSegments(segmentsJSX)
+  }, [clicked, ordered])
+
+  useEffect(() => {
+    setOrdered(props.videos.map((vid, order) => {
+    //const { endTime, startTime } = vid
+      // TODO: not tranfer the blobs around
+      return { ...vid, order };
+    }))
+  }, [props.videos]);
+
 
 
       // const handleClick = (e: React.MouseEvent) => {
@@ -39,6 +60,19 @@ export function ProgressBar(props: ProgressBarProps) {
       //       }
       // }
   
+  const updateLastHover = (index) => {
+        // id for segment is like 'segment-1' or 'segment-10'
+    const segId = index
+    console.log(segId)
+    const rect = progressBarRef.current?.children[segId].getBoundingClientRect()
+    console.log(rect)
+    return {
+      index: segId,
+      minX: rect.x,
+      maxX: rect.x + rect.width,
+    }
+  }
+  
   const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) =>{
     // e.persist()
     // console.log('event', e)
@@ -48,41 +82,72 @@ export function ProgressBar(props: ProgressBarProps) {
     const progressBar = progressBarRef.current
     if (progressBar == null) return
     console.log('progressbar', progressBar)
-
-    const updateSegmentOrder = (event: PointerEvent): void => {
-      // event.stopPropagation()
-
-      // const hoveredSegment = event.target as HTMLDivElement & EventTarget
-      // console.log('updateSegmentOrder called', hoveredSegment.id === clickedSegment.id)
-      // if (hoveredSegment.id === clickedSegment.id) return;
-
-      console.log('updateSegmentOrder : target', event.target.id)
-     }
     
     // progressBar.addEventListener('pointermove', updateSegmentOrder)
 
 
     const clickedSegment = e.target as HTMLDivElement & EventTarget
-    console.log('segment', clickedSegment.getBoundingClientRect())
 
     const rect = clickedSegment.getBoundingClientRect()
-
+    setClicked(parseInt(clickedSegment.id.split('-')[1]))
+    let lastHover = {
+      index: parseInt(clickedSegment.id.split('-')[1]),
+      minX: rect.x,
+      maxX: rect.x + rect.width,
+    }
+
+    // clickedSegment.style.backgroundColor = `inherit`;
+    const dot = document.createElement("div")
+    dot.classList.add("segment")
+    dot.style.position = 'absolute';
+    dot.style.zIndex = '999';
+    dot.style.width = `${rect.width}px`;
+    dot.style.height = `${rect.height}px`;
+    document.body.append(dot)
     const dragSegment = (event: PointerEvent): void => {
       // event.stopPropagation()
-      clickedSegment.style.position = 'absolute';
-      clickedSegment.style.zIndex = '999';
-      clickedSegment.style.width = `${rect.width}px`;
-      clickedSegment.style.height = `${rect.height}px`;
-      clickedSegment.style.left = `${event.offsetX - rect.width/2}px`;
-      clickedSegment.style.top = `${event.offsetY - rect.height/2}px`;
+      dot.style.left = `${event.clientX - rect.width/2}px`;
+      dot.style.top = `${event.clientY - rect.height/2}px`;
+     }
+
+    const updateSegmentOrder = (event: PointerEvent): void => {
+      event.stopPropagation();
+
+      dragSegment(event)
+
+      const oldIndex = lastHover.index
+      const swapSegments = (newIndex) => {
+        if (newIndex == null) return
+        setOrdered(prevOrdered => {
+          const cpy = [...prevOrdered]
+          cpy[oldIndex] = cpy[newIndex]
+          cpy[newIndex] = prevOrdered[oldIndex]
+          return cpy
+        })
+        setClicked(newIndex)
+      }
+
+      const curX = event.clientX;
+      if (curX < lastHover.minX && lastHover.index > 0) {
+        swapSegments(lastHover.index - 1)
+        lastHover = updateLastHover(lastHover.index - 1)
+      }
+      else if (curX > lastHover.maxX && lastHover.index < segments.length - 1) {
+        swapSegments(lastHover.index + 1)
+        lastHover = updateLastHover(lastHover.index + 1)
+      }
      }
 
     progressBar.setPointerCapture(e.pointerId)
-    progressBar.addEventListener('pointermove', dragSegment)
-    progressBar.addEventListener('pointerup', () => {
-      progressBar.removeEventListener('pointermove', dragSegment), { once: true }
-      clickedSegment.style.position = clickedSegment.style.zIndex = 'inherit';
+    progressBar.addEventListener('pointermove', updateSegmentOrder)
+    progressBar.addEventListener('pointerup', (event) => {
+      progressBar.removeEventListener('pointermove', updateSegmentOrder), { once: true }
+      // clickedSegment.style.position = clickedSegment.style.zIndex = 'inherit';
       // progressBar.removeEventListener('pointermove', updateSegmentOrder), { once: true }
+      // clickedSegment.style.backgroundColor = 'red';
+      setClicked(-1);
+      // updateSegmentOrder(event);
+      dot.remove();
     })
   }
 
@@ -93,7 +158,7 @@ export function ProgressBar(props: ProgressBarProps) {
                         style={{ width: `${props.progress}%` }}
                   // onClick={handleClick}
                   ></div> */}
-          {props.videos.map((vid, i) => <div id={`segment-${i.toString()}`} className='segment' style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%` }}></div>)}
+          {segments}
         </div>
       )
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 2420c126f..640e4f5f2 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -351,6 +351,7 @@ export function RecordingView(props: IRecordingViewProps) {
 
               <ProgressBar
                 videos={videos}
+                setVideos={setVideos}
                               // playSegment={playSegment}
                               />
                         </div>
-- 
cgit v1.2.3-70-g09d2


From c656abad84ac56c3067a48ca6d34d567e3fcdd88 Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Wed, 8 Jun 2022 00:05:34 -0400
Subject: Took stab at ffmpeg in server. did some reworking of recordingview
 css. fix smoothness bug for the moving segment that follow cursor.

---
 src/client/Network.ts                              | 15 +++++++-
 .../views/nodes/RecordingBox/ProgressBar.scss      | 33 +++++++++++++++---
 .../views/nodes/RecordingBox/ProgressBar.tsx       |  9 ++---
 .../views/nodes/RecordingBox/RecordingBox.tsx      |  4 +--
 .../views/nodes/RecordingBox/RecordingView.scss    | 29 ++++++++++------
 .../views/nodes/RecordingBox/RecordingView.tsx     | 23 +++++++++----
 src/server/ApiManagers/UploadManager.ts            | 28 +++++++++++++--
 src/server/DashUploadUtils.ts                      | 40 ++++++++++++++--------
 8 files changed, 136 insertions(+), 45 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/Network.ts b/src/client/Network.ts
index 1255e5ce0..2c6d9d711 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -19,7 +19,6 @@ export namespace Networking {
       }
 
       export async function UploadFilesToServer<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> {
-            console.log(files)
             const formData = new FormData();
             if (Array.isArray(files)) {
                   if (!files.length) {
@@ -36,6 +35,19 @@ export namespace Networking {
             const response = await fetch("/uploadFormData", parameters);
             return response.json();
       }
+    
+    export async function UploadSegmentsAndConcatenate<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> {
+            console.log(files)
+            const formData = new FormData();
+            if (!Array.isArray(files) || !files.length) return [];
+            files.forEach(file => formData.append(Utils.GenerateGuid(), file));
+            const parameters = {
+                  method: 'POST',
+                  body: formData
+            };
+            const response = await fetch("/uploadVideosandConcatenate", parameters);
+            return response.json();
+      }
 
       export async function UploadYoutubeToServer<T extends Upload.FileInformation = Upload.FileInformation>(videoId: string): Promise<Upload.FileResponse<T>[]> {
             const parameters = {
@@ -46,5 +58,6 @@ export namespace Networking {
             const response = await fetch("/uploadYoutubeVideo", parameters);
             return response.json();
       }
+    
 
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index 67f96033a..d387468c6 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -1,13 +1,17 @@
 
 .progressbar {
   touch-action: none;
+  vertical-align: middle;
+  text-align: center;
+
+  align-items: center;
 
 
       position: absolute;
       display: flex;
       justify-content: flex-start;
-      bottom: 10px;
-      width: 80%;
+      bottom: 2px;
+      width: 99%;
       height: 30px;
       background-color: gray;
 
@@ -34,11 +38,24 @@
   margin: 1px;
   padding: 0;
   cursor: pointer;
-  transition-duration: .25s;
+  transition-duration: .5s;
+  user-select: none;
 
+  vertical-align: middle;
   text-align: center;
 }
 
+.segment-hide {
+    background-color: inherit;
+    text-align: center;
+    vertical-align: middle;
+    user-select: none;
+    // /* Hide the text. */
+    // text-indent: 100%;
+    // white-space: nowrap;
+    // overflow: hidden;
+}
+
 .segment:first-child {
   margin-left: 2px;
 }
@@ -47,9 +64,17 @@
 }
 
 .segment:hover {
+    background-color: white;
+}
+
+.segment:hover, .segment-selected {
   margin: 0px;
   border: 4px solid red;
-  background-color: black;
   min-width: 10px;
   border-radius: 2px;
 }
+
+.segment-selected {
+  border: 4px solid grey;
+  background-color: red;
+}
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index b17d27d2e..34a771367 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -27,7 +27,7 @@ export function ProgressBar(props: ProgressBarProps) {
 
   useEffect(() => {
     const segmentsJSX = ordered.map((vid, i) =>
-      <div id={`segment-${i.toString()}`} className='segment' style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%`, backgroundColor: clicked === i ? 'inherit' : 'red', }}>{vid.order}</div>);
+        <div id={`segment-${i.toString()}`} className={ clicked === i ? 'segment-hide' : 'segment'} style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%` }}>{vid.order}</div>);
       
     setSegments(segmentsJSX)
   }, [clicked, ordered])
@@ -98,7 +98,8 @@ export function ProgressBar(props: ProgressBarProps) {
 
     // clickedSegment.style.backgroundColor = `inherit`;
     const dot = document.createElement("div")
-    dot.classList.add("segment")
+    dot.classList.add("segment-selected")
+    dot.style.transitionDuration = '0s';
     dot.style.position = 'absolute';
     dot.style.zIndex = '999';
     dot.style.width = `${rect.width}px`;
@@ -106,8 +107,8 @@ export function ProgressBar(props: ProgressBarProps) {
     document.body.append(dot)
     const dragSegment = (event: PointerEvent): void => {
       // event.stopPropagation()
-      dot.style.left = `${event.clientX - rect.width/2}px`;
-      dot.style.top = `${event.clientY - rect.height/2}px`;
+        dot.style.left = `${event.clientX - rect.width/2}px`;
+        dot.style.top = `${event.clientY - rect.height/2}px`;
      }
 
     const updateSegmentOrder = (event: PointerEvent): void => {
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 159271223..68f3b3ad4 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -54,9 +54,9 @@ export class RecordingBox extends ViewBoxBaseComponent() {
       }
 
       render() {
-            // console.log("Proto[Is]: ", this.rootDoc.proto?.[Id])
+            console.log("Proto[Is]: ", this.rootDoc.proto?.[Id])
             return <div className="recordingBox" ref={this._ref}>
-                  {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={this.rootDoc.proto?.[Id]} />}
+                  {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={this.rootDoc.proto?.[Id] || ''} />}
             </div>;
       }
 }
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.scss b/src/client/views/nodes/RecordingBox/RecordingView.scss
index 9b2f6d070..0b7b1918f 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.scss
+++ b/src/client/views/nodes/RecordingBox/RecordingView.scss
@@ -33,7 +33,7 @@ button {
 }
 
 .video-wrapper:hover .controls {
-      bottom: 30px;
+      bottom: 34.5px;
       transform: translateY(0%);
       opacity: 100%;
 }
@@ -43,8 +43,8 @@ button {
       align-items: center;
       justify-content: space-evenly;
       position: absolute;
-      padding: 14px;
-      width: 100%;
+      // padding: 14px;
+      //width: 100%;
       max-width: 500px;
       // max-height: 20%;
       flex-wrap: wrap;
@@ -56,7 +56,14 @@ button {
       // transform: translateY(150%);
       transition: all 0.3s ease-in-out;
       // opacity: 0%;
-      bottom: 30px;
+      bottom: 34.5px;
+      height: 60px;
+      right: 2px;
+      // bottom: -150px;
+}
+
+.controls:active {
+      bottom: 40px;
       // bottom: -150px;
 }
 
@@ -127,9 +134,8 @@ button {
 .controls-inner-container {
       display: flex;
       flex-direction: row;
-      justify-content: center;
-      width: 100%;
-
+      align-content: center;
+      position: relative;
 }
 
 .record-button-wrapper {
@@ -180,14 +186,14 @@ button {
       height: 100%;
       display: flex;
       flex-direction: row;
-      align-items: center;
-      position: absolute;
+      align-content: center;
+      position: relative;
       top: 0;
       bottom: 0;
 
       &.video-edit-wrapper {
 
-            right: 50% - 15;
+            // right: 50% - 15;
 
             .track-screen {
                   font-weight: 200;
@@ -197,10 +203,11 @@ button {
 
       &.track-screen-wrapper {
 
-            right: 50% - 30;
+            // right: 50% - 30;
 
             .track-screen {
                   font-weight: 200;
+                  color: aqua;
             }
 
       }
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 640e4f5f2..1e9ce22f1 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -71,13 +71,21 @@ export function RecordingView(props: IRecordingViewProps) {
                     inputPaths.push(name)
                   });
                 
-                console.log(videoFiles)
+                  console.log(videoFiles)
+                  
+                  const data = await Networking.UploadSegmentsAndConcatenate(videoFiles)
+                  console.log('data', data)
+                  const result = data[0].result
+                  if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+                      props.setResult(result, trackScreen)
+                  } else {
+                      alert("video conversion failed");
+                  }
                 
                 
                 
                 // const inputListName = 'order.txt';
                 // fs.writeFileSync(inputListName, inputPaths.join('\n'));
-
                 // var merge = ffmpeg();
                 // merge.input(inputListName)
                 //   .inputOptions(['-f concat', '-safe 0'])
@@ -345,16 +353,17 @@ export function RecordingView(props: IRecordingViewProps) {
                                                       <span className="checkmark"></span>
                                                       Track Screen
                                                 </label>
-                                          </div>)}
-
-                              </div>
+                              </div>)}
 
-              <ProgressBar
+                      </div>
+                              
+                  </div>
+                  
+                                         <ProgressBar
                 videos={videos}
                 setVideos={setVideos}
                               // playSegment={playSegment}
                               />
-                        </div>
                   </div>
             </div>)
 }
\ No newline at end of file
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 634548154..faf36c6e5 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -40,7 +40,31 @@ export function clientPathToFile(directory: Directory, filename: string) {
 
 export default class UploadManager extends ApiManager {
 
-      protected initialize(register: Registration): void {
+    protected initialize(register: Registration): void {
+          
+            register({
+                  method: Method.POST,
+                  subscription: "/uploadVideosAndConcatenate",
+                  secureHandler: async ({ req, res }) => {
+                        const form = new formidable.IncomingForm();
+                        form.keepExtensions = true;
+                        form.uploadDir = pathToDirectory(Directory.parsed_files);
+                        return new Promise<void>(resolve => {
+                              form.parse(req, async (_err, _fields, files) => {
+                                    const result: Upload.FileResponse[] = [];
+                                    for (const key in files) {
+                                        const f = files[key];
+                                        if (Array.isArray(f)) {
+                                            const result = await DashUploadUtils.concatenateVideos(f);
+                                            console.log('concatenated', result);
+                                            result && !(result.result instanceof Error) && _success(res, result);
+                                        }
+                                    }
+                                    resolve();
+                              });
+                        });
+                  }
+            });
 
             register({
                   method: Method.POST,
@@ -50,7 +74,7 @@ export default class UploadManager extends ApiManager {
                         form.keepExtensions = true;
                         form.uploadDir = pathToDirectory(Directory.parsed_files);
                         return new Promise<void>(resolve => {
-                              form.parse(req, async (_err, _fields, files) => {
+                            form.parse(req, async (_err, _fields, files) => {
                                     const results: Upload.FileResponse[] = [];
                                     for (const key in files) {
                                           const f = files[key];
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 0c4f87905..6a7c8543d 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -1,9 +1,9 @@
 import { green, red } from 'colors';
 import { ExifImage } from 'exif';
+import * as exifr from 'exifr';
 import { File } from 'formidable';
 import { createWriteStream, existsSync, readFileSync, rename, unlinkSync, writeFile } from 'fs';
 import * as path from 'path';
-import * as exifr from 'exifr';
 import { basename } from "path";
 import * as sharp from 'sharp';
 import { Stream } from 'stream';
@@ -17,7 +17,6 @@ import { resolvedServerUrl } from "./server_Initialization";
 import { AcceptableMedia, Upload } from './SharedMediaTypes';
 import request = require('request-promise');
 import formidable = require('formidable');
-import { output } from '../../webpack.config';
 const { exec } = require("child_process");
 const parse = require('pdf-parse');
 const ffmpeg = require("fluent-ffmpeg");
@@ -63,19 +62,31 @@ export namespace DashUploadUtils {
 
     const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia; //TODO:glr 
     
-    export async function combineSegments(filePtr: File[], inputPaths: string[]): Promise<File> {
-        const inputListName = 'order.txt';
-
-        return new Promise<File>((resolve, reject) => {
-            fs.writeFileSync(inputListName, inputPaths.join('\n'));
-            ffmpeg(inputListName).inputOptions(['-f concat', '-safe 0']).outputOptions('-c copy').save('output.mp4')
+    export async function concatenateVideos(videoFiles: File[]): Promise<Upload.FileResponse> {
+        // make a list of paths to create the ordered text file for ffmpeg
+        const filePaths = videoFiles.map(file => file.path);
+        // write the text file to the file system
+        const inputListName = 'concat.txt';
+        const textFilePath = path.join(filesDirectory, "concat.txt");
+        writeFile(textFilePath, filePaths.join("\n"), (err) => console.log(err));
+
+
+        // make output file name based on timestamp
+        const outputFileName = `output-${Utils.GenerateGuid()}.mp4`;
+        await new Promise((resolve, reject) => {
+            ffmpeg(inputListName).inputOptions(['-f concat', '-safe 0']).outputOptions('-c copy').save(outputFileName)
                 .on("error", reject)
-                .on("end", () => {
-                    fs.unlinkSync(inputListName);
-                    filePtr[0].path = 'output.mp4';
-                    resolve(filePtr[0]);
-                });
-        });
+                .on("end", resolve);
+        })
+
+        // delete concat.txt from the file system
+        unlinkSync(textFilePath);
+
+        // read the output file from the file system
+        const outputFile = fs.readFileSync(outputFileName);
+        console.log('outputFile', outputFile);
+        // move only the output file to the videos directory
+        return MoveParsedFile(outputFile, Directory.videos)
     }
 
     export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {
@@ -237,6 +248,7 @@ export namespace DashUploadUtils {
         }
         let resolvedUrl: string;
         /**
+         * 
          * At this point, we want to take whatever url we have and make sure it's requestable.
          * Anything that's hosted by some other website already is, but if the url is a local file url
          * (locates the file on this server machine), we have to resolve the client side url by cutting out the
-- 
cgit v1.2.3-70-g09d2


From f7966035392675e8670914f1df81cda9084c2cbe Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Wed, 8 Jun 2022 14:24:09 -0400
Subject: Refactor code, but there is a bug. May go back.

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 147 +++++++++++++--------
 1 file changed, 91 insertions(+), 56 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 34a771367..fbb5e8a8b 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -2,7 +2,7 @@ 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 { useEffect, useState, useCallback, useRef, useMemo } from "react"
 import "./ProgressBar.scss"
 import { MediaSegment } from './RecordingView';
 
@@ -11,33 +11,52 @@ interface ProgressBarProps {
   setVideos,
 }
 
+interface SegmentBox {
+  endTime: number,
+  startTime: number,
+  order: number,
+}
+interface CurrentHover {
+  index: number,
+  minX: number,
+  maxX: number
+}
+
 export function ProgressBar(props: ProgressBarProps) {
 
   const progressBarRef = useRef<HTMLDivElement | null>(null)
 
   // array for the order of video segments
   const [segments, setSegments] = useState<JSX.Element[]>([]);
-  const [ordered, setOrdered] = useState<Object[]>([]);
+  const [ordered, setOrdered] = useState<SegmentBox[]>([]);
 
-  const [clicked, setClicked] = useState<number>(-1);
+  const [dragged, setDragged] = useState<number>(-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((vid, i) =>
-        <div id={`segment-${i.toString()}`} className={ clicked === i ? 'segment-hide' : 'segment'} style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%` }}>{vid.order}</div>);
+    const segmentsJSX = ordered.map((seg, i) =>
+        <div id={`segment-${i.toString()}`} className={ dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime()) * 100}%` }}>{seg.order}</div>);
       
     setSegments(segmentsJSX)
-  }, [clicked, ordered])
+  }, [dragged, ordered])
 
   useEffect(() => {
-    setOrdered(props.videos.map((vid, order) => {
-    //const { endTime, startTime } = vid
-      // TODO: not tranfer the blobs around
-      return { ...vid, order };
-    }))
+    const order = props.videos.length
+    if (order) {
+      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]);
 
 
@@ -60,12 +79,10 @@ export function ProgressBar(props: ProgressBarProps) {
       //       }
       // }
   
-  const updateLastHover = (index) => {
-        // id for segment is like 'segment-1' or 'segment-10'
-    const segId = index
-    console.log(segId)
+  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()
-    console.log(rect)
+    if (rect == null) return null
     return {
       index: segId,
       minX: rect.x,
@@ -73,51 +90,46 @@ export function ProgressBar(props: ProgressBarProps) {
     }
   }
   
-  const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) =>{
-    // e.persist()
-    // console.log('event', e)
+  const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+    console.log('pointer down')
     // don't move the videobox element
     e.stopPropagation()
 
-    const progressBar = progressBarRef.current
-    if (progressBar == null) return
-    console.log('progressbar', progressBar)
-    
-    // progressBar.addEventListener('pointermove', updateSegmentOrder)
-
-
+    // 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
+    progressBar.setPointerCapture(e.pointerId)
+
     const rect = clickedSegment.getBoundingClientRect()
-    setClicked(parseInt(clickedSegment.id.split('-')[1]))
-    let lastHover = {
-      index: parseInt(clickedSegment.id.split('-')[1]),
+    // 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,
     }
 
-    // clickedSegment.style.backgroundColor = `inherit`;
-    const dot = document.createElement("div")
-    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`;
-    document.body.append(dot)
-    const dragSegment = (event: PointerEvent): void => {
-      // event.stopPropagation()
-        dot.style.left = `${event.clientX - rect.width/2}px`;
-        dot.style.top = `${event.clientY - rect.height/2}px`;
-     }
+    // create the div element that tracks the cursor
+    const detchedSegment = document.createElement("div")
+    initDeatchSegment(detchedSegment, rect);
 
-    const updateSegmentOrder = (event: PointerEvent): void => {
+    function updateSegmentOrder(event: PointerEvent): void{
       event.stopPropagation();
 
-      dragSegment(event)
+      followCursor(event, detchedSegment, rect)
 
       const oldIndex = lastHover.index
-      const swapSegments = (newIndex) => {
+      const swapSegments = (newIndex: number) => {
         if (newIndex == null) return
         setOrdered(prevOrdered => {
           const cpy = [...prevOrdered]
@@ -125,32 +137,55 @@ export function ProgressBar(props: ProgressBarProps) {
           cpy[newIndex] = prevOrdered[oldIndex]
           return cpy
         })
-        setClicked(newIndex)
+        setDragged(newIndex)
       }
 
       const curX = event.clientX;
       if (curX < lastHover.minX && lastHover.index > 0) {
         swapSegments(lastHover.index - 1)
-        lastHover = updateLastHover(lastHover.index - 1)
+        lastHover = updateLastHover(lastHover.index - 1) ?? lastHover
       }
       else if (curX > lastHover.maxX && lastHover.index < segments.length - 1) {
         swapSegments(lastHover.index + 1)
-        lastHover = updateLastHover(lastHover.index + 1)
+        lastHover = updateLastHover(lastHover.index + 1) ?? lastHover
       }
      }
 
-    progressBar.setPointerCapture(e.pointerId)
     progressBar.addEventListener('pointermove', updateSegmentOrder)
-    progressBar.addEventListener('pointerup', (event) => {
-      progressBar.removeEventListener('pointermove', updateSegmentOrder), { once: true }
+    progressBar.addEventListener('pointerup', () => {
+      progressBar.removeEventListener('pointermove', updateSegmentOrder), { once : true }
       // clickedSegment.style.position = clickedSegment.style.zIndex = 'inherit';
       // progressBar.removeEventListener('pointermove', updateSegmentOrder), { once: true }
       // clickedSegment.style.backgroundColor = 'red';
-      setClicked(-1);
-      // updateSegmentOrder(event);
-      dot.remove();
-    })
+      console.log('removed dot')
+      detchedSegment.remove()
+      setDragged(-1);
+    }, { once: true })
+  }
+
+
+  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`;
+    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 (
         <div className="progressbar" id="progressbar" onPointerDown={onPointerDown} ref={progressBarRef}>
-- 
cgit v1.2.3-70-g09d2


From 2b416e0b836af692e0ce7f121e25e167919f3681 Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Wed, 8 Jun 2022 15:15:47 -0400
Subject: found a hacky way to stop the bug with the mouse. touch devices seems
 to be working in a weird way tho

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 68 +++++++++++++---------
 1 file changed, 41 insertions(+), 27 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index fbb5e8a8b..3314da355 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -8,7 +8,7 @@ import { MediaSegment } from './RecordingView';
 
 interface ProgressBarProps {
   videos: MediaSegment[],
-  setVideos,
+  setVideos: React.Dispatch<React.SetStateAction<MediaSegment[]>>,
 }
 
 interface SegmentBox {
@@ -102,7 +102,8 @@ export function ProgressBar(props: ProgressBarProps) {
     // don't do anything if null
     const progressBar = progressBarRef.current
     if (progressBar == null || clickedSegment.id === progressBar.id) return
-    progressBar.setPointerCapture(e.pointerId)
+    const ptrId = e.pointerId;
+    progressBar.setPointerCapture(ptrId)
 
     const rect = clickedSegment.getBoundingClientRect()
     // id for segment is like 'segment-1' or 'segment-10', 
@@ -123,46 +124,58 @@ export function ProgressBar(props: ProgressBarProps) {
     const detchedSegment = document.createElement("div")
     initDeatchSegment(detchedSegment, rect);
 
-    function updateSegmentOrder(event: PointerEvent): void{
+    const updateSegmentOrder = (event: PointerEvent): void => {
       event.stopPropagation();
+      event.preventDefault();
 
-      followCursor(event, detchedSegment, rect)
-
-      const oldIndex = lastHover.index
-      const swapSegments = (newIndex: number) => {
-        if (newIndex == null) return
-        setOrdered(prevOrdered => {
-          const cpy = [...prevOrdered]
-          cpy[oldIndex] = cpy[newIndex]
-          cpy[newIndex] = prevOrdered[oldIndex]
-          return cpy
-        })
-        setDragged(newIndex)
+      // 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 - 1)
+        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 + 1)
+        swapSegments(lastHover.index, lastHover.index + 1)
         lastHover = updateLastHover(lastHover.index + 1) ?? lastHover
       }
-     }
-
-    progressBar.addEventListener('pointermove', updateSegmentOrder)
-    progressBar.addEventListener('pointerup', () => {
-      progressBar.removeEventListener('pointermove', updateSegmentOrder), { once : true }
-      // clickedSegment.style.position = clickedSegment.style.zIndex = 'inherit';
-      // progressBar.removeEventListener('pointermove', updateSegmentOrder), { once: true }
-      // clickedSegment.style.backgroundColor = 'red';
-      console.log('removed dot')
+    }
+    
+    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);
-    }, { once: true })
+    }
+
+
+    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")
@@ -173,6 +186,7 @@ export function ProgressBar(props: ProgressBarProps) {
     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) => {
-- 
cgit v1.2.3-70-g09d2


From bc6aa7b8e7c9e43901f500d58acb0ebb6450b0a5 Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Wed, 8 Jun 2022 15:38:40 -0400
Subject: got basic ordering to work for the videos that go to the server

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       |  7 +++++-
 .../views/nodes/RecordingBox/RecordingView.tsx     | 25 ++++++++++++++++------
 2 files changed, 25 insertions(+), 7 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 3314da355..a91656cbc 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -9,6 +9,7 @@ import { MediaSegment } from './RecordingView';
 interface ProgressBarProps {
   videos: MediaSegment[],
   setVideos: React.Dispatch<React.SetStateAction<MediaSegment[]>>,
+  orderVideos: boolean,
 }
 
 interface SegmentBox {
@@ -46,7 +47,7 @@ export function ProgressBar(props: ProgressBarProps) {
 
   useEffect(() => {
     const order = props.videos.length
-    if (order) {
+    if (order && !props.orderVideos) {
       const { endTime, startTime } = props.videos.lastElement();
       setOrdered(prevOrdered => {
         return [...prevOrdered, { endTime, startTime , order }];
@@ -59,6 +60,10 @@ export function ProgressBar(props: ProgressBarProps) {
     // }))
   }, [props.videos]);
 
+  useEffect(() => {
+    props.setVideos(vids => ordered.map((seg) => vids[seg.order - 1]));
+  }, [props.orderVideos]);
+
 
 
       // const handleClick = (e: React.MouseEvent) => {
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index aea7f56b5..a5c2dc85c 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -34,7 +34,7 @@ export function RecordingView(props: IRecordingViewProps) {
       const [progress, setProgress] = useState(0);
 
     const [videos, setVideos] = useState<MediaSegment[]>([]);
-    // const [order, setOrder] = useState<number[]>([]);
+    const [orderVideos, setOrderVideos] = useState<boolean>(false);
       const videoRecorder = useRef<MediaRecorder | null>(null);
       const videoElementRef = useRef<HTMLVideoElement | null>(null);
 
@@ -57,13 +57,14 @@ export function RecordingView(props: IRecordingViewProps) {
 
     useEffect(() => {
           
-        console.log('in finish useEffect')
+        console.log('in videos useEffect')
 
           if (finished) {
               (async () => {
                 const inputPaths: string[] = [];
                 const videoFiles: File[] = []
-                  videos.forEach(async (vid, i) => {
+                videos.forEach(async (vid, i) => {
+                    console.log(vid)
                     const videoFile = new File(vid.videoChunks, `segvideo${i}.mkv`, { type: vid.videoChunks[0].type, lastModified: Date.now() });
                     videoFiles.push(videoFile);
                     
@@ -71,7 +72,7 @@ export function RecordingView(props: IRecordingViewProps) {
                     inputPaths.push(name)
                   })
                 
-                  console.log(videoFiles)
+                  console.log(inputPaths)
                   
                   const data = await Networking.UploadSegmentsAndConcatenate(videoFiles)
                   console.log('data', data)
@@ -158,6 +159,17 @@ export function RecordingView(props: IRecordingViewProps) {
             }
 
 
+    }, [videos])
+  
+  useEffect(() => {
+          
+        console.log('in finish useEffect')
+
+    if (finished) {
+      setOrderVideos(true);
+    }
+
+
       }, [finished])
 
       useEffect(() => {
@@ -360,8 +372,9 @@ export function RecordingView(props: IRecordingViewProps) {
                   </div>
                   
                                          <ProgressBar
-                videos={videos}
-                setVideos={setVideos}
+              videos={videos}
+              setVideos={setVideos}
+              orderVideos={orderVideos}
                               // playSegment={playSegment}
                               />
                   </div>
-- 
cgit v1.2.3-70-g09d2


From 48c60bd982734676f972514f7074be6121e7c5df Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Wed, 8 Jun 2022 18:24:53 -0400
Subject: big commit. FINALLY got the combining segments to work using ffmpeg
 using a different workflow. small ui changes as well.

---
 src/client/Network.ts                              |  15 +--
 .../views/nodes/RecordingBox/ProgressBar.tsx       |  20 +++-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   7 +-
 .../views/nodes/RecordingBox/RecordingView.tsx     | 126 ++++-----------------
 src/server/ApiManagers/UploadManager.ts            |  23 +---
 src/server/DashUploadUtils.ts                      |  59 +++-------
 6 files changed, 60 insertions(+), 190 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/Network.ts b/src/client/Network.ts
index 8c1f31488..b26f2458d 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -35,20 +35,7 @@ export namespace Networking {
             const response = await fetch("/uploadFormData", parameters);
             return response.json();
       }
-    
-    export async function UploadSegmentsAndConcatenate<T extends Upload.FileInformation = Upload.FileInformation>(files: File[]): Promise<Upload.FileResponse<T>[]> {
-            console.log("network.ts : uploading segments and concatenating", files);
-            const formData = new FormData();
-            if (!Array.isArray(files) || !files.length) return [];
-            files.forEach(file => formData.append(Utils.GenerateGuid(), file));
-            const parameters = {
-                  method: 'POST',
-                  body: formData
-            };
-            const response = await fetch("/uploadVideosandConcatenate", parameters);
-            return response.json();
-      }
-
+  
       export async function UploadYoutubeToServer<T extends Upload.FileInformation = Upload.FileInformation>(videoId: string): Promise<Upload.FileResponse<T>[]> {
             const parameters = {
                   method: 'POST',
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index a91656cbc..effc3d8a8 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -53,11 +53,6 @@ export function ProgressBar(props: ProgressBarProps) {
         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(() => {
@@ -96,7 +91,6 @@ export function ProgressBar(props: ProgressBarProps) {
   }
   
   const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
-    console.log('pointer down')
     // don't move the videobox element
     e.stopPropagation()
 
@@ -107,6 +101,20 @@ export function ProgressBar(props: ProgressBarProps) {
     // don't do anything if null
     const progressBar = progressBarRef.current
     if (progressBar == null || clickedSegment.id === progressBar.id) return
+
+    console.log('pointer down')
+    // if holding shift key, let's remove that segment
+    if (e.shiftKey) {
+      // TODO: fix this
+      const segId = parseInt(clickedSegment.id.split('-')[1])
+      const o = ordered[segId].order
+      setOrdered(prevOrdered => {
+        return prevOrdered.filter((seg, i) => i !== segId)
+      })
+
+      return
+    }
+
     const ptrId = e.pointerId;
     progressBar.setPointerCapture(ptrId)
 
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 68f3b3ad4..bd4af0a75 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -30,7 +30,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
             Doc.SetNativeHeight(this.dataDoc, 720);
       }
 
-      @observable result: Upload.FileInformation | undefined = undefined
+      @observable result: Upload.AccessPathInfo | undefined = undefined
       @observable videoDuration: number | undefined = undefined
 
       @action
@@ -39,13 +39,14 @@ export class RecordingBox extends ViewBoxBaseComponent() {
       }
 
       @action
-      setResult = (info: Upload.FileInformation, trackScreen: boolean) => {
+      setResult = (info: Upload.AccessPathInfo, trackScreen: boolean) => {
+        if (info == null) return
             this.result = info
             this.dataDoc.type = DocumentType.VID;
             this.dataDoc[this.fieldKey + "-duration"] = this.videoDuration;
 
             this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey);
-            this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.agnostic.client);
+            this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.client);
             this.dataDoc[this.fieldKey + "-recorded"] = true;
             // stringify the presenation and store it
             if (trackScreen) {
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index a5c2dc85c..8c8728fc3 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -18,7 +18,7 @@ export interface MediaSegment {
 }
 
 interface IRecordingViewProps {
-      setResult: (info: Upload.FileInformation, trackScreen: boolean) => void
+      setResult: (info: Upload.AccessPathInfo, trackScreen: boolean) => void
       setDuration: (seconds: number) => void
       id: string
 }
@@ -57,7 +57,7 @@ export function RecordingView(props: IRecordingViewProps) {
 
     useEffect(() => {
           
-        console.log('in videos useEffect')
+        // console.log('in videos useEffect', finished)
 
           if (finished) {
               (async () => {
@@ -71,89 +71,18 @@ export function RecordingView(props: IRecordingViewProps) {
                     const { name } = videoFile;
                     inputPaths.push(name)
                   })
-                
-                  console.log(inputPaths)
                   
-                  const data = await Networking.UploadSegmentsAndConcatenate(videoFiles)
-                  console.log('data', data)
-                  const result = data[0].result
-                  if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                      props.setResult(result, trackScreen)
-                  } else {
-                      alert("video conversion failed");
-                  }
-                
-                
+                const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles))
+                  .map(res => (res.result instanceof Error) ? '' : res.result.accessPaths.agnostic.server)
                 
-                // const inputListName = 'order.txt';
-                // fs.writeFileSync(inputListName, inputPaths.join('\n'));
-                // var merge = ffmpeg();
-                // merge.input(inputListName)
-                //   .inputOptions(['-f concat', '-safe 0'])
-                //   .outputOptions('-c copy')
-                //   .save('output.mp4')
-                
-                // fs.unlinkSync(inputListName);
-
-                // const combined = await DashUploadUtils.combineSegments(videoFiles, inputPaths)
-                // console.log('combined', combined)
-                
-                // const outputFile = new File(['output.mp4'], 'output.mp4', { type: 'video/mp4', lastModified: Date.now() });
-
-                // const data = await Networking.UploadFilesToServer(combined)
-                //   const result = data[0].result
-                //   if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                //       props.setResult(result, trackScreen)
-                //   } else {
-                //       alert("video conversion failed");
-                //   }
-                  
-
-                //   if (format.includes("x-matroska")) {
-                //     await new Promise(res => ffmpeg(file.path)
-                //         .videoCodec("copy") // this will copy the data instead of reencode it
-                //         .save(file.path.replace(".mkv", ".mp4"))
-                //         .on('end', res));
-                //     file.path = file.path.replace(".mkv", ".mp4");
-                //     format = ".mp4";
-                // }
-                //   console.log('crossOriginIsolated', crossOriginIsolated)
-                //   props.setDuration(recordingTimer * 100)
-
-                //   console.log('Loading ffmpeg-core.js');
-                //   const ffmpeg = createFFmpeg({ log: true });
-                //   await ffmpeg.load();
-                //   console.log('ffmpeg-core.js loaded');
-                  
-                //   let allVideoChunks: any = [];
-                //   const inputPaths: string[] = [];
-                //   // write each segment into it's indexed file
-                //   videos.forEach(async (vid, i) => {
-                //       const vidName = `segvideo${i}.mkv`
-                //       inputPaths.push(vidName)
-                //       const videoFile = new File(vid.videoChunks, vidName, { type: allVideoChunks[0].type, lastModified: Date.now() });
-                //       ffmpeg.FS('writeFile', vidName, await fetchFile(videoFile));
-                // //   })
-                //   ffmpeg.FS('writeFile', 'order.txt', inputPaths.join('\n'));
-                  
-                //   console.log('concat')
-                //   await ffmpeg.run('-f', 'concat', '-safe', '0', '-i', 'order.txt', 'ouput.mp4');
-
-                //   const { buffer } = ffmpeg.FS('readFile', 'output.mp4');
-                //   const concatVideo = new File([buffer], 'concat.mp4', { type: "video/mp4" });
-
-                //   const data = await Networking.UploadFilesToServer(concatVideo)
-                //   const result = data[0].result
-                //   if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                //       props.setResult(result, trackScreen)
-                //   } else {
-                //       alert("video conversion failed");
-                //   }
-                  
-                //   // delete all files in MEMFS
-                //   inputPaths.forEach(path => ffmpeg.FS('unlink', path));
-                //   ffmpeg.FS('unlink', 'order.txt');
-                //   ffmpeg.FS('unlink', 'output.mp4');
+                const result: Upload.AccessPathInfo | Error = await Networking.PostToServer('/concatVideos', serverPaths)
+                console.log(result)
+                if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+                    props.setResult(result, trackScreen)
+                } else {
+                    alert("video conversion failed");
+                }
+              
               })();
               
             }
@@ -162,8 +91,6 @@ export function RecordingView(props: IRecordingViewProps) {
     }, [videos])
   
   useEffect(() => {
-          
-        console.log('in finish useEffect')
 
     if (finished) {
       setOrderVideos(true);
@@ -177,7 +104,7 @@ export function RecordingView(props: IRecordingViewProps) {
             if (!navigator.mediaDevices) {
                   console.log('This browser does not support getUserMedia.')
             }
-          console.log('This device has the correct media devices.')
+          // console.log('This device has the correct media devices.')
       }, [])
 
       useEffect(() => {
@@ -244,7 +171,6 @@ export function RecordingView(props: IRecordingViewProps) {
                   // reset the temporary chunks
                   videoChunks = []
                   setRecording(false);
-                  setFinished(true);
                   trackScreen && RecordingApi.Instance.pause();
             }
 
@@ -269,8 +195,10 @@ export function RecordingView(props: IRecordingViewProps) {
       }
 
 
-      const stop = () => {
-            if (videoRecorder.current) {
+  const stop = (e: React.MouseEvent) => {
+        e.stopPropagation()
+        if (videoRecorder.current) {
+                                      setFinished(true);
                   if (videoRecorder.current.state !== "inactive") {
                         videoRecorder.current.stop();
                         // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
@@ -278,19 +206,21 @@ export function RecordingView(props: IRecordingViewProps) {
             }
       }
 
-      const pause = () => {
+      const pause = (e: React.MouseEvent) => {
+        e.stopPropagation()
             if (videoRecorder.current) {
                   if (videoRecorder.current.state === "recording") {
-                        videoRecorder.current.pause();
+                    videoRecorder.current.stop();
                   }
             }
       }
 
-      const startOrResume = () => {
+      const startOrResume =  (e: React.MouseEvent) => {
+        e.stopPropagation()
             if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
                   record();
             } else if (videoRecorder.current.state === "paused") {
-                  videoRecorder.current.resume();
+              videoRecorder.current.start();
             }
       }
 
@@ -315,16 +245,6 @@ export function RecordingView(props: IRecordingViewProps) {
             const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
             return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
       }
-    
-      const doTranscode = async () => {
-          
-        // console.log('Start transcoding');
-        // ffmpeg.FS('writeFile', 'test.avi', await fetchFile('/flame.avi'));
-        // await ffmpeg.run('-i', 'test.avi', 'test.mp4');
-        // console.log('Complete transcoding');
-        // const data = ffmpeg.FS('readFile', 'test.mp4');
-        // console.log(URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })));
-    };
 
       return (
             <div className="recording-container">
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 398b007b5..0ee0a34df 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -44,25 +44,10 @@ export default class UploadManager extends ApiManager {
           
             register({
                   method: Method.POST,
-                  subscription: "/uploadVideosandConcatenate",
-                  secureHandler: async ({ req, res }) => {
-                        const form = new formidable.IncomingForm();
-                        form.keepExtensions = true;
-                        form.uploadDir = pathToDirectory(Directory.parsed_files);
-                        return new Promise<void>(resolve => {
-                              form.parse(req, async (_err, _fields, files) => {
-                                  const results: Upload.FileResponse[] = [];
-
-                                  // create an array of all the file paths
-                                  const filePaths: string[] = Object.keys(files).map(key => files[key].path);
-                                    console.log("uploading files", Array.isArray(filePaths));
-                                    const result = await DashUploadUtils.concatenateVideos(filePaths);
-                                    console.log('concatenated', result);
-                                    result && !(result.result instanceof Error) && results.push(result);
-                                    _success(res, results)
-                                    resolve();
-                              });
-                        });
+                  subscription: "/concatVideos",
+              secureHandler: async ({ req, res }) => {
+                    // req.body contains the array of server paths to the videos
+                    _success(res, await DashUploadUtils.concatVideos(req.body));
                   }
             });
 
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 148b0df65..be30c115d 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -63,21 +63,20 @@ export namespace DashUploadUtils {
 
     const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia; //TODO:glr 
     
-    export async function concatenateVideos(filePaths: string[]): Promise<Upload.FileResponse> {
+    export async function concatVideos(filePaths: string[]): Promise<Upload.AccessPathInfo> {
         // make a list of paths to create the ordered text file for ffmpeg
-        //const filePaths = videoFiles.map(file => file.path);
-        // write the text file to the file system
         const inputListName = 'concat.txt';
         const textFilePath = path.join(filesDirectory, inputListName);
         // make a list of paths to create the ordered text file for ffmpeg
-        const filePathsText = filePaths.map(filePath => `file '${filePath}'`).join('\n');
+      const filePathsText = filePaths.map(filePath => `file '${filePath}'`).join('\n');
+        // write the text file to the file system
         writeFile(textFilePath, filePathsText, (err) => console.log(err));
-        console.log(filePathsText)
+        console.log('fileTextPaths', filePathsText)
 
         // make output file name based on timestamp
-        const outputFileName = `output-${Utils.GenerateGuid()}.mkv`;
-        // create the output file path in the parsed_file directory
-        const outputFilePath = path.join(filesDirectory, outputFileName);
+        const outputFileName = `output-${Utils.GenerateGuid()}.mp4`;
+        // create the output file path in the videos directory
+        const outputFilePath = path.join(pathToDirectory(Directory.videos), outputFileName);
 
         // concatenate the videos
         await new Promise((resolve, reject) => {
@@ -92,45 +91,15 @@ export namespace DashUploadUtils {
                 .on("end", resolve);
         })
 
-        // delete concat.txt from the file system
-        unlinkSync(textFilePath);
-
-        // read the output file from the file system
-        // const outputFile = fs.readFileSync(outputFilePath);
-
-        // make a new blob object with the output file buffer
-        // const outputFileBlob = new Blob([outputFile.buffer], { type: 'x-matroska/mkv' });
-
-        // TODO: make with toJSON()
+      // delete concat.txt from the file system
+      unlinkSync(textFilePath);
+      // delete the old segment videos from the server
+      filePaths.forEach(filePath => unlinkSync(filePath));
 
-        // make a data object
-        const outputFileObject: formidable.File = {
-            size: 0,
-            name: outputFileName,
-            path: outputFilePath,
-            // size: outputFileBlob.size,
-            type: 'video/x-matroska;codecs=avc1,opus',
-            lastModifiedDate: new Date(),
-
-            toJSON: () => ({ ...outputFileObject, filename: outputFilePath.replace(/.*\//, ""), mtime: null, length: 0, mime: "", toJson: () => undefined as any })
+      // return the path(s) to the output file
+      return {
+          accessPaths: getAccessPaths(Directory.videos, outputFileName)
         }
-
-       // const file = { ...outputFileObject, toJSON: () => ({ ...outputFileObject, filename: outputFilePath.replace(/.*\//, ""), mtime: null, length: 0, mime: "", toJson: () => undefined as any }) };
-
-        // this will convert it to mp4 and save it to the server
-        //return await MoveParsedFile(outputFileObject, Directory.videos);
-
-        return await upload(outputFileObject);
-
-        // // return only the output (first) file to the videos directory
-        // return {
-        //     source: file, result: {
-        //         accessPaths: {
-        //             agnostic: getAccessPaths(Directory.videos, outputFileName)
-        //         },
-        //         rawText: undefined
-        //     }
-        // }
     }
 
     export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {
-- 
cgit v1.2.3-70-g09d2


From bb8d6b6ad83bf20cdaf0f9a4b6665b3c1371cb95 Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Thu, 9 Jun 2022 10:40:34 -0400
Subject: got the removing on the shift key to work.

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 72 +++++++++++++---------
 .../views/nodes/RecordingBox/RecordingView.tsx     |  9 ++-
 2 files changed, 52 insertions(+), 29 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index effc3d8a8..7ceae6696 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -33,51 +33,71 @@ export function ProgressBar(props: ProgressBarProps) {
 
   const [dragged, setDragged] = useState<number>(-1);
 
+  const [totalTime, setTotalTime] = useState<number>(0);
+
+  // this holds the index of the videoc segment to be removed
+  const [removed, setRemoved] = useState<number>(-1);
+
   // const totalTime = useMemo(() => props.videos.lastElement().endTime, [props.videos])
 
-  const totalTime = () => props.videos.lastElement().endTime
+  // const totalTime = () => props.videos.lastElement().endTime
   // const memoTotal = useMemo(totalTime, [props.videos])
 
   useEffect(() => {
     const segmentsJSX = ordered.map((seg, i) =>
-        <div id={`segment-${i.toString()}`} className={ dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime()) * 100}%` }}>{seg.order}</div>);
+        <div id={`segment-${i.toString()}`} className={ dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime) * 100}%` }}>{seg.order}</div>);
       
     setSegments(segmentsJSX)
   }, [dragged, ordered])
 
   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
-    if (order && !props.orderVideos) {
+    // in this case, a new video is added
+    if (order && order > ordered.length) {
       const { endTime, startTime } = props.videos.lastElement();
+      // update total time
+      setTotalTime(prevTime => prevTime + (endTime - startTime));
+      // add the new segment to the ordered array
       setOrdered(prevOrdered => {
         return [...prevOrdered, { endTime, startTime , order }];
       });
     }
+
+    // in this case, a segment is removed
+    else if (order < ordered.length) {
+      // update the total time
+      setTotalTime(prevTime => prevTime - (ordered[removed].endTime - ordered[removed].startTime));
+      //remove the segment from the ordered array and decrement the order of the remaining segments 
+      // if they are greater than the removed segment's order 
+      const removedOrder = ordered[removed].order;
+      setOrdered(prevOrdered => 
+        prevOrdered.reduce((acc, seg, i) => {
+          (i !== removed) && acc.push({ ...seg, order: seg.order > removedOrder ? seg.order - 1 : seg.order });
+          return acc;
+        },[] as SegmentBox[])
+      );
+      // reset to default/nullish state
+      setRemoved(-1);
+    }
   }, [props.videos]);
 
   useEffect(() => {
     props.setVideos(vids => ordered.map((seg) => vids[seg.order - 1]));
   }, [props.orderVideos]);
 
+  useEffect(() => {
+    if (removed === -1) return;
 
+    console.log('removed', removed)
 
-      // 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
-      //             }
-      //       }
-      // }
+    // update the videos array -> this will fire the useEffect above, where we update the orders & totalTime
+    const index = ordered[removed].order - 1;
+    props.setVideos(vids => vids.filter((vid, i) => i !== index));
+  }, [removed]);
   
   const updateLastHover = (segId: number): CurrentHover | null => {
     // get the segId of the segment that will become the new bounding area
@@ -102,16 +122,12 @@ export function ProgressBar(props: ProgressBarProps) {
     const progressBar = progressBarRef.current
     if (progressBar == null || clickedSegment.id === progressBar.id) return
 
-    console.log('pointer down')
     // if holding shift key, let's remove that segment
+    // TODO: think of a way to accomodate touch -> maybe if poiuntermove isn't fired after x secs?
     if (e.shiftKey) {
-      // TODO: fix this
-      const segId = parseInt(clickedSegment.id.split('-')[1])
-      const o = ordered[segId].order
-      setOrdered(prevOrdered => {
-        return prevOrdered.filter((seg, i) => i !== segId)
-      })
-
+      const segId = parseInt(clickedSegment.id.split('-')[1]);
+      console.log('removing segment', segId)
+      setRemoved(segId);
       return
     }
 
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 8c8728fc3..68d85855c 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -55,6 +55,7 @@ export function RecordingView(props: IRecordingViewProps) {
             }
       }
 
+
     useEffect(() => {
           
         // console.log('in videos useEffect', finished)
@@ -90,6 +91,12 @@ export function RecordingView(props: IRecordingViewProps) {
 
     }, [videos])
   
+    
+  // make useEffect for progess and log when it fires
+  useEffect(() => {
+    console.log('progress', progress)
+  }, [progress])
+  
   useEffect(() => {
 
     if (finished) {
@@ -198,7 +205,7 @@ export function RecordingView(props: IRecordingViewProps) {
   const stop = (e: React.MouseEvent) => {
         e.stopPropagation()
         if (videoRecorder.current) {
-                                      setFinished(true);
+          setFinished(true);
                   if (videoRecorder.current.state !== "inactive") {
                         videoRecorder.current.stop();
                         // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
-- 
cgit v1.2.3-70-g09d2


From 28ec1d48ee9e8f4d8d82337c2116ac8027ed653d Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Thu, 9 Jun 2022 11:08:34 -0400
Subject: got the progress bar to move while recording, but it is a tad slow

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 35 +++++++++++++++++++---
 .../views/nodes/RecordingBox/RecordingView.tsx     |  8 ++---
 2 files changed, 33 insertions(+), 10 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 7ceae6696..ce231ba62 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -10,6 +10,8 @@ interface ProgressBarProps {
   videos: MediaSegment[],
   setVideos: React.Dispatch<React.SetStateAction<MediaSegment[]>>,
   orderVideos: boolean,
+  progress: number,
+  recording: boolean,
 }
 
 interface SegmentBox {
@@ -33,7 +35,11 @@ export function ProgressBar(props: ProgressBarProps) {
 
   const [dragged, setDragged] = useState<number>(-1);
 
-  const [totalTime, setTotalTime] = useState<number>(0);
+  // total length of the video, in seconds*100
+  // const [totalTime, setTotalTime] = useState<number>(0);
+
+  // 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);
@@ -43,12 +49,31 @@ export function ProgressBar(props: ProgressBarProps) {
   // const totalTime = () => props.videos.lastElement().endTime
   // const memoTotal = useMemo(totalTime, [props.videos])
 
+  // useEffect that updates total time based on progress
+  useEffect(() => {
+    console.log("useEffect progress", props.progress, 'totalRemovedTime', totalRemovedTime)
+    // setTotalTime(prev => {
+    //   // progress is in seconds, prev is in deciseconds?
+    //   const toAdd = props.progress * 10 - prev
+    //   return prev + toAdd
+    // })
+    // setTotalTime(prev => props.progress * 10)
+  }, [props.progress])
+
   useEffect(() => {
+    console.log("useEffect recording", props.recording)
+  }, [props.recording])
+  
+
+  useEffect(() => {
+    console.log('rendering new segments')
+    const totalTime = props.progress*1000 - totalRemovedTime
     const segmentsJSX = ordered.map((seg, i) =>
         <div id={`segment-${i.toString()}`} className={ dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime) * 100}%` }}>{seg.order}</div>);
       
     setSegments(segmentsJSX)
-  }, [dragged, ordered])
+  }, [dragged, ordered, props.progress])
+
 
   useEffect(() => {
     // this useEffect fired when the videos are being rearragned to the order
@@ -60,7 +85,7 @@ export function ProgressBar(props: ProgressBarProps) {
     if (order && order > ordered.length) {
       const { endTime, startTime } = props.videos.lastElement();
       // update total time
-      setTotalTime(prevTime => prevTime + (endTime - startTime));
+      // setTotalTime(prevTime => prevTime + (endTime - startTime));
       // add the new segment to the ordered array
       setOrdered(prevOrdered => {
         return [...prevOrdered, { endTime, startTime , order }];
@@ -70,7 +95,9 @@ export function ProgressBar(props: ProgressBarProps) {
     // in this case, a segment is removed
     else if (order < ordered.length) {
       // update the total time
-      setTotalTime(prevTime => prevTime - (ordered[removed].endTime - ordered[removed].startTime));
+      // setTotalTime(prevTime => prevTime - (ordered[removed].endTime - ordered[removed].startTime));
+      // update total removed time
+      setTotalRemovedTime(prevRemoved => prevRemoved + (ordered[removed].endTime - ordered[removed].startTime));
       //remove the segment from the ordered array and decrement the order of the remaining segments 
       // if they are greater than the removed segment's order 
       const removedOrder = ordered[removed].order;
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 68d85855c..40117c41c 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -91,12 +91,6 @@ export function RecordingView(props: IRecordingViewProps) {
 
     }, [videos])
   
-    
-  // make useEffect for progess and log when it fires
-  useEffect(() => {
-    console.log('progress', progress)
-  }, [progress])
-  
   useEffect(() => {
 
     if (finished) {
@@ -302,6 +296,8 @@ export function RecordingView(props: IRecordingViewProps) {
               videos={videos}
               setVideos={setVideos}
               orderVideos={orderVideos}
+              progress={progress}
+              recording={recording}
                               // playSegment={playSegment}
                               />
                   </div>
-- 
cgit v1.2.3-70-g09d2


From e9ae7c0482f1c9409d905bc6474bfd06abb01743 Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Thu, 9 Jun 2022 11:54:02 -0400
Subject: got the progress to be smooth, but missing final animation

---
 .../views/nodes/RecordingBox/ProgressBar.scss      |  4 ++
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 52 ++++++++++++++++------
 2 files changed, 42 insertions(+), 14 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index c7a190566..3ed7cee3e 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -45,6 +45,10 @@
   text-align: center;
 }
 
+.no-transition {
+  transition-duration: 0s;
+}
+
 .segment-hide {
     background-color: inherit;
     text-align: center;
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index ce231ba62..54bfbc792 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -44,35 +44,57 @@ export function ProgressBar(props: ProgressBarProps) {
   // this holds the index of the videoc segment to be removed
   const [removed, setRemoved] = useState<number>(-1);
 
+  // const totalTime = useMemo(() => props.progress*1000 - totalRemovedTime, [props.progress, totalRemovedTime]);
+
   // const totalTime = useMemo(() => props.videos.lastElement().endTime, [props.videos])
 
   // const totalTime = () => props.videos.lastElement().endTime
   // const memoTotal = useMemo(totalTime, [props.videos])
 
   // useEffect that updates total time based on progress
-  useEffect(() => {
-    console.log("useEffect progress", props.progress, 'totalRemovedTime', totalRemovedTime)
-    // setTotalTime(prev => {
-    //   // progress is in seconds, prev is in deciseconds?
-    //   const toAdd = props.progress * 10 - prev
-    //   return prev + toAdd
-    // })
-    // setTotalTime(prev => props.progress * 10)
-  }, [props.progress])
+  // useEffect(() => {
+  //   console.log("useEffect progress", props.progress, 'totalRemovedTime', totalRemovedTime)
+  //   // setTotalTime(prev => {
+  //   //   // progress is in seconds, prev is in deciseconds?
+  //   //   const toAdd = props.progress * 10 - prev
+  //   //   return prev + toAdd
+  //   // })
+  //   // setTotalTime(prev => props.progress * 10)
+  // }, [props.progress])
 
   useEffect(() => {
     console.log("useEffect recording", props.recording)
+    segments.forEach((seg, i) => {
+      const htmlId = seg.props.id;
+      const segmentHtml = document.getElementById(htmlId);
+      segmentHtml?.classList.toggle('no-transition', props.recording);
+      console.log(segmentHtml)
+    });
   }, [props.recording])
   
 
   useEffect(() => {
-    console.log('rendering new segments')
-    const totalTime = props.progress*1000 - totalRemovedTime
+    console.log('useEffect dragged, ordered')
+    const totalTime = props.progress * 1000 - totalRemovedTime;
     const segmentsJSX = ordered.map((seg, i) =>
-        <div id={`segment-${i.toString()}`} className={ dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime) * 100}%` }}>{seg.order}</div>);
+      <div id={`segment-${i.toString()}`} className={dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime) * 100}%` }}>{seg.order}</div>);
       
     setSegments(segmentsJSX)
-  }, [dragged, ordered, props.progress])
+  }, [dragged, ordered]);
+
+  // to imporve performance, we only want to update the width, not re-render the whole thing
+  useEffect(() => {
+    // console.log('updating width on progress useEffect')
+    const totalTime = props.progress * 1000 - totalRemovedTime;
+    segments.forEach((seg, i) => {
+      const htmlId = seg.props.id;
+      const segmentHtml = document.getElementById(htmlId);
+      segmentHtml?.style.width = `${((ordered[i].endTime - ordered[i].startTime) / totalTime) * 100}%`;
+
+      // console.log('updating width on progress useEffect', htmlId, segmentHtml, segmentHtml?.style.width)
+    });
+  }, [props.progress]);
+      
 
 
   useEffect(() => {
@@ -96,9 +118,11 @@ export function ProgressBar(props: ProgressBarProps) {
     else if (order < ordered.length) {
       // update the total time
       // setTotalTime(prevTime => prevTime - (ordered[removed].endTime - ordered[removed].startTime));
+
       // update total removed time
       setTotalRemovedTime(prevRemoved => prevRemoved + (ordered[removed].endTime - ordered[removed].startTime));
-      //remove the segment from the ordered array and decrement the order of the remaining segments 
+
+      // remove the segment from the ordered array and decrement the order of the remaining segments 
       // if they are greater than the removed segment's order 
       const removedOrder = ordered[removed].order;
       setOrdered(prevOrdered => 
-- 
cgit v1.2.3-70-g09d2


From 9024621c15962f3cdfccee6fc35294b44d4cf1ee Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Thu, 9 Jun 2022 16:27:27 -0400
Subject: fix undo, add disabled ui while recording, create expanding-segment
 to fill the space while recording

---
 .../views/nodes/RecordingBox/ProgressBar.scss      |  25 +++-
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 158 ++++++++++-----------
 .../views/nodes/RecordingBox/RecordingView.tsx     |  40 +++---
 3 files changed, 122 insertions(+), 101 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index 3ed7cee3e..1cf60b82b 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -45,8 +45,31 @@
   text-align: center;
 }
 
-.no-transition {
+// .no-transition {
+//   transition-duration: 0s;
+// }
+
+.segment-expanding {
+border-color: red;
+  background-color: white;
+  transition-duration: 0s;
+  opacity: .75;
+  pointer-events: none;
+}
+
+.segment-expanding:hover {
+    background-color: inherit;
+    cursor: not-allowed;
+}
+
+.segment-disabled {
+  pointer-events: none;
+  opacity: 0.5;
   transition-duration: 0s;
+  /* Hide the text. */
+    text-indent: 100%;
+    white-space: nowrap;
+    overflow: hidden;
 }
 
 .segment-hide {
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 54bfbc792..a00a4643d 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -1,8 +1,9 @@
+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, useMemo } from "react"
+import { useEffect, useState, useCallback, useRef } from "react"
 import "./ProgressBar.scss"
 import { MediaSegment } from './RecordingView';
 
@@ -11,7 +12,8 @@ interface ProgressBarProps {
   setVideos: React.Dispatch<React.SetStateAction<MediaSegment[]>>,
   orderVideos: boolean,
   progress: number,
-  recording: boolean,
+    recording: boolean,
+    doUndo: boolean,
 }
 
 interface SegmentBox {
@@ -31,68 +33,73 @@ export function ProgressBar(props: ProgressBarProps) {
 
   // array for the order of video segments
   const [segments, setSegments] = useState<JSX.Element[]>([]);
-  const [ordered, setOrdered] = useState<SegmentBox[]>([]);
+    const [ordered, setOrdered] = useState<SegmentBox[]>([]);
+    
+    const [undoStack, setUndoStack] = useState<SegmentBox[]>([]);
 
   const [dragged, setDragged] = useState<number>(-1);
 
-  // total length of the video, in seconds*100
-  // const [totalTime, setTotalTime] = useState<number>(0);
-
   // 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);
-
-  // const totalTime = useMemo(() => props.progress*1000 - totalRemovedTime, [props.progress, totalRemovedTime]);
-
-  // const totalTime = useMemo(() => props.videos.lastElement().endTime, [props.videos])
-
-  // const totalTime = () => props.videos.lastElement().endTime
-  // const memoTotal = useMemo(totalTime, [props.videos])
-
-  // useEffect that updates total time based on progress
-  // useEffect(() => {
-  //   console.log("useEffect progress", props.progress, 'totalRemovedTime', totalRemovedTime)
-  //   // setTotalTime(prev => {
-  //   //   // progress is in seconds, prev is in deciseconds?
-  //   //   const toAdd = props.progress * 10 - prev
-  //   //   return prev + toAdd
-  //   // })
-  //   // setTotalTime(prev => props.progress * 10)
-  // }, [props.progress])
+    
+    
+    // abstracted for other uses - brings back the most recently deleted segment
+    const handleUndo = () => {
+        // get the last element from the undo if it exists
+        if (undoStack.length === 0) return;
+        // get and remove the last element from the undo stack
+        const last = undoStack.lastElement();
+        setUndoStack(prevUndo => prevUndo.slice(0, -1));
+
+        // update the removed time and place element back into ordered
+        setTotalRemovedTime(prevRemoved => prevRemoved - (last.endTime - last.startTime));
+        setOrdered(prevOrdered => [...prevOrdered, last]);
+    }
+    useEffect(() => handleUndo(), [props.doUndo])
 
   useEffect(() => {
-    console.log("useEffect recording", props.recording)
-    segments.forEach((seg, i) => {
-      const htmlId = seg.props.id;
-      const segmentHtml = document.getElementById(htmlId);
-      segmentHtml?.classList.toggle('no-transition', props.recording);
-      console.log(segmentHtml)
-    });
+      console.log("useEffect recording", props.recording)
+      // 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));
+      
+      if (props.recording)
+          setSegments(prevSegments => [...prevSegments, <div key='segment-expanding' id='segment-expanding' className='segment segment-expanding' style={{ width: 'auto' }}>{props.videos.length + 1}</div>]);
   }, [props.recording])
   
 
   useEffect(() => {
-    console.log('useEffect dragged, ordered')
     const totalTime = props.progress * 1000 - totalRemovedTime;
     const segmentsJSX = ordered.map((seg, i) =>
-      <div id={`segment-${i.toString()}`} className={dragged === i ? 'segment-hide' : 'segment'} style={{ width: `${((seg.endTime - seg.startTime) / totalTime) * 100}%` }}>{seg.order}</div>);
+        <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(() => {
-    // console.log('updating width on progress useEffect')
-    const totalTime = props.progress * 1000 - totalRemovedTime;
-    segments.forEach((seg, i) => {
-      const htmlId = seg.props.id;
-      const segmentHtml = document.getElementById(htmlId);
-      segmentHtml?.style.width = `${((ordered[i].endTime - ordered[i].startTime) / totalTime) * 100}%`;
+    useEffect(() => {
+      if (!props.recording) return
+        const totalTime = props.progress * 1000 - totalRemovedTime;
+        let remainingTime = totalTime;
+        segments.forEach((seg, i) => {
+            // for the last segment, we need to set that directly
+            if (i === segments.length - 1) return;
+            // update remaining time
+            remainingTime -= (ordered[i].endTime - ordered[i].startTime);
+
+        // 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}%`;
+      });
 
-      // console.log('updating width on progress useEffect', htmlId, segmentHtml, segmentHtml?.style.width)
-    });
+        // 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]);
       
 
@@ -102,52 +109,36 @@ export function ProgressBar(props: ProgressBarProps) {
     // in this case, do nothing.
     if (props.orderVideos) return;
 
-    const order = props.videos.length
-    // in this case, a new video is added
-    if (order && order > ordered.length) {
+    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();
-      // update total time
-      // setTotalTime(prevTime => prevTime + (endTime - startTime));
-      // add the new segment to the ordered array
       setOrdered(prevOrdered => {
         return [...prevOrdered, { endTime, startTime , order }];
       });
     }
 
-    // in this case, a segment is removed
+    // in this case, a video is removed
     else if (order < ordered.length) {
-      // update the total time
-      // setTotalTime(prevTime => prevTime - (ordered[removed].endTime - ordered[removed].startTime));
-
-      // update total removed time
-      setTotalRemovedTime(prevRemoved => prevRemoved + (ordered[removed].endTime - ordered[removed].startTime));
-
-      // remove the segment from the ordered array and decrement the order of the remaining segments 
-      // if they are greater than the removed segment's order 
-      const removedOrder = ordered[removed].order;
-      setOrdered(prevOrdered => 
-        prevOrdered.reduce((acc, seg, i) => {
-          (i !== removed) && acc.push({ ...seg, order: seg.order > removedOrder ? seg.order - 1 : seg.order });
-          return acc;
-        },[] as SegmentBox[])
-      );
-      // reset to default/nullish state
-      setRemoved(-1);
+        console.error('warning: video removed from parent');
     }
   }, [props.videos]);
 
   useEffect(() => {
-    props.setVideos(vids => ordered.map((seg) => vids[seg.order - 1]));
+    props.setVideos(vids => ordered.map((seg) => vids[seg.order]));
   }, [props.orderVideos]);
 
   useEffect(() => {
     if (removed === -1) return;
-
-    console.log('removed', removed)
-
-    // update the videos array -> this will fire the useEffect above, where we update the orders & totalTime
-    const index = ordered[removed].order - 1;
-    props.setVideos(vids => vids.filter((vid, i) => i !== index));
+      // 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 => {
@@ -163,7 +154,10 @@ export function ProgressBar(props: ProgressBarProps) {
   
   const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
     // don't move the videobox element
-    e.stopPropagation()
+      e.stopPropagation()
+      
+      // if recording, do nothing
+        if (props.recording) return;
 
     // get the segment the user clicked on to be dragged
     const clickedSegment = e.target as HTMLDivElement & EventTarget
@@ -177,10 +171,15 @@ export function ProgressBar(props: ProgressBarProps) {
     // TODO: think of a way to accomodate touch -> maybe if poiuntermove isn't fired after x secs?
     if (e.shiftKey) {
       const segId = parseInt(clickedSegment.id.split('-')[1]);
-      console.log('removing segment', segId)
       setRemoved(segId);
       return
     }
+    
+      if (e.ctrlKey) {
+        handleUndo();
+        return;
+    // todo: implement undo stack
+    }
 
     const ptrId = e.pointerId;
     progressBar.setPointerCapture(ptrId)
@@ -209,7 +208,6 @@ export function ProgressBar(props: ProgressBarProps) {
       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;
@@ -269,9 +267,6 @@ export function ProgressBar(props: ProgressBarProps) {
     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()
@@ -283,11 +278,6 @@ export function ProgressBar(props: ProgressBarProps) {
 
       return (
         <div className="progressbar" id="progressbar" onPointerDown={onPointerDown} ref={progressBarRef}>
-                  {/* <div
-                        className="progressbar done"
-                        style={{ width: `${props.progress}%` }}
-                  // onClick={handleClick}
-                  ></div> */}
           {segments}
         </div>
       )
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 40117c41c..e131420c3 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -31,7 +31,9 @@ export function RecordingView(props: IRecordingViewProps) {
       const recordingTimerRef = useRef<number>(0);
       const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
       const [playing, setPlaying] = useState(false);
-      const [progress, setProgress] = useState(0);
+    const [progress, setProgress] = useState(0);
+    
+    const [doUndo, setDoUndo] = useState(false);
 
     const [videos, setVideos] = useState<MediaSegment[]>([]);
     const [orderVideos, setOrderVideos] = useState<boolean>(false);
@@ -225,11 +227,15 @@ export function RecordingView(props: IRecordingViewProps) {
             }
       }
 
-      const clearPrevious = () => {
-            const numVideos = videos.length
-            setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
-            setVideoProgressHelper(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
-            setVideos(videos.filter((_, idx) => idx !== numVideos - 1));
+    const undoPrevious = (e: React.MouseEvent) => {
+        e.stopPropagation();
+        console.log('undo previous', doUndo)
+        setDoUndo(prev => !prev);
+            // const numVideos = videos.length
+            // setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
+            // setVideoProgressHelper(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
+            // setVideos(videos.filter((_, idx) => idx !== numVideos - 1));
+          
       }
 
       const handleOnTimeUpdate = () => {
@@ -247,7 +253,8 @@ export function RecordingView(props: IRecordingViewProps) {
             return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
       }
 
-      return (
+    // TODO: have the undo button only appear if there is something to undo
+    return (
             <div className="recording-container">
                   <div className="video-wrapper">
                         <video id={`video-${props.id}`}
@@ -271,10 +278,10 @@ export function RecordingView(props: IRecordingViewProps) {
 
                                     {!recording && (videos.length > 0 ?
 
-                                          <div className="options-wrapper video-edit-wrapper">
-                                                {/* <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
-                                                      <MdBackspace onClick={clearPrevious} />
-                                                </IconContext.Provider> */}
+                              <div className="options-wrapper video-edit-wrapper">
+                                                <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
+                                                      <MdBackspace onClick={undoPrevious} />
+                                                </IconContext.Provider>
                                                 <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
                                                       <FaCheckCircle onClick={stop} />
                                                 </IconContext.Provider>
@@ -293,11 +300,12 @@ export function RecordingView(props: IRecordingViewProps) {
                   </div>
                   
                                          <ProgressBar
-              videos={videos}
-              setVideos={setVideos}
-              orderVideos={orderVideos}
-              progress={progress}
-              recording={recording}
+                      videos={videos}
+                      setVideos={setVideos}
+                      orderVideos={orderVideos}
+                      progress={progress}
+                      recording={recording}
+                      doUndo={doUndo}
                               // playSegment={playSegment}
                               />
                   </div>
-- 
cgit v1.2.3-70-g09d2


From b80480dbfdf0b7936587af4d8f0d82e9ec9d4854 Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Thu, 9 Jun 2022 16:33:29 -0400
Subject: small progressbar ui changes

---
 src/client/views/nodes/RecordingBox/ProgressBar.scss | 5 +++++
 src/client/views/nodes/RecordingBox/ProgressBar.tsx  | 6 +++---
 2 files changed, 8 insertions(+), 3 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index 1cf60b82b..7aa241b8e 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -5,6 +5,7 @@
   text-align: center;
 
   align-items: center;
+  cursor: pointer;
 
 
       position: absolute;
@@ -32,6 +33,10 @@
       }
 }
 
+.progressbar-disabled {
+    cursor: not-allowed;
+}
+
 .segment {
   border: 3px solid black;
   background-color: red;
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index a00a4643d..7aa84a70f 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -61,12 +61,12 @@ export function ProgressBar(props: ProgressBarProps) {
     useEffect(() => handleUndo(), [props.doUndo])
 
   useEffect(() => {
-      console.log("useEffect recording", props.recording)
       // 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' style={{ width: 'auto' }}>{props.videos.length + 1}</div>]);
+          setSegments(prevSegments => [...prevSegments, <div key='segment-expanding' id='segment-expanding' className='segment segment-expanding' style={{ width: 'min-content' }}>{props.videos.length + 1}</div>]);
   }, [props.recording])
   
 
@@ -168,7 +168,7 @@ export function ProgressBar(props: ProgressBarProps) {
     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 poiuntermove isn't fired after x secs?
+    // 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);
-- 
cgit v1.2.3-70-g09d2


From 42fd1da5c1334803202e73c83fa1efbd7eeba96c Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Thu, 9 Jun 2022 16:42:40 -0400
Subject: small ui + comments

---
 src/client/views/nodes/RecordingBox/ProgressBar.scss |  6 +-----
 src/client/views/nodes/RecordingBox/ProgressBar.tsx  | 11 ++++++-----
 2 files changed, 7 insertions(+), 10 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index 7aa241b8e..9697f8b17 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -5,7 +5,7 @@
   text-align: center;
 
   align-items: center;
-  cursor: pointer;
+  cursor: default;
 
 
       position: absolute;
@@ -50,10 +50,6 @@
   text-align: center;
 }
 
-// .no-transition {
-//   transition-duration: 0s;
-// }
-
 .segment-expanding {
 border-color: red;
   background-color: white;
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 7aa84a70f..b1ff88ba3 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -255,6 +255,7 @@ export function ProgressBar(props: ProgressBarProps) {
     }
 
 
+  // 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';
@@ -276,9 +277,9 @@ export function ProgressBar(props: ProgressBarProps) {
   }
 
 
-      return (
-        <div className="progressbar" id="progressbar" onPointerDown={onPointerDown} ref={progressBarRef}>
-          {segments}
-        </div>
-      )
+    return (
+    <div className="progressbar" id="progressbar" onPointerDown={onPointerDown} ref={progressBarRef}>
+        {segments}
+    </div>
+    )
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 847fcea2f2be083991684e9afdad7678e6c137d9 Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Thu, 9 Jun 2022 16:48:42 -0400
Subject: add blinking animation

---
 src/client/views/nodes/RecordingBox/ProgressBar.scss | 19 +++++++++++++++----
 src/client/views/nodes/RecordingBox/ProgressBar.tsx  |  2 +-
 2 files changed, 16 insertions(+), 5 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index 9697f8b17..7768a2f03 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -37,6 +37,21 @@
     cursor: not-allowed;
 }
 
+
+// citation: https://codepen.io/_Master_/pen/PRdjmQ
+@keyframes blinker {
+  from {opacity: 1.0;}
+  to {opacity: 0.0;}
+}
+.blink {
+	text-decoration: blink;
+	animation-name: blinker;
+	animation-duration: 0.6s;
+	animation-iteration-count:infinite;
+	animation-timing-function:ease-in-out;
+	animation-direction: alternate;
+}
+
 .segment {
   border: 3px solid black;
   background-color: red;
@@ -78,10 +93,6 @@ border-color: red;
     text-align: center;
     vertical-align: middle;
     user-select: none;
-    // /* Hide the text. */
-    // text-indent: 100%;
-    // white-space: nowrap;
-    // overflow: hidden;
 }
 
 .segment:first-child {
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index b1ff88ba3..7da3f45be 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -66,7 +66,7 @@ export function ProgressBar(props: ProgressBarProps) {
       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' style={{ width: 'min-content' }}>{props.videos.length + 1}</div>]);
+          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])
   
 
-- 
cgit v1.2.3-70-g09d2


From 552d2e840e772652b9919531b056ba21858edbfd Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Thu, 9 Jun 2022 17:09:00 -0400
Subject: small ui changes, comments, and formatting :)

---
 .../views/nodes/RecordingBox/ProgressBar.scss      |   7 +-
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 425 ++++++++++----------
 .../views/nodes/RecordingBox/RecordingView.tsx     | 443 ++++++++++-----------
 3 files changed, 433 insertions(+), 442 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
index 7768a2f03..28ad25ffa 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -37,6 +37,9 @@
     cursor: not-allowed;
 }
 
+.progressbar-dragging {
+    cursor: grabbing;
+}
 
 // citation: https://codepen.io/_Master_/pen/PRdjmQ
 @keyframes blinker {
@@ -113,6 +116,8 @@ border-color: red;
 }
 
 .segment-selected {
-  border: 4px solid grey;
+  border: 4px solid #202020;
   background-color: red;
+  opacity: .75;
+  cursor: grabbing;
 }
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
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 701277cbd..3f54a97ce 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -13,298 +13,285 @@ import { DashUploadUtils } from '../../../../server/DashUploadUtils';
 
 export interface MediaSegment {
     videoChunks: any[],
-  endTime: number,
-  startTime: number
+    endTime: number,
+    startTime: number
 }
 
 interface IRecordingViewProps {
-      setResult: (info: Upload.AccessPathInfo, trackScreen: boolean) => void
-      setDuration: (seconds: number) => void
-      id: string
+    setResult: (info: Upload.AccessPathInfo, trackScreen: boolean) => void
+    setDuration: (seconds: number) => void
+    id: string
 }
 
 const MAXTIME = 100000;
 
 export function RecordingView(props: IRecordingViewProps) {
 
-      const [recording, setRecording] = useState(false);
-      const recordingTimerRef = useRef<number>(0);
-      const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
-      const [playing, setPlaying] = useState(false);
+    const [recording, setRecording] = useState(false);
+    const recordingTimerRef = useRef<number>(0);
+    const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
+    const [playing, setPlaying] = useState(false);
     const [progress, setProgress] = useState(0);
-    
+
+    // acts as a "refresh state" to tell progressBar when to undo
     const [doUndo, setDoUndo] = useState(false);
 
     const [videos, setVideos] = useState<MediaSegment[]>([]);
     const [orderVideos, setOrderVideos] = useState<boolean>(false);
-      const videoRecorder = useRef<MediaRecorder | null>(null);
-      const videoElementRef = useRef<HTMLVideoElement | null>(null);
+    const videoRecorder = useRef<MediaRecorder | null>(null);
+    const videoElementRef = useRef<HTMLVideoElement | null>(null);
 
-      const [finished, setFinished] = useState<boolean>(false)
-      const [trackScreen, setTrackScreen] = useState<boolean>(true)
+    const [finished, setFinished] = useState<boolean>(false)
+    const [trackScreen, setTrackScreen] = useState<boolean>(true)
 
 
 
-      const DEFAULT_MEDIA_CONSTRAINTS = {
-            video: {
-                  width: 1280,
-                  height: 720,
-            },
-            audio: {
-                  echoCancellation: true,
-                  noiseSuppression: true,
-                  sampleRate: 44100
-            }
-      }
+    const DEFAULT_MEDIA_CONSTRAINTS = {
+        video: {
+            width: 1280,
+            height: 720,
+        },
+        audio: {
+            echoCancellation: true,
+            noiseSuppression: true,
+            sampleRate: 44100
+        }
+    }
 
 
     useEffect(() => {
-          
-        // console.log('in videos useEffect', finished)
-
-          if (finished) {
-              (async () => {
+        if (finished) {
+            (async () => {
                 const inputPaths: string[] = [];
                 const videoFiles: File[] = []
                 videos.forEach(async (vid, i) => {
                     const videoFile = new File(vid.videoChunks, `segvideo${i}.mkv`, { type: vid.videoChunks[0].type, lastModified: Date.now() });
                     videoFiles.push(videoFile);
-                    
+
                     const { name } = videoFile;
                     inputPaths.push(name)
-                  })
-                  
+                })
+
+                // upload the segments to the server and get their server access paths
                 const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles))
-                  .map(res => (res.result instanceof Error) ? '' : res.result.accessPaths.agnostic.server)
-                
+                    .map(res => (res.result instanceof Error) ? '' : res.result.accessPaths.agnostic.server)
+
+                // concat the segments together using post call
                 const result: Upload.AccessPathInfo | Error = await Networking.PostToServer('/concatVideos', serverPaths)
-                if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+                if (!(result instanceof Error)) {
                     props.setResult(result, trackScreen)
                 } else {
                     alert("video conversion failed");
                 }
-              
-              })();
-              
-            }
+            })();
+        }
+    }, [videos])
 
+    // this will call upon the progress bar to edit videos to be in the correct order
+    useEffect(() => {
+        if (finished) setOrderVideos(true);
+    }, [finished])
 
-    }, [videos])
-  
-  useEffect(() => {
+    useEffect(() => {
+        // check if the browser supports media devices on first load
+        if (!navigator.mediaDevices) {
+            console.log('This browser does not support getUserMedia.')
+        }
+        // console.log('This device has the correct media devices.')
+    }, [])
 
-    if (finished) {
-      setOrderVideos(true);
-    }
+    useEffect(() => {
+        // get access to the video element on every render
+        videoElementRef.current = document.getElementById(`video-${props.id}`) as HTMLVideoElement;
+    });
 
+    useEffect(() => {
+        let interval: any = null;
+        if (recording) {
+            interval = setInterval(() => {
+                setRecordingTimer(unit => unit + 1);
+            }, 10);
+        } else if (!recording && recordingTimer !== 0) {
+            clearInterval(interval);
+        }
+        return () => clearInterval(interval);
+    }, [recording])
 
-      }, [finished])
+    useEffect(() => {
+        setVideoProgressHelper(recordingTimer)
+        recordingTimerRef.current = recordingTimer;
+    }, [recordingTimer])
 
-      useEffect(() => {
-            // check if the browser supports media devices on first load
-            if (!navigator.mediaDevices) {
-                  console.log('This browser does not support getUserMedia.')
-            }
-          // console.log('This device has the correct media devices.')
-      }, [])
-
-      useEffect(() => {
-            // get access to the video element on every render
-            videoElementRef.current = document.getElementById(`video-${props.id}`) as HTMLVideoElement;
-      })
-
-      useEffect(() => {
-            let interval: any = null;
-            if (recording) {
-                  interval = setInterval(() => {
-                        setRecordingTimer(unit => unit + 1);
-                  }, 10);
-            } else if (!recording && recordingTimer !== 0) {
-                  clearInterval(interval);
-            }
-            return () => clearInterval(interval);
-      }, [recording])
-
-      useEffect(() => {
-            setVideoProgressHelper(recordingTimer)
-            recordingTimerRef.current = recordingTimer;
-      }, [recordingTimer])
-
-      const setVideoProgressHelper = (progress: number) => {
-            const newProgress = (progress / MAXTIME) * 100;
-            setProgress(newProgress)
-      }
-      const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
-            const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
-
-            videoElementRef.current!.src = ""
-            videoElementRef.current!.srcObject = stream
-            videoElementRef.current!.muted = true
-
-            return stream
-      }
-
-      const record = async () => {
-            const stream = await startShowingStream();
-            videoRecorder.current = new MediaRecorder(stream)
-
-            // temporary chunks of video
-            let videoChunks: any = []
-
-            videoRecorder.current.ondataavailable = (event: any) => {
-                  if (event.data.size > 0) {
-                        videoChunks.push(event.data)
-                  }
-            }
+    const setVideoProgressHelper = (progress: number) => {
+        const newProgress = (progress / MAXTIME) * 100;
+        setProgress(newProgress)
+    }
+    const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
+        const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
 
-            videoRecorder.current.onstart = (event: any) => {
-                  setRecording(true);
-                  trackScreen && RecordingApi.Instance.start();
-            }
+        videoElementRef.current!.src = ""
+        videoElementRef.current!.srcObject = stream
+        videoElementRef.current!.muted = true
 
-            videoRecorder.current.onstop = () => {
-                  // if we have a last portion
-                  if (videoChunks.length > 1) {
-                        // append the current portion to the video pieces
-                        setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
-                  }
-
-                  // reset the temporary chunks
-                  videoChunks = []
-                  setRecording(false);
-                  trackScreen && RecordingApi.Instance.pause();
-            }
+        return stream
+    }
 
-            // recording paused
-            videoRecorder.current.onpause = (event: any) => {
-                  // append the current portion to the video pieces
-                  setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
+    const record = async () => {
+        const stream = await startShowingStream();
+        videoRecorder.current = new MediaRecorder(stream)
 
-                  // reset the temporary chunks
-                  videoChunks = []
-                  setRecording(false);
-                  trackScreen && RecordingApi.Instance.pause();
-            }
+        // temporary chunks of video
+        let videoChunks: any = []
 
-            videoRecorder.current.onresume = async (event: any) => {
-                  await startShowingStream();
-                  setRecording(true);
-                  trackScreen && RecordingApi.Instance.resume();
+        videoRecorder.current.ondataavailable = (event: any) => {
+            if (event.data.size > 0) {
+                videoChunks.push(event.data)
+            }
+        }
+
+        videoRecorder.current.onstart = (event: any) => {
+            setRecording(true);
+            trackScreen && RecordingApi.Instance.start();
+        }
+
+        videoRecorder.current.onstop = () => {
+            // if we have a last portion
+            if (videoChunks.length > 1) {
+                // append the current portion to the video pieces
+                setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
             }
 
-            videoRecorder.current.start(200)
-      }
+            // reset the temporary chunks
+            videoChunks = []
+            setRecording(false);
+            trackScreen && RecordingApi.Instance.pause();
+        }
+
+        // recording paused
+        videoRecorder.current.onpause = (event: any) => {
+            // append the current portion to the video pieces
+            setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
+
+            // reset the temporary chunks
+            videoChunks = []
+            setRecording(false);
+            trackScreen && RecordingApi.Instance.pause();
+        }
+
+        videoRecorder.current.onresume = async (event: any) => {
+            await startShowingStream();
+            setRecording(true);
+            trackScreen && RecordingApi.Instance.resume();
+        }
+
+        videoRecorder.current.start(200)
+    }
 
 
-  const stop = (e: React.MouseEvent) => {
+    const stop = (e: React.MouseEvent) => {
         e.stopPropagation()
         if (videoRecorder.current) {
-          setFinished(true);
-                  if (videoRecorder.current.state !== "inactive") {
-                        videoRecorder.current.stop();
-                        // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
-                  }
+            setFinished(true);
+            if (videoRecorder.current.state !== "inactive") {
+                videoRecorder.current.stop();
+                // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
             }
-      }
+        }
+    }
 
-      const pause = (e: React.MouseEvent) => {
+    const pause = (e: React.MouseEvent) => {
         e.stopPropagation()
-            if (videoRecorder.current) {
-                  if (videoRecorder.current.state === "recording") {
-                    videoRecorder.current.stop();
-                  }
+        if (videoRecorder.current) {
+            if (videoRecorder.current.state === "recording") {
+                videoRecorder.current.stop();
             }
-      }
+        }
+    }
 
-      const startOrResume =  (e: React.MouseEvent) => {
+    const startOrResume = (e: React.MouseEvent) => {
         e.stopPropagation()
-            if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
-                  record();
-            } else if (videoRecorder.current.state === "paused") {
-              videoRecorder.current.start();
-            }
-      }
+        if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
+            record();
+        } else if (videoRecorder.current.state === "paused") {
+            videoRecorder.current.start();
+        }
+    }
 
     const undoPrevious = (e: React.MouseEvent) => {
         e.stopPropagation();
         setDoUndo(prev => !prev);
-            // const numVideos = videos.length
-            // setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
-            // setVideoProgressHelper(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
-            // setVideos(videos.filter((_, idx) => idx !== numVideos - 1));
-          
-      }
-
-      const handleOnTimeUpdate = () => {
-            if (playing) {
-                  setVideoProgressHelper(videoElementRef.current!.currentTime)
-            }
-      };
+    }
 
-      const millisecondToMinuteSecond = (milliseconds: number) => {
-            const toTwoDigit = (digit: number) => {
-                  return String(digit).length == 1 ? "0" + digit : digit
-            }
-            const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
-            const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
-            return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
-      }
+    const handleOnTimeUpdate = () => {
+        if (playing) {
+            setVideoProgressHelper(videoElementRef.current!.currentTime)
+        }
+    };
+
+    const millisecondToMinuteSecond = (milliseconds: number) => {
+        const toTwoDigit = (digit: number) => {
+            return String(digit).length == 1 ? "0" + digit : digit
+        }
+        const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
+        const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
+        return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
+    }
 
     // TODO: have the undo button only appear if there is something to undo
     return (
-            <div className="recording-container">
-                  <div className="video-wrapper">
-                        <video id={`video-${props.id}`}
-                              autoPlay
-                              muted
-                              onTimeUpdate={handleOnTimeUpdate}
-                        />
-                        <div className="recording-sign">
-                              <span className="dot" />
-                              <p className="timer">{millisecondToMinuteSecond(recordingTimer * 10)}</p>
+        <div className="recording-container">
+            <div className="video-wrapper">
+                <video id={`video-${props.id}`}
+                    autoPlay
+                    muted
+                    onTimeUpdate={handleOnTimeUpdate}
+                />
+                <div className="recording-sign">
+                    <span className="dot" />
+                    <p className="timer">{millisecondToMinuteSecond(recordingTimer * 10)}</p>
+                </div>
+                <div className="controls">
+
+                    <div className="controls-inner-container">
+                        <div className="record-button-wrapper">
+                            {recording ?
+                                <button className="stop-button" onClick={pause} /> :
+                                <button className="record-button" onClick={startOrResume} />
+                            }
                         </div>
-                        <div className="controls">
-
-                              <div className="controls-inner-container">
-                                    <div className="record-button-wrapper">
-                                          {recording ?
-                                                <button className="stop-button" onClick={pause} /> :
-                                                <button className="record-button" onClick={startOrResume} />
-                                          }
-                                    </div>
-
-                                    {!recording && (videos.length > 0 ?
-
-                              <div className="options-wrapper video-edit-wrapper">
-                                                <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
-                                                      <MdBackspace onClick={undoPrevious} />
-                                                </IconContext.Provider>
-                                                <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
-                                                      <FaCheckCircle onClick={stop} />
-                                                </IconContext.Provider>
-                                          </div>
-
-                                          : <div className="options-wrapper track-screen-wrapper">
-                                                <label className="track-screen">
-                                                      <input type="checkbox" checked={trackScreen} onChange={(e) => { setTrackScreen(e.target.checked) }} />
-                                                      <span className="checkmark"></span>
-                                                      Track Screen
-                                                </label>
-                              </div>)}
-
-                      </div>
-                              
-                  </div>
-                  
-                                         <ProgressBar
-                      videos={videos}
-                      setVideos={setVideos}
-                      orderVideos={orderVideos}
-                      progress={progress}
-                      recording={recording}
-                      doUndo={doUndo}
-                              // playSegment={playSegment}
-                              />
-                  </div>
-            </div>)
+
+                        {!recording && (videos.length > 0 ?
+
+                            <div className="options-wrapper video-edit-wrapper">
+                                <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
+                                    <MdBackspace onClick={undoPrevious} />
+                                </IconContext.Provider>
+                                <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
+                                    <FaCheckCircle onClick={stop} />
+                                </IconContext.Provider>
+                            </div>
+
+                            : <div className="options-wrapper track-screen-wrapper">
+                                <label className="track-screen">
+                                    <input type="checkbox" checked={trackScreen} onChange={(e) => { setTrackScreen(e.target.checked) }} />
+                                    <span className="checkmark"></span>
+                                    Track Screen
+                                </label>
+                            </div>)}
+
+                    </div>
+
+                </div>
+
+                <ProgressBar
+                    videos={videos}
+                    setVideos={setVideos}
+                    orderVideos={orderVideos}
+                    progress={progress}
+                    recording={recording}
+                    doUndo={doUndo}
+                // playSegment={playSegment}
+                />
+            </div>
+        </div>)
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 0badf084bed0f5337cbf9ad89f1fea924481c9b0 Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Thu, 9 Jun 2022 17:45:48 -0400
Subject: add way to remove segments using touch going off screen, only show
 undo button when something is on undo stack

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 30 +++++++++++++---------
 .../views/nodes/RecordingBox/RecordingView.tsx     |  5 +++-
 2 files changed, 22 insertions(+), 13 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 81f753e44..131d5c837 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -10,6 +10,7 @@ interface ProgressBarProps {
     progress: number,
     recording: boolean,
     doUndo: boolean,
+    setCanUndo?: React.Dispatch<React.SetStateAction<boolean>>,
 }
 
 interface SegmentBox {
@@ -41,6 +42,8 @@ export function ProgressBar(props: ProgressBarProps) {
     // this holds the index of the videoc segment to be removed
     const [removed, setRemoved] = useState<number>(-1);
 
+    useEffect(() => props.setCanUndo?.(undoStack.length > 0), [undoStack.length]);
+
 
     // abstracted for other uses - brings back the most recently deleted segment
     const handleUndo = () => {
@@ -142,7 +145,7 @@ export function ProgressBar(props: ProgressBarProps) {
         setRemoved(-1);
     }, [removed]);
 
-    const updateLastHover = (segId: number): CurrentHover | null => {
+    const updateCurrentHover = (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
@@ -169,9 +172,9 @@ export function ProgressBar(props: ProgressBarProps) {
         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]);
+            console.log('removing segment shift', segId);
             setRemoved(segId);
             return
         }
@@ -193,7 +196,7 @@ export function ProgressBar(props: ProgressBarProps) {
 
         // 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 = {
+        let currentHover: CurrentHover = {
             index: segId,
             minX: rect.x,
             maxX: rect.x + rect.width,
@@ -216,19 +219,23 @@ export function ProgressBar(props: ProgressBarProps) {
             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
+            if (curX < currentHover.minX && currentHover.index > 0) {
+                swapSegments(currentHover.index, currentHover.index - 1)
+                currentHover = updateCurrentHover(currentHover.index - 1) ?? currentHover
             }
-            else if (curX > lastHover.maxX && lastHover.index < segments.length - 1) {
-                swapSegments(lastHover.index, lastHover.index + 1)
-                lastHover = updateLastHover(lastHover.index + 1) ?? lastHover
+            else if (curX > currentHover.maxX && currentHover.index < segments.length - 1) {
+                swapSegments(currentHover.index, currentHover.index + 1)
+                currentHover = updateCurrentHover(currentHover.index + 1) ?? currentHover
             }
         }
 
         const placeSegmentandCleanup = (event?: PointerEvent): void => {
             event?.stopPropagation();
             event?.preventDefault();
+            // if they put the segment outside of the bounds, remove it
+            if (event && (event.clientX < 0 || event.clientX > document.body.clientWidth || event.clientY < 0 || event.clientY > document.body.clientHeight))
+                setRemoved(currentHover.index);
+            
             // remove the update event listener for pointermove
             progressBar.removeEventListener('pointermove', updateSegmentOrder);
             // remove the floating segment from the DOM
@@ -238,9 +245,8 @@ export function ProgressBar(props: ProgressBarProps) {
             setDragged(-1);
         }
 
-
-        progressBar.addEventListener('pointermove', updateSegmentOrder)
-        progressBar.addEventListener('pointerup', placeSegmentandCleanup, { once: true })
+        progressBar.addEventListener('pointermove', updateSegmentOrder);
+        progressBar.addEventListener('pointerup', placeSegmentandCleanup, { once: true });
     }
 
     const swapSegments = (oldIndex: number, newIndex: number) => {
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 3f54a97ce..7cff54ff2 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -35,6 +35,8 @@ export function RecordingView(props: IRecordingViewProps) {
 
     // acts as a "refresh state" to tell progressBar when to undo
     const [doUndo, setDoUndo] = useState(false);
+    // whether an undo can occur or not
+    const [canUndo, setCanUndo] = useState(false);
 
     const [videos, setVideos] = useState<MediaSegment[]>([]);
     const [orderVideos, setOrderVideos] = useState<boolean>(false);
@@ -263,7 +265,7 @@ export function RecordingView(props: IRecordingViewProps) {
                         {!recording && (videos.length > 0 ?
 
                             <div className="options-wrapper video-edit-wrapper">
-                                <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
+                                <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons", style: {display: canUndo ? 'inherit' : 'none'} }}>
                                     <MdBackspace onClick={undoPrevious} />
                                 </IconContext.Provider>
                                 <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
@@ -290,6 +292,7 @@ export function RecordingView(props: IRecordingViewProps) {
                     progress={progress}
                     recording={recording}
                     doUndo={doUndo}
+                    setCanUndo={setCanUndo}
                 // playSegment={playSegment}
                 />
             </div>
-- 
cgit v1.2.3-70-g09d2


From a2c3ed470c941b108aede045ac294e3b966990bc Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Thu, 9 Jun 2022 18:15:36 -0400
Subject: more comments + testing

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       | 63 +++++++++++++---------
 1 file changed, 37 insertions(+), 26 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 131d5c837..dfe57db3f 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -25,15 +25,17 @@ interface CurrentHover {
 }
 
 export function ProgressBar(props: ProgressBarProps) {
-
     const progressBarRef = useRef<HTMLDivElement | null>(null)
 
-    // array for the order of video segments
+    // the actual list of JSX elements rendered as segments
     const [segments, setSegments] = useState<JSX.Element[]>([]);
+    // array for the order of video segments
     const [ordered, setOrdered] = useState<SegmentBox[]>([]);
 
     const [undoStack, setUndoStack] = useState<SegmentBox[]>([]);
 
+    // -1 if no segment is currently being dragged around; else, it is the id of that segment over
+    // NOTE: the id of a segment is its index in the ordered array
     const [dragged, setDragged] = useState<number>(-1);
 
     // length of the time removed from the video, in seconds*100
@@ -42,10 +44,11 @@ export function ProgressBar(props: ProgressBarProps) {
     // this holds the index of the videoc segment to be removed
     const [removed, setRemoved] = useState<number>(-1);
 
+    // update the canUndo props based on undo stack
     useEffect(() => props.setCanUndo?.(undoStack.length > 0), [undoStack.length]);
 
-
-    // abstracted for other uses - brings back the most recently deleted segment
+    // useEffect for undo - brings back the most recently deleted segment
+    useEffect(() => handleUndo(), [props.doUndo])
     const handleUndo = () => {
         // get the last element from the undo if it exists
         if (undoStack.length === 0) return;
@@ -57,18 +60,20 @@ export function ProgressBar(props: ProgressBarProps) {
         setTotalRemovedTime(prevRemoved => prevRemoved - (last.endTime - last.startTime));
         setOrdered(prevOrdered => [...prevOrdered, last]);
     }
-    useEffect(() => handleUndo(), [props.doUndo])
 
+    // useEffect for recording changes - changes style to disabled and adds the "expanding-segment"
     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>]);
+            setSegments(prevSegments => [...prevSegments, <div key='segment-expanding' id='segment-expanding' className='segment segment-expanding blink' style={{ width: '0px;' }}>{props.videos.length + 1}</div>]);
     }, [props.recording])
 
 
+    // useEffect that updates the segmentsJSX, which is rendered
+    // only updated when ordered is updated or if the user is dragging around a segment
     useEffect(() => {
         const totalTime = props.progress * 1000 - totalRemovedTime;
         const segmentsJSX = ordered.map((seg, i) =>
@@ -77,12 +82,12 @@ export function ProgressBar(props: ProgressBarProps) {
         setSegments(segmentsJSX)
     }, [dragged, ordered]);
 
-    // update the cursor to be dragging while moving the floating segment
+    // useEffect for dragged - update the cursor to be grabbing while grabbing
     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
+    // to imporve performance, only want to update the CSS width, not re-render the whole JSXList
     useEffect(() => {
         if (!props.recording) return
         const totalTime = props.progress * 1000 - totalRemovedTime;
@@ -103,11 +108,9 @@ export function ProgressBar(props: ProgressBarProps) {
         const segExapandHtml = document.getElementById('segment-expanding');
         if (segExapandHtml)
             segExapandHtml.style.width = ordered.length === 0 ? '100%' : `${(remainingTime / totalTime) * 100}%`;
-
     }, [props.progress]);
 
-
-
+    // useEffect for props.videos - update the ordered array when a new video is added
     useEffect(() => {
         // this useEffect fired when the videos are being rearragned to the order
         // in this case, do nothing.
@@ -128,10 +131,10 @@ export function ProgressBar(props: ProgressBarProps) {
         }
     }, [props.videos]);
 
-    useEffect(() => {
-        props.setVideos(vids => ordered.map((seg) => vids[seg.order]));
-    }, [props.orderVideos]);
+    // useEffect for props.orderVideos - matched the order array with the videos array before the export
+    useEffect(() => props.setVideos(vids => ordered.map((seg) => vids[seg.order])), [props.orderVideos]);
 
+    // useEffect for removed - handles logic for removing a segment
     useEffect(() => {
         if (removed === -1) return;
         // update total removed time
@@ -145,6 +148,7 @@ export function ProgressBar(props: ProgressBarProps) {
         setRemoved(-1);
     }, [removed]);
 
+    // returns the new currentHover based on the new index
     const updateCurrentHover = (segId: number): CurrentHover | null => {
         // get the segId of the segment that will become the new bounding area
         const rect = progressBarRef.current?.children[segId].getBoundingClientRect()
@@ -156,6 +160,7 @@ export function ProgressBar(props: ProgressBarProps) {
         }
     }
 
+    // pointerdown event for the progress bar
     const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         // don't move the videobox element
         e.stopPropagation()
@@ -174,16 +179,18 @@ export function ProgressBar(props: ProgressBarProps) {
         // if holding shift key, let's remove that segment
         if (e.shiftKey) {
             const segId = parseInt(clickedSegment.id.split('-')[1]);
-            console.log('removing segment shift', segId);
             setRemoved(segId);
             return
         }
 
+        // if holding ctrl key and click, let's undo that segment #hiddenfeature lol
         if (e.ctrlKey) {
             handleUndo();
             return;
         }
 
+        // if we're here, the user is dragging a segment around
+        // let the progress bar capture all the pointer events until the user releases (pointerUp)
         const ptrId = e.pointerId;
         progressBar.setPointerCapture(ptrId)
 
@@ -194,15 +201,15 @@ export function ProgressBar(props: ProgressBarProps) {
         // 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
+        // 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 currentHover: CurrentHover = {
             index: segId,
             minX: rect.x,
             maxX: rect.x + rect.width,
         }
 
-        // create the div element that tracks the cursor
+        // create the floating segment that tracks the cursor
         const detchedSegment = document.createElement("div")
         initDeatchSegment(detchedSegment, rect);
 
@@ -219,16 +226,19 @@ export function ProgressBar(props: ProgressBarProps) {
             followCursor(event, detchedSegment);
 
             const curX = event.clientX;
+            // handle the left bound
             if (curX < currentHover.minX && currentHover.index > 0) {
                 swapSegments(currentHover.index, currentHover.index - 1)
                 currentHover = updateCurrentHover(currentHover.index - 1) ?? currentHover
             }
+            // handle the right bound
             else if (curX > currentHover.maxX && currentHover.index < segments.length - 1) {
                 swapSegments(currentHover.index, currentHover.index + 1)
                 currentHover = updateCurrentHover(currentHover.index + 1) ?? currentHover
             }
         }
 
+        // handles when the user is done dragging the segment (pointerUp)
         const placeSegmentandCleanup = (event?: PointerEvent): void => {
             event?.stopPropagation();
             event?.preventDefault();
@@ -245,22 +255,23 @@ export function ProgressBar(props: ProgressBarProps) {
             setDragged(-1);
         }
 
+        // event listeners that allow the user to drag and release the floating segment
         progressBar.addEventListener('pointermove', updateSegmentOrder);
         progressBar.addEventListener('pointerup', placeSegmentandCleanup, { once: true });
     }
 
     const swapSegments = (oldIndex: number, newIndex: number) => {
-        if (newIndex == null) return
+        if (newIndex == null) return;
         setOrdered(prevOrdered => {
-            const cpy = [...prevOrdered]
-            cpy[oldIndex] = cpy[newIndex]
-            cpy[newIndex] = prevOrdered[oldIndex]
-            return cpy
-        })
-        setDragged(newIndex)
+            const temp = { ...prevOrdered[oldIndex] }
+            prevOrdered[oldIndex] = prevOrdered[newIndex]
+            prevOrdered[newIndex] = temp
+            return prevOrdered
+        });
+        // update visually where the segment is hovering over
+        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");
-- 
cgit v1.2.3-70-g09d2


From 23f6651153333a9979b37fe725c54080babe0133 Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local>
Date: Fri, 10 Jun 2022 12:29:49 -0400
Subject: cleanup code from performance. stop streams after video is done.

---
 .../views/nodes/RecordingBox/ProgressBar.tsx       |  2 +-
 .../views/nodes/RecordingBox/RecordingView.tsx     | 48 +++++++---------------
 2 files changed, 15 insertions(+), 35 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index dfe57db3f..493069394 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -68,7 +68,7 @@ export function ProgressBar(props: ProgressBarProps) {
         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: '0px;' }}>{props.videos.length + 1}</div>]);
+            setSegments(prevSegments => [...prevSegments, <div key='segment-expanding' id='segment-expanding' className='segment segment-expanding blink' style={{ width: 'fit-content' }}>{props.videos.length + 1}</div>]);
     }, [props.recording])
 
 
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index e8737dce2..a8cc82ec0 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -91,22 +91,16 @@ export function RecordingView(props: IRecordingViewProps) {
 
     // this will call upon the progress bar to edit videos to be in the correct order
     useEffect(() => {
-        if (finished) setOrderVideos(true);
+        finished && setOrderVideos(true);
     }, [finished])
 
     useEffect(() => {
         // check if the browser supports media devices on first load
         if (!navigator.mediaDevices) {
-            console.log('This browser does not support getUserMedia.')
+            console.warn('This browser does not support getUserMedia.')
         }
-        // console.log('This device has the correct media devices.')
     }, [])
 
-    useEffect(() => {
-        // get access to the video element on every render
-        videoElementRef.current = document.getElementById(`video-${props.id}`) as HTMLVideoElement;
-    });
-
     useEffect(() => {
         let interval: any = null;
         if (recording) {
@@ -128,6 +122,7 @@ export function RecordingView(props: IRecordingViewProps) {
         const newProgress = (progress / MAXTIME) * 100;
         setProgress(newProgress)
     }
+
     const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
         const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
 
@@ -139,8 +134,8 @@ export function RecordingView(props: IRecordingViewProps) {
     }
 
     const record = async () => {
-        const stream = await startShowingStream();
-        videoRecorder.current = new MediaRecorder(stream)
+        // don't need to start a new stream every time we start recording a new segment
+        if (!videoRecorder.current) videoRecorder.current = new MediaRecorder(await startShowingStream())
 
         // temporary chunks of video
         let videoChunks: any = []
@@ -169,23 +164,6 @@ export function RecordingView(props: IRecordingViewProps) {
             trackScreen && RecordingApi.Instance.pause();
         }
 
-        // recording paused
-        // videoRecorder.current.onpause = (event: any) => {
-        //     // append the current portion to the video pieces
-        //     setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
-
-        //     // reset the temporary chunks
-        //     videoChunks = []
-        //     setRecording(false);
-        //     trackScreen && RecordingApi.Instance.pause();
-        // }
-
-        // videoRecorder.current.onresume = async (event: any) => {
-        //     await startShowingStream();
-        //     setRecording(true);
-        //     trackScreen && RecordingApi.Instance.resume();
-        // }
-
         videoRecorder.current.start(200)
     }
 
@@ -193,11 +171,16 @@ export function RecordingView(props: IRecordingViewProps) {
     const stop = (e: React.PointerEvent) => {
         e.stopPropagation();
         if (videoRecorder.current) {
-            setFinished(true);
             if (videoRecorder.current.state !== "inactive") {
                 videoRecorder.current.stop();
-                // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
             }
+
+            // this will call upon progessbar to update videos to be in the correct order
+            setFinished(true);
+
+            // end the streams (audio/video) to remove recording icon
+            const stream = videoElementRef.current!.srcObject;
+            stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
         }
     }
 
@@ -213,11 +196,7 @@ export function RecordingView(props: IRecordingViewProps) {
     const startOrResume = (e: React.PointerEvent) => {
         // the code to start or resume does not get triggered if we start dragging the button
         setupMoveUpEvents({}, e, returnTrue, returnFalse, e => {
-            if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
-                record();
-            } else if (videoRecorder.current.state === "paused") {
-                videoRecorder.current.start();
-            }
+            (!videoRecorder.current || videoRecorder.current.state === "inactive") && record();
             return true; // cancels propagation to documentView to avoid selecting it.
         }, false, false);
     }
@@ -245,6 +224,7 @@ export function RecordingView(props: IRecordingViewProps) {
                     autoPlay
                     muted
                     onTimeUpdate={handleOnTimeUpdate}
+                    ref={videoElementRef}
                 />
                 <div className="recording-sign">
                     <span className="dot" />
-- 
cgit v1.2.3-70-g09d2


From 1688af3de54419029773fb85b78bca4500f7f0de Mon Sep 17 00:00:00 2001
From: Michael <michael.foiani@gmail.com>
Date: Fri, 10 Jun 2022 15:41:38 -0400
Subject: big bug problems with recording. some reason interacting with the
 canvas is making the recording inactive - i think :/

---
 src/client/util/RecordingApi.ts                    | 153 +++++++++++++--------
 .../views/nodes/RecordingBox/ProgressBar.tsx       |   8 +-
 .../views/nodes/RecordingBox/RecordingBox.tsx      |  12 +-
 .../views/nodes/RecordingBox/RecordingView.tsx     |  99 ++++++++-----
 4 files changed, 166 insertions(+), 106 deletions(-)

(limited to 'src/client/views/nodes/RecordingBox/ProgressBar.tsx')

diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts
index 021feee9a..ab6935e3b 100644
--- a/src/client/util/RecordingApi.ts
+++ b/src/client/util/RecordingApi.ts
@@ -3,8 +3,6 @@ import { IReactionDisposer, observable, reaction } from "mobx";
 import { NumCast } from "../../fields/Types";
 import { Doc } from "../../fields/Doc";
 import { VideoBox } from "../views/nodes/VideoBox";
-import { scaleDiverging } from "d3-scale";
-import { Transform } from "./Transform";
 
 type Movement = {
     time: number,
@@ -13,17 +11,17 @@ type Movement = {
     scale: number,
 }
 
-type Presentation = {
-    movements: Array<Movement> | null
-    meta: Object,
+export type Presentation = {
+  movements: Movement[] | null,
+  totalTime: number,
+  meta: any,
 }
 
 export class RecordingApi {
 
-    private static NULL_PRESENTATION: Presentation = {
-        movements: null,
-        meta: {},
-    }
+  private static get NULL_PRESENTATION(): Presentation {
+    return { movements: null, meta: {}, totalTime: -1,}
+  }
 
     // instance variables
     private currentPresentation: Presentation;
@@ -50,6 +48,8 @@ export class RecordingApi {
         // for now, set playFFView
         this.playFFView = null;
         this.timers = null;
+      
+      // put a pointerdown event on the doucment to see what the target
     }
 
     // little helper :)
@@ -59,8 +59,9 @@ export class RecordingApi {
 
     public start = (meta?: Object): Error | undefined => {
         // check if already init a presentation
-        if (!this.isInitPresenation) {
-            console.error('[recordingApi.ts] start() failed: current presentation data exists. please call clear() first.')
+      if (!this.isInitPresenation) {
+          console.log(this.currentPresentation)
+            console.trace('[recordingApi.ts] start() failed: current presentation data exists. please call clear() first.')
             return new Error('[recordingApi.ts] start()')
         }
 
@@ -80,50 +81,61 @@ export class RecordingApi {
         this.isRecording = true
     }
 
-    public clear = (): Error | Presentation => {
+  /* stops the video and returns the presentatation; if no presentation, returns undefined */  
+  public getPresentation = (): undefined | Presentation => {
         // TODO: maybe archive the data?
-        if (this.isRecording) {
-            console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() first')
-            return new Error('[recordingApi.ts] clear()')
-        }
-
-        // update the presentation mode
-        Doc.UserDoc().presentationMode = 'none'
-        // set the previus recording view to the play view
-        this.playFFView = this.recordingFFView
-
-        const presCopy = { ...this.currentPresentation }
-
-        // clear presenation data
-        this.currentPresentation = RecordingApi.NULL_PRESENTATION
-        // clear isRecording
-        this.isRecording = false
-        // clear absoluteStart
-        this.absoluteStart = -1
-        // clear the disposeFunc
-        this.removeRecordingFFView()
-
-        return presCopy;
+    if (this.isRecording) console.warn('[recordingApi.ts] getPresentation() : currently recording presentation.');
+
+    // update the presentation mode
+    Doc.UserDoc().presentationMode = 'none';
+    // set the previus recording view to the play view
+    this.playFFView = this.recordingFFView;
+
+    // ensure we add the endTime now that they are done recording    
+    return { ...this.currentPresentation, totalTime: new Date().getTime() - this.absoluteStart };
+  }
+
+  public stop = (): void => {
+    // make is recording false
+    this.isRecording = false
+  }
+
+  public clear = (): void => {
+    // clear presenation data
+    this.currentPresentation = RecordingApi.NULL_PRESENTATION
+    // clear isRecording
+    this.isRecording = false
+    // clear absoluteStart
+    this.absoluteStart = -1
+    // clear the disposeFunc
+    this.removeRecordingFFView()
+  }
+  
+  
+    // call on dispose function to stop tracking movements
+    public removeRecordingFFView = (): void => {
+        this.disposeFunc?.();
+        this.disposeFunc = null;
     }
 
-    public pause = (): Error | undefined => {
-        if (this.isInitPresenation) {
-            console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first')
-            return new Error('[recordingApi.ts] pause(): no presentation')
-        }
-        // don't allow track movments
-        this.isRecording = false
-
-        // set adjust absoluteStart to add the time difference
-        const timestamp = new Date().getTime()
-        this.absoluteStart = timestamp - this.absoluteStart
-    }
+    // public pause = (): Error | undefined => {
+    //     if (this.isInitPresenation) {
+    //         console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first')
+    //         return new Error('[recordingApi.ts] pause(): no presentation')
+    //     }
+    //     // don't allow track movments
+    //     this.isRecording = false
+
+    //     // set adjust absoluteStart to add the time difference
+    //     const timestamp = new Date().getTime()
+    //     this.absoluteStart = timestamp - this.absoluteStart
+    // }
 
-    public resume = () => {
-        this.isRecording = true
-        // set absoluteStart to the difference in time
-        this.absoluteStart = new Date().getTime() - this.absoluteStart
-    }
+    // public resume = () => {
+    //     this.isRecording = true
+    //     // set absoluteStart to the difference in time
+    //     this.absoluteStart = new Date().getTime() - this.absoluteStart
+    // }
 
     private trackMovements = (panX: number, panY: number, scale: number = 0): Error | undefined => {
         // ensure we are recording
@@ -134,6 +146,8 @@ export class RecordingApi {
         if (this.isInitPresenation) {
             return new Error('[recordingApi.ts] trackMovements(): no presentation')
         }
+      
+      console.log('track movment')
 
         // get the time
         const time = new Date().getTime() - this.absoluteStart
@@ -164,12 +178,6 @@ export class RecordingApi {
         this.recordingFFView = view;
     }
 
-    // call on dispose function to stop tracking movements
-    public removeRecordingFFView = (): void => {
-        this.disposeFunc?.();
-        this.disposeFunc = null;
-    }
-
     // TODO: extract this into different class with pause and resume recording
     // TODO: store the FFview with the movements
     private playFFView: CollectionFreeFormView | null;
@@ -250,6 +258,37 @@ export class RecordingApi {
             }, timeDiff)
         })
     }
+  
+  // make a public method that concatenates the movements of the an array of presentations into one array
+  // TODO: consider the meta data of the presentations
+  public concatPresentations = (presentations: Presentation[]): Presentation => {
+    console.info(presentations);
+    if (presentations.length === 0) return RecordingApi.NULL_PRESENTATION;
+    const firstPresentation = presentations[0];
+
+    let sumTime = presentations[0].totalTime;
+    let combinedPresentations = { ...firstPresentation }
+    presentations.forEach((presentation, i) => {
+      // already consider the first presentation
+      if (i === 0) return;
+
+      const { movements, totalTime } = presentation;
+      if (movements === null) return;
+
+      // add the summed time to the movements
+      const addedTimeMovements = movements.map(move => { return { ...move, time: move.time + sumTime } });
+      // concat the movements already in the combined presentation with these new ones
+      const newMovements = [...combinedPresentations.movements || [], ...addedTimeMovements];
+
+      combinedPresentations = { ...combinedPresentations, movements: newMovements }
+
+      // update the totalTime
+      sumTime += totalTime;
+    });
+
+    // return the combined presentation with the updated total summed time
+    return { ...combinedPresentations, totalTime: sumTime };
+  }
 
     // Unfinished code for tracing multiple free form views
     // export let pres: Map<CollectionFreeFormView, IReactionDisposer> = new Map()
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index 493069394..1bb2b7c84 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -161,12 +161,12 @@ export function ProgressBar(props: ProgressBarProps) {
     }
 
     // pointerdown event for the progress bar
-    const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
-        // don't move the videobox element
-        e.stopPropagation()
+  const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+           // don't move the videobox element
+    e.stopPropagation();  
 
         // if recording, do nothing
-        if (props.recording) return;
+      if (props.recording) return;
 
         // get the segment the user clicked on to be dragged
         const clickedSegment = e.target as HTMLDivElement & EventTarget
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 5e97e3eb5..6fe67b6db 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -8,8 +8,8 @@ import { FieldView } from "../FieldView";
 import { VideoBox } from "../VideoBox";
 import { RecordingView } from './RecordingView';
 import { DocumentType } from "../../../documents/DocumentTypes";
-import { RecordingApi } from "../../../util/RecordingApi";
-import { Doc, FieldsSym } from "../../../../fields/Doc";
+import { Presentation } from "../../../util/RecordingApi";
+import { Doc } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 
 
@@ -21,7 +21,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
       private _ref: React.RefObject<HTMLDivElement> = React.createRef();
 
       constructor(props: any) {
-            super(props);
+        super(props);
       }
 
       componentDidMount() {
@@ -38,7 +38,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
       }
 
       @action
-      setResult = (info: Upload.AccessPathInfo, trackScreen: boolean) => {
+      setResult = (info: Upload.AccessPathInfo, presentation?: Presentation) => {
             this.result = info
             this.dataDoc.type = DocumentType.VID;
             this.dataDoc[this.fieldKey + "-duration"] = this.videoDuration;
@@ -47,9 +47,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
             this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.client);
             this.dataDoc[this.fieldKey + "-recorded"] = true;
             // stringify the presenation and store it
-            if (trackScreen) {
-                  this.dataDoc[this.fieldKey + "-presentation"] = JSON.stringify(RecordingApi.Instance.clear());
-            }
+            presentation?.movements && (this.dataDoc[this.fieldKey + "-presentation"] = JSON.stringify(presentation));
       }
 
       render() {
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 138e72274..208eaf45a 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -8,16 +8,17 @@ import { IconContext } from "react-icons";
 import { Networking } from '../../../Network';
 import { Upload } from '../../../../server/SharedMediaTypes';
 import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-import { RecordingApi } from '../../../util/RecordingApi';
+import { Presentation, RecordingApi } from '../../../util/RecordingApi';
 
 export interface MediaSegment {
     videoChunks: any[],
     endTime: number,
-    startTime: number
+  startTime: number,
+  presenation?: Presentation,
 }
 
 interface IRecordingViewProps {
-    setResult: (info: Upload.AccessPathInfo, trackScreen: boolean) => void
+    setResult: (info: Upload.AccessPathInfo, presentation?: Presentation) => void
     setDuration: (seconds: number) => void
     id: string
 }
@@ -58,13 +59,18 @@ export function RecordingView(props: IRecordingViewProps) {
             sampleRate: 44100
         }
     }
+  
+  useEffect(() => console.info('progess', progress), [progress])
 
 
     useEffect(() => {
-        if (finished) {
+      if (finished) {
+        // make the total presentation that'll match the concatted video
+        const concatPres = trackScreen ? RecordingApi.Instance.concatPresentations(videos.map(v => v.presenation as Presentation)) : undefined;
+
             // this async function uses the server to create the concatted video and then sets the result to it's accessPaths
             (async () => {
-                const videoFiles = videos.map((vid, i) => new File(vid.videoChunks, `segvideo${i}.mkv`, { type: vid.videoChunks[0].type, lastModified: Date.now() }));
+              const videoFiles = videos.map((vid, i) => new File(vid.videoChunks, `segvideo${i}.mkv`, { type: vid.videoChunks[0].type, lastModified: Date.now() }));
 
                 // upload the segments to the server and get their server access paths
                 const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles))
@@ -72,7 +78,7 @@ export function RecordingView(props: IRecordingViewProps) {
 
                 // concat the segments together using post call
                 const result: Upload.AccessPathInfo | Error = await Networking.PostToServer('/concatVideos', serverPaths);
-                !(result instanceof Error) ? props.setResult(result, trackScreen) : console.error("video conversion failed");
+                !(result instanceof Error) ? props.setResult(result, concatPres) : console.error("video conversion failed");
             })();
         }
     }, [videos])
@@ -133,53 +139,70 @@ export function RecordingView(props: IRecordingViewProps) {
             trackScreen && RecordingApi.Instance.start();
         }
 
-        videoRecorder.current.onstop = () => {
+      videoRecorder.current.onstop = () => {
+          RecordingApi.Instance.stop();
             // if we have a last portion
-            if (videoChunks.length > 1) {
+          if (videoChunks.length > 1) {
                 // append the current portion to the video pieces
-                setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
+                  setVideos(videos => [...videos, {
+                    videoChunks: videoChunks,
+                    endTime: recordingTimerRef.current,
+                    startTime: videos?.lastElement()?.endTime || 0,
+                    // RecordingApi.stop() will return undefined if no track screen
+                    presenation: RecordingApi.Instance.getPresentation()
+                  }])
+                  // now that we got the presentation data, we can clear for the next segment to be recorded
+                  RecordingApi.Instance.clear();
             }
 
-            // reset the temporary chunks
-            videoChunks = []
-            setRecording(false);
-            trackScreen && RecordingApi.Instance.pause();
+          // reset the temporary chunks
+          videoChunks = []
+          setRecording(false);
         }
 
         videoRecorder.current.start(200)
     }
 
 
-    const stop = (e: React.PointerEvent) => {
-        e.stopPropagation();
-        if (videoRecorder.current) {
-            if (videoRecorder.current.state !== "inactive") {
-                videoRecorder.current.stop();
-            }
-
-            // this will call upon progessbar to update videos to be in the correct order
-            setFinished(true);
-
-            // end the streams (audio/video) to remove recording icon
-            const stream = videoElementRef.current!.srcObject;
-            stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
-        }
+  const finish = (e: React.PointerEvent) => {
+    e.stopPropagation();
+    console.log('finish', videoRecorder.current)
+      // if inactive, then we're done recording all the segments
+    if (videoRecorder.current && videoRecorder.current.state !== "inactive") {
+        console.log('stopping recorder', videoRecorder.current?.state)
+        
+        
+        
+        // call stop on the video recorder
+        videoRecorder.current?.stop();
+
+        // end the streams (audio/video) to remove recording icon
+        const stream = videoElementRef.current!.srcObject;
+        stream instanceof MediaStream && stream.getTracks().forEach(track => track.stop());
+          
+        // clear the recoringApi - this is done in the stop method
+        // RecordingApi.Instance.clear();
+
+        // this will call upon progessbar to update videos to be in the correct order
+        console.log('setFinished to true', finished)
+        setFinished(true);
+      }
     }
 
     const pause = (e: React.PointerEvent) => {
-        e.stopPropagation()
-        if (videoRecorder.current) {
-            if (videoRecorder.current.state === "recording") {
-                videoRecorder.current.stop();
-            }
-        }
+      e.stopPropagation()
+      // if recording, then this is just a new segment
+      videoRecorder.current?.state === "recording" && videoRecorder.current.stop();
     }
 
-    const startOrResume = (e: React.PointerEvent) => {
+    const start = (e: React.PointerEvent) => {
         // the code to start or resume does not get triggered if we start dragging the button
         setupMoveUpEvents({}, e, returnTrue, returnFalse, e => {
-            (!videoRecorder.current || videoRecorder.current.state === "inactive") && record();
-            return true; // cancels propagation to documentView to avoid selecting it.
+          if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
+            record();
+            // trackScreen && RecordingApi.Instance.start();
+          }
+          return true; // cancels propagation to documentView to avoid selecting it.
         }, false, false);
     }
 
@@ -218,7 +241,7 @@ export function RecordingView(props: IRecordingViewProps) {
                         <div className="record-button-wrapper">
                             {recording ?
                                 <button className="stop-button" onPointerDown={pause} /> :
-                                <button className="record-button" onPointerDown={startOrResume} />
+                                <button className="record-button" onPointerDown={start} />
                             }
                         </div>
 
@@ -229,7 +252,7 @@ export function RecordingView(props: IRecordingViewProps) {
                                     <MdBackspace onPointerDown={undoPrevious} />
                                 </IconContext.Provider>
                                 <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
-                                    <FaCheckCircle onPointerDown={stop} />
+                                    <FaCheckCircle onPointerDown={finish} />
                                 </IconContext.Provider>
                             </div>
 
-- 
cgit v1.2.3-70-g09d2

