diff --git a/src/app/room/[id]/page.tsx b/src/app/room/[id]/page.tsx index e2aac04..86147ef 100644 --- a/src/app/room/[id]/page.tsx +++ b/src/app/room/[id]/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState, useCallback } from "react"; -import { useParams } from "next/navigation"; +import { useParams, useRouter } from "next/navigation"; import TopNav from "@/components/TopNav"; import SwipeDeck from "@/components/SwipeDeck"; import { useRoomPolling } from "@/hooks/useRoomPolling"; @@ -9,13 +9,15 @@ import { getUserId } from "@/lib/userId"; export default function RoomPage() { const params = useParams<{ id: string }>(); + const router = useRouter(); const roomId = params.id; const [userId, setUserId] = useState(""); const [joined, setJoined] = useState(false); + const [joinFailed, setJoinFailed] = useState(false); const { - userCount, match, matchType, matchLikes, runnerUps, likeCounts, swipeCounts, restaurants, mutate, + userCount, match, matchType, matchLikes, runnerUps, likeCounts, swipeCounts, restaurants, notFound, mutate, } = useRoomPolling(roomId); useEffect(() => { @@ -26,7 +28,10 @@ export default function RoomPage() { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId: id }), - }).then(() => setJoined(true)); + }).then((res) => { + if (res.ok) setJoined(true); + else setJoinFailed(true); + }).catch(() => setJoinFailed(true)); }, [roomId]); const handleReset = useCallback(async () => { @@ -43,6 +48,22 @@ export default function RoomPage() { await mutate(); }, [roomId, mutate]); + if (notFound || joinFailed) { + return ( +
+

🍜

+

房间不存在或已过期

+

房间号可能有误,或房间已超过 24 小时

+ +
+ ); + } + const initialIndex = swipeCounts[userId] ?? 0; const ready = joined && userId && restaurants.length > 0; diff --git a/src/hooks/useRoomPolling.ts b/src/hooks/useRoomPolling.ts index 87f5178..1aea2fd 100644 --- a/src/hooks/useRoomPolling.ts +++ b/src/hooks/useRoomPolling.ts @@ -4,16 +4,27 @@ import useSWR from "swr"; import { useEffect, useRef } from "react"; import { RoomStatus } from "@/types"; -const fetcher = (url: string) => fetch(url).then((r) => r.json()); +async function fetcher(url: string) { + const r = await fetch(url); + if (!r.ok) { + const err = new Error(r.status === 404 ? "NOT_FOUND" : "FETCH_ERROR"); + throw err; + } + return r.json(); +} export function useRoomPolling(roomId: string) { const { data, error, isLoading, mutate } = useSWR( `/api/room/${roomId}`, fetcher, - { revalidateOnFocus: true }, + { + revalidateOnFocus: true, + shouldRetryOnError: (err) => err?.message !== "NOT_FOUND", + }, ); const fallbackRef = useRef | null>(null); + const notFound = error?.message === "NOT_FOUND"; useEffect(() => { const es = new EventSource(`/api/room/${roomId}/events`); @@ -60,6 +71,7 @@ export function useRoomPolling(roomId: string) { likeCounts: data?.likeCounts ?? {}, swipeCounts: data?.swipeCounts ?? {}, restaurants: data?.restaurants ?? [], + notFound, isLoading, error, mutate,