feat: add save function and persist shapes

This commit is contained in:
chrislicodes 2023-05-15 22:14:35 +02:00
parent b9bf576025
commit cdd4c088bf
6 changed files with 94 additions and 14 deletions

View file

@ -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>
);

View file

@ -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={{

View file

@ -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>

View file

@ -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>
);

View file

@ -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(

View file

@ -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> {