초기 커밋: 스테이션(측점) 기반 주행영상 플레이어
- 클라이언트(React/Vite): Video.js 플레이어, 하단 스테이션바, POI/구조물 영상 오버레이, 카메라 파라미터 보정, 미니맵/RoutePanel - 서버(Express): Range 스트리밍, HLS 변환, 프레임 추출, tus 업로드, 주석 API - 최근 작업: 스테이션바 종점역 표출/양끝 정렬/미도착(재생불가) 표시, POI 라벨 '구분' 우선·컴팩트 팝업·겹침제외 토글·동일좌표 다중행, 진행방향(상/하) 우선 표출, 커서 배지 크기·픽셀 떨림 개선 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 사용자 설정 스토어 (Zustand + localStorage 지속)
|
||||
*
|
||||
* 현재는 구조물 시설종별(1종/2종/3종) 표시 필터만 보관한다.
|
||||
* VideoPlayer 의 체크박스 UI 가 토글하고, StationBar(재생바)/RoutePanel(좌측 목록)
|
||||
* 이 구독해 표시 여부를 결정한다.
|
||||
*
|
||||
* 지속: zustand persist 미들웨어. localStorage 키 'ghivideo.settings'.
|
||||
*/
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
/** 필터에서 관리하는 시설종별 집합. 이 외(예 '인상' 등)는 항상 표시. */
|
||||
export const KNOWN_GRADES = ['1종', '2종', '3종', '기타'] as const;
|
||||
|
||||
interface SettingsStore {
|
||||
/** 시설종별별 표시 여부. 기본 1종☑ 2종☑ 3종☐. */
|
||||
gradeFilter: Record<string, boolean>;
|
||||
/** 특정 시설종별 체크 토글. */
|
||||
setGradeFilter: (grade: string, checked: boolean) => void;
|
||||
/** 화면 POI 겹침제외(겹치면 1개만). true=겹친 것 숨김(기본), false=모든 POI 표시. */
|
||||
poiOverlapExclude: boolean;
|
||||
setPoiOverlapExclude: (v: boolean) => void;
|
||||
}
|
||||
|
||||
const DEFAULT_GRADE_FILTER: Record<string, boolean> = {
|
||||
'1종': true,
|
||||
'2종': true,
|
||||
'3종': false,
|
||||
'기타': true,
|
||||
};
|
||||
|
||||
export const useSettingsStore = create<SettingsStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
gradeFilter: { ...DEFAULT_GRADE_FILTER },
|
||||
setGradeFilter: (grade, checked) =>
|
||||
set((s) => ({ gradeFilter: { ...s.gradeFilter, [grade]: checked } })),
|
||||
poiOverlapExclude: true,
|
||||
setPoiOverlapExclude: (v) => set({ poiOverlapExclude: v }),
|
||||
}),
|
||||
{
|
||||
name: 'ghivideo.settings',
|
||||
// 과거 저장본에 누락된 종별 키가 있으면 기본값으로 보충(스키마 진화 대비).
|
||||
merge: (persisted, current) => {
|
||||
const p = (persisted ?? {}) as Partial<SettingsStore>;
|
||||
return {
|
||||
...current,
|
||||
...p,
|
||||
gradeFilter: { ...DEFAULT_GRADE_FILTER, ...(p.gradeFilter ?? {}) },
|
||||
};
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* 구조물 표시 규칙(공유). 단일 소스로 StationBar/RoutePanel 이 동일 규칙을 쓴다.
|
||||
* - grade 없음(구교 등) 또는 미관리 등급('기타' 등) → 항상 표시
|
||||
* - 1/2/3종 → gradeFilter 가 true 일 때만 표시
|
||||
*/
|
||||
export function isGradeVisible(
|
||||
grade: string | undefined,
|
||||
gradeFilter: Record<string, boolean>,
|
||||
): boolean {
|
||||
if (!grade) return true;
|
||||
if (!(KNOWN_GRADES as readonly string[]).includes(grade)) return true;
|
||||
return gradeFilter[grade] === true;
|
||||
}
|
||||
Reference in New Issue
Block a user