import { useCallback, useEffect, useRef, useState } from "react"; import { endStationLockActivity, getActiveStationLockActivities, isAvailable, startStationLockActivity, StationLockParams, StationLockState, updateStationLockActivity, } from "expo-live-activity"; type Status = "idle" | "active" | "loading" | "error"; type UseStationLockActivityResult = { /** Live Activity が利用可能なデバイス・OS かどうか */ available: boolean; /** 現在の状態 */ status: Status; /** エラーメッセージ (status === "error" のとき) */ error: string | null; /** 現在のアクティビティ ID (active のときのみ非 null) */ activityId: string | null; /** 駅ロック Live Activity を開始する */ start: (params: StationLockParams) => Promise; /** Live Activity の状態を更新する */ update: (state: StationLockState) => Promise; /** Live Activity を終了する */ end: () => Promise; }; /** * 駅ロック Live Activity を管理するフック。 * アンマウント時に自動で Live Activity を終了する。 */ export function useStationLockActivity(): UseStationLockActivityResult { const [available] = useState(() => isAvailable()); const [status, setStatus] = useState("idle"); const [error, setError] = useState(null); const [activityId, setActivityId] = useState(() => { // アプリ再起動後など、既存のアクティビティを復元する const ids = getActiveStationLockActivities(); return ids.length > 0 ? ids[0] : null; }); // マウント時に既存アクティビティがあれば active に設定 useEffect(() => { if (activityId) setStatus("active"); }, []); // eslint-disable-line react-hooks/exhaustive-deps // アンマウント時に自動終了 const activityIdRef = useRef(activityId); useEffect(() => { activityIdRef.current = activityId; }, [activityId]); useEffect(() => { return () => { if (activityIdRef.current) { endStationLockActivity(activityIdRef.current).catch(() => {}); } }; }, []); const start = useCallback(async (params: StationLockParams) => { if (!available) return; setStatus("loading"); setError(null); try { const id = await startStationLockActivity(params); setActivityId(id); setStatus("active"); } catch (e: unknown) { const msg = e instanceof Error ? e.message : String(e); setError(msg); setStatus("error"); } }, [available]); const update = useCallback(async (state: StationLockState) => { if (!activityIdRef.current) return; try { await updateStationLockActivity(activityIdRef.current, state); } catch (e: unknown) { const msg = e instanceof Error ? e.message : String(e); setError(msg); setStatus("error"); } }, []); const end = useCallback(async () => { if (!activityIdRef.current) return; try { await endStationLockActivity(activityIdRef.current); setActivityId(null); setStatus("idle"); } catch (e: unknown) { const msg = e instanceof Error ? e.message : String(e); setError(msg); setStatus("error"); } }, []); return { available, status, error, activityId, start, update, end }; }