Przeglądaj źródła

refactor: use :block/order for whiteboard blocks

Tienson Qin 1 rok temu
rodzic
commit
3bbcc8cd68

+ 1 - 1
packages/tldraw/apps/tldraw-logseq/src/lib/shapes/HTMLShape.tsx

@@ -96,7 +96,7 @@ export class HTMLShape extends TLBoxShape<HTMLShapeProps> {
     React.useEffect(() => {
     React.useEffect(() => {
       if (this.props.size[1] === 0) {
       if (this.props.size[1] === 0) {
         this.onResetBounds({ zoom: app.viewport.camera.zoom })
         this.onResetBounds({ zoom: app.viewport.camera.zoom })
-        app.persist(true)
+        app.persist({replace: true})
       }
       }
     }, [])
     }, [])
 
 

+ 2 - 2
packages/tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx

@@ -296,7 +296,7 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
             size: [this.props.size[0], newHeight],
             size: [this.props.size[0], newHeight],
           })
           })
 
 
-          if (loaded) app.persist(true)
+          if (loaded) app.persist({replace: true})
         }
         }
       }
       }
     }, [innerHeight, this.props.isAutoResizing])
     }, [innerHeight, this.props.isAutoResizing])
@@ -305,7 +305,7 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
       if (!this.initialHeightCalculated) {
       if (!this.initialHeightCalculated) {
         setTimeout(() => {
         setTimeout(() => {
           this.onResetBounds()
           this.onResetBounds()
-          app.persist(true)
+          app.persist({replace: true})
         })
         })
       }
       }
     }, [this.initialHeightCalculated])
     }, [this.initialHeightCalculated])

+ 2 - 2
packages/tldraw/apps/tldraw-logseq/src/lib/shapes/TweetShape.tsx

@@ -61,7 +61,7 @@ export class TweetShape extends TLBoxShape<TweetShapeProps> {
         this.update({
         this.update({
           size: [this.props.size[0], newHeight],
           size: [this.props.size[0], newHeight],
         })
         })
-        app.persist(true)
+        app.persist({replace: true})
       }
       }
     }, [innerHeight])
     }, [innerHeight])
 
 
@@ -69,7 +69,7 @@ export class TweetShape extends TLBoxShape<TweetShapeProps> {
       if (!this.initialHeightCalculated) {
       if (!this.initialHeightCalculated) {
         setTimeout(() => {
         setTimeout(() => {
           this.onResetBounds()
           this.onResetBounds()
-          app.persist(true)
+          app.persist({replace: true})
         })
         })
       }
       }
     }, [this.initialHeightCalculated])
     }, [this.initialHeightCalculated])

+ 3 - 2
packages/tldraw/packages/core/src/lib/TLApi/TLApi.ts

@@ -450,7 +450,7 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
     this.app.currentPage.removeShapes(...selectedGroups)
     this.app.currentPage.removeShapes(...selectedGroups)
 
 
     // group all shapes
     // group all shapes
-    const selectedShapes = shapes.filter(s => s.type !== 'group')
+    let selectedShapes = shapes.filter(s => s.type !== 'group')
     if (selectedShapes.length > 1) {
     if (selectedShapes.length > 1) {
       const ShapeGroup = this.app.getShapeClass('group')
       const ShapeGroup = this.app.getShapeClass('group')
       const group = new ShapeGroup({
       const group = new ShapeGroup({
@@ -461,11 +461,12 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
       })
       })
       this.app.currentPage.addShapes(group)
       this.app.currentPage.addShapes(group)
       this.app.setSelectedShapes([group])
       this.app.setSelectedShapes([group])
+      selectedShapes.push(group)
       // the shapes in the group should also be moved to the bottom of the array (to be on top on the canvas)
       // the shapes in the group should also be moved to the bottom of the array (to be on top on the canvas)
       this.app.bringForward(selectedShapes)
       this.app.bringForward(selectedShapes)
     }
     }
     this.app.history.resume()
     this.app.history.resume()
