Skip to content

Commit 095b71a

Browse files
authored
fix: autosave (#27)
* fix: autosave * fix: race condition
1 parent a2f7b05 commit 095b71a

File tree

1 file changed

+48
-23
lines changed

1 file changed

+48
-23
lines changed

src/views/Page.tsx

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from "react";
1+
import { useEffect, useState, useCallback } from "react";
22
import {
33
Tooltip,
44
TooltipContent,
@@ -16,8 +16,7 @@ import { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types/types";
1616
import { useQuery, useMutation } from "@tanstack/react-query";
1717
import { RefreshCcw } from "lucide-react";
1818
import { getDrawData, setDrawData } from "@/db/draw";
19-
import { drawDataStore } from "@/stores/drawDataStore"; // Adjust the import path as needed
20-
import { queryClient } from "@/main";
19+
import { drawDataStore } from "@/stores/drawDataStore";
2120

2221
type PageProps = {
2322
id: string;
@@ -27,6 +26,7 @@ export default function Page({ id }: PageProps) {
2726
const [excalidrawAPI, setExcalidrawAPI] =
2827
useState<ExcalidrawImperativeAPI | null>(null);
2928
const [name, setName] = useState("");
29+
const [isSaving, setIsSaving] = useState(false);
3030
const { theme } = useTheme();
3131

3232
const { data, isLoading } = useQuery({
@@ -40,16 +40,18 @@ export default function Page({ id }: PageProps) {
4040
name: string;
4141
}) => setDrawData(id, data.elements, data.name),
4242
onSuccess: () => {
43-
queryClient.invalidateQueries({ queryKey: ["page", id] });
44-
toast("Your page has been saved to the server!");
43+
setIsSaving(false);
4544
},
4645
onError: (error: Error) => {
46+
setIsSaving(false);
4747
toast("An error occurred while saving to the server", {
4848
description: error.message,
4949
});
5050
},
5151
});
5252

53+
const { mutate } = mutation;
54+
5355
async function updateScene() {
5456
if (data?.data && excalidrawAPI) {
5557
const elements = data.data[0].page_elements.elements;
@@ -58,37 +60,55 @@ export default function Page({ id }: PageProps) {
5860
appState: { theme: theme },
5961
});
6062
setName(data.data[0].name);
61-
toast("Scene updated");
6263
}
6364
if (data?.error) {
6465
toast("An error occurred", { description: data.error.message });
6566
}
6667
}
6768

68-
async function setSceneData() {
69+
const setSceneData = useCallback(async () => {
6970
if (excalidrawAPI) {
7071
const scene = excalidrawAPI.getSceneElements();
7172
const updatedAt = new Date().toISOString();
7273

73-
// Save locally first
74-
drawDataStore.getState().setPageData(id, scene, updatedAt, name);
75-
toast("Your page has been saved locally!");
74+
const existingData = drawDataStore.getState().getPageData(id);
7675

77-
// Then push to API
78-
mutation.mutate({
79-
elements: scene as NonDeletedExcalidrawElement[],
80-
name,
81-
});
76+
if (JSON.stringify(existingData?.elements) !== JSON.stringify(scene)) {
77+
setIsSaving(true);
78+
// Save locally first
79+
drawDataStore.getState().setPageData(id, scene, updatedAt, name);
80+
81+
// Then push to API
82+
mutate(
83+
{
84+
elements: scene as NonDeletedExcalidrawElement[],
85+
name,
86+
},
87+
{
88+
onSettled() {
89+
setIsSaving(false);
90+
},
91+
},
92+
);
93+
}
8294
}
83-
}
95+
}, [excalidrawAPI, id, name, mutate]);
8496

8597
useEffect(() => {
8698
if (!isLoading && data?.data && excalidrawAPI) {
87-
setTimeout(updateScene, 10);
99+
setTimeout(updateScene, 1000);
88100
}
89101
// eslint-disable-next-line react-hooks/exhaustive-deps
90102
}, [isLoading, data, excalidrawAPI]);
91103

104+
useEffect(() => {
105+
const interval = setInterval(() => {
106+
setSceneData();
107+
}, 3000);
108+
109+
return () => clearInterval(interval);
110+
}, [setSceneData]);
111+
92112
useEffect(() => {
93113
// Load data from local storage if available
94114
const localData = drawDataStore.getState().getPageData(id);
@@ -98,7 +118,6 @@ export default function Page({ id }: PageProps) {
98118
appState: { theme: theme },
99119
});
100120
setName(localData.name);
101-
toast("Loaded data from local storage");
102121
}
103122
}, [id, excalidrawAPI, theme]);
104123

@@ -116,20 +135,26 @@ export default function Page({ id }: PageProps) {
116135
<Input
117136
onChange={(e) => setName(e.target.value)}
118137
value={name}
119-
className="w-40"
138+
className="h-9 w-40"
139+
placeholder="Page Title"
120140
/>
121-
<Button variant="secondary" onClick={setSceneData}>
122-
Save
141+
<Button
142+
variant="secondary"
143+
onClick={setSceneData}
144+
disabled={isSaving}
145+
size="sm"
146+
>
147+
{isSaving ? "Saving..." : "Save"}
123148
</Button>
124149
<TooltipProvider>
125150
<Tooltip>
126151
<TooltipTrigger asChild>
127152
<Button
128153
variant="secondary"
129-
size="icon"
154+
size="sm"
130155
onClick={updateScene}
131156
>
132-
<RefreshCcw className="h-5 w-5" />
157+
<RefreshCcw className="h-4 w-4" />
133158
</Button>
134159
</TooltipTrigger>
135160
<TooltipContent>

0 commit comments

Comments
 (0)