diff --git a/packages/element/src/index.ts b/packages/element/src/index.ts
index 4fc1ef5579..d677859ad5 100644
--- a/packages/element/src/index.ts
+++ b/packages/element/src/index.ts
@@ -29,6 +29,9 @@ export const hashElementsVersion = (elements: ElementsMapOrArray): number => {
// string hash function (using djb2). Not cryptographically secure, use only
// for versioning and such.
+// note: hashes individual code units (not code points),
+// but for hashing purposes this is fine as it iterates through every code unit
+// (as such, no need to encode to byte string first)
export const hashString = (s: string): number => {
let hash: number = 5381;
for (let i = 0; i < s.length; i++) {
diff --git a/packages/excalidraw/components/PublishLibrary.tsx b/packages/excalidraw/components/PublishLibrary.tsx
index 076b303d70..cdc038dac3 100644
--- a/packages/excalidraw/components/PublishLibrary.tsx
+++ b/packages/excalidraw/components/PublishLibrary.tsx
@@ -518,7 +518,7 @@ const PublishLibrary = ({
diff --git a/packages/excalidraw/data/library.ts b/packages/excalidraw/data/library.ts
index 429ba1046c..abe2fec853 100644
--- a/packages/excalidraw/data/library.ts
+++ b/packages/excalidraw/data/library.ts
@@ -62,6 +62,7 @@ type LibraryUpdate = {
deletedItems: Map;
/** newly added items in the library */
addedItems: Map;
+ updatedItems: Map;
};
// an object so that we can later add more properties to it without breaking,
@@ -170,6 +171,7 @@ const createLibraryUpdate = (
const update: LibraryUpdate = {
deletedItems: new Map(),
addedItems: new Map(),
+ updatedItems: new Map(),
};
for (const item of prevLibraryItems) {
@@ -181,8 +183,11 @@ const createLibraryUpdate = (
const prevItemsMap = arrayToMap(prevLibraryItems);
for (const item of nextLibraryItems) {
- if (!prevItemsMap.has(item.id)) {
+ const prevItem = prevItemsMap.get(item.id);
+ if (!prevItem) {
update.addedItems.set(item.id, item);
+ } else if (getLibraryItemHash(prevItem) !== getLibraryItemHash(item)) {
+ update.updatedItems.set(item.id, item);
}
}
@@ -586,12 +591,14 @@ class AdapterTransaction {
let lastSavedLibraryItemsHash = 0;
let librarySaveCounter = 0;
+const getLibraryItemHash = (item: LibraryItem) => {
+ return `${item.id}:${item.name || ""}:${hashElementsVersion(item.elements)}`;
+};
+
export const getLibraryItemsHash = (items: LibraryItems) => {
return hashString(
items
- .map((item) => {
- return `${item.id}:${hashElementsVersion(item.elements)}`;
- })
+ .map((item) => getLibraryItemHash(item))
.sort()
.join(),
);
@@ -641,6 +648,13 @@ const persistLibraryUpdate = async (
}
}
+ // replace existing items with their updated versions
+ if (update.updatedItems) {
+ for (const [id, item] of update.updatedItems) {
+ nextLibraryItemsMap.set(id, item);
+ }
+ }
+
const nextLibraryItems = addedItems.concat(
Array.from(nextLibraryItemsMap.values()),
);
diff --git a/packages/excalidraw/locales/en.json b/packages/excalidraw/locales/en.json
index 8279cb4344..4bd76fe876 100644
--- a/packages/excalidraw/locales/en.json
+++ b/packages/excalidraw/locales/en.json
@@ -230,10 +230,11 @@
"objectsSnapMode": "Snap to objects",
"exitZenMode": "Exit zen mode",
"cancel": "Cancel",
+ "saveLibNames": "Save name(s) and exit",
"clear": "Clear",
"remove": "Remove",
"embed": "Toggle embedding",
- "publishLibrary": "Publish selected",
+ "publishLibrary": "Rename or publish",
"submit": "Submit",
"confirm": "Confirm",
"embeddableInteractionButton": "Click to interact"