-    this.app.persist()
+    this.app.persist(this.app.currentPage.persistInfo)
   }
   }
 
 
   unGroup = (shapes: S[] = this.app.allSelectedShapesArray) => {
   unGroup = (shapes: S[] = this.app.allSelectedShapesArray) => {

+ 2 - 2
packages/tldraw/packages/core/src/lib/TLHistory.ts

@@ -28,9 +28,9 @@ export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEve
     this.isPaused = false
     this.isPaused = false
   }
   }
 
 
-  @action persist = (replace = false) => {
+  @action persist = (info) => {
     if (this.isPaused || this.creating) return
     if (this.isPaused || this.creating) return
-    this.app.notify('persist', { replace })
+    this.app.notify('persist', info)
   }
   }
 
 
   @action undo = () => {
   @action undo = () => {

+ 11 - 30
packages/tldraw/packages/core/src/lib/TLPage/TLPage.ts

@@ -33,6 +33,7 @@ export class TLPage<S extends TLShape = TLShape, E extends TLEventMap = TLEventM
     this.bindings = Object.assign({}, bindings) // make sure it is type of object
     this.bindings = Object.assign({}, bindings) // make sure it is type of object
     this.app = app
     this.app = app
     this.nonce = nonce || 0
     this.nonce = nonce || 0
+    this.persistInfo = null
     this.addShapes(...shapes)
     this.addShapes(...shapes)
     makeObservable(this)
     makeObservable(this)
 
 
@@ -137,50 +138,30 @@ export class TLPage<S extends TLShape = TLShape, E extends TLEventMap = TLEventM
   }
   }
 
 
   @action bringForward = (shapes: S[] | string[]): this => {
   @action bringForward = (shapes: S[] | string[]): this => {
-    const shapesToMove = this.parseShapesArg(shapes)
-    shapesToMove
-      .sort((a, b) => this.shapes.indexOf(b) - this.shapes.indexOf(a))
-      .map(shape => this.shapes.indexOf(shape))
-      .forEach(index => {
-        if (index === this.shapes.length - 1) return
-        const next = this.shapes[index + 1]
-        if (shapesToMove.includes(next)) return
-        const t = this.shapes[index]
-        this.shapes[index] = this.shapes[index + 1]
-        this.shapes[index + 1] = t
-      })
-    this.app.persist()
+    this.bringToFront(shapes)
     return this
     return this
   }
   }
 
 
   @action sendBackward = (shapes: S[] | string[]): this => {
   @action sendBackward = (shapes: S[] | string[]): this => {
-    const shapesToMove = this.parseShapesArg(shapes)
-    shapesToMove
-      .sort((a, b) => this.shapes.indexOf(a) - this.shapes.indexOf(b))
-      .map(shape => this.shapes.indexOf(shape))
-      .forEach(index => {
-        if (index === 0) return
-        const next = this.shapes[index - 1]
-        if (shapesToMove.includes(next)) return
-        const t = this.shapes[index]
-        this.shapes[index] = this.shapes[index - 1]
-        this.shapes[index - 1] = t
-      })
-    this.app.persist()
+    this.sendToBack(shapes)
     return this
     return this
   }
   }
 
 
   @action bringToFront = (shapes: S[] | string[]): this => {
   @action bringToFront = (shapes: S[] | string[]): this => {
     const shapesToMove = this.parseShapesArg(shapes)
     const shapesToMove = this.parseShapesArg(shapes)
-    this.shapes = this.shapes.filter(shape => !shapesToMove.includes(shape)).concat(shapesToMove)
-    this.app.persist()
+    let others = this.shapes.filter(shape => !shapesToMove.includes(shape))
+    this.shapes = others.concat(shapesToMove)
+    const info = {op: "bringToFront", shapes: shapesToMove, before: others[others.length - 1]}
+    this.app.persist(info)
+    this.persistInfo = info
     return this
     return this
   }
   }
 
 
   @action sendToBack = (shapes: S[] | string[]): this => {
   @action sendToBack = (shapes: S[] | string[]): this => {
     const shapesToMove = this.parseShapesArg(shapes)
     const shapesToMove = this.parseShapesArg(shapes)
-    this.shapes = shapesToMove.concat(this.shapes.filter(shape => !shapesToMove.includes(shape)))
-    this.app.persist()
+    let others = this.shapes.filter(shape => !shapesToMove.includes(shape))
+    this.shapes = shapesToMove.concat(others)
+    this.app.persist({op: "sendToBack", shapes: shapesToMove, next: others[0]})
     return this
     return this
   }
   }
 
 

