diff --git a/src/components/LinearProgressBar.tsx b/src/components/LinearProgressBar.tsx new file mode 100644 index 0000000..002aa8a --- /dev/null +++ b/src/components/LinearProgressBar.tsx @@ -0,0 +1,39 @@ +import { + Grid, + LinearProgress, + LinearProgressProps, + Typography, +} from "@mui/material"; + +const LinearProgressBar = ( + props: LinearProgressProps & { value: number; label: string } +) => { + if (props.value === 0) return null; + + return ( + + + {props.label} + + + + + + + {`${Math.round( + props.value + )}%`} + + + + ); +}; + +export default LinearProgressBar; diff --git a/src/lib/engine/uciEngine.ts b/src/lib/engine/uciEngine.ts index d7a8425..1c1d6d2 100644 --- a/src/lib/engine/uciEngine.ts +++ b/src/lib/engine/uciEngine.ts @@ -100,8 +100,10 @@ export abstract class UciEngine { uciMoves, depth = 16, multiPv = this.multiPv, + setEvaluationProgress, }: EvaluateGameParams): Promise { this.throwErrorIfNotReady(); + setEvaluationProgress?.(1); await this.setMultiPv(multiPv); this.ready = false; @@ -126,6 +128,9 @@ export abstract class UciEngine { } const result = await this.evaluatePosition(fen, depth); positions.push(result); + setEvaluationProgress?.( + Math.max((fens.indexOf(fen) / fens.length) * 100 - 5, 2) + ); } const positionsWithClassification = getMovesClassification( @@ -136,6 +141,7 @@ export abstract class UciEngine { const accuracy = computeAccuracy(positions); this.ready = true; + setEvaluationProgress?.(99); return { positions: positionsWithClassification, accuracy, diff --git a/src/sections/analysis/reviewPanelHeader/analyzeButton.tsx b/src/sections/analysis/reviewPanelHeader/analyzeButton.tsx index 7e21816..6142d95 100644 --- a/src/sections/analysis/reviewPanelHeader/analyzeButton.tsx +++ b/src/sections/analysis/reviewPanelHeader/analyzeButton.tsx @@ -1,12 +1,12 @@ import { Icon } from "@iconify/react"; -import { useState } from "react"; import { engineDepthAtom, engineMultiPvAtom, + evaluationProgressAtom, gameAtom, gameEvalAtom, } from "../states"; -import { useAtomValue, useSetAtom } from "jotai"; +import { useAtom, useAtomValue } from "jotai"; import { getEvaluateGameParams } from "@/lib/chess"; import { useGameDatabase } from "@/hooks/useGameDatabase"; import { LoadingButton } from "@mui/lab"; @@ -16,36 +16,33 @@ import { logAnalyticsEvent } from "@/lib/firebase"; export default function AnalyzeButton() { const engine = useEngine(EngineName.Stockfish16); - const [evaluationInProgress, setEvaluationInProgress] = useState(false); + const [evaluationProgress, setEvaluationProgress] = useAtom( + evaluationProgressAtom + ); const engineDepth = useAtomValue(engineDepthAtom); const engineMultiPv = useAtomValue(engineMultiPvAtom); const { setGameEval, gameFromUrl } = useGameDatabase(); - const setEval = useSetAtom(gameEvalAtom); + const [gameEval, setEval] = useAtom(gameEvalAtom); const game = useAtomValue(gameAtom); const readyToAnalyse = - engine?.isReady() && game.history().length > 0 && !evaluationInProgress; + engine?.isReady() && game.history().length > 0 && !evaluationProgress; const handleAnalyze = async () => { const params = getEvaluateGameParams(game); - if ( - !engine?.isReady() || - params.fens.length === 0 || - evaluationInProgress - ) { + if (!engine?.isReady() || params.fens.length === 0 || evaluationProgress) { return; } - setEvaluationInProgress(true); - const newGameEval = await engine.evaluateGame({ ...params, depth: engineDepth, multiPv: engineMultiPv, + setEvaluationProgress, }); setEval(newGameEval); - setEvaluationInProgress(false); + setEvaluationProgress(0); if (gameFromUrl) { setGameEval(gameFromUrl.id, newGameEval); @@ -59,26 +56,17 @@ export default function AnalyzeButton() { }); }; + if (evaluationProgress) return null; + return ( - ) - } + startIcon={} onClick={handleAnalyze} disabled={!readyToAnalyse} - loading={evaluationInProgress} - loadingPosition={evaluationInProgress ? "end" : undefined} - endIcon={ - evaluationInProgress && ( - - ) - } > - {evaluationInProgress ? "Analyzing..." : "Analyze"} + {gameEval ? "Analyze again" : "Analyze"} ); } diff --git a/src/sections/analysis/reviewPanelHeader/index.tsx b/src/sections/analysis/reviewPanelHeader/index.tsx index 2af68f5..7ee22b1 100644 --- a/src/sections/analysis/reviewPanelHeader/index.tsx +++ b/src/sections/analysis/reviewPanelHeader/index.tsx @@ -3,8 +3,13 @@ import { Grid, Typography } from "@mui/material"; import GamePanel from "./gamePanel"; import LoadGame from "./loadGame"; import AnalyzeButton from "./analyzeButton"; +import LinearProgressBar from "@/components/LinearProgressBar"; +import { useAtomValue } from "jotai"; +import { evaluationProgressAtom } from "../states"; export default function ReviewPanelHeader() { + const evaluationProgress = useAtomValue(evaluationProgressAtom); + return ( + ); diff --git a/src/sections/analysis/reviewPanelHeader/loadGame.tsx b/src/sections/analysis/reviewPanelHeader/loadGame.tsx index fa10c12..d3aa529 100644 --- a/src/sections/analysis/reviewPanelHeader/loadGame.tsx +++ b/src/sections/analysis/reviewPanelHeader/loadGame.tsx @@ -4,6 +4,7 @@ import { useChessActions } from "@/hooks/useChessActions"; import { boardAtom, boardOrientationAtom, + evaluationProgressAtom, gameAtom, gameEvalAtom, } from "../states"; @@ -20,6 +21,7 @@ export default function LoadGame() { const { gameFromUrl } = useGameDatabase(); const setEval = useSetAtom(gameEvalAtom); const setBoardOrientation = useSetAtom(boardOrientationAtom); + const evaluationProgress = useAtomValue(evaluationProgressAtom); const resetAndSetGamePgn = useCallback( (pgn: string) => { @@ -48,6 +50,8 @@ export default function LoadGame() { const isGameLoaded = gameFromUrl !== undefined || !!game.header().White; + if (evaluationProgress) return null; + return ( void; }