Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat/#58] 팝업 창이 초기에만 표시되도록 수정, 크루 페이지 평균 기능 제공 #99

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading