Skip to content

Commit

Permalink
[feat/#58] 팝업 창이 초기에만 표시되도록 수정, 크루 페이지 평균 기능 제공
Browse files Browse the repository at this point in the history
  • Loading branch information
G-hoon committed Sep 27, 2024
1 parent 29c3a6b commit 60f46df
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 66 deletions.
1 change: 1 addition & 0 deletions src/api/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface groupUserRank {

export interface GroupUserRankData {
groupId: number
avgScore: number
ranks: groupUserRank[]
}

Expand Down
2 changes: 2 additions & 0 deletions src/components/Crew/CrewList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ const CrewList = (): ReactElement => {
}
}, [openJoinCrewModal, searchParams, setSearchParams])

console.log("ranks: ", ranks)

return (
<div className="flex h-full w-full flex-col">
{myGroupData && Object.keys(myGroupData).length > 0 && (
Expand Down
20 changes: 13 additions & 7 deletions src/components/PoseDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { position } from "@/api"
import { duration } from "@/api/notification"
import { poseType } from "@/api/pose"
import { useCameraPermission } from "@/hooks/useCameraPermission"
import { useGuidePopup } from "@/hooks/useGuidePopup"
import { useModals } from "@/hooks/useModals"
import useNotification from "@/hooks/useNotification"
import { useSendPose } from "@/hooks/usePoseMutation"
Expand All @@ -29,12 +28,11 @@ const PoseDetector: React.FC = () => {
const [isTailboneSit, setIsTailboneSit] = useState<boolean | null>(null)
const [isHandOnChin, setIsHandOnChin] = useState<boolean | null>(null)
const [isModelLoaded, setIsModelLoaded] = useState<boolean>(false)
const [isClosedInitialGuidePopup, setIsClosedInitialGuidePopup] = useState(false)
// const [isSnapShotSaved, setIsSnapSaved] = useState<boolean>(false)

const { showNotification, hasPermission: hasNotiPermisson } = usePushNotification()

const { isPopupOpen, handleClosePopup } = useGuidePopup()

const { openModal, isModalOpen } = useModals()

const modelRef = useRef<any>(null)
Expand All @@ -53,7 +51,7 @@ const PoseDetector: React.FC = () => {
const isShowImmediNotiRef = useRef<boolean>(false)
const canvasRef = useRef<HTMLCanvasElement>(null)

const { isSnapShotSaved, snapshot, setSnapShot } = useSnapShotStore()
const { isSnapShotSaved, snapshot, setSnapShot, isInitialSnapShotExist } = useSnapShotStore()
const createSnapMutation = useCreateSnaphot()
const sendPoseMutation = useSendPose()

Expand Down Expand Up @@ -195,7 +193,7 @@ const PoseDetector: React.FC = () => {
if (modelRef && modelRef.current) {
snapRef.current = resultRef.current
if (snapshot === null) {
if (snapRef.current) {
if (snapRef.current && snapRef.current.length > 0) {
const req = snapRef.current[0].keypoints.map((p) => ({
position: p.name.toUpperCase() as position,
x: p.x,
Expand All @@ -212,6 +210,8 @@ const PoseDetector: React.FC = () => {
},
}
)
} else {
alert("브라우저의 카메라 혹은 인공지능 모델에 문제가 발생했습니다. 새로고침 후 다시 시도해주시기 바랍니다.")
}
}
}
Expand Down Expand Up @@ -336,6 +336,10 @@ const PoseDetector: React.FC = () => {
console.log(notification)
}, [notification])

const handleCloseInitialGuidePopup = () => {
setIsClosedInitialGuidePopup(true)
}

return (
<>
{isScriptError ? (
Expand All @@ -347,7 +351,7 @@ const PoseDetector: React.FC = () => {
<Camera detectStart={detectStart} canvasRef={canvasRef} />
{isModelLoaded && (
<>
{!isPopupOpen && !isModalOpen && (
{isInitialSnapShotExist && !isModalOpen && (
<PostureMessage
isSnapShotSaved={isSnapShotSaved}
isShoulderTwist={isShoulderTwist}
Expand All @@ -362,7 +366,9 @@ const PoseDetector: React.FC = () => {
)}
</>
)}
{!isSnapShotSaved && isPopupOpen && <GuidePopupModal onClose={handleClosePopup} />}
{!isClosedInitialGuidePopup && !isInitialSnapShotExist && (
<GuidePopupModal onClose={handleCloseInitialGuidePopup} />
)}
</div>
)}
</>
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/useMyGroup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getGroupScores, getMyGroup, groupUserRank, MyGroupData, withdrawMyGroup } from "@/api"
import { getGroupScores, getMyGroup, groupUserRank, GroupUserRankData, MyGroupData, withdrawMyGroup } from "@/api"
import { useAuthStore } from "@/store"
import { useMyGroupStore } from "@/store/MyGroup"
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
Expand All @@ -18,7 +18,7 @@ export default function useMyGroup() {
})

// Fetch group scores
const myGroupScoresQuery = useQuery<{ data: { ranks: groupUserRank[] } }, Error>({
const myGroupScoresQuery = useQuery<{ data: GroupUserRankData }, Error>({
queryKey: ["groupScores", myGroupData?.id],
queryFn: () => getGroupScores(myGroupData!.id),
enabled: !!myGroupData?.id,
Expand Down Expand Up @@ -67,6 +67,7 @@ export default function useMyGroup() {
return {
myGroupData: myGroupData ?? data?.data,
ranks: myGroupScoresQuery.data?.data.ranks ?? [],
avgScore: myGroupScoresQuery.data?.data.avgScore,
myRank,
isLoading: isLoading || myGroupScoresQuery.isLoading,
error: error || myGroupScoresQuery.error,
Expand Down
24 changes: 1 addition & 23 deletions src/hooks/useSnapshotMutation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createSnapshot, createSnapshotRes, getRecentSnapshot, getSnapshots, snapshot } from "@/api"
import { createSnapshot, createSnapshotRes, snapshot } from "@/api"
import { useMutation, UseMutationResult } from "@tanstack/react-query"

export const useCreateSnaphot = (): UseMutationResult<createSnapshotRes, unknown, snapshot, unknown> => {
Expand All @@ -11,25 +11,3 @@ export const useCreateSnaphot = (): UseMutationResult<createSnapshotRes, unknown
},
})
}

export const useGetSnapshot = (): UseMutationResult<snapshot, unknown, string, unknown> => {
return useMutation({
mutationFn: (id: string) => {
return getSnapshots(id)
},
onSuccess: (data) => {
console.log(data)
},
})
}

export const useGetRecentSnapshot = (): UseMutationResult<snapshot, unknown, void, unknown> => {
return useMutation({
mutationFn: () => {
return getRecentSnapshot()
},
onSuccess: (data) => {
console.log(data)
},
})
}
11 changes: 6 additions & 5 deletions src/pages/AuthPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useOauth, useSignUp, useSignIn, useGetIsSignUp } from "@/hooks/useAuthM
import RoutePath from "@/constants/routes.json"
import { useAuthStore } from "@/store/AuthStore"
import { useSnapShotStore } from "@/store/SnapshotStore"
import { useGetRecentSnapshot } from "@/hooks/useSnapshotMutation"
import { getRecentSnapshot } from "@/api"
// import { useGetNoti } from "@/hooks/useNotiMutation"
// import { useNotificationStore } from "@/store/NotificationStore"

Expand All @@ -16,13 +16,12 @@ const AuthPage: React.FC = () => {
const getIsSignUpMutation = useGetIsSignUp()
const signUpMutation = useSignUp()
const signInMutation = useSignIn()
const getRecentSnapMutation = useGetRecentSnapshot()
// const getNotiMutation = useGetNoti()
const [isLoading, setIsLoading] = useState(true)
const [isError, setIsError] = useState(false)

const setUser = useAuthStore((state) => state.setUser)
const setSnap = useSnapShotStore((state) => state.setSnapShot)
const { setSnapShot } = useSnapShotStore()
// const setNoti = useNotificationStore((state) => state.setNotification)

useEffect(() => {
Expand All @@ -49,11 +48,13 @@ const AuthPage: React.FC = () => {
setUser({ uid, nickname }, accessToken)

// 최근 스냅샷을 가져오기
const userSnap = await getRecentSnapMutation.mutateAsync()
const userSnap = await getRecentSnapshot()

// 스냅샷이 있으면 store에 저장
if (userSnap.id !== -1) {
setSnap(userSnap.points.map((p) => ({ name: p.position.toLocaleLowerCase(), x: p.x, y: p.y, confidence: 1 })))
setSnapShot(
userSnap.points.map((p) => ({ name: p.position.toLocaleLowerCase(), x: p.x, y: p.y, confidence: 1 }))
)
}

// const notification = await getNotiMutation.mutateAsync()
Expand Down
48 changes: 23 additions & 25 deletions src/pages/MonitoringPage.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
import { getRecentSnapshot } from "@/api"
import { PoseDetector } from "@/components"
import PostrueCrew from "@/components/Posture/PostrueCrew"
import usePushNotification from "@/hooks/usePushNotification"
import { useSnapShotStore } from "@/store/SnapshotStore"
import GroupSideIcon from "@assets/icons/group-side-nav-button.svg?react"
import React, { useEffect, useState } from "react"
import { useGetRecentSnapshot } from "@/hooks/useSnapshotMutation"
import { useSnapShotStore } from "@/store/SnapshotStore"
import usePushNotification from "@/hooks/usePushNotification"
import { useGuidePopup } from "@/hooks/useGuidePopup"

const MonitoringPage: React.FC = () => {
const { hasPermission } = usePushNotification()
const getRecentSnapMutation = useGetRecentSnapshot()

const { isPopupOpen } = useGuidePopup()
const { snapshot, setSnapShot } = useSnapShotStore()

const { snapshot, setSnapShot, isInitialSnapShotExist } = useSnapShotStore()
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true)

const toggleSidebar = (): void => {
setIsSidebarOpen((prev) => !prev)
}

const init = async (): Promise<void> => {
// 최근 스냅샷을 가져오기
if (!snapshot) {
const userSnap = await getRecentSnapMutation.mutateAsync()
useEffect(() => {
const init = async (): Promise<void> => {
// 최근 스냅샷을 가져오기
if (!snapshot) {
const userSnap = await getRecentSnapshot()

// 스냅샷이 있으면 store에 저장
if (userSnap.id !== -1) {
setSnapShot(
userSnap.points.map((p) => ({ name: p.position.toLocaleLowerCase(), x: p.x, y: p.y, confidence: 1 }))
)
// 스냅샷이 있으면 store에 저장
if (userSnap.id !== -1) {
setSnapShot(
userSnap.points.map((p) => ({ name: p.position.toLocaleLowerCase(), x: p.x, y: p.y, confidence: 1 }))
)
}
}
}
}

useEffect(() => {
init()
}, [])

return (
<div className="relative flex h-full w-full overflow-hidden">
{/* Main content area */}
<div className={`flex-grow transition-all duration-300 ${isSidebarOpen && !isPopupOpen ? "pr-[224px]" : ""}`}>
<div
className={`flex-grow transition-all duration-300 ${
isSidebarOpen && isInitialSnapShotExist ? "pr-[224px]" : ""
}`}
>
<div id="monitoring-modal-root" className="relative flex h-full items-center justify-center">
<div className="aspect-video w-full max-w-[1280px] p-8">
<PoseDetector />
Expand All @@ -57,14 +55,14 @@ const MonitoringPage: React.FC = () => {
{/* 사이드바 */}
<div
className={`transition-width absolute right-0 top-0 z-10 h-full rounded-2xl bg-[#fafafa] duration-300 ${
isSidebarOpen && !isPopupOpen ? "w-[224px]" : "w-0"
isSidebarOpen && isInitialSnapShotExist ? "w-[224px]" : "w-0"
}`}
>
{isSidebarOpen && !isPopupOpen && <PostrueCrew toggleSidebar={toggleSidebar} />}
{isSidebarOpen && isInitialSnapShotExist && <PostrueCrew toggleSidebar={toggleSidebar} />}
</div>

{/* 토글 버튼 */}
{!isSidebarOpen && !isPopupOpen && (
{!isSidebarOpen && isInitialSnapShotExist && (
<button className="z-1 fixed right-2 top-2 rounded-full p-4" onClick={toggleSidebar}>
<GroupSideIcon />
</button>
Expand Down
24 changes: 21 additions & 3 deletions src/pages/MyCrew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useNavigate } from "react-router-dom"
import { useCallback, useEffect } from "react"

export default function MyCrew() {
const { myGroupData, ranks, myRank, withdrawFromGroup, isLoading } = useMyGroup()
const { myGroupData, ranks, myRank, withdrawFromGroup, isLoading, avgScore } = useMyGroup()
const { openModal } = useModals()
const naviagte = useNavigate()

Expand Down Expand Up @@ -54,6 +54,8 @@ export default function MyCrew() {
})
}, [myGroupData, openModal])

console.log("my crew: ", myRank, " / ", avgScore)

return (
<>
<MyCrewHeader />
Expand Down Expand Up @@ -137,8 +139,24 @@ export default function MyCrew() {

{/* footer */}
<div className="height-[68px] mt-3 flex justify-center rounded-[12px] border-[1px] border-solid border-gray-200 bg-white py-6 font-medium">
나는 우리 크루 평균보다 틀어짐이&nbsp; <span className="font-bold text-[#1A75FF]">6회 더</span>
&nbsp;감지되었어요.
{(!myRank || !myRank.score) &&
(avgScore !== undefined || avgScore !== null) &&
`우리 크루 평균 자세 경고 횟수는 ${avgScore}회 입니다.`}
{myRank &&
myRank.score &&
(avgScore !== undefined || avgScore !== null) &&
`지난 한 시간 동안 나의 자세 경고 횟수는 ${myRank.score}회 입니다.`}
{myRank && myRank.score && avgScore && (
<>
나는 우리 크루 평균보다 자세 경고를{" "}
<span
className={`font-bold ${Number(avgScore) > Number(myRank.score) ? "text-[#1A75FF]" : "text-red-500"} `}
>
{Math.abs(avgScore - myRank?.score)}{Number(avgScore) > Number(myRank?.score) ? "덜" : "더"}
</span>
&nbsp;받았어요.
</>
)}
</div>
</>
)
Expand Down
5 changes: 4 additions & 1 deletion src/store/SnapshotStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { create } from "zustand"
import { persist } from "zustand/middleware"

interface SnapShotState {
isInitialSnapShotExist: boolean
isSnapShotSaved: boolean
snapshot: keypoint[] | null
setSnapShot: (snapshot: keypoint[] | null) => void
Expand All @@ -13,8 +14,10 @@ export const useSnapShotStore = create(
persist<SnapShotState>(
(set) => ({
isSnapShotSaved: false,
isInitialSnapShotExist: false,
snapshot: null,
setSnapShot: (snapshot: keypoint[] | null) => set({ snapshot, isSnapShotSaved: true }),
setSnapShot: (snapshot: keypoint[] | null) =>
set({ snapshot, isSnapShotSaved: true, isInitialSnapShotExist: true }),
resetSnapShot: () => set({ snapshot: null, isSnapShotSaved: false }),
}),
{ name: "snapshotStorage" }
Expand Down

0 comments on commit 60f46df

Please sign in to comment.