import type { ReactNode } from "react"; import { Component } from "react"; import { TriggerObject } from "@/components/three/interaction/TriggerObject"; import { RepairCaseModel } from "@/components/three/gameplay/RepairCaseModel"; import { REPAIR_CASE_MODEL_PATH, REPAIR_CASE_OPEN_SOUND_PATH, } from "@/data/gameplay/repairCaseConfig"; import { AudioManager } from "@/managers/AudioManager"; import type { Vector3Tuple } from "@/types/three/three"; import { logModelLoadError } from "@/utils/three/modelLoadLogger"; const REPAIR_CASE_PAN_RANGE = 20; interface RepairCaseErrorBoundaryProps { children: ReactNode; } interface RepairCaseErrorBoundaryState { hasError: boolean; } class RepairCaseErrorBoundary extends Component< RepairCaseErrorBoundaryProps, RepairCaseErrorBoundaryState > { constructor(props: RepairCaseErrorBoundaryProps) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(): RepairCaseErrorBoundaryState { return { hasError: true }; } componentDidCatch(error: Error): void { logModelLoadError( { modelPath: REPAIR_CASE_MODEL_PATH, scope: "RepairCaseObject", position: [0, -0.45, 0], scale: 1.5, }, error, ); } render(): ReactNode { if (this.state.hasError) { return ; } return this.props.children; } } interface RepairCaseObjectProps { position: Vector3Tuple; open: boolean; onInspect: () => void; } export function RepairCaseObject({ position, open, onInspect, }: RepairCaseObjectProps): React.JSX.Element { const pan = Math.max(-1, Math.min(1, position[0] / REPAIR_CASE_PAN_RANGE)); return ( { if (open) return; AudioManager.getInstance().playSound(REPAIR_CASE_OPEN_SOUND_PATH, 1, { category: "sfx", pan, }); onInspect(); }} > ); } function RepairCaseFallback(): React.JSX.Element { return ( ); }