client_op.cljs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. (ns frontend.worker.rtc.client-op
  2. "Store client-ops in a persisted datascript"
  3. (:require [datascript.core :as d]
  4. [frontend.worker.rtc.const :as rtc-const]
  5. [frontend.worker.state :as worker-state]
  6. [logseq.db.sqlite.util :as sqlite-util]
  7. [malli.core :as ma]
  8. [malli.transform :as mt]
  9. [missionary.core :as m]))
  10. (def op-schema
  11. [:multi {:dispatch first}
  12. [:move
  13. [:catn
  14. [:op :keyword]
  15. [:t :int]
  16. [:value [:map
  17. [:block-uuid :uuid]]]]]
  18. [:remove
  19. [:catn
  20. [:op :keyword]
  21. [:t :int]
  22. [:value [:map
  23. [:block-uuid :uuid]]]]]
  24. [:update-page
  25. [:catn
  26. [:op :keyword]
  27. [:t :int]
  28. [:value [:map
  29. [:block-uuid :uuid]]]]]
  30. [:remove-page
  31. [:catn
  32. [:op :keyword]
  33. [:t :int]
  34. [:value [:map
  35. [:block-uuid :uuid]]]]]
  36. [:update
  37. [:catn
  38. [:op :keyword]
  39. [:t :int]
  40. [:value [:map
  41. [:block-uuid :uuid]
  42. [:av-coll [:sequential rtc-const/av-schema]]]]]]])
  43. (def ops-schema [:sequential op-schema])
  44. (def ops-coercer (ma/coercer ops-schema mt/json-transformer nil
  45. #(do (prn ::bad-ops (:value %))
  46. (ma/-fail! ::ops-schema %))))
  47. (def schema-in-db
  48. "TODO: rename this db-name from client-op to client-metadata+op.
  49. and move it to its own namespace."
  50. {:block/uuid {:db/unique :db.unique/identity}
  51. :local-tx {:db/index true}
  52. :graph-uuid {:db/index true}
  53. :public-key-jwk {:db/index true}
  54. :private-key-jwk {:db/index true}
  55. ;; device
  56. :device/uuid {:db/unique :db.unique/identity}
  57. :device/public-key-jwk {}
  58. :device/private-key-jwk {}})
  59. (defn update-graph-uuid
  60. [repo graph-uuid]
  61. {:pre [(some? graph-uuid)]}
  62. (when-let [conn (worker-state/get-client-ops-conn repo)]
  63. (assert (nil? (first (d/datoms @conn :avet :graph-uuid))))
  64. (d/transact! conn [[:db/add "e" :graph-uuid graph-uuid]])))
  65. (defn update-local-tx
  66. [repo t]
  67. {:pre [(some? t)]}
  68. (when-let [conn (worker-state/get-client-ops-conn repo)]
  69. (let [tx-data
  70. (if-let [datom (first (d/datoms @conn :avet :local-tx))]
  71. [:db/add (:e datom) :local-tx t]
  72. [:db/add "e" :local-tx t])]
  73. (d/transact! conn [tx-data]))))
  74. (defn get-local-tx
  75. [repo]
  76. (when-let [conn (worker-state/get-client-ops-conn repo)]
  77. (:v (first (d/datoms @conn :avet :local-tx)))))
  78. (defn- merge-update-ops
  79. [update-op1 update-op2]
  80. {:pre [(= :update (first update-op1))
  81. (= :update (first update-op2))
  82. (= (:block-uuid (last update-op1))
  83. (:block-uuid (last update-op2)))]}
  84. (let [t1 (second update-op1)
  85. t2 (second update-op2)]
  86. (if (> t1 t2)
  87. (merge-update-ops update-op2 update-op1)
  88. (let [{av-coll1 :av-coll block-uuid :block-uuid} (last update-op1)
  89. {av-coll2 :av-coll} (last update-op2)]
  90. [:update t2
  91. {:block-uuid block-uuid
  92. :av-coll (concat av-coll1 av-coll2)}]))))
  93. (defn add-ops*
  94. [conn ops]
  95. (let [ops (ops-coercer ops)]
  96. (letfn [(already-removed? [remove-op t]
  97. (some-> remove-op second (> t)))
  98. (add-after-remove? [move-op t]
  99. (some-> move-op second (> t)))]
  100. (doseq [op ops]
  101. (let [[op-type t value] op
  102. {:keys [block-uuid]} value
  103. exist-block-ops-entity (d/entity @conn [:block/uuid block-uuid])
  104. e (:db/id exist-block-ops-entity)
  105. tx-data
  106. (case op-type
  107. :move
  108. (let [remove-op (get exist-block-ops-entity :remove)]
  109. (when-not (already-removed? remove-op t)
  110. (cond-> [{:block/uuid block-uuid
  111. :move op}]
  112. remove-op (conj [:db.fn/retractAttribute e :remove]))))
  113. :update
  114. (let [remove-op (get exist-block-ops-entity :remove)]
  115. (when-not (already-removed? remove-op t)
  116. (let [origin-update-op (get exist-block-ops-entity :update)
  117. op* (if origin-update-op (merge-update-ops origin-update-op op) op)]
  118. (cond-> [{:block/uuid block-uuid
  119. :update op*}]
  120. remove-op (conj [:db.fn/retractAttribute e :remove])))))
  121. :remove
  122. (let [move-op (get exist-block-ops-entity :move)]
  123. (when-not (add-after-remove? move-op t)
  124. (cond-> [{:block/uuid block-uuid
  125. :remove op}]
  126. move-op (conj [:db.fn/retractAttribute e :move]))))
  127. :update-page
  128. (let [remove-page-op (get exist-block-ops-entity :remove-page)]
  129. (when-not (already-removed? remove-page-op t)
  130. (cond-> [{:block/uuid block-uuid
  131. :update-page op}]
  132. remove-page-op (conj [:db.fn/retractAttribute e :remove-page]))))
  133. :remove-page
  134. (let [update-page-op (get exist-block-ops-entity :update-page)]
  135. (when-not (add-after-remove? update-page-op t)
  136. (cond-> [{:block/uuid block-uuid
  137. :remove-page op}]
  138. update-page-op (conj [:db.fn/retractAttribute e :update-page])))))]
  139. (when (seq tx-data)
  140. (d/transact! conn tx-data)))))))
  141. (defn add-ops
  142. [repo ops]
  143. (let [conn (worker-state/get-client-ops-conn repo)]
  144. (assert (some? conn) repo)
  145. (add-ops* conn ops)))
  146. (defn- get-all-op-datoms
  147. [conn]
  148. (->> (d/datoms @conn :eavt)
  149. (remove (fn [datom] (contains? #{:graph-uuid :local-tx} (:a datom))))
  150. (group-by :e)))
  151. (defn get-all-ops*
  152. [conn]
  153. (let [e->datoms (get-all-op-datoms conn)]
  154. (map (fn [same-ent-datoms]
  155. (into {} (map (juxt :a :v)) same-ent-datoms))
  156. (vals e->datoms))))
  157. (defn get&remove-all-ops*
  158. [conn]
  159. (let [e->datoms (get-all-op-datoms conn)
  160. retract-all-tx-data (map (fn [e] [:db.fn/retractEntity e]) (keys e->datoms))]
  161. (d/transact! conn retract-all-tx-data)
  162. (map (fn [same-ent-datoms]
  163. (into {} (map (juxt :a :v)) same-ent-datoms))
  164. (vals e->datoms))))
  165. (defn get-all-ops
  166. "Return coll of
  167. {:block/uuid ...
  168. :update ...
  169. :move ...
  170. ...}"
  171. [repo]
  172. (when-let [conn (worker-state/get-client-ops-conn repo)]
  173. (mapcat
  174. (fn [m]
  175. (keep (fn [[k v]]
  176. (when (not= :block/uuid k) v))
  177. m))
  178. (get-all-ops* conn))))
  179. (defn get&remove-all-ops
  180. "Return coll of
  181. {:block/uuid ...
  182. :update ...
  183. :move ...
  184. ...}"
  185. [repo]
  186. (when-let [conn (worker-state/get-client-ops-conn repo)]
  187. (get&remove-all-ops* conn)))
  188. (defn get-unpushed-ops-count
  189. [repo]
  190. (when-let [conn (worker-state/get-client-ops-conn repo)]
  191. (count (get-all-op-datoms conn))))
  192. (defn rtc-db-graph?
  193. "Is db-graph & RTC enabled"
  194. [repo]
  195. (and (sqlite-util/db-based-graph? repo)
  196. (or (exists? js/process)
  197. (some? (get-local-tx repo)))))
  198. (defn create-pending-ops-count-flow
  199. [repo]
  200. (when-let [conn (worker-state/get-client-ops-conn repo)]
  201. (letfn [(datom-count [db]
  202. (count (d/datoms db :avet :block/uuid)))]
  203. (m/relieve
  204. (m/observe
  205. (fn ctor [emit!]
  206. (d/listen! conn :create-pending-ops-count-flow
  207. (fn [{:keys [db-after]}]
  208. (emit! (datom-count db-after))))
  209. (emit! (datom-count @conn))
  210. (fn dtor []
  211. (d/unlisten! conn :create-pending-ops-count-flow))))))))