refactor(page): extract all effects into useGameEffects hook
This commit is contained in:
+14
-116
@@ -1,13 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState } from "react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSoloGame } from "../lib/useSoloGame";
|
||||||
import type { Screen } from "../lib/types";
|
|
||||||
import { useSoloGame, useSoloKeyboard } from "../lib/useSoloGame";
|
|
||||||
import { useMultiGame } from "../lib/useMultiGame";
|
import { useMultiGame } from "../lib/useMultiGame";
|
||||||
import { loadSession, clearSession } from "../lib/session";
|
import { useGameEffects } from "../lib/useGameEffects";
|
||||||
import { fmt, saveGame } from "../lib/utils";
|
|
||||||
import { useGameHandlers } from "../lib/useGameHandlers";
|
import { useGameHandlers } from "../lib/useGameHandlers";
|
||||||
|
import { fmt } from "../lib/utils";
|
||||||
|
import type { Screen } from "../lib/types";
|
||||||
import { HomeScreen } from "./components/HomeScreen";
|
import { HomeScreen } from "./components/HomeScreen";
|
||||||
import { LobbyScreen } from "./components/LobbyScreen";
|
import { LobbyScreen } from "./components/LobbyScreen";
|
||||||
import { SoloScreen } from "./components/SoloScreen";
|
import { SoloScreen } from "./components/SoloScreen";
|
||||||
@@ -16,7 +15,6 @@ import { AuthModal } from "./components/AuthModal";
|
|||||||
import { ProfileScreen } from "./components/ProfileScreen";
|
import { ProfileScreen } from "./components/ProfileScreen";
|
||||||
|
|
||||||
export default function WikiRush() {
|
export default function WikiRush() {
|
||||||
const { data: session } = useSession();
|
|
||||||
const [screen, setScreen] = useState<Screen>("home");
|
const [screen, setScreen] = useState<Screen>("home");
|
||||||
const [playerName, setPlayerName] = useState("");
|
const [playerName, setPlayerName] = useState("");
|
||||||
const [joinCode, setJoinCode] = useState("");
|
const [joinCode, setJoinCode] = useState("");
|
||||||
@@ -27,102 +25,14 @@ export default function WikiRush() {
|
|||||||
const solo = useSoloGame();
|
const solo = useSoloGame();
|
||||||
const multi = useMultiGame();
|
const multi = useMultiGame();
|
||||||
|
|
||||||
const handlers = useGameHandlers({
|
const { session } = useGameEffects({ solo, multi, screen, setScreen, setPlayerName });
|
||||||
solo, multi, playerName, joinCode, setScreen, setError, setLoading,
|
const handlers = useGameHandlers({ solo, multi, playerName, joinCode, setScreen, setError, setLoading });
|
||||||
});
|
|
||||||
|
|
||||||
// Pré-remplir le pseudo avec le nom du compte connecté
|
if (screen === "profile") return (
|
||||||
useEffect(() => {
|
<ProfileScreen userName={session?.user?.name ?? "Joueur"} onBack={() => setScreen("home")} />
|
||||||
if (session?.user?.name && !playerName) setPlayerName(session.user.name);
|
|
||||||
}, [session]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
|
|
||||||
// Restaurer la session après F5
|
|
||||||
useEffect(() => {
|
|
||||||
const saved = loadSession();
|
|
||||||
if (!saved) return;
|
|
||||||
|
|
||||||
if (saved.screen === "solo" && saved.soloPuzzle && saved.soloHistory?.length) {
|
|
||||||
setScreen("solo");
|
|
||||||
solo.restore(saved.soloPuzzle, saved.soloHistory, saved.soloClicks ?? 0)
|
|
||||||
.then((ok) => { if (!ok) { clearSession(); setScreen("home"); } });
|
|
||||||
} else if (
|
|
||||||
(saved.screen === "lobby" || saved.screen === "game") &&
|
|
||||||
saved.multiRoomCode && saved.multiPlayerId
|
|
||||||
) {
|
|
||||||
if (saved.playerName) setPlayerName(saved.playerName);
|
|
||||||
multi.restore(saved.multiRoomCode, saved.multiPlayerId).then((ok) => {
|
|
||||||
if (ok) setScreen("lobby");
|
|
||||||
else { clearSession(); setScreen("home"); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Bloquer le bouton retour navigateur
|
|
||||||
useEffect(() => {
|
|
||||||
const onPop = () => history.pushState(null, "", window.location.href);
|
|
||||||
history.pushState(null, "", window.location.href);
|
|
||||||
window.addEventListener("popstate", onPop);
|
|
||||||
return () => window.removeEventListener("popstate", onPop);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Backspace = retour arrière en solo
|
|
||||||
useSoloKeyboard(screen === "solo" && solo.phase === "playing", solo.goBack);
|
|
||||||
|
|
||||||
// Sync écran quand la room change de phase
|
|
||||||
useEffect(() => {
|
|
||||||
if (!multi.room) return;
|
|
||||||
const { phase } = multi.room;
|
|
||||||
queueMicrotask(() => {
|
|
||||||
if (phase === "countdown" || phase === "playing") setScreen("game");
|
|
||||||
});
|
|
||||||
}, [multi.room]);
|
|
||||||
|
|
||||||
// Sauvegarder partie solo terminée
|
|
||||||
useEffect(() => {
|
|
||||||
if (solo.phase !== "won" || !solo.puzzle) return;
|
|
||||||
saveGame({
|
|
||||||
mode: "solo",
|
|
||||||
startArticle: solo.puzzle.start,
|
|
||||||
targetArticle: solo.puzzle.target,
|
|
||||||
path: solo.history,
|
|
||||||
clicks: solo.clicks,
|
|
||||||
timeSeconds: solo.elapsed,
|
|
||||||
won: true,
|
|
||||||
});
|
|
||||||
}, [solo.phase]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
|
|
||||||
// Sauvegarder partie multi quand les résultats arrivent
|
|
||||||
useEffect(() => {
|
|
||||||
const room = multi.room;
|
|
||||||
const pid = multi.playerId;
|
|
||||||
if (!room || !pid || room.phase !== "results") return;
|
|
||||||
const me = room.players.find((p) => p.id === pid);
|
|
||||||
if (!me) return;
|
|
||||||
saveGame({
|
|
||||||
mode: "multi",
|
|
||||||
startArticle: room.startArticle,
|
|
||||||
targetArticle: room.targetArticle,
|
|
||||||
path: multi.history,
|
|
||||||
clicks: multi.clicks,
|
|
||||||
timeSeconds: multi.elapsed,
|
|
||||||
won: room.roundWinner === pid,
|
|
||||||
});
|
|
||||||
}, [multi.room?.phase]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
||||||
|
|
||||||
// ---- Rendu ----
|
|
||||||
|
|
||||||
if (screen === "profile") {
|
|
||||||
return (
|
|
||||||
<ProfileScreen
|
|
||||||
userName={session?.user?.name ?? "Joueur"}
|
|
||||||
onBack={() => setScreen("home")}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (screen === "home") {
|
if (screen === "home") return (
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
<HomeScreen
|
<HomeScreen
|
||||||
playerName={playerName} setPlayerName={setPlayerName}
|
playerName={playerName} setPlayerName={setPlayerName}
|
||||||
@@ -135,18 +45,11 @@ export default function WikiRush() {
|
|||||||
onShowAuth={() => setShowAuth(true)}
|
onShowAuth={() => setShowAuth(true)}
|
||||||
onShowProfile={() => setScreen("profile")}
|
onShowProfile={() => setScreen("profile")}
|
||||||
/>
|
/>
|
||||||
{showAuth && (
|
{showAuth && <AuthModal onClose={() => setShowAuth(false)} onSuccess={() => setShowAuth(false)} />}
|
||||||
<AuthModal
|
|
||||||
onClose={() => setShowAuth(false)}
|
|
||||||
onSuccess={() => setShowAuth(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (screen === "solo") {
|
if (screen === "solo") return (
|
||||||
return (
|
|
||||||
<SoloScreen
|
<SoloScreen
|
||||||
phase={solo.phase} puzzle={solo.puzzle}
|
phase={solo.phase} puzzle={solo.puzzle}
|
||||||
html={solo.html} title={solo.title}
|
html={solo.html} title={solo.title}
|
||||||
@@ -162,10 +65,8 @@ export default function WikiRush() {
|
|||||||
onRetry={solo.retryLoad}
|
onRetry={solo.retryLoad}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (screen === "lobby" && multi.room && multi.playerId) {
|
if (screen === "lobby" && multi.room && multi.playerId) return (
|
||||||
return (
|
|
||||||
<LobbyScreen
|
<LobbyScreen
|
||||||
room={multi.room} playerId={multi.playerId}
|
room={multi.room} playerId={multi.playerId}
|
||||||
error={error} setError={setError} loading={loading}
|
error={error} setError={setError} loading={loading}
|
||||||
@@ -174,10 +75,8 @@ export default function WikiRush() {
|
|||||||
onReset={handlers.handleResetGame}
|
onReset={handlers.handleResetGame}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (screen === "game" && multi.room && multi.playerId) {
|
if (screen === "game" && multi.room && multi.playerId) return (
|
||||||
return (
|
|
||||||
<GameScreen
|
<GameScreen
|
||||||
room={multi.room} playerId={multi.playerId}
|
room={multi.room} playerId={multi.playerId}
|
||||||
html={multi.html} title={multi.title}
|
html={multi.html} title={multi.title}
|
||||||
@@ -191,7 +90,6 @@ export default function WikiRush() {
|
|||||||
onResetGame={handlers.handleResetGame}
|
onResetGame={handlers.handleResetGame}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Dispatch, SetStateAction, useEffect } from "react";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
import type { Screen } from "./types";
|
||||||
|
import type { useSoloGame } from "./useSoloGame";
|
||||||
|
import type { useMultiGame } from "./useMultiGame";
|
||||||
|
import { loadSession, clearSession } from "./session";
|
||||||
|
import { saveGame } from "./utils";
|
||||||
|
import { useSoloKeyboard } from "./useSoloGame";
|
||||||
|
|
||||||
|
type UseSoloGame = ReturnType<typeof useSoloGame>;
|
||||||
|
type UseMultiGame = ReturnType<typeof useMultiGame>;
|
||||||
|
|
||||||
|
export function useGameEffects({
|
||||||
|
solo,
|
||||||
|
multi,
|
||||||
|
screen,
|
||||||
|
setScreen,
|
||||||
|
setPlayerName,
|
||||||
|
}: {
|
||||||
|
solo: UseSoloGame;
|
||||||
|
multi: UseMultiGame;
|
||||||
|
screen: Screen;
|
||||||
|
setScreen: Dispatch<SetStateAction<Screen>>;
|
||||||
|
setPlayerName: Dispatch<SetStateAction<string>>;
|
||||||
|
}) {
|
||||||
|
const { data: session } = useSession();
|
||||||
|
|
||||||
|
// Pré-remplir le pseudo avec le nom du compte connecté
|
||||||
|
useEffect(() => {
|
||||||
|
if (session?.user?.name) setPlayerName(session.user.name);
|
||||||
|
}, [session]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
// Restaurer la session après F5
|
||||||
|
useEffect(() => {
|
||||||
|
const saved = loadSession();
|
||||||
|
if (!saved) return;
|
||||||
|
|
||||||
|
if (saved.screen === "solo" && saved.soloPuzzle && saved.soloHistory?.length) {
|
||||||
|
setScreen("solo");
|
||||||
|
solo.restore(saved.soloPuzzle, saved.soloHistory, saved.soloClicks ?? 0)
|
||||||
|
.then((ok) => { if (!ok) { clearSession(); setScreen("home"); } });
|
||||||
|
} else if (
|
||||||
|
(saved.screen === "lobby" || saved.screen === "game") &&
|
||||||
|
saved.multiRoomCode && saved.multiPlayerId
|
||||||
|
) {
|
||||||
|
if (saved.playerName) setPlayerName(saved.playerName);
|
||||||
|
multi.restore(saved.multiRoomCode, saved.multiPlayerId).then((ok) => {
|
||||||
|
if (ok) setScreen("lobby");
|
||||||
|
else { clearSession(); setScreen("home"); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Bloquer le bouton retour navigateur
|
||||||
|
useEffect(() => {
|
||||||
|
const onPop = () => history.pushState(null, "", window.location.href);
|
||||||
|
history.pushState(null, "", window.location.href);
|
||||||
|
window.addEventListener("popstate", onPop);
|
||||||
|
return () => window.removeEventListener("popstate", onPop);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Backspace = retour arrière en solo
|
||||||
|
useSoloKeyboard(screen === "solo" && solo.phase === "playing", solo.goBack);
|
||||||
|
|
||||||
|
// Sync écran quand la room change de phase
|
||||||
|
useEffect(() => {
|
||||||
|
if (!multi.room) return;
|
||||||
|
const { phase } = multi.room;
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (phase === "countdown" || phase === "playing") setScreen("game");
|
||||||
|
});
|
||||||
|
}, [multi.room]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
// Sauvegarder partie solo terminée
|
||||||
|
useEffect(() => {
|
||||||
|
if (solo.phase !== "won" || !solo.puzzle) return;
|
||||||
|
saveGame({
|
||||||
|
mode: "solo",
|
||||||
|
startArticle: solo.puzzle.start,
|
||||||
|
targetArticle: solo.puzzle.target,
|
||||||
|
path: solo.history,
|
||||||
|
clicks: solo.clicks,
|
||||||
|
timeSeconds: solo.elapsed,
|
||||||
|
won: true,
|
||||||
|
});
|
||||||
|
}, [solo.phase]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
// Sauvegarder partie multi quand les résultats arrivent
|
||||||
|
useEffect(() => {
|
||||||
|
const room = multi.room;
|
||||||
|
const pid = multi.playerId;
|
||||||
|
if (!room || !pid || room.phase !== "results") return;
|
||||||
|
const me = room.players.find((p) => p.id === pid);
|
||||||
|
if (!me) return;
|
||||||
|
saveGame({
|
||||||
|
mode: "multi",
|
||||||
|
startArticle: room.startArticle,
|
||||||
|
targetArticle: room.targetArticle,
|
||||||
|
path: multi.history,
|
||||||
|
clicks: multi.clicks,
|
||||||
|
timeSeconds: multi.elapsed,
|
||||||
|
won: room.roundWinner === pid,
|
||||||
|
});
|
||||||
|
}, [multi.room?.phase]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
return { session };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user