+ 1 - 1
src/main/frontend/extensions/tldraw.cljs

@@ -178,7 +178,7 @@
    (p/let [_ @*transact-result
    (p/let [_ @*transact-result
            result (p/do!
            result (p/do!
                    (state/set-state! [:whiteboard/last-persisted-at (state/get-current-repo)] (util/time-ms))
                    (state/set-state! [:whiteboard/last-persisted-at (state/get-current-repo)] (util/time-ms))
-                   (whiteboard-handler/<transact-tldr-delta! page-name app (.-replace info)))]
+                   (whiteboard-handler/<transact-tldr-delta! page-name app info))]
      (reset! *transact-result result))
      (reset! *transact-result result))
    (p/catch (fn [^js error]
    (p/catch (fn [^js error]
               (js/console.error error)
               (js/console.error error)

+ 71 - 32
src/main/frontend/handler/whiteboard.cljs

@@ -18,7 +18,9 @@
             [clojure.set :as set]
             [clojure.set :as set]
             [clojure.string :as string]
             [clojure.string :as string]
             [cljs-bean.core :as bean]
             [cljs-bean.core :as bean]
-            [logseq.db.sqlite.util :as sqlite-util]))
+            [logseq.db.sqlite.util :as sqlite-util]
+            [logseq.db.frontend.order :as db-order]
+            [logseq.outliner.core :as outliner-core]))
 
 
 (defn js->clj-keywordize
 (defn js->clj-keywordize
   [obj]
   [obj]
@@ -29,16 +31,12 @@
     (gp-whiteboard/shape->block repo shape page-id)))
     (gp-whiteboard/shape->block repo shape page-id)))
 
 
 (defn- build-shapes
 (defn- build-shapes
-  [page-block blocks]
-  (let [page-metadata (pu/get-block-property-value page-block :logseq.property.tldraw/page)
-        shapes-index (:shapes-index page-metadata)
-        shape-id->index (zipmap shapes-index (range 0 (count shapes-index)))]
+  [blocks]
+  (let [blocks (db/sort-by-order blocks)]
     (->> blocks
     (->> blocks
-         (map (fn [block]
-                (assoc block :index (get shape-id->index (str (:block/uuid block)) 0))))
+         (db/sort-by-order blocks)
          (filter pu/shape-block?)
          (filter pu/shape-block?)
          (map pu/block->shape)
          (map pu/block->shape)
-         (sort-by :index)
          (map (fn [shape]
          (map (fn [shape]
                 (if-let [page-id (:pageId shape)]
                 (if-let [page-id (:pageId shape)]
                   (let [page (db/get-page page-id)]
                   (let [page (db/get-page page-id)]
@@ -48,7 +46,7 @@
 
 
 (defn- whiteboard-clj->tldr [page-block blocks]
 (defn- whiteboard-clj->tldr [page-block blocks]
   (let [id (str (:block/uuid page-block))
   (let [id (str (:block/uuid page-block))
-        shapes (build-shapes page-block blocks)
+        shapes (build-shapes blocks)
         tldr-page (pu/page-block->tldr-page page-block)
         tldr-page (pu/page-block->tldr-page page-block)
         assets (:assets tldr-page)
         assets (:assets tldr-page)
         tldr-page (dissoc tldr-page :assets)]
         tldr-page (dissoc tldr-page :assets)]
@@ -61,14 +59,13 @@
                               :shapes shapes})]})))
                               :shapes shapes})]})))
 
 
 (defn db-build-page-block
 (defn db-build-page-block
-  [page-entity page-name tldraw-page assets shapes-index]
+  [page-entity page-name tldraw-page assets]
   (let [get-k #(gobj/get tldraw-page %)
   (let [get-k #(gobj/get tldraw-page %)
         tldraw-page {:id (get-k "id")
         tldraw-page {:id (get-k "id")
                      :name (get-k "name")
                      :name (get-k "name")
                      :bindings (js->clj-keywordize (get-k "bindings"))
                      :bindings (js->clj-keywordize (get-k "bindings"))
                      :nonce (get-k "nonce")
                      :nonce (get-k "nonce")
-                     :assets (js->clj-keywordize assets)
-                     :shapes-index shapes-index}]
+                     :assets (js->clj-keywordize assets)}]
     {:db/id (:db/id page-entity)
     {:db/id (:db/id page-entity)
      :block/original-name page-name
      :block/original-name page-name
      :block/name (util/page-name-sanity-lc page-name)
      :block/name (util/page-name-sanity-lc page-name)
@@ -81,7 +78,7 @@
                            (util/time-ms))}))
                            (util/time-ms))}))
 
 
 (defn file-build-page-block
 (defn file-build-page-block
-  [page-entity page-name tldraw-page assets shapes-index]
+  [page-entity page-name tldraw-page assets]
   (let [get-k #(gobj/get tldraw-page %)]
   (let [get-k #(gobj/get tldraw-page %)]
     {:block/original-name page-name
     {:block/original-name page-name
      :block/name (util/page-name-sanity-lc page-name)
      :block/name (util/page-name-sanity-lc page-name)
@@ -94,37 +91,36 @@
                          :name (get-k "name")
                          :name (get-k "name")
                          :bindings (js->clj-keywordize (get-k "bindings"))
                          :bindings (js->clj-keywordize (get-k "bindings"))
                          :nonce (get-k "nonce")
                          :nonce (get-k "nonce")
-                         :assets (js->clj-keywordize assets)
-                         :shapes-index shapes-index}}
+                         :assets (js->clj-keywordize assets)}}
      :block/updated-at (util/time-ms)
      :block/updated-at (util/time-ms)
      :block/created-at (or (:block/created-at page-entity)
      :block/created-at (or (:block/created-at page-entity)
                            (util/time-ms))}))
                            (util/time-ms))}))
 
 
 (defn build-page-block
 (defn build-page-block
-  [page-entity page-name tldraw-page assets shapes-index]
+  [page-entity page-name tldraw-page assets]
   (let [f (if (config/db-based-graph? (state/get-current-repo))
   (let [f (if (config/db-based-graph? (state/get-current-repo))
             db-build-page-block
             db-build-page-block
             file-build-page-block)]
             file-build-page-block)]
-    (f page-entity page-name tldraw-page assets shapes-index)))
+    (f page-entity page-name tldraw-page assets)))
 
 
 (defn- compute-tx
 (defn- compute-tx
   [^js app ^js tl-page new-id-nonces db-id-nonces page-uuid replace?]
   [^js app ^js tl-page new-id-nonces db-id-nonces page-uuid replace?]
   (let [page-entity (db/get-page page-uuid)
   (let [page-entity (db/get-page page-uuid)
         assets (js->clj-keywordize (.getCleanUpAssets app))
         assets (js->clj-keywordize (.getCleanUpAssets app))
-        new-shapes (.-shapes tl-page)
-        shapes-index (map #(gobj/get % "id") new-shapes)
-        shape-id->index (zipmap shapes-index (range (.-length new-shapes)))
         upsert-shapes (->> (set/difference new-id-nonces db-id-nonces)
         upsert-shapes (->> (set/difference new-id-nonces db-id-nonces)
                            (map (fn [{:keys [id]}]
                            (map (fn [{:keys [id]}]
                                   (-> (.-serialized ^js (.getShapeById tl-page id))
                                   (-> (.-serialized ^js (.getShapeById tl-page id))
-                                      js->clj-keywordize
-                                      (assoc :index (get shape-id->index id)))))
+                                      js->clj-keywordize)))
                            (set))
                            (set))
         old-ids (set (map :id db-id-nonces))
         old-ids (set (map :id db-id-nonces))
         new-ids (set (map :id new-id-nonces))
         new-ids (set (map :id new-id-nonces))
         created-ids (->> (set/difference new-ids old-ids)
         created-ids (->> (set/difference new-ids old-ids)
                          (remove string/blank?)
                          (remove string/blank?)
                          (set))
                          (set))
+        new-orders (when (seq created-ids)
+                     (let [max-key (last (sort (map :block/order (:block/_page page-entity))))]
+                       (db-order/gen-n-keys (count created-ids) max-key nil)))
+        new-id->order (when (seq created-ids) (zipmap created-ids new-orders))
         created-shapes (set (filter #(created-ids (:id %)) upsert-shapes))
         created-shapes (set (filter #(created-ids (:id %)) upsert-shapes))
         deleted-ids (->> (set/difference old-ids new-ids)
         deleted-ids (->> (set/difference old-ids new-ids)
                          (remove string/blank?))
                          (remove string/blank?))
@@ -136,9 +132,13 @@
         deleted-shapes-tx (mapv (fn [id] [:db/retractEntity [:block/uuid (uuid id)]]) deleted-ids)
         deleted-shapes-tx (mapv (fn [id] [:db/retractEntity [:block/uuid (uuid id)]]) deleted-ids)
         upserted-blocks (->> upsert-shapes
         upserted-blocks (->> upsert-shapes
                              (map #(shape->block % (:db/id page-entity)))
                              (map #(shape->block % (:db/id page-entity)))
-                             (map sqlite-util/block-with-timestamps))
+                             (map sqlite-util/block-with-timestamps)
+                             (map (fn [block]
+                                    (if-let [new-order (when new-id->order (get new-id->order (str (:block/uuid block))))]
+                                      (assoc block :block/order new-order)
+                                      block))))
         page-name (or (:block/original-name page-entity) (str page-uuid))
         page-name (or (:block/original-name page-entity) (str page-uuid))
-        page-block (build-page-block page-entity page-name tl-page assets shapes-index)]
+        page-block (build-page-block page-entity page-name tl-page assets)]
     (when (or (seq upserted-blocks)
     (when (or (seq upserted-blocks)
               (seq deleted-shapes-tx)
               (seq deleted-shapes-tx)
               (not= (:block/properties page-block)
               (not= (:block/properties page-block)
@@ -153,12 +153,51 @@
 
 
 (defonce *last-shapes-nonce (atom {}))
 (defonce *last-shapes-nonce (atom {}))
 
 
+(defn- get-shape-block-id
+  [^js shape]
+  (uuid (.-id shape)))
+
+(defn- handle-order-update!
+  [page info]
+  (let [op (:op info)
+        moved-shapes (:shapes info)
+        shape-ids (mapv get-shape-block-id moved-shapes)]
+    (case op
+      "sendToBack"
+      (let [next-order (when-let [id (get-shape-block-id (:next info))]
+                         (:block/order (db/entity [:block/uuid id])))
+            new-orders (db-order/gen-n-keys (count shape-ids) nil next-order)
+            tx-data (conj
+                     (map-indexed (fn [idx id]
+                                    {:block/uuid id
+                                     :block/order (nth new-orders idx)}) shape-ids)
+                     (outliner-core/block-with-updated-at {:db/id (:db/id page)}))]
+        tx-data)
+
+      "bringToFront"
+      (let [before-order (when-let [id (get-shape-block-id (:before info))]
+                           (:block/order (db/entity [:block/uuid id])))
+            new-orders (db-order/gen-n-keys (count shape-ids) before-order nil)
+            tx-data (conj
+                     (->>
+                      (map-indexed (fn [idx id]
+                                     (when (db/entity [:block/uuid id])
+                                       {:block/uuid id
+                                        :block/order (nth new-orders idx)})) shape-ids)
+                      (remove nil?))
+                     (outliner-core/block-with-updated-at {:db/id (:db/id page)}))]
+        tx-data))))
+
 ;; FIXME: it seems that nonce for the page block will not be updated with new updates for the whiteboard
 ;; FIXME: it seems that nonce for the page block will not be updated with new updates for the whiteboard
 (defn <transact-tldr-delta!
 (defn <transact-tldr-delta!
-  [page-uuid ^js app replace?]
-  (let [tl-page ^js (second (first (.-pages app)))
-        shapes (.-shapes ^js tl-page)
+  [page-uuid ^js app ^js info*]
+  (let [info (bean/->clj info*)
+        replace? (:replace info)
+        tl-page ^js (second (first (.-pages app)))
         page-block (model/get-page page-uuid)
         page-block (model/get-page page-uuid)
+        order-tx-data (when (contains? #{"bringToFront" "sendToBack"} (:op info))
+                        (handle-order-update! page-block info))
+        shapes (.-shapes ^js tl-page)
         new-id-nonces (set (map-indexed (fn [_idx shape]
         new-id-nonces (set (map-indexed (fn [_idx shape]
                                           (let [id (.-id shape)]
                                           (let [id (.-id shape)]
                                             {:id id
                                             {:id id
@@ -170,8 +209,8 @@
                                 (map #(update % :id str)))))
                                 (map #(update % :id str)))))
         {:keys [page-block new-shapes deleted-shapes upserted-blocks delete-blocks metadata] :as result}
         {:keys [page-block new-shapes deleted-shapes upserted-blocks delete-blocks metadata] :as result}
         (compute-tx app tl-page new-id-nonces db-id-nonces page-uuid replace?)]
         (compute-tx app tl-page new-id-nonces db-id-nonces page-uuid replace?)]
-    (when (seq result)
-      (let [tx-data (concat delete-blocks [page-block] upserted-blocks)
+    (when (or (seq result) (seq order-tx-data))
+      (let [tx-data (concat delete-blocks [page-block] upserted-blocks order-tx-data)
             metadata' (cond
             metadata' (cond
                         ;; group
                         ;; group
                         (some #(= "group" (:type %)) new-shapes)
                         (some #(= "group" (:type %)) new-shapes)
@@ -313,7 +352,7 @@
   ([{:keys [pages blocks]} api]
   ([{:keys [pages blocks]} api]
    (let [page-block (first pages)
    (let [page-block (first pages)
          ;; FIXME: should also clone normal blocks
          ;; FIXME: should also clone normal blocks
-         shapes (build-shapes page-block blocks)
+         shapes (build-shapes blocks)
          tldr-page (pu/page-block->tldr-page page-block)
          tldr-page (pu/page-block->tldr-page page-block)
          assets (:assets tldr-page)
          assets (:assets tldr-page)
          bindings (:bindings tldr-page)]
          bindings (:bindings tldr-page)]
@@ -342,8 +381,8 @@
     (let [tl-page ^js (second (first (.-pages app)))]
     (let [tl-page ^js (second (first (.-pages app)))]
       (when tl-page
       (when tl-page
         (when-let [page (db/get-page page-uuid)]
         (when-let [page (db/get-page page-uuid)]
-          (let [page-metadata (pu/get-block-property-value page :logseq.property.tldraw/page)
-                shapes-index (:shapes-index page-metadata)]
+          (let [shapes-index (->> (db/sort-by-order (:block/_page page))
+                                  (map (comp str :block/uuid)))]
             (when (seq shapes-index)
             (when (seq shapes-index)
               (.updateShapesIndex tl-page (bean/->js shapes-index)))))))))
               (.updateShapesIndex tl-page (bean/->js shapes-index)))))))))