瀏覽代碼

feat: Make naming of library items discoverable (#10041)

* updated library relevant strings

* fix: detect name changes

* clarify hashing function

---------

Co-authored-by: dwelle <[email protected]>
zsviczian 2 周之前
父節點
當前提交
fde796a7a0

+ 3 - 0
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++) {

+ 1 - 1
packages/excalidraw/components/PublishLibrary.tsx

@@ -518,7 +518,7 @@ const PublishLibrary = ({
           </div>
           <div className="publish-library__buttons">
             <DialogActionButton
-              label={t("buttons.cancel")}
+              label={t("buttons.saveLibNames")}
               onClick={onDialogClose}
               data-testid="cancel-clear-canvas-button"
             />

+ 18 - 4
packages/excalidraw/data/library.ts

@@ -62,6 +62,7 @@ type LibraryUpdate = {
   deletedItems: Map<LibraryItem["id"], LibraryItem>;
   /** newly added items in the library */
   addedItems: Map<LibraryItem["id"], LibraryItem>;
+  updatedItems: Map<LibraryItem["id"], LibraryItem>;
 };
 
 // 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<LibraryItem["id"], LibraryItem>(),
     addedItems: new Map<LibraryItem["id"], LibraryItem>(),
+    updatedItems: new Map<LibraryItem["id"], LibraryItem>(),
   };
 
   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()),
       );

+ 2 - 1
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"