mirror of
https://github.com/chrislicodes/obsidian-chess-study.git
synced 2026-07-22 07:50:30 +00:00
feat: add save function and persist shapes
This commit is contained in:
parent
b9bf576025
commit
cdd4c088bf
6 changed files with 94 additions and 14 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { App, MarkdownRenderChild } from "obsidian";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom/client";
|
||||
import { ChessifyFileData } from "src/utils";
|
||||
import { ChessifyDataAdapter, ChessifyFileData } from "src/utils";
|
||||
import { Chessify } from "../components/react/Chessify";
|
||||
import { ChessifyPluginSettings } from "./obsidian/SettingsTab";
|
||||
|
||||
|
|
@ -11,19 +11,22 @@ export class ReactView extends MarkdownRenderChild {
|
|||
app: App;
|
||||
settings: ChessifyPluginSettings;
|
||||
data: ChessifyFileData;
|
||||
dataAdapter: ChessifyDataAdapter;
|
||||
|
||||
constructor(
|
||||
containerEL: HTMLElement,
|
||||
source: string,
|
||||
app: App,
|
||||
settings: ChessifyPluginSettings,
|
||||
data: ChessifyFileData
|
||||
data: ChessifyFileData,
|
||||
dataAdapter: ChessifyDataAdapter
|
||||
) {
|
||||
super(containerEL);
|
||||
this.source = source;
|
||||
this.app = app;
|
||||
this.settings = settings;
|
||||
this.data = data;
|
||||
this.dataAdapter = dataAdapter;
|
||||
}
|
||||
|
||||
onload() {
|
||||
|
|
@ -35,6 +38,7 @@ export class ReactView extends MarkdownRenderChild {
|
|||
app={this.app}
|
||||
pluginSettings={this.settings}
|
||||
chessifyData={this.data}
|
||||
dataAdapter={this.dataAdapter}
|
||||
/>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useEffect, useRef } from "react";
|
|||
import { Chess, Move } from "chess.js";
|
||||
import { Api } from "chessground/api";
|
||||
import { Config } from "chessground/config";
|
||||
import { DrawShape } from "chessground/draw";
|
||||
import { playOtherSide, toColor, toDests } from "src/utils";
|
||||
|
||||
export interface ChessGroundSettings {
|
||||
|
|
@ -15,8 +16,11 @@ export interface ChessGroundSettings {
|
|||
boardColor?: "brown" | "green";
|
||||
chess: Chess;
|
||||
setHistory: React.Dispatch<React.SetStateAction<Move[]>>;
|
||||
setShapes: React.Dispatch<React.SetStateAction<DrawShape[][]>>;
|
||||
setMoveNumber: React.Dispatch<React.SetStateAction<number>>;
|
||||
currentMoveNumber: number;
|
||||
isViewOnly: boolean;
|
||||
currentMoveShapes: DrawShape[];
|
||||
}
|
||||
|
||||
export function ChessgroundWrapper({
|
||||
|
|
@ -28,7 +32,10 @@ export function ChessgroundWrapper({
|
|||
chess,
|
||||
setHistory,
|
||||
setMoveNumber,
|
||||
currentMoveNumber,
|
||||
setShapes,
|
||||
isViewOnly,
|
||||
currentMoveShapes,
|
||||
}: ChessGroundSettings) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
|
@ -60,9 +67,6 @@ export function ChessgroundWrapper({
|
|||
check: true,
|
||||
},
|
||||
turnColor: toColor(chess),
|
||||
drawable: {
|
||||
onChange(shapes) {},
|
||||
},
|
||||
...config,
|
||||
});
|
||||
|
||||
|
|
@ -81,13 +85,36 @@ export function ChessgroundWrapper({
|
|||
setApi,
|
||||
setHistory,
|
||||
setMoveNumber,
|
||||
setShapes,
|
||||
currentMoveNumber,
|
||||
]);
|
||||
|
||||
//Sync Is View Only
|
||||
//Sync View Only
|
||||
useEffect(() => {
|
||||
api?.set({ viewOnly: isViewOnly });
|
||||
}, [isViewOnly, api]);
|
||||
|
||||
//Sync Shapes To State
|
||||
useEffect(() => {
|
||||
api?.set({
|
||||
drawable: {
|
||||
onChange(shapes) {
|
||||
setShapes((currentShapes) => {
|
||||
const shapesModified = currentShapes;
|
||||
shapesModified[currentMoveNumber] = shapes;
|
||||
|
||||
return shapesModified;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [api, currentMoveNumber, setShapes]);
|
||||
|
||||
//Load Shapes
|
||||
useEffect(() => {
|
||||
if (currentMoveShapes) api?.setShapes(currentMoveShapes);
|
||||
}, [api, currentMoveNumber, currentMoveShapes, setShapes]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import { Chess, Move } from "chess.js";
|
||||
import { Api } from "chessground/api";
|
||||
import { App } from "obsidian";
|
||||
import { DrawShape } from "chessground/draw";
|
||||
import { App, Notice } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { ChessifyPluginSettings } from "src/components/obsidian/SettingsTab";
|
||||
import { ChessifyFileData, parseUserConfig } from "src/utils";
|
||||
import {
|
||||
ChessifyDataAdapter,
|
||||
ChessifyFileData,
|
||||
parseUserConfig,
|
||||
} from "src/utils";
|
||||
import { ChessGroundSettings, ChessgroundWrapper } from "./ChessgroundWrapper";
|
||||
import { PgnViewer } from "./PgnViewer";
|
||||
|
||||
|
|
@ -15,12 +20,14 @@ interface AppProps {
|
|||
app: App;
|
||||
pluginSettings: ChessifyPluginSettings;
|
||||
chessifyData: ChessifyFileData;
|
||||
dataAdapter: ChessifyDataAdapter;
|
||||
}
|
||||
|
||||
export const Chessify = ({
|
||||
source,
|
||||
pluginSettings,
|
||||
chessifyData,
|
||||
dataAdapter,
|
||||
}: AppProps) => {
|
||||
// Parse Obsidian / Code Block Settings
|
||||
const { boardColor, boardOrientation, chessifyId } = parseUserConfig(
|
||||
|
|
@ -28,6 +35,8 @@ export const Chessify = ({
|
|||
source
|
||||
);
|
||||
|
||||
const [chessifyDataModified] = useState(chessifyData);
|
||||
|
||||
// Setup Chessboard and Chess.js APIs
|
||||
const [chessView, setChessView] = useState<Api | null>(null);
|
||||
const chessLogic = useMemo(() => {
|
||||
|
|
@ -47,6 +56,9 @@ export const Chessify = ({
|
|||
const [history, setHistory] = useState<Move[]>([]);
|
||||
const [isViewOnly, setIsViewOnly] = useState<boolean>(false);
|
||||
const [currentMove, setCurrentMove] = useState<number>(0);
|
||||
const [shapes, setShapes] = useState<DrawShape[][]>(
|
||||
chessifyData.moves.map((data) => data.shapes)
|
||||
);
|
||||
|
||||
//PgnViewer Functions
|
||||
const onBackButtonClick = useCallback(() => {
|
||||
|
|
@ -101,6 +113,27 @@ export const Chessify = ({
|
|||
[chessView, history]
|
||||
);
|
||||
|
||||
const onSaveButtonClick = useCallback(async () => {
|
||||
const chessifyData = {
|
||||
header: chessifyDataModified.header,
|
||||
moves: chessLogic.history({ verbose: true }).map((move, index) => ({
|
||||
...move,
|
||||
subMoves: [],
|
||||
shapes: shapes[index],
|
||||
})),
|
||||
};
|
||||
|
||||
await dataAdapter.saveFile(chessifyData, chessifyId);
|
||||
|
||||
new Notice("Save successfull!");
|
||||
}, [
|
||||
chessLogic,
|
||||
chessifyDataModified.header,
|
||||
chessifyId,
|
||||
dataAdapter,
|
||||
shapes,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
|
@ -126,6 +159,9 @@ export const Chessify = ({
|
|||
setHistory={setHistory}
|
||||
setMoveNumber={setCurrentMove}
|
||||
isViewOnly={isViewOnly}
|
||||
setShapes={setShapes}
|
||||
currentMoveNumber={currentMove}
|
||||
currentMoveShapes={shapes[currentMove]}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1, height: "100%" }}>
|
||||
|
|
@ -135,6 +171,7 @@ export const Chessify = ({
|
|||
onBackButtonClick={onBackButtonClick}
|
||||
onForwardButtonClick={onForwardButtonClick}
|
||||
onMoveItemClick={onMoveItemClick}
|
||||
onSaveButtonClick={onSaveButtonClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Move } from "chess.js";
|
||||
import { ArrowLeft, ArrowRight } from "lucide-react";
|
||||
import { ArrowLeft, ArrowRight, Save } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
|
|
@ -57,12 +57,14 @@ export const PgnViewer = ({
|
|||
onBackButtonClick,
|
||||
onForwardButtonClick,
|
||||
onMoveItemClick,
|
||||
onSaveButtonClick,
|
||||
}: {
|
||||
history: Move[];
|
||||
currentMove: number;
|
||||
onBackButtonClick: () => void;
|
||||
onForwardButtonClick: () => void;
|
||||
onMoveItemClick: (moveIndex: number) => void;
|
||||
onSaveButtonClick: () => void;
|
||||
}) => {
|
||||
const movePairs = useMemo(() => chunkArray(history, 2), [history]);
|
||||
|
||||
|
|
@ -123,6 +125,9 @@ export const PgnViewer = ({
|
|||
<button onClick={() => onForwardButtonClick()}>
|
||||
<ArrowRight />
|
||||
</button>
|
||||
<button onClick={() => onSaveButtonClick()}>
|
||||
<Save strokeWidth={"1px"} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -103,7 +103,14 @@ export default class ChessifyPlugin extends Plugin {
|
|||
const data = await this.dataAdapter.loadFile(chessifyId);
|
||||
|
||||
ctx.addChild(
|
||||
new ReactView(el, source, this.app, this.settings, data)
|
||||
new ReactView(
|
||||
el,
|
||||
source,
|
||||
this.app,
|
||||
this.settings,
|
||||
data,
|
||||
this.dataAdapter
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
new Notice(
|
||||
|
|
|
|||
|
|
@ -94,15 +94,15 @@ export class ChessifyDataAdapter {
|
|||
this.storagePath = storagePath;
|
||||
}
|
||||
|
||||
async saveFile(data: ChessifyFileData) {
|
||||
const id = nanoid();
|
||||
async saveFile(data: ChessifyFileData, id?: string) {
|
||||
const chessifyId = id || nanoid();
|
||||
await this.adapter.write(
|
||||
`${this.storagePath}/${id}.json`,
|
||||
`${this.storagePath}/${chessifyId}.json`,
|
||||
JSON.stringify(data, null, 2),
|
||||
{}
|
||||
);
|
||||
|
||||
return id;
|
||||
return chessifyId;
|
||||
}
|
||||
|
||||
async loadFile(id: string): Promise<ChessifyFileData> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue