feat: library restoring changes (#4995)

* restore library items in all cases & refactor

* export `restoreLibraryItems` from package

* feat: rerender library menu when updating via API

* update readme & changelog

* fix changelog
This commit is contained in:
David Luzar 2022-04-14 16:20:35 +02:00 committed by GitHub
parent bf6d0eeef7
commit 70b3a9de49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 71 additions and 29 deletions

View File

@ -1700,6 +1700,11 @@ class App extends React.Component<AppProps, AppState> {
this.library.saveLibrary(
restoreLibraryItems(sceneData.libraryItems, "unpublished"),
);
if (this.state.isLibraryOpen) {
this.setState({ isLibraryOpen: false }, () => {
this.setState({ isLibraryOpen: true });
});
}
}
},
);

View File

@ -1,7 +1,6 @@
import { loadLibraryFromBlob } from "./blob";
import { LibraryItems, LibraryItem } from "../types";
import { restoreElements, restoreLibraryItems } from "./restore";
import { getNonDeletedElements } from "../element";
import { restoreLibraryItems } from "./restore";
import type App from "../components/App";
class Library {
@ -17,15 +16,11 @@ class Library {
this.libraryCache = [];
};
restoreLibraryItem = (libraryItem: LibraryItem): LibraryItem | null => {
const elements = getNonDeletedElements(
restoreElements(libraryItem.elements, null),
);
return elements.length ? { ...libraryItem, elements } : null;
};
/** imports library (currently merges, removing duplicates) */
async importLibrary(blob: Blob, defaultStatus = "unpublished") {
async importLibrary(
blob: Blob,
defaultStatus: LibraryItem["status"] = "unpublished",
) {
const libraryFile = await loadLibraryFromBlob(blob);
if (!libraryFile || !(libraryFile.libraryItems || libraryFile.library)) {
return;
@ -58,15 +53,11 @@ class Library {
const existingLibraryItems = await this.loadLibrary();
const library = libraryFile.libraryItems || libraryFile.library || [];
const restoredLibItems = restoreLibraryItems(
library,
defaultStatus as "published" | "unpublished",
);
const restoredLibItems = restoreLibraryItems(library, defaultStatus);
const filteredItems = [];
for (const item of restoredLibItems) {
const restoredItem = this.restoreLibraryItem(item as LibraryItem);
if (restoredItem && isUniqueitem(existingLibraryItems, restoredItem)) {
filteredItems.push(restoredItem);
if (isUniqueitem(existingLibraryItems, item)) {
filteredItems.push(item);
}
}
@ -85,13 +76,7 @@ class Library {
return resolve([]);
}
const items = libraryItems.reduce((acc, item) => {
const restoredItem = this.restoreLibraryItem(item);
if (restoredItem) {
acc.push(item);
}
return acc;
}, [] as Mutable<LibraryItems>);
const items = restoreLibraryItems(libraryItems, "unpublished");
// clone to ensure we don't mutate the cached library elements in the app
this.libraryCache = JSON.parse(JSON.stringify(items));

View File

@ -10,7 +10,11 @@ import {
NormalizedZoomValue,
} from "../types";
import { ImportedDataState } from "./types";
import { getNormalizedDimensions, isInvisiblySmallElement } from "../element";
import {
getNonDeletedElements,
getNormalizedDimensions,
isInvisiblySmallElement,
} from "../element";
import { isLinearElementType } from "../element/typeChecks";
import { randomId } from "../random";
import {
@ -293,6 +297,14 @@ export const restore = (
};
};
const restoreLibraryItem = (libraryItem: LibraryItem) => {
const elements = restoreElements(
getNonDeletedElements(libraryItem.elements),
null,
);
return elements.length ? { ...libraryItem, elements } : null;
};
export const restoreLibraryItems = (
libraryItems: NonOptional<ImportedDataState["libraryItems"]>,
defaultStatus: LibraryItem["status"],
@ -301,20 +313,29 @@ export const restoreLibraryItems = (
for (const item of libraryItems) {
// migrate older libraries
if (Array.isArray(item)) {
restoredItems.push({
const restoredItem = restoreLibraryItem({
status: defaultStatus,
elements: item,
id: randomId(),
created: Date.now(),
});
if (restoredItem) {
restoredItems.push(restoredItem);
}
} else {
const _item = item as MarkOptional<LibraryItem, "id" | "status">;
restoredItems.push({
const _item = item as MarkOptional<
LibraryItem,
"id" | "status" | "created"
>;
const restoredItem = restoreLibraryItem({
..._item,
id: _item.id || randomId(),
status: _item.status || defaultStatus,
created: _item.created || Date.now(),
});
if (restoredItem) {
restoredItems.push(restoredItem);
}
}
}
return restoredItems;

View File

@ -15,6 +15,14 @@ Please add the latest change on the top under the correct section.
### Excalidraw API
#### Features
- Exported [`restoreLibraryItems`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#restoreLibraryItems) API useful when dealing with libraries [#4995](https://github.com/excalidraw/excalidraw/pull/4995).
#### Fixes
- Library menu now properly rerenders if open when library is updated using `updateScene({ libraryItems })` [#4995](https://github.com/excalidraw/excalidraw/pull/4995).
#### Refactor
- Rename `appState.elementLocked` to `appState.activeTool.locked` [#4983](https://github.com/excalidraw/excalidraw/pull/4983).

View File

@ -802,6 +802,24 @@ import { restore } from "@excalidraw/excalidraw-next";
This function makes sure elements and state is set to appropriate values and set to default value if not present. It is a combination of [restoreElements](#restoreElements) and [restoreAppState](#restoreAppState).
#### `restoreLibraryItems`
**_Signature_**
<pre>
restoreLibraryItems(libraryItems: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L22">ImportedDataState["libraryItems"]</a>, defaultStatus: "published" | "unpublished")
</pre>
**_How to use_**
```js
import { restoreLibraryItems } from "@excalidraw/excalidraw-next";
restoreLibraryItems(libraryItems, "unpublished");
```
This function normalizes library items elements, adding missing values when needed.
### Export utilities
#### `exportToCanvas`

View File

@ -177,7 +177,12 @@ export {
getNonDeletedElements,
} from "../../element";
export { defaultLang, languages } from "../../i18n";
export { restore, restoreAppState, restoreElements } from "../../data/restore";
export {
restore,
restoreAppState,
restoreElements,
restoreLibraryItems,
} from "../../data/restore";
export {
exportToCanvas,
exportToBlob,