diff --git a/excalidraw/excalidraw-app/app-jotai.ts b/excalidraw/excalidraw-app/app-jotai.ts index c6ed38d..8f78840 100644 --- a/excalidraw/excalidraw-app/app-jotai.ts +++ b/excalidraw/excalidraw-app/app-jotai.ts @@ -48,7 +48,7 @@ const STORAGE_CONFIG_SESSION_STORAGE_KEY = "excalidraw-storage-config-credentials"; const getInitialStorageConfig = (): StorageConfig => { - const defaultConfig: StorageConfig = { type: "default" }; + const defaultConfig: StorageConfig = { type: "indexed-db" }; try { const nonSensitive = localStorage.getItem(STORAGE_CONFIG_LOCAL_STORAGE_KEY); diff --git a/excalidraw/excalidraw-app/components/StorageSettingsDialog.tsx b/excalidraw/excalidraw-app/components/StorageSettingsDialog.tsx index ea42f3c..d78a4d5 100644 --- a/excalidraw/excalidraw-app/components/StorageSettingsDialog.tsx +++ b/excalidraw/excalidraw-app/components/StorageSettingsDialog.tsx @@ -9,8 +9,11 @@ import { storageConfigAtom } from "../app-jotai"; export type StorageType = "default" | "kv" | "s3" | "indexed-db"; const StorageSettingsDialog = ({ onClose }: { onClose: () => void }) => { + const storedToken = localStorage.getItem("token"); // 如果未登录,无法使用后端存储 const [config, setConfig] = useAtom(storageConfigAtom); - const [storageType, setStorageType] = useState(config.type); + const [storageType, setStorageType] = useState( + !storedToken && config.type === "default" ? "indexed-db" : config.type, + ); // Local state for form inputs const [kvUrl, setKvUrl] = useState(config.kvUrl || ""); @@ -96,8 +99,9 @@ const StorageSettingsDialog = ({ onClose }: { onClose: () => void }) => { default: return (

- Your data is stored on the default backend of this Excalidraw - instance. This requires you to be logged in. + {!storedToken + ? "You must be logged in to use the default backend storage. Please log in and try again." + : "Your data is stored on the default backend of this Excalidraw instance. This requires you to be logged in."}

); } @@ -125,7 +129,15 @@ const StorageSettingsDialog = ({ onClose }: { onClose: () => void }) => { }} > - + diff --git a/excalidraw/excalidraw-app/data/storageAdapters/BackendStorageAdapter.ts b/excalidraw/excalidraw-app/data/storageAdapters/BackendStorageAdapter.ts index a50a156..306d107 100644 --- a/excalidraw/excalidraw-app/data/storageAdapters/BackendStorageAdapter.ts +++ b/excalidraw/excalidraw-app/data/storageAdapters/BackendStorageAdapter.ts @@ -7,6 +7,7 @@ import { } from "../storage"; import { nanoid } from "nanoid"; import { jwtDecode } from "jwt-decode"; +import { generateThumbnail } from "../thumbnail"; export class AuthError extends Error { constructor(message: string) { @@ -64,19 +65,8 @@ export class BackendStorageAdapter implements IStorageAdapter { } throw new Error(`Failed to list canvases: ${response.statusText}`); } - // Backend doesn't send userId, so we enrich the data here. - const canvases: Omit[] = await response.json(); - const token = localStorage.getItem("token"); - if (!token) { - return []; - } - const userId = getUserIdFromJwt(token); - if (!userId) { - console.error("Could not determine userId from token."); - return []; - } - - return canvases.map((canvas) => ({ ...canvas, userId })); + const canvases: CanvasMetadata[] = await response.json(); + return canvases; } async loadCanvas(id: string): Promise { @@ -98,7 +88,21 @@ export class BackendStorageAdapter implements IStorageAdapter { } async saveCanvas(id: string, data: CanvasData): Promise { - const saveData = dehydrateCanvasData(data); + let dataForUpload: CanvasData; + if (data.thumbnail) { + dataForUpload = data; + } else { + const thumbnail = await generateThumbnail( + data.elements, + data.appState, + data.files, + ); + dataForUpload = { + ...data, + thumbnail: data.elements.length > 0 ? thumbnail : undefined, + }; + } + const saveData = dehydrateCanvasData(dataForUpload); const response = await fetch(`${API_BASE_URL}/${id}`, { method: "PUT", @@ -123,14 +127,24 @@ export class BackendStorageAdapter implements IStorageAdapter { if (!userId) { throw new Error("Could not parse user ID from token."); } + const thumbnail = await generateThumbnail( + data.elements, + data.appState, + data.files, + ); + const dataWithThumbnail: CanvasData = { + ...data, + thumbnail: data.elements.length > 0 ? thumbnail : undefined, + }; - await this.saveCanvas(newId, data); + await this.saveCanvas(newId, dataWithThumbnail); return { id: newId, name: data.appState?.name || "Untitled", createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), userId, + thumbnail: dataWithThumbnail.thumbnail, }; }