Skip to content

Commit

Permalink
refactor database options
Browse files Browse the repository at this point in the history
  • Loading branch information
franciscoBSalgueiro committed Oct 20, 2023
1 parent 8a3a7ad commit 175ea70
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 149 deletions.
6 changes: 3 additions & 3 deletions src-tauri/src/db/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ impl<'de> Deserialize<'de> for PositionQuery {
serde_json::Value::Object(map) => {
let type_ = map
.get("type")
.ok_or(serde::de::Error::custom("Missing key"))?;
.ok_or(serde::de::Error::custom("Missing type"))?;
let value = map
.get("value")
.ok_or(serde::de::Error::custom("Missing key"))?;
.get("fen")
.ok_or(serde::de::Error::custom("Missing fen"))?;
match type_.as_str().unwrap() {
"exact" => {
let fen = value.as_str().unwrap();
Expand Down
18 changes: 13 additions & 5 deletions src/atoms/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Tab, genID } from "@/utils/tabs";
import { MantineColor } from "@mantine/core";
import { Session } from "../utils/session";
import { PrimitiveAtom, atom } from "jotai";
import { DatabaseInfo, PositionQuery } from "@/utils/db";
import { DatabaseInfo } from "@/utils/db";
import { MissingMove } from "@/utils/repertoire";
import { Card, buildFromTree } from "@/components/files/opening";
import { GameHeaders, TreeNode } from "@/utils/treeReducer";
Expand All @@ -13,6 +13,7 @@ import { AsyncStringStorage } from "jotai/vanilla/utils/atomWithStorage";
import { BaseDirectory, readTextFile, removeFile, writeTextFile } from "@tauri-apps/api/fs";
import { Engine } from "@/utils/engines";
import { LichessGamesOptions, MasterGamesOptions } from "@/utils/lichess/lichessexplorer";
import { LocalOptions } from "@/components/panels/database/DatabasePanel";


const options = { dir: BaseDirectory.AppData };
Expand Down Expand Up @@ -167,13 +168,23 @@ export const currentInvisibleAtom = tabValue(invisibleFamily);
const tabFamily = atomFamily((tab: string) => atom("info"));
export const currentTabSelectedAtom = tabValue(tabFamily);

const localOptionsFamily = atomFamily((tab: string) => atom<LocalOptions>({
path: null,
type: "exact",
fen: ""
}));
export const currentLocalOptionsAtom = tabValue(localOptionsFamily);

const lichessOptionsFamily = atomFamily((tab: string) => atom<LichessGamesOptions>({
fen: "",
ratings: [1000, 1200, 1400, 1600, 1800, 2000, 2200, 2500],
speeds: ["bullet", "blitz", "rapid", "classical", "correspondence"],
}));
export const currentLichessOptionsAtom = tabValue(lichessOptionsFamily);

const masterOptionsFamily = atomFamily((tab: string) => atom<MasterGamesOptions>({}));
const masterOptionsFamily = atomFamily((tab: string) => atom<MasterGamesOptions>({
fen: ""
}));
export const currentMasterOptionsAtom = tabValue(masterOptionsFamily);

const dbTypeFamily = atomFamily((tab: string) => atom<"local" | "lch_all" | "lch_master">("local"));
Expand All @@ -185,9 +196,6 @@ export const currentDbTabAtom = tabValue(dbTabFamily);
const analysisTabFamily = atomFamily((tab: string) => atom("engines"));
export const currentAnalysisTabAtom = tabValue(analysisTabFamily);

const positionQueryFamily = atomFamily((tab: string) => atom<PositionQuery>({ value: "", type: "exact" }));
export const currentPositionQueryAtom = tabValue(positionQueryFamily);

const pgnOptionsFamily = atomFamily((tab: string) => atom({
comments: true,
annotations: true,
Expand Down
136 changes: 72 additions & 64 deletions src/components/panels/database/DatabasePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Alert, Group, SegmentedControl, Tabs, Text } from "@mantine/core";
import { memo, useEffect } from "react";
import { Opening, PositionQuery, searchPosition } from "@/utils/db";
import { Opening, searchPosition } from "@/utils/db";
import {
currentDbTabAtom,
currentDbTypeAtom,
currentLichessOptionsAtom,
currentLocalOptionsAtom,
currentMasterOptionsAtom,
currentPositionQueryAtom,
currentTabAtom,
referenceDbAtom,
} from "@/atoms/atoms";
Expand All @@ -18,33 +18,42 @@ import {
} from "@/utils/lichess";
import GamesTable from "./GamesTable";
import OpeningsTable from "./OpeningsTable";
import SearchPanel from "./SearchPanel";
import { match } from "ts-pattern";
import useSWR from "swr";
import useSWR from "swr/immutable";
import { useDebouncedValue } from "@mantine/hooks";
import NoDatabaseWarning from "./NoDatabaseWarning";
import { formatNumber } from "@/utils/format";
import DatabaseLoader from "./DatabaseLoader";
import { LichessGamesOptions, MasterGamesOptions } from "@/utils/lichess/lichessexplorer";
import {
LichessGamesOptions,
MasterGamesOptions,
} from "@/utils/lichess/lichessexplorer";
import LichessOptionsPanel from "./options/LichessOptionsPanel";
import MasterOptionsPanel from "./options/MastersOptionsPanel";
import LocalOptionsPanel from "./options/LocalOptionsPanel";

type DBType =
| { type: "local"; db: string | null }
| { type: "lch_all" }
| { type: "lch_master" };
| { type: "local"; options: LocalOptions }
| { type: "lch_all"; options: LichessGamesOptions }
| { type: "lch_master"; options: MasterGamesOptions };

export type LocalOptions = {
path: string | null;
fen: string;
type: "exact" | "partial";
};

function sortOpenings(openings: Opening[]) {
return openings.sort(
(a, b) => b.black + b.draw + b.white - (a.black + a.draw + a.white)
);
}

async function fetchOpening(query: PositionQuery, db: DBType, tab: string, lichessOptions: LichessGamesOptions, masterOptions: MasterGamesOptions) {
if (query.value === "") return { openings: [], games: [] };
async function fetchOpening(db: DBType, tab: string) {
if (db.options.fen === "") return { openings: [], games: [] };
return match(db)
.with({ type: "lch_all" }, async () => {
const data = await getLichessGames(query.value, lichessOptions);
.with({ type: "lch_all" }, async ({ options }) => {
const data = await getLichessGames(options);
return {
openings: data.moves.map((move) => ({
move: move.san,
Expand All @@ -55,8 +64,8 @@ async function fetchOpening(query: PositionQuery, db: DBType, tab: string, liche
games: await convertToNormalized(data.topGames),
};
})
.with({ type: "lch_master" }, async () => {
const data = await getMasterGames(query.value, masterOptions);
.with({ type: "lch_master" }, async ({ options }) => {
const data = await getMasterGames(options);
return {
openings: data.moves.map((move) => ({
move: move.san,
Expand All @@ -67,9 +76,9 @@ async function fetchOpening(query: PositionQuery, db: DBType, tab: string, liche
games: await convertToNormalized(data.topGames),
};
})
.with({ type: "local" }, async ({ db }) => {
if (!db) throw Error("Missing reference database");
const positionData = await searchPosition(db, query, tab);
.with({ type: "local" }, async ({ options }) => {
if (!options.path) throw Error("Missing reference database");
const positionData = await searchPosition(options, tab);
return {
openings: sortOpenings(positionData[0]),
games: positionData[1],
Expand All @@ -81,35 +90,46 @@ async function fetchOpening(query: PositionQuery, db: DBType, tab: string, liche
function DatabasePanel({ height, fen }: { height: number; fen: string }) {
const referenceDatabase = useAtomValue(referenceDbAtom);
const [debouncedFen] = useDebouncedValue(fen, 50);
const [lichessOptions, setLichessOptions] = useAtom(currentLichessOptionsAtom);
const [lichessOptions, setLichessOptions] = useAtom(
currentLichessOptionsAtom
);
const [masterOptions, setMasterOptions] = useAtom(currentMasterOptionsAtom);
const [debouncedLichessOptions] = useDebouncedValue(lichessOptions, 500);
const [query, setQuery] = useAtom(currentPositionQueryAtom);
const [localOptions, setLocalOptions] = useAtom(currentLocalOptionsAtom);
const [db, setDb] = useAtom(currentDbTypeAtom);

useEffect(() => {
setQuery((q) => (q.type === "exact" && q.value != debouncedFen) ? ({ type: "exact", value: debouncedFen }): q);
}, [debouncedFen, setQuery]);
if (db === "local") {
setLocalOptions((q) => ({ ...q, fen: debouncedFen }));
} else if (db === "lch_all") {
setLichessOptions((q) => ({ ...q, fen: debouncedFen }));
} else if (db === "lch_master") {
setMasterOptions((q) => ({ ...q, fen: debouncedFen }));
}
}, [debouncedFen, setLocalOptions, setMasterOptions, setLichessOptions, db]);

useEffect(() => {
if (db === "local") {
setLocalOptions((q) => ({ ...q, path: referenceDatabase }));
}
}, [referenceDatabase, setLocalOptions, db]);

const dbType: DBType = match(db)
.with("local", (v) => {
return {
type: v,
db: referenceDatabase,
};
})
.otherwise((v) => ({
.with("local", (v) => ({
type: v,
}));
options: localOptions,
}))
.with("lch_all", (v) => ({ type: v, options: lichessOptions }))
.with("lch_master", (v) => ({ type: v, options: masterOptions }))
.exhaustive();

const tab = useAtomValue(currentTabAtom);

const {
data: openingData,
isLoading,
error,
} = useSWR([dbType, query, debouncedLichessOptions, masterOptions], async ([dbType, query, lichessOptions, masterOptions]) => {
return fetchOpening(query, dbType, tab?.value || "", lichessOptions, masterOptions);
} = useSWR([dbType], async ([dbType]) => {
return fetchOpening(dbType, tab?.value || "");
});

const [tabType, setTabType] = useAtom(currentDbTabAtom);
Expand Down Expand Up @@ -150,49 +170,38 @@ function DatabasePanel({ height, fen }: { height: number; fen: string }) {
onTabChange={(v) => setTabType(v!)}
>
<Tabs.List>
<Tabs.Tab value="stats" disabled={query.type === "partial"}>
<Tabs.Tab
value="stats"
disabled={
dbType.type === "local" && dbType.options.type === "partial"
}
>
Stats
</Tabs.Tab>
<Tabs.Tab value="games">Games</Tabs.Tab>
<Tabs.Tab value="options">
Options
</Tabs.Tab>
<Tabs.Tab value="options">Options</Tabs.Tab>
</Tabs.List>

<PanelWithError value="stats" error={error} db={db}>
<PanelWithError value="stats" error={error} type={db}>
<OpeningsTable
openings={openingData?.openings || []}
height={height}
loading={isLoading}
/>
</PanelWithError>
<PanelWithError value="games" error={error} db={db}>
<PanelWithError value="games" error={error} type={db}>
<GamesTable
games={openingData?.games || []}
height={height}
loading={isLoading}
/>
</PanelWithError>
<PanelWithError value="options" error={error} db={db}>
<PanelWithError value="options" error={error} type={db}>
{match(db)
.with("local", () =>
<SearchPanel
boardFen={debouncedFen}
query={query}
setQuery={setQuery}
/>
).with("lch_all", () =>
<LichessOptionsPanel
options={lichessOptions}
setOptions={setLichessOptions}
/>
).with("lch_master", () =>
<MasterOptionsPanel
options={masterOptions}
setOptions={setMasterOptions}
/>
).exhaustive()
}
.with("local", () => <LocalOptionsPanel boardFen={debouncedFen} />)
.with("lch_all", () => <LichessOptionsPanel />)
.with("lch_master", () => <MasterOptionsPanel />)
.exhaustive()}
</PanelWithError>
</Tabs>
</>
Expand All @@ -202,21 +211,20 @@ function DatabasePanel({ height, fen }: { height: number; fen: string }) {
function PanelWithError(props: {
value: string;
error: string;
db: "local" | string;
type: string;
children: React.ReactNode;
}) {
const referenceDatabase = useAtomValue(referenceDbAtom);
let children = props.children;
if (props.db === "local" && !referenceDatabase) {
children = <NoDatabaseWarning />;
if (props.type === "local" && !referenceDatabase) {
props.children = <NoDatabaseWarning />;
}
if (props.error && props.db !== "local") {
children = <Alert color="red">{props.error.toString()}</Alert>;
if (props.error && props.type !== "local") {
props.children = <Alert color="red">{props.error.toString()}</Alert>;
}

return (
<Tabs.Panel pt="xs" mr="xs" value={props.value}>
{children}
{props.children}
</Tabs.Panel>
);
}
Expand Down
Loading

0 comments on commit 175ea70

Please sign in to comment.