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.unmounted = true;
|
||||||
this.removeEventListeners();
|
this.removeEventListeners();
|
||||||
this.scene.destroy();
|
this.scene.destroy();
|
||||||
|
this.library.destroy();
|
||||||
clearTimeout(touchTimeout);
|
clearTimeout(touchTimeout);
|
||||||
touchTimeout = 0;
|
touchTimeout = 0;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import DropdownMenu from "./dropdownMenu/DropdownMenu";
|
|||||||
import { isLibraryMenuOpenAtom } from "./LibraryMenu";
|
import { isLibraryMenuOpenAtom } from "./LibraryMenu";
|
||||||
import { useUIAppState } from "../context/ui-appState";
|
import { useUIAppState } from "../context/ui-appState";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import { useLibraryCache } from "../hooks/useLibraryItemSvg";
|
||||||
|
|
||||||
const getSelectedItems = (
|
const getSelectedItems = (
|
||||||
libraryItems: LibraryItems,
|
libraryItems: LibraryItems,
|
||||||
@ -55,7 +56,7 @@ export const LibraryDropdownMenuButton: React.FC<{
|
|||||||
jotaiScope,
|
jotaiScope,
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderRemoveLibAlert = useCallback(() => {
|
const renderRemoveLibAlert = () => {
|
||||||
const content = selectedItems.length
|
const content = selectedItems.length
|
||||||
? t("alerts.removeItemsFromsLibrary", { count: selectedItems.length })
|
? t("alerts.removeItemsFromsLibrary", { count: selectedItems.length })
|
||||||
: t("alerts.resetLibrary");
|
: t("alerts.resetLibrary");
|
||||||
@ -80,7 +81,7 @@ export const LibraryDropdownMenuButton: React.FC<{
|
|||||||
<p>{content}</p>
|
<p>{content}</p>
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
);
|
);
|
||||||
}, [selectedItems, onRemoveFromLibrary, resetLibrary]);
|
};
|
||||||
|
|
||||||
const [showRemoveLibAlert, setShowRemoveLibAlert] = useState(false);
|
const [showRemoveLibAlert, setShowRemoveLibAlert] = useState(false);
|
||||||
|
|
||||||
@ -136,20 +137,20 @@ export const LibraryDropdownMenuButton: React.FC<{
|
|||||||
);
|
);
|
||||||
}, [setPublishLibSuccess, publishLibSuccess]);
|
}, [setPublishLibSuccess, publishLibSuccess]);
|
||||||
|
|
||||||
const onPublishLibSuccess = useCallback(
|
const onPublishLibSuccess = (
|
||||||
(data: { url: string; authorName: string }, libraryItems: LibraryItems) => {
|
data: { url: string; authorName: string },
|
||||||
setShowPublishLibraryDialog(false);
|
libraryItems: LibraryItems,
|
||||||
setPublishLibSuccess({ url: data.url, authorName: data.authorName });
|
) => {
|
||||||
const nextLibItems = libraryItems.slice();
|
setShowPublishLibraryDialog(false);
|
||||||
nextLibItems.forEach((libItem) => {
|
setPublishLibSuccess({ url: data.url, authorName: data.authorName });
|
||||||
if (selectedItems.includes(libItem.id)) {
|
const nextLibItems = libraryItems.slice();
|
||||||
libItem.status = "published";
|
nextLibItems.forEach((libItem) => {
|
||||||
}
|
if (selectedItems.includes(libItem.id)) {
|
||||||
});
|
libItem.status = "published";
|
||||||
library.setLibrary(nextLibItems);
|
}
|
||||||
},
|
});
|
||||||
[setShowPublishLibraryDialog, setPublishLibSuccess, selectedItems, library],
|
library.setLibrary(nextLibItems);
|
||||||
);
|
};
|
||||||
|
|
||||||
const onLibraryImport = async () => {
|
const onLibraryImport = async () => {
|
||||||
try {
|
try {
|
||||||
@ -280,27 +281,29 @@ export const LibraryDropdownMenu = ({
|
|||||||
className?: string;
|
className?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { library } = useApp();
|
const { library } = useApp();
|
||||||
|
const { clearLibraryCache, deleteItemsFromLibraryCache } = useLibraryCache();
|
||||||
const appState = useUIAppState();
|
const appState = useUIAppState();
|
||||||
const setAppState = useExcalidrawSetAppState();
|
const setAppState = useExcalidrawSetAppState();
|
||||||
|
|
||||||
const [libraryItemsData] = useAtom(libraryItemsAtom, jotaiScope);
|
const [libraryItemsData] = useAtom(libraryItemsAtom, jotaiScope);
|
||||||
|
|
||||||
const removeFromLibrary = useCallback(
|
const removeFromLibrary = async (libraryItems: LibraryItems) => {
|
||||||
async (libraryItems: LibraryItems) => {
|
const nextItems = libraryItems.filter(
|
||||||
const nextItems = libraryItems.filter(
|
(item) => !selectedItems.includes(item.id),
|
||||||
(item) => !selectedItems.includes(item.id),
|
);
|
||||||
);
|
library.setLibrary(nextItems).catch(() => {
|
||||||
library.setLibrary(nextItems).catch(() => {
|
setAppState({ errorMessage: t("alerts.errorRemovingFromLibrary") });
|
||||||
setAppState({ errorMessage: t("alerts.errorRemovingFromLibrary") });
|
});
|
||||||
});
|
|
||||||
onSelectItems([]);
|
|
||||||
},
|
|
||||||
[library, setAppState, selectedItems, onSelectItems],
|
|
||||||
);
|
|
||||||
|
|
||||||
const resetLibrary = useCallback(() => {
|
deleteItemsFromLibraryCache(selectedItems);
|
||||||
|
|
||||||
|
onSelectItems([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetLibrary = () => {
|
||||||
library.resetLibrary();
|
library.resetLibrary();
|
||||||
}, [library]);
|
clearLibraryCache();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LibraryDropdownMenuButton
|
<LibraryDropdownMenuButton
|
||||||
|
@ -15,6 +15,7 @@ import { duplicateElements } from "../element/newElement";
|
|||||||
import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons";
|
import { LibraryMenuControlButtons } from "./LibraryMenuControlButtons";
|
||||||
import { LibraryDropdownMenu } from "./LibraryMenuHeaderContent";
|
import { LibraryDropdownMenu } from "./LibraryMenuHeaderContent";
|
||||||
import LibraryMenuSection from "./LibraryMenuSection";
|
import LibraryMenuSection from "./LibraryMenuSection";
|
||||||
|
import { useLibraryCache } from "../hooks/useLibraryItemSvg";
|
||||||
|
|
||||||
import "./LibraryMenuItems.scss";
|
import "./LibraryMenuItems.scss";
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ export default function LibraryMenuItems({
|
|||||||
id: string;
|
id: string;
|
||||||
}) {
|
}) {
|
||||||
const [selectedItems, setSelectedItems] = useState<LibraryItem["id"][]>([]);
|
const [selectedItems, setSelectedItems] = useState<LibraryItem["id"][]>([]);
|
||||||
|
const { svgCache } = useLibraryCache();
|
||||||
|
|
||||||
const unpublishedItems = libraryItems.filter(
|
const unpublishedItems = libraryItems.filter(
|
||||||
(item) => item.status !== "published",
|
(item) => item.status !== "published",
|
||||||
@ -224,6 +226,7 @@ export default function LibraryMenuItems({
|
|||||||
onItemDrag={onItemDrag}
|
onItemDrag={onItemDrag}
|
||||||
onClick={onItemClick}
|
onClick={onItemClick}
|
||||||
isItemSelected={isItemSelected}
|
isItemSelected={isItemSelected}
|
||||||
|
svgCache={svgCache}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@ -243,6 +246,7 @@ export default function LibraryMenuItems({
|
|||||||
onItemDrag={onItemDrag}
|
onItemDrag={onItemDrag}
|
||||||
onClick={onItemClick}
|
onClick={onItemClick}
|
||||||
isItemSelected={isItemSelected}
|
isItemSelected={isItemSelected}
|
||||||
|
svgCache={svgCache}
|
||||||
/>
|
/>
|
||||||
) : unpublishedItems.length > 0 ? (
|
) : unpublishedItems.length > 0 ? (
|
||||||
<div
|
<div
|
||||||
|
@ -4,8 +4,7 @@ import { LibraryItem } from "../types";
|
|||||||
import Stack from "./Stack";
|
import Stack from "./Stack";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { ExcalidrawElement, NonDeleted } from "../element/types";
|
import { ExcalidrawElement, NonDeleted } from "../element/types";
|
||||||
import { useAtom } from "jotai";
|
import { SvgCache } from "../hooks/useLibraryItemSvg";
|
||||||
import { libraryItemSvgsCache } from "../hooks/useLibraryItemSvg";
|
|
||||||
import { useTransition } from "../hooks/useTransition";
|
import { useTransition } from "../hooks/useTransition";
|
||||||
|
|
||||||
const ITEMS_PER_ROW = 4;
|
const ITEMS_PER_ROW = 4;
|
||||||
@ -26,6 +25,7 @@ interface Props {
|
|||||||
onItemSelectToggle: (id: LibraryItem["id"], event: React.MouseEvent) => void;
|
onItemSelectToggle: (id: LibraryItem["id"], event: React.MouseEvent) => void;
|
||||||
onItemDrag: (id: LibraryItem["id"], event: React.DragEvent) => void;
|
onItemDrag: (id: LibraryItem["id"], event: React.DragEvent) => void;
|
||||||
isItemSelected: (id: LibraryItem["id"] | null) => boolean;
|
isItemSelected: (id: LibraryItem["id"] | null) => boolean;
|
||||||
|
svgCache: SvgCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
function LibraryRow({
|
function LibraryRow({
|
||||||
@ -34,6 +34,7 @@ function LibraryRow({
|
|||||||
onItemDrag,
|
onItemDrag,
|
||||||
isItemSelected,
|
isItemSelected,
|
||||||
onClick,
|
onClick,
|
||||||
|
svgCache,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<Stack.Row className="library-menu-items-container__row">
|
<Stack.Row className="library-menu-items-container__row">
|
||||||
@ -47,6 +48,7 @@ function LibraryRow({
|
|||||||
selected={isItemSelected(item.id)}
|
selected={isItemSelected(item.id)}
|
||||||
onToggle={onItemSelectToggle}
|
onToggle={onItemSelectToggle}
|
||||||
onDrag={onItemDrag}
|
onDrag={onItemDrag}
|
||||||
|
svgCache={svgCache}
|
||||||
/>
|
/>
|
||||||
</Stack.Col>
|
</Stack.Col>
|
||||||
))}
|
))}
|
||||||
@ -68,11 +70,11 @@ function LibraryMenuSection({
|
|||||||
onItemDrag,
|
onItemDrag,
|
||||||
isItemSelected,
|
isItemSelected,
|
||||||
onClick,
|
onClick,
|
||||||
|
svgCache,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const rows = Math.ceil(items.length / ITEMS_PER_ROW);
|
const rows = Math.ceil(items.length / ITEMS_PER_ROW);
|
||||||
const [, startTransition] = useTransition();
|
const [, startTransition] = useTransition();
|
||||||
const [index, setIndex] = useState(0);
|
const [index, setIndex] = useState(0);
|
||||||
const [svgCache] = useAtom(libraryItemSvgsCache);
|
|
||||||
|
|
||||||
const rowsRenderedPerBatch = useMemo(() => {
|
const rowsRenderedPerBatch = useMemo(() => {
|
||||||
return svgCache.size === 0
|
return svgCache.size === 0
|
||||||
@ -99,6 +101,7 @@ function LibraryMenuSection({
|
|||||||
onItemDrag={onItemDrag}
|
onItemDrag={onItemDrag}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
isItemSelected={isItemSelected}
|
isItemSelected={isItemSelected}
|
||||||
|
svgCache={svgCache}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<EmptyLibraryRow key={i} />
|
<EmptyLibraryRow key={i} />
|
||||||
|
@ -5,7 +5,7 @@ import { LibraryItem } from "../types";
|
|||||||
import "./LibraryUnit.scss";
|
import "./LibraryUnit.scss";
|
||||||
import { CheckboxItem } from "./CheckboxItem";
|
import { CheckboxItem } from "./CheckboxItem";
|
||||||
import { PlusIcon } from "./icons";
|
import { PlusIcon } from "./icons";
|
||||||
import { useLibraryItemSvg } from "../hooks/useLibraryItemSvg";
|
import { SvgCache, useLibraryItemSvg } from "../hooks/useLibraryItemSvg";
|
||||||
|
|
||||||
export const LibraryUnit = ({
|
export const LibraryUnit = ({
|
||||||
id,
|
id,
|
||||||
@ -15,6 +15,7 @@ export const LibraryUnit = ({
|
|||||||
selected,
|
selected,
|
||||||
onToggle,
|
onToggle,
|
||||||
onDrag,
|
onDrag,
|
||||||
|
svgCache,
|
||||||
}: {
|
}: {
|
||||||
id: LibraryItem["id"] | /** for pending item */ null;
|
id: LibraryItem["id"] | /** for pending item */ null;
|
||||||
elements?: LibraryItem["elements"];
|
elements?: LibraryItem["elements"];
|
||||||
@ -23,9 +24,10 @@ export const LibraryUnit = ({
|
|||||||
selected: boolean;
|
selected: boolean;
|
||||||
onToggle: (id: string, event: React.MouseEvent) => void;
|
onToggle: (id: string, event: React.MouseEvent) => void;
|
||||||
onDrag: (id: string, event: React.DragEvent) => void;
|
onDrag: (id: string, event: React.DragEvent) => void;
|
||||||
|
svgCache: SvgCache;
|
||||||
}) => {
|
}) => {
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
const ref = useRef<HTMLDivElement | null>(null);
|
||||||
const svg = useLibraryItemSvg(id, elements);
|
const svg = useLibraryItemSvg(id, elements, svgCache);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const node = ref.current;
|
const node = ref.current;
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
DEFAULT_SIDEBAR,
|
DEFAULT_SIDEBAR,
|
||||||
LIBRARY_SIDEBAR_TAB,
|
LIBRARY_SIDEBAR_TAB,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
|
import { libraryItemSvgsCache } from "../hooks/useLibraryItemSvg";
|
||||||
|
|
||||||
export const libraryItemsAtom = atom<{
|
export const libraryItemsAtom = atom<{
|
||||||
status: "loading" | "loaded";
|
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 = () => {
|
resetLibrary = () => {
|
||||||
return this.setLibrary([]);
|
return this.setLibrary([]);
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { atom, useAtom } from "jotai";
|
import { atom, useAtom } from "jotai";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { COLOR_PALETTE } from "../colors";
|
import { COLOR_PALETTE } from "../colors";
|
||||||
|
import { jotaiScope } from "../jotai";
|
||||||
import { exportToSvg } from "../packages/utils";
|
import { exportToSvg } from "../packages/utils";
|
||||||
import { LibraryItem } from "../types";
|
import { LibraryItem } from "../types";
|
||||||
|
|
||||||
export const libraryItemSvgsCache = atom<Map<LibraryItem["id"], SVGSVGElement>>(
|
export type SvgCache = Map<LibraryItem["id"], SVGSVGElement>;
|
||||||
new Map(),
|
|
||||||
);
|
export const libraryItemSvgsCache = atom<SvgCache>(new Map());
|
||||||
|
|
||||||
const exportLibraryItemToSvg = async (elements: LibraryItem["elements"]) => {
|
const exportLibraryItemToSvg = async (elements: LibraryItem["elements"]) => {
|
||||||
return await exportToSvg({
|
return await exportToSvg({
|
||||||
@ -22,8 +23,8 @@ const exportLibraryItemToSvg = async (elements: LibraryItem["elements"]) => {
|
|||||||
export const useLibraryItemSvg = (
|
export const useLibraryItemSvg = (
|
||||||
id: LibraryItem["id"] | null,
|
id: LibraryItem["id"] | null,
|
||||||
elements: LibraryItem["elements"] | undefined,
|
elements: LibraryItem["elements"] | undefined,
|
||||||
|
svgCache: SvgCache,
|
||||||
): SVGSVGElement | undefined => {
|
): SVGSVGElement | undefined => {
|
||||||
const [svgCache, setSvgCache] = useAtom(libraryItemSvgsCache);
|
|
||||||
const [svg, setSvg] = useState<SVGSVGElement>();
|
const [svg, setSvg] = useState<SVGSVGElement>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -40,7 +41,7 @@ export const useLibraryItemSvg = (
|
|||||||
const exportedSvg = await exportLibraryItemToSvg(elements);
|
const exportedSvg = await exportLibraryItemToSvg(elements);
|
||||||
|
|
||||||
if (exportedSvg) {
|
if (exportedSvg) {
|
||||||
setSvgCache(svgCache.set(id, exportedSvg));
|
svgCache.set(id, exportedSvg);
|
||||||
setSvg(exportedSvg);
|
setSvg(exportedSvg);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@ -53,7 +54,23 @@ export const useLibraryItemSvg = (
|
|||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [id, elements, svgCache, setSvgCache, setSvg]);
|
}, [id, elements, svgCache, setSvg]);
|
||||||
|
|
||||||
return svg;
|
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