diff --git a/frontend/src/components/Header/components/HeaderInstructorControls.tsx b/frontend/src/components/Header/components/HeaderInstructorControls.tsx
index ed95911..b504ac0 100644
--- a/frontend/src/components/Header/components/HeaderInstructorControls.tsx
+++ b/frontend/src/components/Header/components/HeaderInstructorControls.tsx
@@ -75,17 +75,16 @@ const HeaderInstructorControls = ({ setLectureCode }: HeaderInstructorControlsPr
axios
.get(`${import.meta.env.VITE_API_SERVER_URL}/lecture?code=${roomid}`)
.then((result) => {
- //현재 강의 생성이 잘 되지 않아서 보류
- console.log(result.data);
- //setTitle(result.data.title);
+ const lecureTitle = result.data.title;
+ console.log(lecureTitle);
+ // 이후 작업 내일 예정
})
.catch(() => {
- showToast({ message: "존재하지 않는 강의실입니다.", type: "alert" });
- //navigate("/");
+ // showToast({ message: "존재하지 않는 강의실입니다.", type: "alert" });
+ // navigate("/");
});
setLectureCode(roomid);
window.addEventListener("popstate", handlePopstate);
- console.log("Strart");
}, []);
useEffect(() => {
inputMicVolumeRef.current = inputMicVolume;
diff --git a/frontend/src/components/Header/components/HeaderParticipantControls.tsx b/frontend/src/components/Header/components/HeaderParticipantControls.tsx
index 12b8b18..9bbed88 100644
--- a/frontend/src/components/Header/components/HeaderParticipantControls.tsx
+++ b/frontend/src/components/Header/components/HeaderParticipantControls.tsx
@@ -323,7 +323,7 @@ const HeaderParticipantControls = ({ setLectureCode }: HeaderParticipantControls
isModalOpen={isModalOpen}
setIsModalOpen={setIsModalOpen}
/>
-
+
>
);
};
diff --git a/frontend/src/components/LogContainer/LogContainer.tsx b/frontend/src/components/LogContainer/LogContainer.tsx
index 44ce268..43fecf9 100644
--- a/frontend/src/components/LogContainer/LogContainer.tsx
+++ b/frontend/src/components/LogContainer/LogContainer.tsx
@@ -2,10 +2,9 @@ import SendMessage from "@/assets/svgs/sendMessage.svg?react";
import participantSocketRefState from "@/stores/stateParticipantSocketRef";
import { CSSProperties, useEffect, useLayoutEffect, useRef, useState } from "react";
-import { useRecoilState, useRecoilValue } from "recoil";
+import { useRecoilValue } from "recoil";
import { useLocation } from "react-router-dom";
import { convertMsToTimeString } from "@/utils/convertMsToTimeString";
-import axios from "axios";
import progressMsTimeState from "@/stores/stateProgressMsTime";
import convertTimeStringToMS from "@/utils/converTimeStringToMS";
@@ -21,6 +20,7 @@ interface LogItemInterface {
interface LogContainerInterface {
type: "question" | "prompt";
className: string;
+ scriptList?: Array<{ start: string; text: string }>;
updateProgressMsTime?: (time: number) => void;
}
@@ -37,30 +37,20 @@ const LogItem = ({ title, contents, className, onClick, style }: LogItemInterfac
);
};
-const LogContainer = ({ type, className, updateProgressMsTime }: LogContainerInterface) => {
+const LogContainer = ({ type, className, scriptList, updateProgressMsTime }: LogContainerInterface) => {
const [isInputEmpty, setIsInputEmpty] = useState(true);
const [questionList, setQuestionList] = useState>([]);
- const [scriptList, setScriptList] = useState>([]);
const messageInputRef = useRef(null);
const logContainerRef = useRef(null);
- const [progressMsTime, setProgressMsTime] = useRecoilState(progressMsTimeState);
+ const progressMsTime = useRecoilValue(progressMsTimeState);
const socket = useRecoilValue(participantSocketRefState);
const [hilightedItemIndex, setHilightedItemIndex] = useState(0);
const roomid = new URLSearchParams(useLocation().search).get("roomid") || "999999";
if (type === "prompt") {
- useEffect(() => {
- axios("./reviewLecture.json")
- .then(({ data }) => {
- setScriptList(data);
- })
- .catch((error) => {
- console.log("프롬프트에 표시할 스크립트 로딩 실패", error);
- });
- }, []);
-
useLayoutEffect(() => {
+ if (!scriptList) return;
let currentIndexOfPrompt =
scriptList.findIndex((value) => {
const startTime = Math.floor(+value.start / 1000) * 1000;
@@ -132,7 +122,7 @@ const LogContainer = ({ type, className, updateProgressMsTime }: LogContainerInt
)}
{type === "prompt" && (
- {scriptList.map(({ start, text }, index) => {
+ {scriptList?.map(({ start, text }, index) => {
return (
diff --git a/frontend/src/pages/Review/Review.tsx b/frontend/src/pages/Review/Review.tsx
index 88c0c29..a667f12 100644
--- a/frontend/src/pages/Review/Review.tsx
+++ b/frontend/src/pages/Review/Review.tsx
@@ -1,33 +1,38 @@
import { useRecoilValue, useRecoilState } from "recoil";
import { useState, useEffect, useRef } from "react";
import { fabric } from "fabric";
-import { ICanvasData, loadCanvasData, updateCanvasSize } from "@/utils/fabricCanvasUtil";
-import progressMsTimeState from "@/stores/stateProgressMsTime";
-
import axios from "axios";
+import { ICanvasData, loadCanvasData, updateCanvasSize } from "@/utils/fabricCanvasUtil";
+
import CloseIcon from "@/assets/svgs/close.svg?react";
import ScriptIcon from "@/assets/svgs/whiteboard/script.svg?react";
+import progressMsTimeState from "@/stores/stateProgressMsTime";
import isQuestionLogOpenState from "@/stores/stateIsQuestionLogOpen";
import LogToggleButton from "@/components/Button/LogToggleButton";
import LogContainer from "@/components/LogContainer/LogContainer";
import Header from "@/components/Header/Header";
import ProgressBar from "./components/ProgressBar";
+import { useToast } from "@/components/Toast/useToast";
const Review = () => {
+ const [prograssBarState, setPrograssBarState] = useState<"disabled" | "playing" | "paused">("disabled");
const isQuestionLogOpen = useRecoilValue(isQuestionLogOpenState);
const [progressMsTime, setProgressMsTime] = useRecoilState(progressMsTimeState);
- const loadedDataRef = useRef();
+ const showToast = useToast();
+ const loadedDataRef = useRef();
+ const scriptListRef = useRef>();
const onFrameIdRef = useRef(null); // 마이크 볼륨 측정 타이머 id
const canvasContainerRef = useRef(null);
const canvasRef = useRef(null);
+ const localAudioRef = useRef(null);
let fabricCanvasRef = useRef();
let canvasCntRef = useRef(0);
+ let totalTimeRef = useRef(0);
- const [prograssBarState, setPrograssBarState] = useState<"disabled" | "playing" | "paused">("disabled");
let startTime = Date.now();
let canvasData: ICanvasData = {
canvasJSON: "",
@@ -36,8 +41,6 @@ const Review = () => {
width: 0,
height: 0
};
- // 추후 해당 다시보기의 전체 플레이 타임을 받아올 수 있어야 할 것 같습니다.
- let TOTAL_MS_TIME_OF_REVIEW = 200000;
useEffect(() => {
handleInitCanvas();
@@ -48,7 +51,6 @@ const Review = () => {
window.addEventListener("resize", handleResize);
};
}, []);
-
// 윈도우 리사이즈 이벤트 감지
const handleInitCanvas = () => {
if (!canvasContainerRef.current || !canvasRef.current) return;
@@ -74,15 +76,34 @@ const Review = () => {
fabricCanvasRef.current = newCanvas;
};
const handleLoadData = () => {
+ /*
+ 현재 /lecture/record/:id 에서 불러오는 임시 데이터의 canvasJSON 데이터가
+ 실제 canvasJSON 데이터와 다르기 때문에 임시로 더미 데이터를 불러오도록 설정했습니다.
+ */
axios("./dummyCanvasData.json")
.then(({ data }) => {
- // 추후 해당 다시보기의 전체 플레이 타임을 받아올 수 있어야 할 것 같습니다.
- TOTAL_MS_TIME_OF_REVIEW = 200000;
loadedDataRef.current = data;
setPrograssBarState("paused");
})
- .catch((error) => {
- console.log("화이트보드 데이터 로딩 실패", error);
+ .catch(() => {
+ showToast({ message: "강의 데이터를 불러오는 데 실패했습니다.", type: "alert" });
+ });
+
+ // 실제 데이터를 불러오는 코드
+ axios
+ .get(`https://boarlog.shop/lecture/record/6576c9dfccd3e23a8e0fe473`)
+ .then((result) => {
+ // console.log(result.data);
+ // loadedDataRef.current = result.data.;
+ scriptListRef.current = result.data.subtitles;
+ localAudioRef.current!.src = result.data.audio_file;
+ localAudioRef.current!.addEventListener("loadedmetadata", () => {
+ totalTimeRef.current = localAudioRef.current!.duration * 1000;
+ });
+ //setPrograssBarState("paused");
+ })
+ .catch(() => {
+ showToast({ message: "강의 데이터를 불러오는 데 실패했습니다.", type: "alert" });
});
};
const handleResize = () => {
@@ -106,8 +127,12 @@ const Review = () => {
});
canvasCntRef.current += 1;
}
- if (elapsedTime < TOTAL_MS_TIME_OF_REVIEW) onFrameIdRef.current = window.requestAnimationFrame(onFrame);
- else console.log("다시보기 끝");
+ if (elapsedTime < totalTimeRef.current) onFrameIdRef.current = window.requestAnimationFrame(onFrame);
+ else {
+ setPrograssBarState("paused");
+ setProgressMsTime(0);
+ canvasCntRef.current = 0;
+ }
};
const play = () => {
@@ -123,10 +148,13 @@ const Review = () => {
}
onFrameIdRef.current = window.requestAnimationFrame(onFrame);
+ localAudioRef.current!.play();
setPrograssBarState("playing");
};
const pause = () => {
if (onFrameIdRef.current) window.cancelAnimationFrame(onFrameIdRef.current);
+
+ localAudioRef.current!.pause();
setPrograssBarState("paused");
};
@@ -144,11 +172,12 @@ const Review = () => {
}
}
- return closestSmallerIndex;
+ return closestSmallerIndex >= 0 ? closestSmallerIndex : 0;
};
// logContainer에서 프롬프트를 클릭하거나 프로그래스 바를 클릭했을 때 진행시간을 조정하는 함수입니다.
const updateProgressMsTime = (newProgressMsTime: number) => {
+ setProgressMsTime(newProgressMsTime);
const currentProgressBarState = prograssBarState;
pause();
const newCanvasCntRef = findClosest(loadedDataRef.current!, newProgressMsTime);
@@ -161,10 +190,12 @@ const Review = () => {
canvasCntRef.current = newCanvasCntRef + 1;
startTime = Date.now() - newProgressMsTime;
+ localAudioRef.current!.currentTime = newProgressMsTime / 1000;
if (currentProgressBarState === "playing") {
onFrameIdRef.current = window.requestAnimationFrame(onFrame);
setPrograssBarState("playing");
+ localAudioRef.current!.play();
}
};
@@ -176,6 +207,7 @@ const Review = () => {
@@ -183,13 +215,14 @@ const Review = () => {
+
>
);
};
diff --git a/frontend/src/pages/Review/components/ProgressBar.tsx b/frontend/src/pages/Review/components/ProgressBar.tsx
index 8d910bc..1b20a87 100644
--- a/frontend/src/pages/Review/components/ProgressBar.tsx
+++ b/frontend/src/pages/Review/components/ProgressBar.tsx
@@ -1,8 +1,8 @@
import PlayIcon from "@/assets/svgs/progressPlay.svg?react";
import PauseIcon from "@/assets/svgs/progressPause.svg?react";
-import { useRecoilState } from "recoil";
-import { useEffect, useRef, useState } from "react";
+import { useRecoilValue } from "recoil";
+import { useEffect, useRef } from "react";
import { convertMsToTimeString } from "@/utils/convertMsToTimeString";
import progressMsTimeState from "@/stores/stateProgressMsTime";
@@ -39,41 +39,18 @@ const ProgressBar = ({
pause,
updateProgressMsTime
}: ProgressBarProps) => {
- const [isPlaying, setIsPlaying] = useState(false);
- const [progressMsTime, setProgressMsTime] = useRecoilState(progressMsTimeState);
+ const progressMsTime = useRecoilValue(progressMsTimeState);
const timerRef = useRef();
- const lastUpdatedTime = useRef();
- const UPDATE_INTERVAL_MS = 150;
const handleProgressBarMouseDown = (event: React.MouseEvent) => {
const { left, width } = event.currentTarget.getBoundingClientRect();
const mouseClickedX = event.clientX;
const percent = (mouseClickedX - left) / width;
- setProgressMsTime(Math.round(totalTime * percent));
updateProgressMsTime(Math.round(totalTime * percent));
};
- useEffect(() => {
- if (isPlaying) {
- lastUpdatedTime.current = new Date().getTime();
-
- timerRef.current = setInterval(() => {
- const dateNow = new Date().getTime();
- const diffTime = dateNow - lastUpdatedTime.current;
- setProgressMsTime((progressMsTime) => progressMsTime + diffTime);
-
- lastUpdatedTime.current = dateNow;
- }, UPDATE_INTERVAL_MS);
- } else {
- clearInterval(timerRef.current);
- }
- }, [isPlaying]);
-
useEffect(() => {
if (progressMsTime >= totalTime) {
- prograssBarState = "disabled";
- setProgressMsTime(totalTime);
- setIsPlaying(false);
clearInterval(timerRef.current);
}
}, [progressMsTime]);
@@ -95,7 +72,7 @@ const ProgressBar = ({
) : prograssBarState === "playing" ? (
) : (
- = totalTime && "gray"}`} />
+
)}