excalidraw/src/data/library.ts

103 lines
3.3 KiB
TypeScript
Raw Normal View History

import { loadLibraryFromBlob } from "./blob";
import { LibraryItems, LibraryItem } from "../types";
2020-10-30 21:01:41 +01:00
import { restoreElements } from "./restore";
import { STORAGE_KEYS } from "../constants";
2020-11-08 17:08:22 +01:00
import { getNonDeletedElements } from "../element";
import { NonDeleted, ExcalidrawElement } from "../element/types";
import { nanoid } from "nanoid";
export class Library {
2020-10-30 21:01:41 +01:00
private static libraryCache: LibraryItems | null = null;
public static csrfToken = nanoid();
2020-10-30 21:01:41 +01:00
static resetLibrary = () => {
Library.libraryCache = null;
localStorage.removeItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY);
};
/** imports library (currently merges, removing duplicates) */
2020-10-19 10:53:37 +02:00
static async importLibrary(blob: Blob) {
const libraryFile = await loadLibraryFromBlob(blob);
if (!libraryFile || !libraryFile.library) {
return;
}
/**
* checks if library item does not exist already in current library
*/
const isUniqueitem = (
existingLibraryItems: LibraryItems,
targetLibraryItem: LibraryItem,
) => {
return !existingLibraryItems.find((libraryItem) => {
if (libraryItem.length !== targetLibraryItem.length) {
return false;
}
// detect z-index difference by checking the excalidraw elements
// are in order
return libraryItem.every((libItemExcalidrawItem, idx) => {
return (
libItemExcalidrawItem.id === targetLibraryItem[idx].id &&
libItemExcalidrawItem.versionNonce ===
targetLibraryItem[idx].versionNonce
);
});
});
};
2020-10-30 21:01:41 +01:00
const existingLibraryItems = await Library.loadLibrary();
2020-11-08 17:08:22 +01:00
const filtered = libraryFile.library!.reduce((acc, libraryItem) => {
const restored = getNonDeletedElements(restoreElements(libraryItem));
if (isUniqueitem(existingLibraryItems, restored)) {
acc.push(restored);
}
return acc;
}, [] as (readonly NonDeleted<ExcalidrawElement>[])[]);
2020-10-30 21:01:41 +01:00
Library.saveLibrary([...existingLibraryItems, ...filtered]);
}
2020-10-30 21:01:41 +01:00
static loadLibrary = (): Promise<LibraryItems> => {
return new Promise(async (resolve) => {
if (Library.libraryCache) {
return resolve(JSON.parse(JSON.stringify(Library.libraryCache)));
}
try {
const data = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY);
if (!data) {
return resolve([]);
}
const items = (JSON.parse(data) as LibraryItems).map((elements) =>
restoreElements(elements),
) as Mutable<LibraryItems>;
// clone to ensure we don't mutate the cached library elements in the app
Library.libraryCache = JSON.parse(JSON.stringify(items));
resolve(items);
} catch (error) {
console.error(error);
2020-10-30 21:01:41 +01:00
resolve([]);
}
});
};
static saveLibrary = (items: LibraryItems) => {
const prevLibraryItems = Library.libraryCache;
try {
const serializedItems = JSON.stringify(items);
// cache optimistically so that consumers have access to the latest
// immediately
2020-10-30 21:01:41 +01:00
Library.libraryCache = JSON.parse(serializedItems);
localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY, serializedItems);
} catch (error) {
2020-10-30 21:01:41 +01:00
Library.libraryCache = prevLibraryItems;
console.error(error);
2020-10-30 21:01:41 +01:00
}
};
}