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;
}