diff --git a/src/app/mobile/webcam/[key]/page.tsx b/src/app/mobile/webcam/[key]/page.tsx new file mode 100644 index 0000000..8a0da76 --- /dev/null +++ b/src/app/mobile/webcam/[key]/page.tsx @@ -0,0 +1,20 @@ +import { redirect } from 'next/navigation'; +import React from 'react'; +import WebcamMobileMapPage from '@/pages/webcam/ui/webcam-mobile-map-page'; +import { JISAN } from '@/entities/slop/model'; + +const domainMap = { + jisan: JISAN, +}; + +const Page = ({ params }: { params: { key: string } }) => { + if (!(params.key in domainMap)) { + redirect('/not-found'); + } + + const domain = domainMap[params.key as keyof typeof domainMap]; + + return ; +}; + +export default Page; diff --git a/src/app/mobile/webcam/page.tsx b/src/app/mobile/webcam/page.tsx deleted file mode 100644 index d65b865..0000000 --- a/src/app/mobile/webcam/page.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@/pages/webcam/ui/webcam-mobile-map-page'; diff --git a/src/entities/slop/model/jisan.tsx b/src/entities/slop/model/jisan.tsx index 5699cbc..38a4d4b 100644 --- a/src/entities/slop/model/jisan.tsx +++ b/src/entities/slop/model/jisan.tsx @@ -1,3 +1,5 @@ +'use client'; + import BlueLiftPath from '../image/jisan/blue-lift-path'; import Lemon1Sub1LiftPath from '../image/jisan/lemon1-1-lift-path'; import Lemon1LiftPath from '../image/jisan/lemon1-lift-path'; @@ -17,138 +19,129 @@ export const JISAN: ResortInfo = { level: 'BEGINNER', name: '레몬 리프트 1', Element: Lemon1LiftPath, - webcam: { - id: 'lemon-station', - name: '레몬 탑승장', - position: { - top: 'top-[84%]', - left: 'left-[20%]', - }, - scale: 2, - }, + webcamId: 'lemon-station', isOpen: true, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, }, { id: 'lemon1-1-lift', level: 'BEGINNER', name: '레몬 리프트 1-1', Element: Lemon1Sub1LiftPath, - webcam: null, isOpen: false, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, }, { id: 'orange2-lift', level: 'INTERMEDIATE', name: '오렌지 리프트 2', Element: Orange2LiftPath, - webcam: { - id: 'orange-station', - name: '오렌지 탑승장', - position: { - top: 'top-[69%]', - left: 'left-[29%]', - }, - scale: 1, - src: 'http://konjiam.live.cdn.cloudn.co.kr/konjiam/cam01.stream/playlist.m3u8', - }, + webcamId: 'orange-station', isOpen: true, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, }, { id: 'orange3-lift', level: 'INTERMEDIATE', name: '오렌지 리프트 3', Element: Orange3LiftPath, - webcam: { - id: 'new-orange-station', - name: '뉴오렌지 탑승장', - position: { - top: 'top-[64%]', - left: 'left-[38%]', - }, - scale: 2, - src: 'http://konjiam.live.cdn.cloudn.co.kr/konjiam/cam01.stream/playlist.m3u8', - }, + webcamId: 'new-orange-station', isOpen: true, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, }, { id: 'new-orange-lift', level: 'ADVANCED', name: '뉴오렌지 리프트', Element: NewOrangeLiftPath, - webcam: null, isOpen: true, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, }, { id: 'blue-lift', level: 'ADVANCED', name: '블루 리프트 5', Element: BlueLiftPath, - webcam: { - id: 'blue-station', - name: '블루 탑승장', - scale: 2, - position: { - top: 'top-[69%]', - left: 'left-[56%]', - }, - }, + webcamId: 'blue-station', isOpen: true, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, }, { id: 'silver6-lift', level: 'EXPERT', name: '실버 리프트 6', Element: Silver6LiftPath, - webcam: { - id: '5-station', - name: '5번 슬로프', - scale: 1, - position: { - top: 'top-[47%]', - left: 'left-[37%]', - }, - }, + webcamId: '5-station', isOpen: true, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, }, { id: 'silver7-lift', level: 'UPPER_INTERMEDIATE', name: '실버 리프트 7', Element: Silver7LiftPath, - webcam: { - id: 'silver-station', - name: '실버 탑승장', - scale: 1, - position: { - top: 'top-[72%]', - left: 'left-[68%]', - }, - }, + webcamId: 'silver-station', isOpen: true, - isDayOpen: false, - isNightOpen: false, - isLateNightOpen: false, + }, + ], + webcams: [ + { + id: 'lemon-station', + name: '레몬 탑승장', + position: { + top: 'top-[84%]', + left: 'left-[20%]', + }, + scale: 1, + }, + { + id: 'orange-station', + name: '오렌지 탑승장', + position: { + top: 'top-[69%]', + left: 'left-[29%]', + }, + scale: 1, + src: 'http://konjiam.live.cdn.cloudn.co.kr/konjiam/cam01.stream/playlist.m3u8', + }, + { + id: 'blue-station', + name: '블루 탑승장', + scale: 1, + position: { + top: 'top-[69%]', + left: 'left-[56%]', + }, + }, + { + id: 'silver-station', + name: '실버 탑승장', + scale: 1, + position: { + top: 'top-[72%]', + left: 'left-[68%]', + }, + }, + { + id: '5-station', + name: '5번 슬로프', + scale: 1, + + position: { + top: 'top-[47%]', + left: 'left-[37%]', + }, + }, + { + id: 'new-orange-station', + name: '뉴오렌지 탑승장', + scale: 1, + position: { + top: 'top-[64%]', + left: 'left-[38%]', + }, + }, + { + id: 'jisan-overview', + name: '지산 전경', + scale: 1, + position: { + top: 'top-[38%]', + left: 'left-[52%]', + }, }, ], }; diff --git a/src/entities/slop/model/model.d.ts b/src/entities/slop/model/model.d.ts index ef2c144..2fe417a 100644 --- a/src/entities/slop/model/model.d.ts +++ b/src/entities/slop/model/model.d.ts @@ -9,6 +9,7 @@ export type Level = export type ResortInfo = { MapComponent: ComponentType; slops: Slop[]; + webcams: Webcam[]; }; export type Webcam = { @@ -27,12 +28,8 @@ export type Slop = { level: Level; name: string; Element: React.FC; + webcamId?: string; isOpen: boolean; - isDayOpen: boolean; - isNightOpen: boolean; - isLateNightOpen: boolean; - - webcam: Webcam | null; }; export type Position = { diff --git a/src/features/slop/ui/slop-camera.tsx b/src/features/slop/ui/slop-camera.tsx index 550bb6b..c7fb6aa 100644 --- a/src/features/slop/ui/slop-camera.tsx +++ b/src/features/slop/ui/slop-camera.tsx @@ -27,13 +27,12 @@ const SlopCamera = ({ const cameraRef = useRef(null); useEffect(() => { - console.log(updateCameraPosition); cameraRef.current && updateCameraPosition(id, { x: cameraRef.current.getBoundingClientRect().x, y: cameraRef.current.getBoundingClientRect().y, }); - }, [id, updateCameraPosition]); + }, [id, updateCameraPosition, cameraRef]); const toggleVideo = () => { setIsVideoOpen((pre) => !pre); diff --git a/src/pages/webcam/ui/webcam-mobile-map-page.tsx b/src/pages/webcam/ui/webcam-mobile-map-page.tsx index 2ff1b6d..6ae8fa9 100644 --- a/src/pages/webcam/ui/webcam-mobile-map-page.tsx +++ b/src/pages/webcam/ui/webcam-mobile-map-page.tsx @@ -4,13 +4,10 @@ import React, { useCallback, useState } from 'react'; import { WebcamMap, WebcamSlopList } from '@/widgets/webcam/ui'; import useMapPinch from '@/features/slop/hooks/useMapPinch'; import calculateWebcamPosition from '@/features/slop/lib/calculateWebcamPosition'; -import { JISAN } from '@/entities/slop/model'; -import type { Position } from '@/entities/slop/model/model'; +import type { Position, ResortInfo } from '@/entities/slop/model/model'; import { cn } from '@/shared/lib'; -const WebCamMobileMapPage = () => { - const DUMMY2 = JISAN; - +const WebCamMobileMapPage = ({ data }: { data: ResortInfo }) => { const [cameraPositions, setCameraPositions] = useState<{ [key: string]: Position; }>({}); @@ -39,12 +36,17 @@ const WebCamMobileMapPage = () => { ref={ref} style={style} containerRef={containerRef} - slops={DUMMY2.slops} - MapComponent={DUMMY2.MapComponent} + slops={data.slops} + webcams={data.webcams} + MapComponent={data.MapComponent} onCameraClick={handleFocusSlopCamClick} updateCameraPosition={updateCameraPosition} /> - + ); }; diff --git a/src/widgets/webcam/ui/webcam-map.tsx b/src/widgets/webcam/ui/webcam-map.tsx index 121ed03..8775aad 100644 --- a/src/widgets/webcam/ui/webcam-map.tsx +++ b/src/widgets/webcam/ui/webcam-map.tsx @@ -18,30 +18,29 @@ interface WebcamMapProps extends ResortInfo { } const WebcamMap = forwardRef( - ({ slops, style, MapComponent, onCameraClick, containerRef, updateCameraPosition }, ref) => { + ( + { slops, webcams, style, MapComponent, onCameraClick, containerRef, updateCameraPosition }, + ref + ) => { const { selectedSlop } = useSlopStore(); return (
- {slops - .filter( - ( - slop - ): slop is WebcamMapProps['slops'][number] & { - webcam: NonNullable; - } => slop.webcam !== null - ) - .map(({ id, webcam }) => ( + {webcams.map((webcam) => { + const slop = slops.find((slop) => slop.webcamId === webcam.id); + + return ( - ))} + ); + })}
); diff --git a/src/widgets/webcam/ui/webcam-slop-list.tsx b/src/widgets/webcam/ui/webcam-slop-list.tsx index 4641eee..c5e4379 100644 --- a/src/widgets/webcam/ui/webcam-slop-list.tsx +++ b/src/widgets/webcam/ui/webcam-slop-list.tsx @@ -1,6 +1,6 @@ import React from 'react'; import useSlopStore from '@/features/slop/hooks/useSlopStore'; -import type { Slop } from '@/entities/slop/model/model'; +import type { Slop, Webcam } from '@/entities/slop/model/model'; import LevelChip from '@/entities/slop/ui/level-chip'; import { cn } from '@/shared/lib'; import CameraButton from '@/shared/ui/cam-button'; @@ -8,10 +8,11 @@ import CameraButton from '@/shared/ui/cam-button'; interface WebcamSlopListProps { className?: string; list: Slop[]; + webcams: Webcam[]; onItemClick: ({ scale, id }: { scale: number; id: string }) => void; } -const WebcamSlopList = ({ className, list, onItemClick }: WebcamSlopListProps) => { +const WebcamSlopList = ({ className, list, webcams, onItemClick }: WebcamSlopListProps) => { const { selectedSlop, setSelectedSlop } = useSlopStore(); return ( @@ -31,17 +32,19 @@ const WebcamSlopList = ({ className, list, onItemClick }: WebcamSlopListProps) = } else { setSelectedSlop(item.id); } - if (item.webcam) { + if (item.webcamId) { + const webcam = webcams.find((webcam) => webcam.id === item.webcamId); + if (!webcam) return; onItemClick({ - scale: item.webcam.scale, - id: item.webcam.id, + scale: webcam.scale, + id: webcam.id, }); } }} >

{item.name}

- {item.webcam && } + {item.webcamId && }