refactor: 提取 useGeolocation hook 和 joinRoom 工具函数
- useGeolocation: 将 PanicPage 中 ~50 行 GPS 定位逻辑(requestGps + reverseGeocode + 状态管理)提取为独立 hook - joinRoom: 统一 3 处重复的 POST /api/room/:id/join 调用(room、invite、panic 页面)
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
|
||||
export type GpsStatus = "idle" | "locating" | "success" | "failed" | "denied";
|
||||
|
||||
type GpsResult =
|
||||
| { ok: true; lat: number; lng: number }
|
||||
| { ok: false; reason: "unsupported" | "denied" | "timeout" | "unknown" };
|
||||
|
||||
function requestGps(): Promise<GpsResult> {
|
||||
return new Promise((resolve) => {
|
||||
if (!navigator.geolocation) {
|
||||
resolve({ ok: false, reason: "unsupported" });
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos) =>
|
||||
resolve({ ok: true, lat: pos.coords.latitude, lng: pos.coords.longitude }),
|
||||
(err) => {
|
||||
const reason =
|
||||
err.code === err.PERMISSION_DENIED
|
||||
? "denied"
|
||||
: err.code === err.TIMEOUT
|
||||
? "timeout"
|
||||
: "unknown";
|
||||
resolve({ ok: false, reason });
|
||||
},
|
||||
{ timeout: 8000, enableHighAccuracy: false },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function reverseGeocode(lat: number, lng: number): Promise<string | null> {
|
||||
try {
|
||||
const res = await fetch(`/api/location/regeo?lat=${lat}&lng=${lng}`);
|
||||
const data = await res.json();
|
||||
return data.name || data.formatted || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function useGeolocation() {
|
||||
const [status, setStatus] = useState<GpsStatus>("idle");
|
||||
const [coords, setCoords] = useState<{ lat: number; lng: number } | null>(null);
|
||||
const [locationName, setLocationName] = useState<string | null>(null);
|
||||
|
||||
const locate = useCallback(async () => {
|
||||
setStatus("locating");
|
||||
const result = await requestGps();
|
||||
if (result.ok) {
|
||||
setCoords({ lat: result.lat, lng: result.lng });
|
||||
setStatus("success");
|
||||
const name = await reverseGeocode(result.lat, result.lng);
|
||||
if (name) setLocationName(name);
|
||||
} else {
|
||||
setCoords(null);
|
||||
setLocationName(null);
|
||||
setStatus(result.reason === "denied" ? "denied" : "failed");
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
locate();
|
||||
}, [locate]);
|
||||
|
||||
return { status, coords, locationName, retry: locate };
|
||||
}
|
||||
Reference in New Issue
Block a user