remote_update.cljs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. (ns frontend.worker.rtc.remote-update
  2. "Fns about applying remote updates"
  3. (:require [clojure.data :as data]
  4. [clojure.set :as set]
  5. [clojure.string :as string]
  6. [datascript.core :as d]
  7. [frontend.worker-common.util :as worker-util]
  8. [frontend.worker.handler.page :as worker-page]
  9. [frontend.worker.rtc.asset :as r.asset]
  10. [frontend.worker.rtc.client-op :as client-op]
  11. [frontend.worker.rtc.const :as rtc-const]
  12. [frontend.worker.rtc.log-and-state :as rtc-log-and-state]
  13. [frontend.worker.rtc.malli-schema :as rtc-schema]
  14. [frontend.worker.state :as worker-state]
  15. [lambdaisland.glogi :as log]
  16. [logseq.clj-fractional-indexing :as index]
  17. [logseq.common.defkeywords :refer [defkeywords]]
  18. [logseq.common.util :as common-util]
  19. [logseq.db :as ldb]
  20. [logseq.db.common.property-util :as db-property-util]
  21. [logseq.db.frontend.property :as db-property]
  22. [logseq.graph-parser.whiteboard :as gp-whiteboard]
  23. [logseq.outliner.batch-tx :as batch-tx]
  24. [logseq.outliner.core :as outliner-core]
  25. [logseq.outliner.transaction :as outliner-tx]))
  26. (defkeywords
  27. ::need-pull-remote-data {:doc "
  28. remote-update's :remote-t-before > :local-tx,
  29. so need to pull earlier remote-data from websocket."})
  30. (defmulti ^:private transact-db! (fn [action & _args] action))
  31. (defmethod transact-db! :delete-blocks [_ & args]
  32. (outliner-tx/transact!
  33. {:persist-op? false
  34. :gen-undo-ops? false
  35. :outliner-op :delete-blocks
  36. :transact-opts {:repo (first args)
  37. :conn (second args)}}
  38. (apply outliner-core/delete-blocks! args)))
  39. (defmethod transact-db! :move-blocks [_ & args]
  40. (outliner-tx/transact!
  41. {:persist-op? false
  42. :gen-undo-ops? false
  43. :outliner-op :move-blocks
  44. :transact-opts {:repo (first args)
  45. :conn (second args)}}
  46. (apply outliner-core/move-blocks! args)))
  47. (defmethod transact-db! :update-block-order-directly [_ _repo conn block-uuid block-parent-uuid block-order]
  48. ;; transact :block/parent and :block/order directly,
  49. ;; check :block/order has any conflicts with other blocks
  50. (let [parent-ent (when block-parent-uuid (d/entity @conn [:block/uuid block-parent-uuid]))
  51. sorted-order+block-uuid-coll (sort-by first (map (juxt :block/order :block/uuid) (:block/_parent parent-ent)))
  52. block-order*
  53. (if-let [[start-order end-order]
  54. (reduce
  55. (fn [[start-order] [current-order current-block-uuid]]
  56. (when start-order
  57. (if (= current-block-uuid block-uuid)
  58. (reduced nil)
  59. (reduced [start-order current-order])))
  60. (let [compare-order (compare current-order block-order)]
  61. (cond
  62. (and (zero? compare-order)
  63. (not= current-block-uuid block-uuid))
  64. ;; found conflict order
  65. [current-order nil]
  66. (and (zero? compare-order)
  67. (= current-block-uuid block-uuid))
  68. ;; this block already has expected :block/order
  69. (reduced nil)
  70. (pos? compare-order) ;not found conflict order
  71. (reduced nil)
  72. (neg? compare-order)
  73. nil)))
  74. nil sorted-order+block-uuid-coll)]
  75. (index/generate-key-between start-order end-order)
  76. block-order)]
  77. (ldb/transact! conn [{:block/uuid block-uuid :block/order block-order*}]
  78. {:rtc-op? true
  79. :persist-op? false
  80. :gen-undo-ops? false})
  81. ;; TODO: add ops when block-order* != block-order
  82. ))
  83. (defmethod transact-db! :move-blocks&persist-op [_ & args]
  84. (outliner-tx/transact!
  85. {:persist-op? true
  86. :gen-undo-ops? false
  87. :outliner-op :move-blocks
  88. :transact-opts {:repo (first args)
  89. :conn (second args)}}
  90. (apply outliner-core/move-blocks! args)))
  91. (defmethod transact-db! :insert-blocks [_ repo conn blocks target opts]
  92. (outliner-tx/transact!
  93. {:persist-op? false
  94. :gen-undo-ops? false
  95. :outliner-op :insert-blocks
  96. :transact-opts {:repo repo
  97. :conn conn}}
  98. (let [opts' (assoc opts :keep-block-order? true)]
  99. (outliner-core/insert-blocks! repo conn blocks target opts')))
  100. (doseq [block blocks]
  101. (assert (some? (d/entity @conn [:block/uuid (:block/uuid block)]))
  102. {:msg "insert-block failed"
  103. :block block
  104. :target target})))
  105. (defmethod transact-db! :insert-no-order-blocks [_ conn block-uuid+parent-coll]
  106. (ldb/transact! conn
  107. (mapv (fn [[block-uuid block-parent]]
  108. ;; add block/content block/format to satisfy the normal-block schema
  109. (cond-> {:block/uuid block-uuid}
  110. block-parent (assoc :block/parent [:block/uuid block-parent])))
  111. block-uuid+parent-coll)
  112. {:persist-op? false
  113. :gen-undo-ops? false
  114. :rtc-op? true}))
  115. (defmethod transact-db! :save-block [_ & args]
  116. (outliner-tx/transact!
  117. {:persist-op? false
  118. :gen-undo-ops? false
  119. :outliner-op :save-block
  120. :transact-opts {:repo (first args)
  121. :conn (second args)}}
  122. (apply outliner-core/save-block! args)))
  123. (defmethod transact-db! :delete-whiteboard-blocks [_ conn block-uuids]
  124. (ldb/transact! conn
  125. (mapv (fn [block-uuid] [:db/retractEntity [:block/uuid block-uuid]]) block-uuids)
  126. {:persist-op? false
  127. :gen-undo-ops? false
  128. :rtc-op? true}))
  129. (defmethod transact-db! :upsert-whiteboard-block [_ conn blocks]
  130. (ldb/transact! conn blocks {:persist-op? false
  131. :gen-undo-ops? false
  132. :rtc-op? true}))
  133. (defn- group-remote-remove-ops-by-whiteboard-block
  134. "return {true [<whiteboard-block-ops>], false [<other-ops>]}"
  135. [db remote-remove-ops]
  136. (group-by (fn [{:keys [block-uuid]}]
  137. (boolean
  138. (when-let [block (d/entity db [:block/uuid block-uuid])]
  139. (ldb/whiteboard? (:block/parent block)))))
  140. remote-remove-ops))
  141. (defn- apply-remote-remove-ops-helper
  142. [conn remove-ops]
  143. (let [block-uuid->entity (into {}
  144. (keep
  145. (fn [op]
  146. (when-let [block-uuid (:block-uuid op)]
  147. (when-let [ent (d/entity @conn [:block/uuid block-uuid])]
  148. [block-uuid ent])))
  149. remove-ops))
  150. block-uuid-set (set (keys block-uuid->entity))
  151. block-uuids-need-move
  152. (set
  153. (mapcat
  154. (fn [[_block-uuid ent]]
  155. (set/difference (set (map :block/uuid (:block/_parent ent))) block-uuid-set))
  156. block-uuid->entity))]
  157. {:block-uuids-need-move block-uuids-need-move
  158. :block-uuids-to-remove block-uuid-set}))
  159. (defn- apply-remote-remove-ops
  160. [repo conn date-formatter remove-ops]
  161. (let [{whiteboard-block-ops true other-ops false} (group-remote-remove-ops-by-whiteboard-block @conn remove-ops)]
  162. (transact-db! :delete-whiteboard-blocks conn (map :block-uuid whiteboard-block-ops))
  163. (let [{:keys [block-uuids-need-move block-uuids-to-remove]}
  164. (apply-remote-remove-ops-helper conn other-ops)]
  165. ;; move to page-block's first child
  166. (doseq [block-uuid block-uuids-need-move]
  167. (when-let [b (d/entity @conn [:block/uuid block-uuid])]
  168. (when-let [target-b
  169. (d/entity @conn (:db/id (:block/page (d/entity @conn [:block/uuid block-uuid]))))]
  170. (transact-db! :move-blocks&persist-op repo conn [b] target-b {:sibling? false}))))
  171. (doseq [block-uuid block-uuids-to-remove]
  172. (when-let [b (d/entity @conn [:block/uuid block-uuid])]
  173. (transact-db! :delete-blocks repo conn date-formatter [b] {}))))))
  174. (defn- insert-or-move-block
  175. [repo conn block-uuid remote-parents remote-block-order move? op-value]
  176. (when (or (seq remote-parents) remote-block-order) ;at least one of parent|order exists
  177. (let [first-remote-parent (first remote-parents)
  178. local-parent (when first-remote-parent (d/entity @conn [:block/uuid first-remote-parent]))
  179. whiteboard-page-block? (ldb/whiteboard? local-parent)
  180. b (d/entity @conn [:block/uuid block-uuid])]
  181. (case [whiteboard-page-block? (some? local-parent) (some? remote-block-order)]
  182. [false true true]
  183. (do (if move?
  184. (transact-db! :move-blocks repo conn [b] local-parent {:sibling? false})
  185. (transact-db! :insert-blocks repo conn
  186. [{:block/uuid block-uuid
  187. :block/title ""}]
  188. local-parent {:sibling? false :keep-uuid? true}))
  189. (transact-db! :update-block-order-directly repo conn block-uuid first-remote-parent remote-block-order))
  190. [false true false]
  191. (if move?
  192. (transact-db! :move-blocks repo conn [b] local-parent
  193. {:sibling? false})
  194. (transact-db! :insert-no-order-blocks conn [[block-uuid first-remote-parent]]))
  195. [false false true] ;no parent, only update order. e.g. update property's order
  196. (when (and (empty? remote-parents) move?)
  197. (transact-db! :update-block-order-directly repo conn block-uuid nil remote-block-order))
  198. ([true false false] [true false true] [true true false] [true true true])
  199. (throw (ex-info "Not implemented yet for whiteboard" {:op-value op-value}))
  200. (let [e (ex-info "Don't know where to insert" {:block-uuid block-uuid
  201. :remote-parents remote-parents
  202. :remote-block-order remote-block-order
  203. :move? move?
  204. :op-value op-value})]
  205. (log/error :insert-or-move-block e)
  206. (throw e))))))
  207. (defn- move-ops-map->sorted-move-ops
  208. [move-ops-map]
  209. (let [uuid->dep-uuids (into {} (map (fn [[uuid env]] [uuid (set (conj (:parents env)))]) move-ops-map))
  210. all-uuids (set (keys move-ops-map))
  211. sorted-uuids
  212. (loop [r []
  213. rest-uuids all-uuids
  214. uuid (first rest-uuids)]
  215. (if-not uuid
  216. r
  217. (let [dep-uuids (uuid->dep-uuids uuid)]
  218. (if-let [next-uuid (first (set/intersection dep-uuids rest-uuids))]
  219. (recur r rest-uuids next-uuid)
  220. (let [rest-uuids* (disj rest-uuids uuid)]
  221. (recur (conj r uuid) rest-uuids* (first rest-uuids*)))))))]
  222. (mapv move-ops-map sorted-uuids)))
  223. (defn- apply-remote-remove-page-ops
  224. [repo conn remove-page-ops]
  225. (doseq [op remove-page-ops]
  226. (worker-page/delete! repo conn (:block-uuid op) {:persist-op? false})))
  227. (defn- get-schema-ref+cardinality
  228. [db-schema attr]
  229. (when-let [k-schema (get db-schema attr)]
  230. [(= :db.type/ref (:db/valueType k-schema))
  231. (= :db.cardinality/many (:db/cardinality k-schema))]))
  232. (defn- patch-remote-attr-map-by-local-av-coll
  233. [remote-attr-map local-av-coll]
  234. (let [a->add->v-set
  235. (reduce
  236. (fn [m [a v _t add?]]
  237. (let [{add-vset true retract-vset false} (get m a {true #{} false #{}})]
  238. (assoc m a {true ((if add? conj disj) add-vset v)
  239. false ((if add? disj conj) retract-vset v)})))
  240. {} local-av-coll)
  241. updated-remote-attr-map1
  242. (keep
  243. (fn [[remote-a remote-v]]
  244. (when-let [{add-vset true retract-vset false} (get a->add->v-set remote-a)]
  245. [remote-a
  246. (if (coll? remote-v)
  247. (-> (set remote-v)
  248. (set/union add-vset)
  249. (set/difference retract-vset)
  250. vec)
  251. (cond
  252. (seq add-vset) (first add-vset)
  253. (contains? retract-vset remote-v) nil))]))
  254. remote-attr-map)
  255. updated-remote-attr-map2
  256. (keep
  257. (fn [[a add->v-set]]
  258. (when-let [ns (namespace a)]
  259. (when (and (not (contains? #{"block"} ns))
  260. ;; FIXME: only handle non-block/xxx attrs,
  261. ;; because some :block/xxx attrs are card-one, we only generate card-many values here
  262. (not (contains? remote-attr-map a)))
  263. (when-let [v-set (not-empty (get add->v-set true))]
  264. [a (vec v-set)]))))
  265. a->add->v-set)]
  266. (into remote-attr-map
  267. (concat updated-remote-attr-map1 updated-remote-attr-map2))))
  268. (defn- update-remote-data-by-local-unpushed-ops
  269. "when remote-data request client to move/update/remove/... blocks,
  270. these updates maybe not needed or need to update, because this client just updated some of these blocks,
  271. so we need to update these remote-data by local-ops"
  272. [affected-blocks-map local-unpushed-ops]
  273. (assert (client-op/ops-coercer local-unpushed-ops) local-unpushed-ops)
  274. (reduce
  275. (fn [affected-blocks-map local-op]
  276. (let [local-op-value (last local-op)]
  277. (case (first local-op)
  278. :move
  279. (let [block-uuid (:block-uuid local-op-value)
  280. remote-op (get affected-blocks-map block-uuid)]
  281. (case (:op remote-op)
  282. :remove (dissoc affected-blocks-map (:block-uuid remote-op))
  283. :move (dissoc affected-blocks-map (:self remote-op))
  284. ;; remove block/order, parents in update-attrs, if there're some unpushed local move-ops
  285. (:update-attrs :move+update-attrs)
  286. (update affected-blocks-map (:self remote-op) dissoc :block/order :parents)
  287. ;; default
  288. affected-blocks-map))
  289. :update
  290. (let [block-uuid (:block-uuid local-op-value)]
  291. (if-let [remote-op (get affected-blocks-map block-uuid)]
  292. (let [remote-op* (if (#{:update-attrs :move :move+update-attrs} (:op remote-op))
  293. (patch-remote-attr-map-by-local-av-coll remote-op (:av-coll local-op-value))
  294. remote-op)]
  295. (assoc affected-blocks-map block-uuid remote-op*))
  296. affected-blocks-map))
  297. :remove
  298. ;; TODO: if this block's updated by others, we shouldn't remove it
  299. ;; but now, we don't know who updated this block recv from remote
  300. ;; once we have this attr(:block/updated-by, :block/created-by), we can finish this TODO
  301. (let [block-uuid (:block-uuid local-op-value)]
  302. (dissoc affected-blocks-map block-uuid))
  303. ;;else
  304. affected-blocks-map)))
  305. affected-blocks-map local-unpushed-ops))
  306. (defn- affected-blocks->diff-type-ops
  307. [repo affected-blocks]
  308. (let [unpushed-block-ops (client-op/get-all-block-ops repo)
  309. affected-blocks-map* (if unpushed-block-ops
  310. (update-remote-data-by-local-unpushed-ops
  311. affected-blocks unpushed-block-ops)
  312. affected-blocks)
  313. {remove-ops-map :remove move-ops-map :move update-ops-map :update-attrs
  314. move+update-ops-map :move+update-attrs
  315. update-page-ops-map :update-page remove-page-ops-map :remove-page}
  316. (update-vals
  317. (group-by (fn [[_ env]] (get env :op)) affected-blocks-map*)
  318. (partial into {}))]
  319. {:remove-ops-map remove-ops-map
  320. :move-ops-map (merge move-ops-map move+update-ops-map)
  321. :update-ops-map (merge update-ops-map move+update-ops-map)
  322. :update-page-ops-map update-page-ops-map
  323. :remove-page-ops-map remove-page-ops-map}))
  324. (defn- check-block-pos
  325. "NOTE: some blocks don't have :block/order (e.g. whiteboard blocks)"
  326. [db block-uuid remote-parents remote-block-order]
  327. (let [local-b (d/entity db [:block/uuid block-uuid])
  328. remote-parent-uuid (first remote-parents)]
  329. (cond
  330. (nil? local-b)
  331. :not-exist
  332. (not= [remote-block-order remote-parent-uuid]
  333. [(:block/order local-b) (:block/uuid (:block/parent local-b))])
  334. :wrong-pos
  335. :else nil)))
  336. (defn- upsert-whiteboard-block
  337. [repo conn {:keys [parents properties] :as _op-value}]
  338. (let [db @conn
  339. first-remote-parent (first parents)]
  340. (when-let [local-parent (d/entity db [:block/uuid first-remote-parent])]
  341. (let [page-id (:db/id local-parent)
  342. properties* (ldb/read-transit-str properties)
  343. shape-property-id (db-property-util/get-pid repo :logseq.property.tldraw/shape)
  344. shape (and (map? properties*)
  345. (get properties* shape-property-id))]
  346. (assert (some? page-id) local-parent)
  347. (assert (some? shape) properties*)
  348. (transact-db! :upsert-whiteboard-block conn [(gp-whiteboard/shape->block repo shape page-id)])))))
  349. (def ^:private update-op-watched-attrs
  350. #{:block/title
  351. :block/updated-at
  352. :block/created-at
  353. :block/alias
  354. :block/tags
  355. :block/link
  356. :block/journal-day
  357. :logseq.property/classes
  358. :logseq.property/value})
  359. (def ^:private watched-attr-ns
  360. (conj db-property/logseq-property-namespaces "logseq.class"))
  361. (defn- update-op-watched-attr?
  362. [attr]
  363. (or (contains? update-op-watched-attrs attr)
  364. (when-let [ns (namespace attr)]
  365. (or (contains? watched-attr-ns ns)
  366. (string/ends-with? ns ".property")
  367. (string/ends-with? ns ".class")))))
  368. (defn- diff-block-kv->tx-data
  369. [db db-schema e k local-v remote-v]
  370. (when-let [[ref? card-many?] (get-schema-ref+cardinality db-schema k)]
  371. (case [ref? card-many?]
  372. [true true]
  373. (let [[local-only remote-only] (data/diff (set local-v) (set remote-v))]
  374. (cond-> []
  375. (seq local-only) (concat (map (fn [block-uuid] [:db/retract e k [:block/uuid block-uuid]]) local-only))
  376. (seq remote-only) (concat (keep (fn [block-uuid]
  377. (when-let [db-id (:db/id (d/entity db [:block/uuid block-uuid]))]
  378. [:db/add e k db-id])) remote-only))))
  379. [true false]
  380. (let [remote-block-uuid (if (coll? remote-v) (first remote-v) remote-v)]
  381. (when (not= local-v remote-block-uuid)
  382. (if (nil? remote-block-uuid)
  383. [[:db/retract e k]]
  384. (when-let [db-id (:db/id (d/entity db [:block/uuid remote-block-uuid]))]
  385. [[:db/add e k db-id]]))))
  386. [false false]
  387. (let [remote-v* (if (coll? remote-v)
  388. (first (map ldb/read-transit-str remote-v))
  389. (ldb/read-transit-str remote-v))]
  390. (when (not= local-v remote-v*)
  391. (if (nil? remote-v*)
  392. [[:db/retract e k]]
  393. [[:db/add e k remote-v*]])))
  394. [false true]
  395. (let [_ (assert (or (nil? remote-v) (coll? remote-v)) {:remote-v remote-v :a k :e e})
  396. remote-v* (set (map ldb/read-transit-str remote-v))
  397. [local-only remote-only] (data/diff (set local-v) remote-v*)]
  398. (cond-> []
  399. (seq local-only) (concat (map (fn [v] [:db/retract e k v]) local-only))
  400. (seq remote-only) (concat (map (fn [v] [:db/add e k v]) remote-only)))))))
  401. (defn- diff-block-map->tx-data
  402. [db e local-block-map remote-block-map]
  403. (let [db-schema (d/schema db)
  404. tx-data1
  405. (mapcat
  406. (fn [[k local-v]]
  407. (let [remote-v (get remote-block-map k)]
  408. (seq (diff-block-kv->tx-data db db-schema e k local-v remote-v))))
  409. local-block-map)
  410. tx-data2
  411. (mapcat
  412. (fn [[k remote-v]]
  413. (let [local-v (get local-block-map k)]
  414. (seq (diff-block-kv->tx-data db db-schema e k local-v remote-v))))
  415. (apply dissoc remote-block-map (keys local-block-map)))]
  416. (concat tx-data1 tx-data2)))
  417. (defn- remote-op-value->tx-data
  418. "ignore-attr-set: don't update local attrs in this set"
  419. [db ent op-value ignore-attr-set]
  420. (assert (some? (:db/id ent)) ent)
  421. (let [db-schema (d/schema db)
  422. local-block-map (->> ent
  423. (filter (fn [[attr _]]
  424. (and (update-op-watched-attr? attr)
  425. (not (contains? ignore-attr-set attr)))))
  426. (keep (fn [[k v]]
  427. (when-let [[ref? card-many?] (get-schema-ref+cardinality db-schema k)]
  428. [k
  429. (case [ref? card-many?]
  430. [true true]
  431. (keep (fn [x] (when-let [e (:db/id x)] (:block/uuid (d/entity db e)))) v)
  432. [true false]
  433. (let [v* (some->> (:db/id v) (d/entity db) :block/uuid)]
  434. (assert (some? v*) v)
  435. v*)
  436. ;; else
  437. v)])))
  438. (into {}))
  439. remote-block-map (->> op-value
  440. (filter (comp update-op-watched-attr? first))
  441. (keep (fn [[k v]]
  442. ;; all non-built-in attrs is card-many in remote-op,
  443. ;; convert them according to the client db-schema
  444. (when-let [[_ref? card-many?] (get-schema-ref+cardinality db-schema k)]
  445. [k
  446. (if (and (coll? v) (not card-many?))
  447. (first v)
  448. v)])))
  449. (into {}))]
  450. (diff-block-map->tx-data db (:db/id ent) local-block-map remote-block-map)))
  451. (defn- remote-op-value->schema-tx-data
  452. [block-uuid op-value]
  453. (when-let [db-ident (:db/ident op-value)]
  454. (let [schema-map (some-> op-value :client/schema ldb/read-transit-str)]
  455. [(merge {:block/uuid block-uuid :db/ident db-ident} schema-map)])))
  456. (defn- update-block-order
  457. [e op-value]
  458. (if-let [order (:block/order op-value)]
  459. {:op-value (dissoc op-value :block/order)
  460. :tx-data [[:db/add e :block/order order]]}
  461. {:op-value op-value}))
  462. (defn- update-block-attrs
  463. [repo conn block-uuid {:keys [parents] :as op-value}]
  464. (when-let [ent (d/entity @conn [:block/uuid block-uuid])]
  465. (when (some (fn [k] (= "block" (namespace k))) (keys op-value)) ; there exists some :block/xxx attrs
  466. (let [{update-block-order-tx-data :tx-data op-value :op-value} (update-block-order (:db/id ent) op-value)
  467. first-remote-parent (first parents)
  468. local-parent (d/entity @conn [:block/uuid first-remote-parent])
  469. whiteboard-page-block? (ldb/whiteboard? local-parent)
  470. tx-meta {:persist-op? false :gen-undo-ops? false :rtc-op? true}]
  471. (if whiteboard-page-block?
  472. (upsert-whiteboard-block repo conn op-value)
  473. (do (when-let [schema-tx-data (remote-op-value->schema-tx-data block-uuid op-value)]
  474. (ldb/transact! conn schema-tx-data tx-meta))
  475. (when-let [tx-data (seq (remote-op-value->tx-data @conn ent (dissoc op-value :client/schema)
  476. rtc-const/ignore-attrs-when-syncing))]
  477. (ldb/transact! conn (concat tx-data update-block-order-tx-data) tx-meta))))))))
  478. (defn- apply-remote-update-ops
  479. [repo conn update-ops]
  480. (doseq [{:keys [parents self] block-order :block/order :as op-value} update-ops]
  481. (when (and parents block-order)
  482. (let [r (check-block-pos @conn self parents block-order)]
  483. (case r
  484. :not-exist
  485. (insert-or-move-block repo conn self parents block-order false op-value)
  486. :wrong-pos
  487. (insert-or-move-block repo conn self parents block-order true op-value)
  488. nil)))
  489. (update-block-attrs repo conn self op-value)))
  490. (defn- apply-remote-move-ops
  491. [repo conn sorted-move-ops]
  492. (doseq [{:keys [parents self] block-order :block/order :as op-value} sorted-move-ops]
  493. (let [r (check-block-pos @conn self parents block-order)]
  494. (case r
  495. :not-exist
  496. (do (insert-or-move-block repo conn self parents block-order false op-value)
  497. (update-block-attrs repo conn self op-value))
  498. :wrong-pos
  499. (insert-or-move-block repo conn self parents block-order true op-value)
  500. ;; else
  501. nil))))
  502. (defn- apply-remote-update-page-ops
  503. [repo conn update-page-ops]
  504. (let [config (worker-state/get-config repo)]
  505. (doseq [{:keys [self _page-name]
  506. title :block/title
  507. :as op-value} update-page-ops]
  508. (let [create-opts {:uuid self}
  509. [_ page-name page-uuid] (worker-page/rtc-create-page! conn config (ldb/read-transit-str title) create-opts)]
  510. ;; TODO: current page-create fn is buggy, even provide :uuid option, it will create-page with different uuid,
  511. ;; if there's already existing same name page
  512. (assert (= page-uuid self) {:page-name page-name :page-uuid page-uuid :should-be self})
  513. (assert (some? (d/entity @conn [:block/uuid page-uuid])) {:page-uuid page-uuid :page-name page-name})
  514. (update-block-attrs repo conn self op-value)))))
  515. (defn- ensure-refed-blocks-exist
  516. "Ensure refed-blocks from remote existing in client"
  517. [repo conn refed-blocks]
  518. (let [sorted-refed-blocks (common-util/sort-coll-by-dependency :block/uuid :block/parent refed-blocks)]
  519. (doseq [refed-block sorted-refed-blocks]
  520. (let [ent (d/entity @conn [:block/uuid (:block/uuid refed-block)])]
  521. (when-not ent
  522. (log/info :ensure-refed-blocks-exist refed-block)
  523. (if (:block/name refed-block)
  524. (apply-remote-update-page-ops repo conn [(-> refed-block
  525. (assoc :self (:block/uuid refed-block))
  526. (dissoc :block/uuid))])
  527. (apply-remote-move-ops repo conn [(-> refed-block
  528. (assoc :self (:block/uuid refed-block)
  529. :parents [(:block/parent refed-block)])
  530. (dissoc :block/uuid))])))))))
  531. (defn apply-remote-update
  532. "Apply remote-update(`remote-update-event`)"
  533. [graph-uuid repo conn date-formatter remote-update-event add-log-fn]
  534. (let [remote-update-data (:value remote-update-event)]
  535. (assert (rtc-schema/data-from-ws-validator remote-update-data) remote-update-data)
  536. (let [remote-t (:t remote-update-data)
  537. remote-t-before (:t-before remote-update-data)
  538. local-tx (client-op/get-local-tx repo)]
  539. (rtc-log-and-state/update-remote-t graph-uuid remote-t)
  540. (cond
  541. (not (and (pos? remote-t)
  542. (pos? remote-t-before)))
  543. (throw (ex-info "invalid remote-data" {:data remote-update-data}))
  544. (<= remote-t local-tx)
  545. (add-log-fn :rtc.log/apply-remote-update {:sub-type :skip :remote-t remote-t :local-t local-tx})
  546. (< local-tx remote-t-before)
  547. (do (add-log-fn :rtc.log/apply-remote-update {:sub-type :need-pull-remote-data
  548. :remote-t remote-t :local-t local-tx
  549. :remote-t-before remote-t-before})
  550. (throw (ex-info "need pull earlier remote-data"
  551. {:type ::need-pull-remote-data
  552. :local-tx local-tx})))
  553. (<= remote-t-before local-tx remote-t)
  554. (let [{affected-blocks-map :affected-blocks refed-blocks :refed-blocks} remote-update-data
  555. {:keys [remove-ops-map move-ops-map update-ops-map update-page-ops-map remove-page-ops-map]}
  556. (affected-blocks->diff-type-ops repo affected-blocks-map)
  557. remove-ops (vals remove-ops-map)
  558. sorted-move-ops (move-ops-map->sorted-move-ops move-ops-map)
  559. update-ops (vals update-ops-map)
  560. update-page-ops (vals update-page-ops-map)
  561. remove-page-ops (vals remove-page-ops-map)
  562. db-before @conn]
  563. (js/console.groupCollapsed "rtc/apply-remote-ops-log")
  564. (batch-tx/with-batch-tx-mode conn {:rtc-tx? true
  565. :persist-op? false
  566. :gen-undo-ops? false
  567. :frontend.worker.pipeline/skip-store-conn rtc-const/RTC-E2E-TEST}
  568. (worker-util/profile :ensure-refed-blocks-exist (ensure-refed-blocks-exist repo conn refed-blocks))
  569. (worker-util/profile :apply-remote-update-page-ops (apply-remote-update-page-ops repo conn update-page-ops))
  570. (worker-util/profile :apply-remote-move-ops (apply-remote-move-ops repo conn sorted-move-ops))
  571. (worker-util/profile :apply-remote-update-ops (apply-remote-update-ops repo conn update-ops))
  572. (worker-util/profile :apply-remote-remove-page-ops (apply-remote-remove-page-ops repo conn remove-page-ops)))
  573. ;; NOTE: we cannot set :persist-op? = true when batch-tx/with-batch-tx-mode (already set to false)
  574. ;; and there're some transactions in `apply-remote-remove-ops` need to :persist-op?=true
  575. (worker-util/profile :apply-remote-remove-ops (apply-remote-remove-ops repo conn date-formatter remove-ops))
  576. ;; wait all remote-ops transacted into db,
  577. ;; then start to check any asset-updates in remote
  578. (let [db-after @conn]
  579. (r.asset/emit-remote-asset-updates-from-block-ops db-before db-after remove-ops update-ops))
  580. (js/console.groupEnd)
  581. (client-op/update-local-tx repo remote-t)
  582. (rtc-log-and-state/update-local-t graph-uuid remote-t))
  583. :else (throw (ex-info "unreachable" {:remote-t remote-t
  584. :remote-t-before remote-t-before
  585. :local-t local-tx}))))))