feat: 拦截浏览器返回键,退出房间前弹窗确认

This commit is contained in:
2026-02-24 20:21:47 +08:00
parent a6fc523f4f
commit fc0a2a018b
3 changed files with 124 additions and 6 deletions
+46 -2
View File
@@ -1,9 +1,10 @@
"use client";
import { useEffect, useState, useCallback } from "react";
import { useEffect, useState, useCallback, useRef } from "react";
import { useParams, useRouter } from "next/navigation";
import TopNav from "@/components/TopNav";
import SwipeDeck from "@/components/SwipeDeck";
import LeaveConfirmModal from "@/components/LeaveConfirmModal";
import { useRoomPolling } from "@/hooks/useRoomPolling";
import { getUserId } from "@/lib/userId";
@@ -15,6 +16,8 @@ export default function RoomPage() {
const [userId, setUserId] = useState("");
const [joined, setJoined] = useState(false);
const [joinFailed, setJoinFailed] = useState(false);
const [showLeaveConfirm, setShowLeaveConfirm] = useState(false);
const leavingRef = useRef(false);
const {
userCount, match, matchType, matchLikes, runnerUps, likeCounts, swipeCounts, restaurants, notFound, mutate,
@@ -34,6 +37,42 @@ export default function RoomPage() {
}).catch(() => setJoinFailed(true));
}, [roomId]);
useEffect(() => {
window.history.pushState({ roomGuard: true }, "");
const handlePopState = () => {
if (leavingRef.current) return;
window.history.pushState({ roomGuard: true }, "");
setShowLeaveConfirm(true);
};
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
if (leavingRef.current) return;
e.preventDefault();
};
window.addEventListener("popstate", handlePopState);
window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
window.removeEventListener("popstate", handlePopState);
window.removeEventListener("beforeunload", handleBeforeUnload);
};
}, []);
const confirmLeave = useCallback(() => {
leavingRef.current = true;
setShowLeaveConfirm(false);
router.push("/");
}, [router]);
const cancelLeave = useCallback(() => {
setShowLeaveConfirm(false);
}, []);
const handleExitRequest = useCallback(() => {
setShowLeaveConfirm(true);
}, []);
const handleReset = useCallback(async () => {
await fetch(`/api/room/${roomId}/reset`, { method: "POST" });
await mutate();
@@ -78,7 +117,7 @@ export default function RoomPage() {
return (
<div className="flex h-dvh flex-col bg-background">
<TopNav roomId={roomId} userCount={userCount} />
<TopNav roomId={roomId} userCount={userCount} onExit={handleExitRequest} />
<SwipeDeck
restaurants={restaurants}
roomId={roomId}
@@ -94,6 +133,11 @@ export default function RoomPage() {
onReset={handleReset}
onNarrow={handleNarrow}
/>
<LeaveConfirmModal
open={showLeaveConfirm}
onConfirm={confirmLeave}
onCancel={cancelLeave}
/>
</div>
);
}