refactor: use :block/order for whiteboard blocks

experiment/tanstack-table
Tienson Qin 2024-06-04 12:29:45 +08:00
parent 3c1299c20a
commit 3bbcc8cd68
8 changed files with 93 additions and 72 deletions

View File

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

View File

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

View File

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

View File

@ -450,7 +450,7 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
this.app.currentPage.removeShapes(...selectedGroups)
// group all shapes
const selectedShapes = shapes.filter(s => s.type !== 'group')
let selectedShapes = shapes.filter(s => s.type !== 'group')
if (selectedShapes.length > 1) {
const ShapeGroup = this.app.getShapeClass('group')
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.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)
this.app.bringForward(selectedShapes)
}
this.app.history.resume()
this.app.persist()
this.app.persist(this.app.currentPage.persistInfo)
}
unGroup = (shapes: S[] = this.app.allSelectedShapesArray) => {

View File

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

View File

@ -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.app = app
this.nonce = nonce || 0
this.persistInfo = null
this.addShapes(...shapes)
makeObservable(this)
@ -137,50 +138,30 @@ export class TLPage<S extends TLShape = TLShape, E extends TLEventMap = TLEventM
}
@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
}
@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
}
@action bringToFront = (shapes: S[] | string[]): this => {
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
}
@action sendToBack = (shapes: S[] | string[]): this => {
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
}

View File

@ -178,7 +178,7 @@
(p/let [_ @*transact-result
result (p/do!
(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))
(p/catch (fn [^js error]
(js/console.error error)

View File

@ -18,7 +18,9 @@
[clojure.set :as set]
[clojure.string :as string]
[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
[obj]
@ -29,16 +31,12 @@
(gp-whiteboard/shape->block repo shape page-id)))
(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
(map (fn [block]
(assoc block :index (get shape-id->index (str (:block/uuid block)) 0))))
(db/sort-by-order blocks)
(filter pu/shape-block?)
(map pu/block->shape)
(sort-by :index)
(map (fn [shape]
(if-let [page-id (:pageId shape)]
(let [page (db/get-page page-id)]
@ -48,7 +46,7 @@
(defn- whiteboard-clj->tldr [page-block blocks]
(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)
assets (:assets tldr-page)
tldr-page (dissoc tldr-page :assets)]
@ -61,14 +59,13 @@
:shapes shapes})]})))
(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 %)
tldraw-page {:id (get-k "id")
:name (get-k "name")
:bindings (js->clj-keywordize (get-k "bindings"))
:nonce (get-k "nonce")
:assets (js->clj-keywordize assets)
:shapes-index shapes-index}]
:assets (js->clj-keywordize assets)}]
{:db/id (:db/id page-entity)
:block/original-name page-name
:block/name (util/page-name-sanity-lc page-name)
@ -81,7 +78,7 @@
(util/time-ms))}))
(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 %)]
{:block/original-name page-name
:block/name (util/page-name-sanity-lc page-name)
@ -94,37 +91,36 @@
:name (get-k "name")
:bindings (js->clj-keywordize (get-k "bindings"))
: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/created-at (or (:block/created-at page-entity)
(util/time-ms))}))
(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))
db-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
[^js app ^js tl-page new-id-nonces db-id-nonces page-uuid replace?]
(let [page-entity (db/get-page page-uuid)
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)
(map (fn [{:keys [id]}]
(-> (.-serialized ^js (.getShapeById tl-page id))
js->clj-keywordize
(assoc :index (get shape-id->index id)))))
js->clj-keywordize)))
(set))
old-ids (set (map :id db-id-nonces))
new-ids (set (map :id new-id-nonces))
created-ids (->> (set/difference new-ids old-ids)
(remove string/blank?)
(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))
deleted-ids (->> (set/difference old-ids new-ids)
(remove string/blank?))
@ -136,9 +132,13 @@
deleted-shapes-tx (mapv (fn [id] [:db/retractEntity [:block/uuid (uuid id)]]) deleted-ids)
upserted-blocks (->> upsert-shapes
(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-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)
(seq deleted-shapes-tx)
(not= (:block/properties page-block)
@ -153,12 +153,51 @@
(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
(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)
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]
(let [id (.-id shape)]
{:id id
@ -170,8 +209,8 @@
(map #(update % :id str)))))
{: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?)]
(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
;; group
(some #(= "group" (:type %)) new-shapes)
@ -313,7 +352,7 @@
([{:keys [pages blocks]} api]
(let [page-block (first pages)
;; 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)
assets (:assets tldr-page)
bindings (:bindings tldr-page)]
@ -342,8 +381,8 @@
(let [tl-page ^js (second (first (.-pages app)))]
(when tl-page
(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)
(.updateShapesIndex tl-page (bean/->js shapes-index)))))))))