feat: clearing library cache (#6621)
Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
08563e7d7b
commit
a91e401554
@ -1072,6 +1072,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.unmounted = true;
|
||||
this.removeEventListeners();
|
||||
this.scene.destroy();
|
||||
this.library.destroy();
|
||||
clearTimeout(touchTimeout);
|
||||
touchTimeout = 0;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import DropdownMenu from "./dropdownMenu/DropdownMenu";
|
||||
import { isLibraryMenuOpenAtom } from "./LibraryMenu";
|
||||
import { useUIAppState } from "../context/ui-appState";
|
||||
import clsx from "clsx";
|
||||
import { useLibraryCache } from "../hooks/useLibraryItemSvg";
|
||||
|
||||
const getSelectedItems = (
|
||||
libraryItems: LibraryItems,
|
||||
@ -55,7 +56,7 @@ export const LibraryDropdownMenuButton: React.FC<{
|
||||
jotaiScope,
|
||||
);
|
||||
|
||||
const renderRemoveLibAlert = useCallback(() => {
|
||||
const renderRemoveLibAlert = () => {
|
||||
const content = selectedItems.length
|
||||
? t("alerts.removeItemsFromsLibrary", { count: selectedItems.length })
|
||||
: t("alerts.resetLibrary");
|
||||
@ -80,7 +81,7 @@ export const LibraryDropdownMenuButton: React.FC<{
|
||||
<p>{content}</p>
|
||||
</ConfirmDialog>
|
||||
);
|
||||
}, [selectedItems, onRemoveFromLibrary, resetLibrary]);
|
||||
};
|
||||
|
||||
const [showRemoveLibAlert, setShowRemoveLibAlert] = useState(false);
|
||||
|
||||
@ -136,8 +137,10 @@ export const LibraryDropdownMenuButton: React.FC<{
|
||||
);
|
||||
}, [setPublishLibSuccess, publishLibSuccess]);
|
||||
|
||||
const onPublishLibSuccess = useCallback(
|
||||
(data: { url: string; authorName: string }, libraryItems: LibraryItems) => {
|
||||
const onPublishLibSuccess = (
|
||||
data: { url: string; authorName: string },
|
||||
libraryItems: LibraryItems,
|
||||
) => {
|
||||
setShowPublishLibraryDialog(false);
|
||||
setPublishLibSuccess({ url: data.url, authorName: data.authorName });
|
||||
const nextLibItems = libraryItems.slice();
|
||||
@ -147,9 +150,7 @@ export const LibraryDropdownMenuButton: React.FC<{
|
||||
}
|
||||
});
|
||||
library.setLibrary(nextLibItems);
|
||||
},
|
||||
[setShowPublishLibraryDialog, setPublishLibSuccess, selectedItems, library],
|
||||
);
|
||||
};
|
||||
|
||||
const onLibraryImport = async () => {
|
||||
try {
|
||||
@ -280,27 +281,29 @@ export const LibraryDropdownMenu = ({
|
||||
className?: string;
|
||||
}) => {
|
||||
const { library } = useApp();
|
||||
const { clearLibraryCache, deleteItemsFromLibraryCache } = useLibraryCache();
|
||||
const appState = useUIAppState();
|
||||
const setAppState = useExcalidrawSetAppState();
|
||||
|
||||
const [libraryItemsData] = useAtom(libraryItemsAtom, jotaiScope);
|
||||
|
||||
const removeFromLibrary = useCallback(
|
||||
async (libraryItems: LibraryItems) => {
|
||||
const removeFromLibrary = async (libraryItems: LibraryItems) => {
|
||||
const nextItems = libraryItems.filter(
|
||||
(item) => !selectedItems.includes(item.id),
|
||||
);
|
||||
library.setLibrary(nextItems).catch(() => {
|
||||
setAppState({ errorMessage: t("alerts.errorRemovingFromLibrary") });
|
||||
});
|
||||
onSelectItems([]);
|
||||
},
|
||||
[library, setAppState, selectedItems, onSelectItems],
|
||||
);
|
||||
|
||||
const resetLibrary = useCallback(() => {
|
||||
deleteItemsFromLibraryCache(selectedItems);
|
||||
|
||||
onSelectItems([]);
|
||||
};
|
||||
|
||||
const resetLibrary = () => {
|
||||
library.resetLibrary();
|
||||
}, [library]);
|
||||
clearLibraryCache();
|
||||
};
|
||||
|
||||
return (
|
||||
<LibraryDropdownMenuButton
|
||||
|
@ -15,6 +15,7 @@ import { duplicateElements } from "../element/newElement";
|
||||
import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons";
|
||||
import { LibraryDropdownMenu } from "./LibraryMenuHeaderContent";
|
||||
import LibraryMenuSection from "./LibraryMenuSection";
|
||||
import { useLibraryCache } from "../hooks/useLibraryItemSvg";
|
||||
|
||||
import "./LibraryMenuItems.scss";
|
||||
|
||||
@ -38,6 +39,7 @@ export default function LibraryMenuItems({
|
||||
id: string;
|
||||
}) {
|
||||
const [selectedItems, setSelectedItems] = useState<LibraryItem["id"][]>([]);
|
||||
const { svgCache } = useLibraryCache();
|
||||
|
||||
const unpublishedItems = libraryItems.filter(
|
||||
(item) => item.status !== "published",
|
||||
@ -224,6 +226,7 @@ export default function LibraryMenuItems({
|
||||
onItemDrag={onItemDrag}
|
||||
onClick={onItemClick}
|
||||
isItemSelected={isItemSelected}
|
||||
svgCache={svgCache}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@ -243,6 +246,7 @@ export default function LibraryMenuItems({
|
||||
onItemDrag={onItemDrag}
|
||||
onClick={onItemClick}
|
||||
isItemSelected={isItemSelected}
|
||||
svgCache={svgCache}
|
||||
/>
|
||||
) : unpublishedItems.length > 0 ? (
|
||||
<div
|
||||
|
@ -4,8 +4,7 @@ import { LibraryItem } from "../types";
|
||||
import Stack from "./Stack";
|
||||
import clsx from "clsx";
|
||||
import { ExcalidrawElement, NonDeleted } from "../element/types";
|
||||
import { useAtom } from "jotai";
|
||||
import { libraryItemSvgsCache } from "../hooks/useLibraryItemSvg";
|
||||
import { SvgCache } from "../hooks/useLibraryItemSvg";
|
||||
import { useTransition } from "../hooks/useTransition";
|
||||
|
||||
const ITEMS_PER_ROW = 4;
|
||||
@ -26,6 +25,7 @@ interface Props {
|
||||
onItemSelectToggle: (id: LibraryItem["id"], event: React.MouseEvent) => void;
|
||||
onItemDrag: (id: LibraryItem["id"], event: React.DragEvent) => void;
|
||||
isItemSelected: (id: LibraryItem["id"] | null) => boolean;
|
||||
svgCache: SvgCache;
|
||||
}
|
||||
|
||||
function LibraryRow({
|
||||
@ -34,6 +34,7 @@ function LibraryRow({
|
||||
onItemDrag,
|
||||
isItemSelected,
|
||||
onClick,
|
||||
svgCache,
|
||||
}: Props) {
|
||||
return (
|
||||
<Stack.Row className="library-menu-items-container__row">
|
||||
@ -47,6 +48,7 @@ function LibraryRow({
|
||||
selected={isItemSelected(item.id)}
|
||||
onToggle={onItemSelectToggle}
|
||||
onDrag={onItemDrag}
|
||||
svgCache={svgCache}
|
||||
/>
|
||||
</Stack.Col>
|
||||
))}
|
||||
@ -68,11 +70,11 @@ function LibraryMenuSection({
|
||||
onItemDrag,
|
||||
isItemSelected,
|
||||
onClick,
|
||||
svgCache,
|
||||
}: Props) {
|
||||
const rows = Math.ceil(items.length / ITEMS_PER_ROW);
|
||||
const [, startTransition] = useTransition();
|
||||
const [index, setIndex] = useState(0);
|
||||
const [svgCache] = useAtom(libraryItemSvgsCache);
|
||||
|
||||
const rowsRenderedPerBatch = useMemo(() => {
|
||||
return svgCache.size === 0
|
||||
@ -99,6 +101,7 @@ function LibraryMenuSection({
|
||||
onItemDrag={onItemDrag}
|
||||
onClick={onClick}
|
||||
isItemSelected={isItemSelected}
|
||||
svgCache={svgCache}
|
||||
/>
|
||||
) : (
|
||||
<EmptyLibraryRow key={i} />
|
||||
|
@ -5,7 +5,7 @@ import { LibraryItem } from "../types";
|
||||
import "./LibraryUnit.scss";
|
||||
import { CheckboxItem } from "./CheckboxItem";
|
||||
import { PlusIcon } from "./icons";
|
||||
import { useLibraryItemSvg } from "../hooks/useLibraryItemSvg";
|
||||
import { SvgCache, useLibraryItemSvg } from "../hooks/useLibraryItemSvg";
|
||||
|
||||
export const LibraryUnit = ({
|
||||
id,
|
||||
@ -15,6 +15,7 @@ export const LibraryUnit = ({
|
||||
selected,
|
||||
onToggle,
|
||||
onDrag,
|
||||
svgCache,
|
||||
}: {
|
||||
id: LibraryItem["id"] | /** for pending item */ null;
|
||||
elements?: LibraryItem["elements"];
|
||||
@ -23,9 +24,10 @@ export const LibraryUnit = ({
|
||||
selected: boolean;
|
||||
onToggle: (id: string, event: React.MouseEvent) => void;
|
||||
onDrag: (id: string, event: React.DragEvent) => void;
|
||||
svgCache: SvgCache;
|
||||
}) => {
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
const svg = useLibraryItemSvg(id, elements);
|
||||
const svg = useLibraryItemSvg(id, elements, svgCache);
|
||||
|
||||
useEffect(() => {
|
||||
const node = ref.current;
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
DEFAULT_SIDEBAR,
|
||||
LIBRARY_SIDEBAR_TAB,
|
||||
} from "../constants";
|
||||
import { libraryItemSvgsCache } from "../hooks/useLibraryItemSvg";
|
||||
|
||||
export const libraryItemsAtom = atom<{
|
||||
status: "loading" | "loaded";
|
||||
@ -115,6 +116,20 @@ class Library {
|
||||
}
|
||||
};
|
||||
|
||||
/** call on excalidraw instance unmount */
|
||||
destroy = () => {
|
||||
this.isInitialized = false;
|
||||
this.updateQueue = [];
|
||||
this.lastLibraryItems = [];
|
||||
jotaiStore.set(libraryItemSvgsCache, new Map());
|
||||
// TODO uncomment after/if we make jotai store scoped to each excal instance
|
||||
// jotaiStore.set(libraryItemsAtom, {
|
||||
// status: "loading",
|
||||
// isInitialized: false,
|
||||
// libraryItems: [],
|
||||
// });
|
||||
};
|
||||
|
||||
resetLibrary = () => {
|
||||
return this.setLibrary([]);
|
||||
};
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { atom, useAtom } from "jotai";
|
||||
import { useEffect, useState } from "react";
|
||||
import { COLOR_PALETTE } from "../colors";
|
||||
import { jotaiScope } from "../jotai";
|
||||
import { exportToSvg } from "../packages/utils";
|
||||
import { LibraryItem } from "../types";
|
||||
|
||||
export const libraryItemSvgsCache = atom<Map<LibraryItem["id"], SVGSVGElement>>(
|
||||
new Map(),
|
||||
);
|
||||
export type SvgCache = Map<LibraryItem["id"], SVGSVGElement>;
|
||||
|
||||
export const libraryItemSvgsCache = atom<SvgCache>(new Map());
|
||||
|
||||
const exportLibraryItemToSvg = async (elements: LibraryItem["elements"]) => {
|
||||
return await exportToSvg({
|
||||
@ -22,8 +23,8 @@ const exportLibraryItemToSvg = async (elements: LibraryItem["elements"]) => {
|
||||
export const useLibraryItemSvg = (
|
||||
id: LibraryItem["id"] | null,
|
||||
elements: LibraryItem["elements"] | undefined,
|
||||
svgCache: SvgCache,
|
||||
): SVGSVGElement | undefined => {
|
||||
const [svgCache, setSvgCache] = useAtom(libraryItemSvgsCache);
|
||||
const [svg, setSvg] = useState<SVGSVGElement>();
|
||||
|
||||
useEffect(() => {
|
||||
@ -40,7 +41,7 @@ export const useLibraryItemSvg = (
|
||||
const exportedSvg = await exportLibraryItemToSvg(elements);
|
||||
|
||||
if (exportedSvg) {
|
||||
setSvgCache(svgCache.set(id, exportedSvg));
|
||||
svgCache.set(id, exportedSvg);
|
||||
setSvg(exportedSvg);
|
||||
}
|
||||
})();
|
||||
@ -53,7 +54,23 @@ export const useLibraryItemSvg = (
|
||||
})();
|
||||
}
|
||||
}
|
||||
}, [id, elements, svgCache, setSvgCache, setSvg]);
|
||||
}, [id, elements, svgCache, setSvg]);
|
||||
|
||||
return svg;
|
||||
};
|
||||
|
||||
export const useLibraryCache = () => {
|
||||
const [svgCache] = useAtom(libraryItemSvgsCache, jotaiScope);
|
||||
|
||||
const clearLibraryCache = () => svgCache.clear();
|
||||
|
||||
const deleteItemsFromLibraryCache = (items: LibraryItem["id"][]) => {
|
||||
items.forEach((item) => svgCache.delete(item));
|
||||
};
|
||||
|
||||
return {
|
||||
clearLibraryCache,
|
||||
deleteItemsFromLibraryCache,
|
||||
svgCache,
|
||||
};
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user