diff --git a/src/components/Camera.tsx b/src/components/Camera.tsx index 2a07b42..75c307d 100644 --- a/src/components/Camera.tsx +++ b/src/components/Camera.tsx @@ -1,4 +1,5 @@ import React, { useRef, useEffect } from "react" +import { useCameraPermission } from "@/hooks/useCameraPermission" // 커스텀 훅을 가져옵니다. interface CameraProps { detectStart: (video: HTMLVideoElement) => void @@ -9,6 +10,7 @@ export default function Camera(props: CameraProps): React.ReactElement { const { detectStart, canvasRef } = props const videoRef = useRef(null) + // 비디오를 시작하는 함수 const startVideo = (): void => { navigator.mediaDevices .getUserMedia({ @@ -38,9 +40,34 @@ export default function Camera(props: CameraProps): React.ReactElement { }) } + // 비디오를 중지하는 함수 + const stopVideo = (): void => { + if (videoRef.current && videoRef.current.srcObject) { + const stream = videoRef.current.srcObject as MediaStream + const tracks = stream.getTracks() + + tracks.forEach((track) => { + track.stop() // 모든 트랙 중지 + }) + + videoRef.current.srcObject = null // 비디오 스트림 초기화 + } + } + + // 커스텀 훅을 사용해 권한 상태 확인 + const { hasPermission, isPermissionDenied } = useCameraPermission() + useEffect(() => { - startVideo() - }, []) + if (hasPermission) { + startVideo() + } else if (isPermissionDenied) { + stopVideo() + } + + return () => { + stopVideo() // 컴포넌트가 언마운트될 때 비디오 중지 + } + }, [hasPermission, isPermissionDenied]) return (
-
+
{/* list */} -
-
- {/* crew name */} -
- -
{"개발자들 다 모여"}
-
- {/* crew user cnt */} -
- -
{`10/30명`}
-
- {/* detail button */} - -
-
- {/* crew name */} -
- -
{"개발자들 다 모여"}
-
- {/* crew user cnt */} -
- -
{`10/30명`}
-
- {/* detail button */} - -
-
- {/* crew name */} -
- -
{"개발자들 다 모여"}
-
- {/* crew user cnt */} -
- -
{`10/30명`}
-
- {/* detail button */} - -
-
- {/* crew name */} -
- -
{"개발자들 다 모여"}
-
- {/* crew user cnt */} -
- -
{`10/30명`}
-
- {/* detail button */} - -
-
+ {isLoading ? "로딩 중입니다..." : isError ? "데이터를 불러오는데 실패했습니다." : createGroupList(data?.data)} {}} /> {}} />
diff --git a/src/components/PoseDetector.tsx b/src/components/PoseDetector.tsx index dda4097..e806802 100644 --- a/src/components/PoseDetector.tsx +++ b/src/components/PoseDetector.tsx @@ -15,6 +15,7 @@ import PostureMessage from "./Posture/PostureMessage" import Controls from "./Posture/Controls" import { useNotificationStore } from "@/store/NotificationStore" import { duration } from "@/api/notification" +import { useCameraPermission } from "@/hooks/useCameraPermission" const PoseDetector: React.FC = () => { const [isScriptLoaded, setIsScriptLoaded] = useState(false) @@ -54,7 +55,7 @@ const PoseDetector: React.FC = () => { const random = Math.random() < 0.5 const { requestNotificationPermission } = usePushNotification() - + const { hasPermission } = useCameraPermission() // webgl 설정 const initializeBackend = async (): Promise => { await window.ml5.setBackend("webgl") @@ -140,11 +141,6 @@ const PoseDetector: React.FC = () => { const detect = useCallback( (results: pose[]): void => { resultRef.current = results - - if (canvasRef.current) { - drawPose(results, canvasRef.current) - } - if (snapRef.current) { const _isShoulderTwist = detectSlope(snapRef.current, results, false) const _isTextNeck = detectTextNeck(snapRef.current, results, true) @@ -169,6 +165,10 @@ const PoseDetector: React.FC = () => { ) managePoseTimer(_isTailboneSit, tailboneSitTimer, "TAILBONE_SIT", isSnapSaved, tailboneSitCnt, _isShowNoti) managePoseTimer(_isHandOnChin, chinUtpTimer, "CHIN_UTP", isSnapSaved, chinUtpCnt, _isShowNoti) + const isRight = !_isTextNeck && !_isHandOnChin && !_isShoulderTwist && !_isTailboneSit + if (canvasRef.current) drawPose(results, canvasRef.current, isRight) + } else { + if (canvasRef.current) drawPose(results, canvasRef.current) } }, [setIsShoulderTwist, setIsTextNeck, setIsHandOnChin, setIsTailboneSit, isSnapSaved, managePoseTimer, userNoti] @@ -298,13 +298,13 @@ const PoseDetector: React.FC = () => { }, [isSnapSaved]) useEffect(() => { - if (isModelLoaded) { + if (isModelLoaded && hasPermission) { const video = document.querySelector("video") if (video) { detectStart(video) } } - }, [isModelLoaded, detectStart]) + }, [isModelLoaded, hasPermission, detectStart]) useEffect(() => { if (snapshot) getUserSnap() @@ -352,12 +352,14 @@ const PoseDetector: React.FC = () => { isTextNeck={isTextNeck} isHandOnChin={isHandOnChin} isTailboneSit={isTailboneSit} + hasPermission={hasPermission} /> )} diff --git a/src/components/Posture/Controls.tsx b/src/components/Posture/Controls.tsx index 446a52b..5c6ceb4 100644 --- a/src/components/Posture/Controls.tsx +++ b/src/components/Posture/Controls.tsx @@ -3,16 +3,17 @@ import GuideIcon from "@assets/icons/posture-guide-button-icon.svg?react" const Controls: React.FC<{ isSnapSaved: boolean + hasPermission: boolean getInitSnap: () => void clearSnap: () => void handleShowPopup: () => void -}> = ({ isSnapSaved, getInitSnap, clearSnap, handleShowPopup }) => { - return ( +}> = ({ isSnapSaved, getInitSnap, clearSnap, handleShowPopup, hasPermission }) => { + return !hasPermission ? null : (
{!isSnapSaved ? ( <>