From 04256938d583a1af396103e7e0b70a36843f2807 Mon Sep 17 00:00:00 2001 From: "lkh14011424@gmail.com" Date: Sun, 25 Aug 2024 21:07:57 +0900 Subject: [PATCH] =?UTF-8?q?[feat/#28]=20=EC=8A=A4=EB=83=85=EC=83=B7=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80,=20=EC=A0=80=EC=9E=A5=EB=90=9C?= =?UTF-8?q?=20=EC=8A=A4=EB=83=85=EC=83=B7=EC=9D=B4=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=ED=95=B4=EB=8B=B9=20=EC=8A=A4=EB=83=85?= =?UTF-8?q?=EC=83=B7=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/axiosInstance.ts | 14 +++---- src/api/index.ts | 1 + src/api/snapshot.ts | 64 ++++++++++++++++++++++++++++++++ src/components/PoseDetector.tsx | 35 +++++++++++++++++ src/hooks/useSnapshotMutation.ts | 35 +++++++++++++++++ src/pages/AuthPage.tsx | 12 ++++++ src/routes/Router.tsx | 8 ++-- src/store/SnapshotStore.ts | 12 ++++++ src/utils/detector.ts | 40 ++++++++++---------- 9 files changed, 190 insertions(+), 31 deletions(-) create mode 100644 src/api/snapshot.ts create mode 100644 src/hooks/useSnapshotMutation.ts create mode 100644 src/store/SnapshotStore.ts diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts index 5eb6638..b4f7f12 100644 --- a/src/api/axiosInstance.ts +++ b/src/api/axiosInstance.ts @@ -13,19 +13,19 @@ const axiosInstance = axios.create({ // localStorage에서 토큰 가져오기 const token = localStorage.getItem("accessToken") if (token) { - axiosInstance.defaults.headers.common["Authorization"] = `Bearer ${token}` + axiosInstance.defaults.headers.common["X-HERO-AUTH-TOKEN"] = token } // 엑세스 토큰 설정 함수 -export const setAccessToken = (token: string) => { - axiosInstance.defaults.headers.common["Authorization"] = `Bearer ${token}` - localStorage.setItem("accessToken", token) +export const setAccessToken = (_token: string): void => { + axiosInstance.defaults.headers.common["X-HERO-AUTH-TOKEN"] = _token + // localStorage.setItem("accessToken", token) } // 엑세스 토큰 제거 함수 -export const clearAccessToken = () => { - delete axiosInstance.defaults.headers.common["Authorization"] - localStorage.removeItem("accessToken") +export const clearAccessToken = (): void => { + delete axiosInstance.defaults.headers.common["X-HERO-AUTH-TOKEN"] + // localStorage.removeItem("accessToken") } export default axiosInstance diff --git a/src/api/index.ts b/src/api/index.ts index 306751a..ab24654 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1 +1,2 @@ export * from "./auth" +export * from "./snapshot" diff --git a/src/api/snapshot.ts b/src/api/snapshot.ts new file mode 100644 index 0000000..9bab53c --- /dev/null +++ b/src/api/snapshot.ts @@ -0,0 +1,64 @@ +import axiosInstance from "./axiosInstance" + +export type position = + | "NOSE" + | "LEFT_EYE" + | "RIGHT_EYE" + | "LEFT_EAR" + | "RIGHT_EAR" + | "LEFT_SHOULDER" + | "RIGHT_SHOULDER" + | "LEFT_ELBOW" + | "RIGHT_ELBOW" + | "LEFT_WRIST" + | "RIGHT_WRIST" + | "LEFT_HIP" + | "RIGHT_HIP" + | "LEFT_KNEE" + | "RIGHT_KNEE" + | "LEFT_ANKLE" + | "RIGHT_ANKLE" + +export interface point { + position: position + x: number + y: number +} + +export interface snapshot { + id?: number + points: point[] +} + +export interface createSnapshotRes { + id: string +} + +export const createSnapshot = async (snapshot: snapshot): Promise => { + try { + const res = await axiosInstance.post(`/pose-layouts`, { snapshot }) + const { id } = res.data.data + + return { id } + } catch (e) { + throw e + } +} + +export const getSnapshots = async (id: string): Promise => { + try { + const res = await axiosInstance.get(`/pose-layouts/${id}`) + return res.data.data + } catch (e) { + throw e + } +} + +export const getRecentSnapshot = async (): Promise => { + try { + const res = await axiosInstance.get(`/pose-layouts/recent`) + return res.data.data + } catch (e) { + throw e + } +} diff --git a/src/components/PoseDetector.tsx b/src/components/PoseDetector.tsx index eecee31..02eb368 100644 --- a/src/components/PoseDetector.tsx +++ b/src/components/PoseDetector.tsx @@ -6,6 +6,9 @@ import { worker } from "@/utils/worker" import { useCallback, useEffect, useRef, useState } from "react" import Camera from "./Camera" import GuidePopup from "./Posture/GuidePopup" +import { useSnapshotStore } from "@/store/SnapshotStore" +import { useCreateSnaphot } from "@/hooks/useSnapshotMutation" +import { position } from "@/api" const PoseDetector: React.FC = () => { const [isScriptLoaded, setIsScriptLoaded] = useState(false) @@ -23,6 +26,10 @@ const PoseDetector: React.FC = () => { const timer = useRef(null) const canvasRef = useRef(null) + const snapshot = useSnapshotStore((state) => state.snapshot) + const createSnapMutation = useCreateSnaphot() + const setSnap = useSnapshotStore((state) => state.setSnapshot) + const { requestNotificationPermission, showNotification } = usePushNotification() const requestApi = (delay: number): Promise => new Promise((resolve) => setTimeout(resolve, delay)) @@ -114,6 +121,30 @@ const PoseDetector: React.FC = () => { const getInitSnap = (): void => { if (modelRef && modelRef.current) { snapRef.current = resultRef.current + if (snapshot === null) { + if (snapRef.current) { + const req = snapRef.current[0].keypoints.map((p) => ({ + position: p.name.toUpperCase() as position, + x: p.x, + y: p.y, + })) + createSnapMutation.mutate( + { points: req }, + { + onSuccess: (data: any) => { + setSnap(data) + }, + } + ) + } + } + setIsSnapSaved(true) + } + } + + const getUserSnap = (): void => { + if (snapshot) { + snapRef.current = [{ keypoints: snapshot }] setIsSnapSaved(true) } } @@ -137,6 +168,10 @@ const PoseDetector: React.FC = () => { } }, [isModelLoaded, detectStart]) + useEffect(() => { + getUserSnap() + }, [snapshot]) + // const initializePoseMonitoring = () => { // setIsTextNeck(null) // setSlope(null) diff --git a/src/hooks/useSnapshotMutation.ts b/src/hooks/useSnapshotMutation.ts new file mode 100644 index 0000000..458e08c --- /dev/null +++ b/src/hooks/useSnapshotMutation.ts @@ -0,0 +1,35 @@ +import { createSnapshot, createSnapshotRes, getRecentSnapshot, getSnapshots, snapshot } from "@/api" +import { useMutation, UseMutationResult } from "@tanstack/react-query" + +export const useCreateSnaphot = (): UseMutationResult => { + return useMutation({ + mutationFn: (snapshot: snapshot) => { + return createSnapshot(snapshot) + }, + onSuccess: (data) => { + console.log(data) + }, + }) +} + +export const useGetSnapshot = (): UseMutationResult => { + return useMutation({ + mutationFn: (id: string) => { + return getSnapshots(id) + }, + onSuccess: (data) => { + console.log(data) + }, + }) +} + +export const useGetRecentSnapshot = (): UseMutationResult => { + return useMutation({ + mutationFn: () => { + return getRecentSnapshot() + }, + onSuccess: (data) => { + console.log(data) + }, + }) +} diff --git a/src/pages/AuthPage.tsx b/src/pages/AuthPage.tsx index dd03c0b..63563be 100644 --- a/src/pages/AuthPage.tsx +++ b/src/pages/AuthPage.tsx @@ -4,6 +4,8 @@ import Login from "@/components/Login" import { useOauth, useSignUp, useSignIn, useGetIsSignUp } from "@/hooks/useAuthMutation" import RoutePath from "@/constants/routes.json" import { useAuthStore } from "@/store/AuthStore" +import { useSnapshotStore } from "@/store/SnapshotStore" +import { useGetRecentSnapshot } from "@/hooks/useSnapshotMutation" const AuthPage: React.FC = () => { const navigate = useNavigate() @@ -12,11 +14,13 @@ const AuthPage: React.FC = () => { const getIsSignUpMutation = useGetIsSignUp() const signUpMutation = useSignUp() const signInMutation = useSignIn() + const getRecentSnapMutation = useGetRecentSnapshot() const [isLoading, setIsLoading] = useState(true) const [isError, setIsError] = useState(false) const setUser = useAuthStore((state) => state.setUser) + const setSnap = useSnapshotStore((state) => state.setSnapshot) useEffect(() => { const authenticate = async (): Promise => { @@ -41,6 +45,14 @@ const AuthPage: React.FC = () => { // AuthStore에 사용자 정보와 토큰 저장 setUser({ uid, nickname }, accessToken) + // 최근 스냅샷을 가져오기 + const userSnap = await getRecentSnapMutation.mutateAsync() + + // 스냅샷이 있으면 store에 저장 + if (userSnap.id !== -1) { + setSnap(userSnap.points.map((p) => ({ name: p.position.toLocaleLowerCase(), x: p.x, y: p.y, confidence: 1 }))) + } + setIsLoading(false) navigate(RoutePath.MONITORING) } catch (error) { diff --git a/src/routes/Router.tsx b/src/routes/Router.tsx index 8dec20b..4646df3 100644 --- a/src/routes/Router.tsx +++ b/src/routes/Router.tsx @@ -3,7 +3,7 @@ import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom" import { AuthPage, MonitoringPage, HomePage } from "@/pages" import { Layout } from "@/layouts" import RoutePath from "@/constants/routes.json" -// import AuthRoute from "@/routes/AuthRoute" // ToDo: 로그인 정상화 될때까지 routing 보호하지 않도록 수정 +import AuthRoute from "@/routes/AuthRoute" const Router: React.FC = () => { return ( @@ -13,9 +13,9 @@ const Router: React.FC = () => { } /> }> {/* AuthRoute로 보호된 경로를 감쌉니다 */} - {/* }> */} - } /> - {/* */} + }> + } /> + } /> diff --git a/src/store/SnapshotStore.ts b/src/store/SnapshotStore.ts new file mode 100644 index 0000000..29f631d --- /dev/null +++ b/src/store/SnapshotStore.ts @@ -0,0 +1,12 @@ +import { keypoint } from "@/utils" +import { create } from "zustand" + +interface SnapshotState { + snapshot: keypoint[] | null + setSnapshot: (snapshot: keypoint[]) => void +} + +export const useSnapshotStore = create((set) => ({ + snapshot: null, + setSnapshot: (snapshot: keypoint[]) => set({ snapshot }), +})) diff --git a/src/utils/detector.ts b/src/utils/detector.ts index 12a67b4..8fa79dd 100644 --- a/src/utils/detector.ts +++ b/src/utils/detector.ts @@ -21,26 +21,26 @@ export interface box { // 포즈 객체의 타입 정의 export interface pose { keypoints: keypoint[] - box: box - score: number - id: number - nose: keypoint - left_eye: keypoint - right_eye: keypoint - left_ear: keypoint - right_ear: keypoint - left_shoulder: keypoint - right_shoulder: keypoint - left_elbow: keypoint - right_elbow: keypoint - left_wrist: keypoint - right_wrist: keypoint - left_hip: keypoint - right_hip: keypoint - left_knee: keypoint - right_knee: keypoint - left_ankle: keypoint - right_ankle: keypoint + box?: box + score?: number + id?: number + nose?: keypoint + left_eye?: keypoint + right_eye?: keypoint + left_ear?: keypoint + right_ear?: keypoint + left_shoulder?: keypoint + right_shoulder?: keypoint + left_elbow?: keypoint + right_elbow?: keypoint + left_wrist?: keypoint + right_wrist?: keypoint + left_hip?: keypoint + right_hip?: keypoint + left_knee?: keypoint + right_knee?: keypoint + left_ankle?: keypoint + right_ankle?: keypoint } /**