Skip to content

Commit

Permalink
Merge pull request #73 from DDD-Community/feat/#58
Browse files Browse the repository at this point in the history
[feat/#58] 모니터링 페이지에 가이드 팝업 수정 , 현재 접속된 크루 보기가 기본 off 되어 보이도록 수정, 소켓 …
  • Loading branch information
G-hoon authored Sep 21, 2024
2 parents 25c4c43 + 8aab4a8 commit 6b4d665
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 47 deletions.
4 changes: 2 additions & 2 deletions src/api/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const getNotification = async (): Promise<notification | null> => {
}
}

export const modifyNotification = async (notification: notification): Promise<notification> => {
export const registerNotification = async (notification: notification): Promise<notification> => {
try {
const res = await axiosInstance.post(`/pose-notifications`, { ...notification })
const { id, duration } = res.data.data
Expand All @@ -31,7 +31,7 @@ export const modifyNotification = async (notification: notification): Promise<no
}
}

export const patchNotification = async (notification: notification): Promise<notification> => {
export const updateNotification = async (notification: notification): Promise<notification> => {
try {
const res = await axiosInstance.patch(`/pose-notifications/${notification.id}`, { ...notification })
const { id, isActive, duration } = res.data.data
Expand Down
Binary file added src/assets/images/posture-snapshot-guide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/PoseDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { drawPose } from "@/utils/drawer"
import { worker } from "@/utils/worker"
import { useCallback, useEffect, useRef, useState } from "react"
import Camera from "./Camera"
import GuidePopup from "./Posture/GuidePopup"
import GuidePopup from "./Posture/GuidePopup/GuidePopup"
import { useSnapshotStore } from "@/store/SnapshotStore"
import { useCreateSnaphot } from "@/hooks/useSnapshotMutation"
import { position } from "@/api"
Expand Down
28 changes: 0 additions & 28 deletions src/components/Posture/GuidePopup.tsx

This file was deleted.

38 changes: 38 additions & 0 deletions src/components/Posture/GuidePopup/GuidePopup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ReactElement, useState } from "react"
import ServiceIntroduction from "./ServiceIntroduction"
import SnapshotGuide from "./SnapshotGuide"

const GuidePopup = ({ onClose }: { onClose: () => void }): ReactElement => {
const [step, setStep] = useState(0)
const onClickNext = () => {
setStep(1)
}
return (
<div className="absolute inset-0 flex items-center justify-center rounded-3xl backdrop-blur-lg">
{/* blur 처리 */}
<div className="pointer-events-auto relative flex h-[504px] w-[800px] flex-col items-center rounded-lg bg-white p-8 shadow-lg">
{step === 0 && (
<>
<ServiceIntroduction />
<button
className="h-[50px] w-[354px] rounded-full bg-[#1A75FF] px-[39px] py-3 text-white"
onClick={onClickNext}
>
다음
</button>
</>
)}
{step === 1 && (
<>
<SnapshotGuide />
<button className="w-[354px] rounded-full bg-[#1A75FF] px-4 py-3 text-white" onClick={onClose}>
모니터링 시작하기
</button>
</>
)}
</div>
</div>
)
}

export default GuidePopup
42 changes: 42 additions & 0 deletions src/components/Posture/GuidePopup/ServiceIntroduction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export default function ServiceIntroduction() {
return (
<>
<div className="mb-8 flex gap-2">
<div className="h-2 w-2 rounded-full bg-[#1A75FF]"></div>
<div className="h-2 w-2 rounded-full bg-zinc-300"></div>
</div>
<div className="mb-6">
<div className="mb-3 text-center text-[30px] font-bold leading-10 text-[#1E2535]">
자세공작소는 실시간 모니터링으로 <br />
바른 자세 유지를 돕는 서비스입니다
</div>
<span className="text-[14px] font-normal text-zinc-500">
더 세밀한 모니터링을 위해, 최초 1회 자세 기준점 설정이 필요합니다.
</span>
</div>
{/* content */}
<div className="mb-8 flex gap-4">
<div className="flex w-80 flex-col items-center rounded-[17px] bg-[#EFEFF0] px-10 py-6">
<div className="mb-4 h-[24px] w-[24px] rounded-full bg-[#5A9CFF] text-center text-[15px] font-semibold text-white">
1
</div>
<span className="mb-2 text-[20px] font-semibold text-[#1E2535]">바른 자세 취하기</span>
<div className="text-center font-normal leading-6 text-zinc-500">
가이드에 따라 <br />
바른 자세를 취해 주세요.
</div>
</div>
<div className="flex w-80 flex-col items-center rounded-[17px] bg-[#EFEFF0] px-10 py-6">
<div className="mb-4 h-[24px] w-[24px] rounded-full bg-[#5A9CFF] text-center text-[15px] font-semibold text-white">
2
</div>
<span className="mb-2 text-[20px] font-semibold text-[#1E2535]">스냅샷 촬영하기</span>
<div className="text-center font-normal leading-6 text-zinc-500">
기준점 설정을 위해 <br />
촬영을 진행해 주세요.
</div>
</div>
</div>
</>
)
}
47 changes: 47 additions & 0 deletions src/components/Posture/GuidePopup/SnapshotGuide.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import CloseCrewPanelIcon from "@assets/images/posture-snapshot-guide.png"

export default function SnapshotGuide() {
return (
<>
<div className="mb-6 flex gap-2">
<div className="h-2 w-2 rounded-full bg-zinc-300"></div>
<div className="h-2 w-2 rounded-full bg-[#1A75FF]"></div>
</div>
<div className="mb-6">
<div className="mb-3 text-center text-[30px] font-bold text-[#1E2535]">바른 자세를 취해주세요</div>
</div>
{/* content */}
<div className="mb-8 flex items-center gap-8">
<div className="flex h-[254px] w-[284px] flex-col items-center justify-end rounded-[17px] bg-[#EFEFF0]">
<img src={CloseCrewPanelIcon} alt="스냅샷 가이드" />
</div>
<div className="flex flex-col gap-5">
<div className="flex gap-3">
<span className="flex h-6 w-6 justify-center rounded-full bg-[#5A9CFF] text-center font-semibold text-white">
1
</span>
<span className="font-[20px] font-semibold text-green-900">머리와 목을 일직선으로 곧게 펴기</span>
</div>
<div className="flex gap-3">
<span className="flex h-6 w-6 justify-center rounded-full bg-[#5A9CFF] text-center font-semibold text-white">
2
</span>
<span className="font-[20px] font-semibold text-green-900">양쪽 어깨 일직선 유지하기</span>
</div>
<div className="flex gap-3">
<span className="flex h-6 w-6 justify-center rounded-full bg-[#5A9CFF] text-center font-semibold text-white">
3
</span>
<span className="font-[20px] font-semibold text-green-900">팔은 책상 위에 수평으로 두기</span>
</div>
<div className="flex gap-3">
<span className="flex h-6 w-6 justify-center rounded-full bg-[#5A9CFF] text-center font-semibold text-white">
4
</span>
<span className="font-[20px] font-semibold text-green-900">등과 허리는 등받이에 지지하기</span>
</div>
</div>
</div>
</>
)
}
43 changes: 34 additions & 9 deletions src/components/Posture/PostrueCrew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import CloseCrewPanelIcon from "@assets/icons/crew-panel-close-button.svg?react"
import QuestionIcon from "@assets/icons/question-info-icon.svg?react"
import PostureGuide from "@assets/icons/posture-guide-button-icon.svg?react"
import RankingGuideToolTip from "@assets/images/ranking-guide.png"
import { ReactElement, useEffect, useState } from "react"
import { ReactElement, useCallback, useEffect, useState } from "react"
import SelectBox from "@components/SelectBox"
import { useAuthStore } from "@/store"
import { duration, notification } from "@/api/notification"
Expand Down Expand Up @@ -35,11 +35,16 @@ const NOTI_OPTIONS: NotiOption[] = [
{ value: "MIN_60", label: "1시간 간격" },
]

const MAX_RECONNECT_ATTEMPTS = 5
const INITIAL_RECONNECT_DELAY = 1000 //

export default function PostrueCrew(props: PostureCrewProps): ReactElement {
const { toggleSidebar } = props
const accessToken = useAuthStore((state) => state.accessToken)
const [crews, setCrews] = useState<IPostureCrew[]>([])
const [isConnected, setIsConnected] = useState<"loading" | "success" | "disconnected">("loading")
const [socket, setSocket] = useState<WebSocket | null>(null)
const [reconnectAttempts, setReconnectAttempts] = useState(0)

const userNoti = useNotificationStore((state) => state.notification)
const setUserNoti = useNotificationStore((state) => state.setNotification)
Expand All @@ -49,32 +54,52 @@ export default function PostrueCrew(props: PostureCrewProps): ReactElement {
const [isEnabled, setIsEnabled] = useState(userNoti?.isActive)
const [notiAlarmTime, setNotiAlarmTime] = useState(NOTI_OPTIONS.find((n) => n.value === userNoti?.duration)?.label)

useEffect(() => {
const socket = new WebSocket(`wss://api.alignlab.site/ws/v1/groups/1/users?X-HERO-AUTH-TOKEN=${accessToken}`)
const connectWebSocket = useCallback(() => {
const newSocket = new WebSocket(`wss://api.alignlab.site/ws/v1/groups/1/users?X-HERO-AUTH-TOKEN=${accessToken}`)

socket.onopen = () => {
newSocket.onopen = () => {
console.log("WebSocket connected")
setIsConnected("success")
setReconnectAttempts(0)
}

socket.onmessage = (event) => {
newSocket.onmessage = (event) => {
const data = JSON.parse(event.data)
setCrews(data.groupUsers || [])
}

socket.onerror = (error) => {
newSocket.onerror = (error) => {
console.error("WebSocket error:", error)
}

socket.onclose = (event) => {
newSocket.onclose = (event) => {
console.log("WebSocket disconnected. Code:", event.code, "Reason:", event.reason)
setIsConnected("disconnected")

if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
const delay = INITIAL_RECONNECT_DELAY * Math.pow(2, reconnectAttempts)
console.log(`Attempting to reconnect in ${delay}ms...`)
setTimeout(() => {
setReconnectAttempts((prev) => prev + 1)
connectWebSocket()
}, delay)
} else {
console.log("Max reconnection attempts reached. Please try again later.")
}
}

setSocket(newSocket)
}, [accessToken, reconnectAttempts])

useEffect(() => {
connectWebSocket()

return () => {
socket.close()
if (socket) {
socket.close()
}
}
}, [])
}, [connectWebSocket])

const onClickCloseSideNavButton = (): void => {
toggleSidebar()
Expand Down
2 changes: 1 addition & 1 deletion src/components/SelectBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface SelectBoxOption {
interface SelectBoxProps {
value: string | undefined
options: SelectBoxOption[]
isDisabled: boolean
isDisabled?: boolean
onClick: (selectedOption: SelectBoxOption) => void
}

Expand Down
6 changes: 3 additions & 3 deletions src/hooks/useNotiMutation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getNotification, modifyNotification, notification, patchNotification } from "@/api/notification"
import { getNotification, registerNotification, notification, updateNotification } from "@/api/notification"
import { useMutation, UseMutationResult } from "@tanstack/react-query"

export const useGetNoti = (): UseMutationResult<notification | null, unknown, void, unknown> => {
Expand All @@ -15,7 +15,7 @@ export const useGetNoti = (): UseMutationResult<notification | null, unknown, vo
export const useModifyNoti = (): UseMutationResult<notification, unknown, notification, unknown> => {
return useMutation({
mutationFn: (notification: notification) => {
return modifyNotification(notification)
return registerNotification(notification)
},
onSuccess: (data) => {
console.log(data)
Expand All @@ -26,7 +26,7 @@ export const useModifyNoti = (): UseMutationResult<notification, unknown, notifi
export const usePatchNoti = (): UseMutationResult<notification, unknown, notification, unknown> => {
return useMutation({
mutationFn: (notification: notification) => {
return patchNotification(notification)
return updateNotification(notification)
},
onSuccess: (data) => {
console.log(data)
Expand Down
2 changes: 1 addition & 1 deletion src/pages/MonitoringPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const MonitoringPage: React.FC = () => {
const setSnap = useSnapshotStore((state) => state.setSnapshot)
const snapshot = useSnapshotStore((state) => state.snapshot)

const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true)
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(false)

const toggleSidebar = (): void => {
setIsSidebarOpen((prev) => !prev)
Expand Down
10 changes: 8 additions & 2 deletions src/pages/MyCrew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import CrewUserIcon from "@assets/icons/crew-user-icon.svg?react"
import dayjs from "dayjs"
import { modals } from "@/components/Modal/Modals"
import { useNavigate } from "react-router-dom"
import { useCallback } from "react"
import { useCallback, useEffect } from "react"

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

Expand All @@ -38,6 +38,12 @@ export default function MyCrew() {
})
}

useEffect(() => {
if (!myGroupData && !isLoading) {
naviagte("/crew")
}
}, [myGroupData, isLoading])

const openInviteModal = useCallback((): void => {
openModal(modals.inviteCrewModal, {
id: Number(myGroupData?.id),
Expand Down

0 comments on commit 6b4d665

Please sign in to comment.