diff --git a/app/page.tsx b/app/page.tsx index 15e63ed..7c8047e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,13 +1,12 @@ "use client"; -import { useState, useEffect } from "react"; -import { useSession } from "next-auth/react"; -import type { Screen } from "../lib/types"; -import { useSoloGame, useSoloKeyboard } from "../lib/useSoloGame"; +import { useState } from "react"; +import { useSoloGame } from "../lib/useSoloGame"; import { useMultiGame } from "../lib/useMultiGame"; -import { loadSession, clearSession } from "../lib/session"; -import { fmt, saveGame } from "../lib/utils"; +import { useGameEffects } from "../lib/useGameEffects"; import { useGameHandlers } from "../lib/useGameHandlers"; +import { fmt } from "../lib/utils"; +import type { Screen } from "../lib/types"; import { HomeScreen } from "./components/HomeScreen"; import { LobbyScreen } from "./components/LobbyScreen"; import { SoloScreen } from "./components/SoloScreen"; @@ -16,7 +15,6 @@ import { AuthModal } from "./components/AuthModal"; import { ProfileScreen } from "./components/ProfileScreen"; export default function WikiRush() { - const { data: session } = useSession(); const [screen, setScreen] = useState("home"); const [playerName, setPlayerName] = useState(""); const [joinCode, setJoinCode] = useState(""); @@ -27,171 +25,71 @@ export default function WikiRush() { const solo = useSoloGame(); const multi = useMultiGame(); - const handlers = useGameHandlers({ - solo, multi, playerName, joinCode, setScreen, setError, setLoading, - }); + const { session } = useGameEffects({ solo, multi, screen, setScreen, setPlayerName }); + const handlers = useGameHandlers({ solo, multi, playerName, joinCode, setScreen, setError, setLoading }); - // Pré-remplir le pseudo avec le nom du compte connecté - useEffect(() => { - if (session?.user?.name && !playerName) setPlayerName(session.user.name); - }, [session]); // eslint-disable-line react-hooks/exhaustive-deps + if (screen === "profile") return ( + setScreen("home")} /> + ); - // 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 ( - setScreen("home")} - /> - ); - } - - if (screen === "home") { - return ( - <> - setShowAuth(true)} - onShowProfile={() => setScreen("profile")} - /> - {showAuth && ( - setShowAuth(false)} - onSuccess={() => setShowAuth(false)} - /> - )} - - ); - } - - if (screen === "solo") { - return ( - { solo.reset(); setScreen("home"); }} - onNewGame={solo.start} - onRetry={solo.retryLoad} - /> - ); - } - - if (screen === "lobby" && multi.room && multi.playerId) { - return ( - + setShowAuth(true)} + onShowProfile={() => setScreen("profile")} /> - ); - } + {showAuth && setShowAuth(false)} onSuccess={() => setShowAuth(false)} />} + + ); - if (screen === "game" && multi.room && multi.playerId) { - return ( - - ); - } + if (screen === "solo") return ( + { solo.reset(); setScreen("home"); }} + onNewGame={solo.start} + onRetry={solo.retryLoad} + /> + ); + + if (screen === "lobby" && multi.room && multi.playerId) return ( + + ); + + if (screen === "game" && multi.room && multi.playerId) return ( + + ); return null; } diff --git a/lib/useGameEffects.ts b/lib/useGameEffects.ts new file mode 100644 index 0000000..aaaf380 --- /dev/null +++ b/lib/useGameEffects.ts @@ -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; +type UseMultiGame = ReturnType; + +export function useGameEffects({ + solo, + multi, + screen, + setScreen, + setPlayerName, +}: { + solo: UseSoloGame; + multi: UseMultiGame; + screen: Screen; + setScreen: Dispatch>; + setPlayerName: Dispatch>; +}) { + 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 }; +}