diff --git a/src/db/draw.ts b/src/db/draw.ts index d3b4de4..da73229 100644 --- a/src/db/draw.ts +++ b/src/db/draw.ts @@ -1,4 +1,5 @@ import { NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; +import { BinaryFiles } from "@excalidraw/excalidraw/types"; import { supabase } from "./supabase"; import { AuthError, PostgrestError } from "@supabase/supabase-js"; @@ -8,6 +9,12 @@ export type DBResponse = { error: PostgrestError | AuthError | null; }; +export type ExcalidrawData = { + elements: readonly NonDeletedExcalidrawElement[]; + appState?: Record; + files?: BinaryFiles; +}; + export const DB_NAME = "draw"; export async function getPages(user_id: string): Promise { @@ -32,12 +39,17 @@ export async function getDrawData(id: string): Promise { export async function createNewPage( elements?: readonly NonDeletedExcalidrawElement[], + files?: BinaryFiles, ): Promise { const { data: profile, error: profileError } = await supabase.auth.getUser(); if (profile) { + const excalidrawData: ExcalidrawData = { + elements: elements || [], + files: files || {} + }; const { data, error } = await supabase .from(DB_NAME) - .insert({ user_id: profile.user?.id, page_elements: { elements } }) + .insert({ user_id: profile.user?.id, page_elements: excalidrawData }) .select(); return { data, error }; } @@ -48,11 +60,16 @@ export async function setDrawData( id: string, elements: readonly NonDeletedExcalidrawElement[], name: string, + files?: BinaryFiles, ): Promise { const updateTime = new Date().toISOString(); + const excalidrawData: ExcalidrawData = { + elements, + files: files || {} + }; const { data, error } = await supabase .from(DB_NAME) - .update({ name: name, page_elements: { elements }, updated_at: updateTime }) + .update({ name: name, page_elements: excalidrawData, updated_at: updateTime }) .eq("page_id", id) .select(); diff --git a/src/stores/drawDataStore.ts b/src/stores/drawDataStore.ts index 75331f3..12a9295 100644 --- a/src/stores/drawDataStore.ts +++ b/src/stores/drawDataStore.ts @@ -1,4 +1,5 @@ import { NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; +import { BinaryFiles } from "@excalidraw/excalidraw/types"; import { create } from "zustand"; import { persist } from "zustand/middleware"; @@ -7,6 +8,7 @@ export type DrawData = { elements: readonly NonDeletedExcalidrawElement[]; updatedAt: string; name: string; + files?: BinaryFiles; }; }; @@ -17,6 +19,7 @@ type DrawDataStore = { elements: readonly NonDeletedExcalidrawElement[], updatedAt: string, name: string, + files?: BinaryFiles, ) => void; getPageData: (page_id: string) => DrawData[string] | undefined; }; @@ -25,7 +28,7 @@ const drawDataStore = create()( persist( (set, get) => ({ data: {}, - setPageData: (page_id, elements, updatedAt, name) => + setPageData: (page_id, elements, updatedAt, name, files) => set((state) => { const currentData = state.data[page_id]; if ( @@ -35,7 +38,7 @@ const drawDataStore = create()( return { data: { ...state.data, - [page_id]: { elements, updatedAt, name }, + [page_id]: { elements, updatedAt, name, files }, }, }; } diff --git a/src/views/Mermaid.tsx b/src/views/Mermaid.tsx index fbb0006..73266b6 100644 --- a/src/views/Mermaid.tsx +++ b/src/views/Mermaid.tsx @@ -58,7 +58,8 @@ export default function Mermaid() { async function handleSaveAsNewPage() { const elements = excalidrawAPI?.getSceneElements(); - const data = await createNewPage(elements); + const files = excalidrawAPI?.getFiles(); + const data = await createNewPage(elements, files); if (data.data && data.data[0]?.page_id) { goToPage(data.data[0].page_id); diff --git a/src/views/Page.tsx b/src/views/Page.tsx index 225bbcc..a4b5421 100644 --- a/src/views/Page.tsx +++ b/src/views/Page.tsx @@ -12,7 +12,7 @@ import { Input } from "@/components/ui/input"; import { toast } from "sonner"; import { Excalidraw, WelcomeScreen } from "@excalidraw/excalidraw"; import { NonDeletedExcalidrawElement } from "@excalidraw/excalidraw/element/types"; -import { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types"; +import { ExcalidrawImperativeAPI, BinaryFiles } from "@excalidraw/excalidraw/types"; import { useQuery, useMutation } from "@tanstack/react-query"; import { RefreshCcw } from "lucide-react"; import { getDrawData, setDrawData } from "@/db/draw"; @@ -38,7 +38,8 @@ export default function Page({ id }: PageProps) { mutationFn: (data: { elements: NonDeletedExcalidrawElement[]; name: string; - }) => setDrawData(id, data.elements, data.name), + files?: BinaryFiles; + }) => setDrawData(id, data.elements, data.name, data.files), onSuccess: () => { setIsSaving(false); }, @@ -54,11 +55,20 @@ export default function Page({ id }: PageProps) { async function updateScene() { if (data?.data && excalidrawAPI) { - const elements = data.data[0].page_elements.elements; + const pageData = data.data[0].page_elements; + const elements = pageData.elements || []; + const files = pageData.files || {}; + excalidrawAPI.updateScene({ elements: elements, appState: { theme: theme }, }); + + // Update files if they exist + if (Object.keys(files).length > 0) { + excalidrawAPI.addFiles(Object.values(files)); + } + setName(data.data[0].name); } if (data?.error) { @@ -69,20 +79,23 @@ export default function Page({ id }: PageProps) { const setSceneData = useCallback(async () => { if (excalidrawAPI) { const scene = excalidrawAPI.getSceneElements(); + const files = excalidrawAPI.getFiles(); const updatedAt = new Date().toISOString(); const existingData = drawDataStore.getState().getPageData(id); - if (JSON.stringify(existingData?.elements) !== JSON.stringify(scene)) { + if (JSON.stringify(existingData?.elements) !== JSON.stringify(scene) || + JSON.stringify(existingData?.files) !== JSON.stringify(files)) { setIsSaving(true); // Save locally first - drawDataStore.getState().setPageData(id, scene, updatedAt, name); + drawDataStore.getState().setPageData(id, scene, updatedAt, name, files); // Then push to API mutate( { elements: scene as NonDeletedExcalidrawElement[], name, + files, }, { onSettled() { @@ -117,6 +130,12 @@ export default function Page({ id }: PageProps) { elements: localData.elements, appState: { theme: theme }, }); + + // Load files if they exist + if (localData.files && Object.keys(localData.files).length > 0) { + excalidrawAPI.addFiles(Object.values(localData.files)); + } + setName(localData.name); } }, [id, excalidrawAPI, theme]);