refactor(page): extract all effects into useGameEffects hook

This commit is contained in:
jessy-david-dev
2026-04-10 16:45:57 +02:00
parent 4f6e793c74
commit 8f62b7edbf
2 changed files with 175 additions and 167 deletions
+65 -167
View File
@@ -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,171 +25,71 @@ 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 if (screen === "home") return (
useEffect(() => { <>
const saved = loadSession(); <HomeScreen
if (!saved) return; playerName={playerName} setPlayerName={setPlayerName}
joinCode={joinCode} setJoinCode={setJoinCode}
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") {
return (
<>
<HomeScreen
playerName={playerName} setPlayerName={setPlayerName}
joinCode={joinCode} setJoinCode={setJoinCode}
error={error} setError={setError} loading={loading}
onCreateRoom={handlers.handleCreateRoom}
onJoinRoom={handlers.handleJoinRoom}
onSolo={handlers.handleSolo}
session={session}
onShowAuth={() => setShowAuth(true)}
onShowProfile={() => setScreen("profile")}
/>
{showAuth && (
<AuthModal
onClose={() => setShowAuth(false)}
onSuccess={() => setShowAuth(false)}
/>
)}
</>
);
}
if (screen === "solo") {
return (
<SoloScreen
phase={solo.phase} puzzle={solo.puzzle}
html={solo.html} title={solo.title}
loading={solo.loading} loadError={solo.loadError}
history={solo.history} clicks={solo.clicks}
elapsedDisplay={fmt(solo.elapsed)}
canGoBack={solo.canGoBack}
onStart={solo.start}
onNavigate={solo.navigate}
onBack={solo.goBack}
onQuit={() => { solo.reset(); setScreen("home"); }}
onNewGame={solo.start}
onRetry={solo.retryLoad}
/>
);
}
if (screen === "lobby" && multi.room && multi.playerId) {
return (
<LobbyScreen
room={multi.room} playerId={multi.playerId}
error={error} setError={setError} loading={loading} error={error} setError={setError} loading={loading}
onLeave={handlers.handleLeave} onCreateRoom={handlers.handleCreateRoom}
onStart={handlers.handleStartGame} onJoinRoom={handlers.handleJoinRoom}
onReset={handlers.handleResetGame} onSolo={handlers.handleSolo}
session={session}
onShowAuth={() => setShowAuth(true)}
onShowProfile={() => setScreen("profile")}
/> />
); {showAuth && <AuthModal onClose={() => setShowAuth(false)} onSuccess={() => setShowAuth(false)} />}
} </>
);
if (screen === "game" && multi.room && multi.playerId) { if (screen === "solo") return (
return ( <SoloScreen
<GameScreen phase={solo.phase} puzzle={solo.puzzle}
room={multi.room} playerId={multi.playerId} html={solo.html} title={solo.title}
html={multi.html} title={multi.title} loading={solo.loading} loadError={solo.loadError}
loading={multi.loading} loadError={multi.loadError} history={solo.history} clicks={solo.clicks}
history={multi.history} clicks={multi.clicks} elapsedDisplay={fmt(solo.elapsed)}
elapsed={fmt(multi.elapsed)} canGoBack={solo.canGoBack}
countdown={multi.countdown} onStart={solo.start}
onNavigate={multi.navigate} onNavigate={solo.navigate}
onRetry={multi.retryLoad} onBack={solo.goBack}
onNextRound={handlers.handleNextRound} onQuit={() => { solo.reset(); setScreen("home"); }}
onResetGame={handlers.handleResetGame} onNewGame={solo.start}
/> onRetry={solo.retryLoad}
); />
} );
if (screen === "lobby" && multi.room && multi.playerId) return (
<LobbyScreen
room={multi.room} playerId={multi.playerId}
error={error} setError={setError} loading={loading}
onLeave={handlers.handleLeave}
onStart={handlers.handleStartGame}
onReset={handlers.handleResetGame}
/>
);
if (screen === "game" && multi.room && multi.playerId) return (
<GameScreen
room={multi.room} playerId={multi.playerId}
html={multi.html} title={multi.title}
loading={multi.loading} loadError={multi.loadError}
history={multi.history} clicks={multi.clicks}
elapsed={fmt(multi.elapsed)}
countdown={multi.countdown}
onNavigate={multi.navigate}
onRetry={multi.retryLoad}
onNextRound={handlers.handleNextRound}
onResetGame={handlers.handleResetGame}
/>
);
return null; return null;
} }
+110
View File
@@ -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 };
}