Files
jrshikoku/lib/useStationLockActivity.ts

105 lines
3.3 KiB
TypeScript

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<void>;
/** Live Activity の状態を更新する */
update: (state: StationLockState) => Promise<void>;
/** Live Activity を終了する */
end: () => Promise<void>;
};
/**
* 駅ロック Live Activity を管理するフック。
* アンマウント時に自動で Live Activity を終了する。
*/
export function useStationLockActivity(): UseStationLockActivityResult {
const [available] = useState(() => isAvailable());
const [status, setStatus] = useState<Status>("idle");
const [error, setError] = useState<string | null>(null);
const [activityId, setActivityId] = useState<string | null>(() => {
// アプリ再起動後など、既存のアクティビティを復元する
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 };
}