fix: 房间不存在时展示错误页面,替代无限加载转圈
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
import TopNav from "@/components/TopNav";
|
import TopNav from "@/components/TopNav";
|
||||||
import SwipeDeck from "@/components/SwipeDeck";
|
import SwipeDeck from "@/components/SwipeDeck";
|
||||||
import { useRoomPolling } from "@/hooks/useRoomPolling";
|
import { useRoomPolling } from "@/hooks/useRoomPolling";
|
||||||
@@ -9,13 +9,15 @@ import { getUserId } from "@/lib/userId";
|
|||||||
|
|
||||||
export default function RoomPage() {
|
export default function RoomPage() {
|
||||||
const params = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
|
const router = useRouter();
|
||||||
const roomId = params.id;
|
const roomId = params.id;
|
||||||
|
|
||||||
const [userId, setUserId] = useState("");
|
const [userId, setUserId] = useState("");
|
||||||
const [joined, setJoined] = useState(false);
|
const [joined, setJoined] = useState(false);
|
||||||
|
const [joinFailed, setJoinFailed] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
userCount, match, matchType, matchLikes, runnerUps, likeCounts, swipeCounts, restaurants, mutate,
|
userCount, match, matchType, matchLikes, runnerUps, likeCounts, swipeCounts, restaurants, notFound, mutate,
|
||||||
} = useRoomPolling(roomId);
|
} = useRoomPolling(roomId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -26,7 +28,10 @@ export default function RoomPage() {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ userId: id }),
|
body: JSON.stringify({ userId: id }),
|
||||||
}).then(() => setJoined(true));
|
}).then((res) => {
|
||||||
|
if (res.ok) setJoined(true);
|
||||||
|
else setJoinFailed(true);
|
||||||
|
}).catch(() => setJoinFailed(true));
|
||||||
}, [roomId]);
|
}, [roomId]);
|
||||||
|
|
||||||
const handleReset = useCallback(async () => {
|
const handleReset = useCallback(async () => {
|
||||||
@@ -43,6 +48,22 @@ export default function RoomPage() {
|
|||||||
await mutate();
|
await mutate();
|
||||||
}, [roomId, mutate]);
|
}, [roomId, mutate]);
|
||||||
|
|
||||||
|
if (notFound || joinFailed) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-dvh flex-col items-center justify-center gap-4 bg-background px-6">
|
||||||
|
<p className="text-4xl">🍜</p>
|
||||||
|
<p className="text-base font-semibold text-zinc-700">房间不存在或已过期</p>
|
||||||
|
<p className="text-sm text-zinc-400">房间号可能有误,或房间已超过 24 小时</p>
|
||||||
|
<button
|
||||||
|
onClick={() => router.push("/")}
|
||||||
|
className="mt-2 h-10 rounded-xl bg-emerald-500 px-6 text-sm font-bold text-white shadow-sm transition-colors hover:bg-emerald-600"
|
||||||
|
>
|
||||||
|
返回首页
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const initialIndex = swipeCounts[userId] ?? 0;
|
const initialIndex = swipeCounts[userId] ?? 0;
|
||||||
const ready = joined && userId && restaurants.length > 0;
|
const ready = joined && userId && restaurants.length > 0;
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,27 @@ import useSWR from "swr";
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { RoomStatus } from "@/types";
|
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) {
|
export function useRoomPolling(roomId: string) {
|
||||||
const { data, error, isLoading, mutate } = useSWR<RoomStatus>(
|
const { data, error, isLoading, mutate } = useSWR<RoomStatus>(
|
||||||
`/api/room/${roomId}`,
|
`/api/room/${roomId}`,
|
||||||
fetcher,
|
fetcher,
|
||||||
{ revalidateOnFocus: true },
|
{
|
||||||
|
revalidateOnFocus: true,
|
||||||
|
shouldRetryOnError: (err) => err?.message !== "NOT_FOUND",
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const fallbackRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
const fallbackRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
const notFound = error?.message === "NOT_FOUND";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const es = new EventSource(`/api/room/${roomId}/events`);
|
const es = new EventSource(`/api/room/${roomId}/events`);
|
||||||
@@ -60,6 +71,7 @@ export function useRoomPolling(roomId: string) {
|
|||||||
likeCounts: data?.likeCounts ?? {},
|
likeCounts: data?.likeCounts ?? {},
|
||||||
swipeCounts: data?.swipeCounts ?? {},
|
swipeCounts: data?.swipeCounts ?? {},
|
||||||
restaurants: data?.restaurants ?? [],
|
restaurants: data?.restaurants ?? [],
|
||||||
|
notFound,
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
mutate,
|
mutate,
|
||||||
|
|||||||
Reference in New Issue
Block a user