1
0

react.cljs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. (ns frontend.db.react
  2. "Transact the tx with some specified relationship so that the components will
  3. be refreshed when subscribed data changed.
  4. It'll be great if we can find an automatically resolving and performant
  5. solution.
  6. "
  7. (:require [clojure.string :as string]
  8. [datascript.core :as d]
  9. [frontend.config :as config]
  10. [frontend.date :as date]
  11. [frontend.db.conn :as conn]
  12. [frontend.db.utils :as db-utils]
  13. [frontend.state :as state]
  14. [frontend.util :as util :refer [react]]
  15. [frontend.util.marker :as marker]
  16. [frontend.db.rules :as rules]))
  17. ;; Query atom of map of Key ([repo q inputs]) -> atom
  18. ;; TODO: replace with LRUCache, only keep the latest 20 or 50 items?
  19. (defonce query-state (atom {}))
  20. (def ^:dynamic *query-component*)
  21. ;; key -> components
  22. (defonce query-components (atom {}))
  23. (defn set-new-result!
  24. [k new-result]
  25. (when-let [result-atom (get-in @query-state [k :result])]
  26. (reset! result-atom new-result)))
  27. ;; KV
  28. (defn kv
  29. [key value]
  30. {:db/id -1
  31. :db/ident key
  32. key value})
  33. (defn remove-key!
  34. [repo-url key]
  35. (db-utils/transact! repo-url [[:db.fn/retractEntity [:db/ident key]]])
  36. (set-new-result! [repo-url :kv key] nil))
  37. (defn clear-query-state!
  38. []
  39. (reset! query-state {}))
  40. (defn clear-query-state-without-refs-and-embeds!
  41. []
  42. (let [state @query-state
  43. state (->> (filter (fn [[[_repo k] _v]]
  44. (contains? #{:blocks :block/block :custom} k)) state)
  45. (into {}))]
  46. (reset! query-state state)))
  47. (defn get-current-repo-refs-keys
  48. [{:keys [data]}]
  49. (when-let [current-repo (state/get-current-repo)]
  50. (->>
  51. (map (fn [[repo k id]]
  52. (when (and (= repo current-repo)
  53. (contains? #{:block/refed-blocks :block/unlinked-refs} k))
  54. (if (= k :block/refed-blocks)
  55. (if (every? (fn [m]
  56. (when (map? m)
  57. (= id (:db/id (:block/page m))))) data)
  58. nil
  59. [k id])
  60. [k id])))
  61. (keys @query-state))
  62. (remove nil?))))
  63. ;; TODO: Add components which subscribed to a specific query
  64. (defn add-q!
  65. [k query inputs result-atom transform-fn query-fn inputs-fn]
  66. (swap! query-state assoc k {:query query
  67. :inputs inputs
  68. :result result-atom
  69. :transform-fn transform-fn
  70. :query-fn query-fn
  71. :inputs-fn inputs-fn})
  72. result-atom)
  73. (defn remove-q!
  74. [k]
  75. (swap! query-state dissoc k))
  76. (defn add-query-component!
  77. [key component]
  78. (swap! query-components update key
  79. (fn [components]
  80. (distinct (conj components component)))))
  81. (defn remove-query-component!
  82. [component]
  83. (reset!
  84. query-components
  85. (->> (for [[k components] @query-components
  86. :let [new-components (remove #(= component %) components)]]
  87. (if (empty? new-components) ; no subscribed components
  88. (do (remove-q! k)
  89. nil)
  90. [k new-components]))
  91. (keep identity)
  92. (into {}))))
  93. (defn get-page-blocks-cache-atom
  94. [repo page-id]
  95. (:result (get @query-state [repo :page/blocks page-id])))
  96. (defn get-block-blocks-cache-atom
  97. [repo block-id]
  98. (:result (get @query-state [repo :block/block block-id])))
  99. ;; TODO: rename :custom to :query/custom
  100. (defn remove-custom-query!
  101. [repo query]
  102. (remove-q! [repo :custom query]))
  103. ;; Reactive query
  104. (defn query-entity-in-component
  105. ([id-or-lookup-ref]
  106. (db-utils/entity (state/get-current-repo) id-or-lookup-ref))
  107. ([repo id-or-lookup-ref]
  108. (let [k [:entity id-or-lookup-ref]
  109. result-atom (:result (get @query-state k))]
  110. (when-let [component *query-component*]
  111. (add-query-component! k component))
  112. (when-let [db (conn/get-conn repo)]
  113. (let [result (d/entity db id-or-lookup-ref)
  114. result-atom (or result-atom (atom nil))]
  115. (set! (.-state result-atom) result)
  116. (add-q! k nil nil result-atom identity identity identity))))))
  117. (defn add-rules-to-inputs
  118. [inputs]
  119. (conj (vec inputs) rules/rules))
  120. (defn q
  121. [repo k {:keys [use-cache? transform-fn query-fn inputs-fn disable-reactive?]
  122. :or {use-cache? true
  123. transform-fn identity}} query & inputs]
  124. (let [kv? (and (vector? k) (= :kv (first k)))
  125. k (vec (cons repo k))]
  126. (when-let [conn (conn/get-conn repo)]
  127. (let [result-atom (:result (get @query-state k))]
  128. (when-let [component *query-component*]
  129. (add-query-component! k component))
  130. (if (and use-cache? result-atom)
  131. result-atom
  132. (let [result (cond
  133. query-fn
  134. (query-fn conn)
  135. inputs-fn
  136. (let [inputs (inputs-fn)]
  137. (apply d/q query conn inputs))
  138. kv?
  139. (d/entity conn (last k))
  140. (seq inputs)
  141. (apply d/q query conn inputs)
  142. :else
  143. (d/q query conn))
  144. result (transform-fn result)
  145. result-atom (or result-atom (atom nil))]
  146. ;; Don't notify watches now
  147. (set! (.-state result-atom) result)
  148. (if-not disable-reactive?
  149. (add-q! k query inputs result-atom transform-fn query-fn inputs-fn)
  150. result-atom)))))))
  151. ;; TODO: Extract several parts to handlers
  152. (defn get-current-page
  153. []
  154. (let [match (:route-match @state/state)
  155. route-name (get-in match [:data :name])
  156. page (case route-name
  157. :page
  158. (get-in match [:path-params :name])
  159. :file
  160. (get-in match [:path-params :path])
  161. (date/journal-name))]
  162. (when page
  163. (let [page-name (util/page-name-sanity-lc page)]
  164. (db-utils/entity [:block/name page-name])))))
  165. (defn get-current-priority
  166. []
  167. (let [match (:route-match @state/state)
  168. route-name (get-in match [:data :name])]
  169. (when (= route-name :page)
  170. (when-let [page-name (get-in match [:path-params :name])]
  171. (and (contains? #{"a" "b" "c"} (string/lower-case page-name))
  172. (string/upper-case page-name))))))
  173. (defn get-current-marker
  174. []
  175. (let [match (:route-match @state/state)
  176. route-name (get-in match [:data :name])]
  177. (when (= route-name :page)
  178. (when-let [page-name (get-in match [:path-params :name])]
  179. (and (marker/marker? page-name)
  180. (string/upper-case page-name))))))
  181. (defn get-related-keys
  182. [{:keys [key data]}]
  183. (cond
  184. (coll? key)
  185. [key]
  186. :else
  187. (case key
  188. (:block/change :block/insert)
  189. (when-let [blocks (seq data)]
  190. (let [pre-block? (:block/pre-block? (first blocks))
  191. current-priority (get-current-priority)
  192. current-marker (get-current-marker)
  193. current-page-id (:db/id (get-current-page))
  194. related-keys (->>
  195. (util/concat-without-nil
  196. (mapcat
  197. (fn [block]
  198. (when-let [page-id (or (:db/id (:block/page block))
  199. (and (int? (:block/page block))
  200. (:block/page block)))]
  201. [[:blocks (:block/uuid block)]
  202. [:page/blocks page-id]
  203. [:page/ref-pages page-id]]))
  204. blocks)
  205. (when pre-block?
  206. [[:contents]
  207. [:page/published]])
  208. ;; affected priority
  209. (when current-priority
  210. [[:priority/blocks current-priority]])
  211. (when current-marker
  212. [[:marker/blocks current-marker]])
  213. (when current-page-id
  214. [[:page/ref-pages current-page-id]
  215. [:page/mentioned-pages current-page-id]])
  216. (apply concat
  217. (for [{:block/keys [refs]} blocks]
  218. (map (fn [ref]
  219. (cond
  220. (and (map? ref) (:block/name ref))
  221. [:page/blocks (:db/id (db-utils/entity [:block/name (:block/name ref)]))]
  222. (and (vector? ref) (= (first ref) :block/uuid))
  223. [:block/refs-count (second ref)]
  224. :else
  225. nil))
  226. refs))))
  227. (distinct))
  228. refed-pages (map
  229. (fn [[k page-id]]
  230. (when (= k :block/refed-blocks)
  231. [:page/ref-pages page-id]))
  232. related-keys)
  233. all-refed-blocks (get-current-repo-refs-keys {:key key
  234. :data data})
  235. custom-queries (some->>
  236. (filter (fn [v]
  237. (and (= (first v) (state/get-current-repo))
  238. (= (second v) :custom)))
  239. (keys @query-state))
  240. (map (fn [v]
  241. (vec (drop 1 v)))))
  242. block-blocks (some->>
  243. (filter (fn [v]
  244. (and (= (first v) (state/get-current-repo))
  245. (= (second v) :block/block)))
  246. (keys @query-state))
  247. (map (fn [v]
  248. (vec (drop 1 v)))))]
  249. (->>
  250. (util/concat-without-nil
  251. related-keys
  252. refed-pages
  253. all-refed-blocks
  254. custom-queries
  255. block-blocks)
  256. distinct)))
  257. [[key]])))
  258. (defn refresh!
  259. [repo-url handler-opts]
  260. (let [related-keys (get-related-keys handler-opts)
  261. db (conn/get-conn repo-url)]
  262. (doseq [related-key related-keys]
  263. (let [related-key (vec (cons repo-url related-key))]
  264. (when-let [cache (get @query-state related-key)]
  265. (let [{:keys [query inputs transform-fn query-fn inputs-fn]} cache]
  266. (when (or query query-fn)
  267. (let [new-result (->
  268. (cond
  269. query-fn
  270. (let [result (query-fn db)]
  271. (if (coll? result)
  272. (doall result)
  273. result))
  274. inputs-fn
  275. (let [inputs (inputs-fn)]
  276. (apply d/q query db inputs))
  277. (keyword? query)
  278. (db-utils/get-key-value repo-url query)
  279. (seq inputs)
  280. (apply d/q query db inputs)
  281. :else
  282. (d/q query db))
  283. transform-fn)]
  284. (set-new-result! related-key new-result)))))))))
  285. (defn transact-react!
  286. [repo-url tx-data handler-opts]
  287. (when-not config/publishing?
  288. (let [repo-url (or repo-url (state/get-current-repo))
  289. tx-data (->> (util/remove-nils tx-data)
  290. (remove nil?))
  291. get-conn (fn [] (conn/get-conn repo-url false))]
  292. (when (and (seq tx-data) (get-conn))
  293. (d/transact! (get-conn) (vec tx-data))
  294. (refresh! repo-url handler-opts)))))
  295. (defn set-key-value
  296. [repo-url key value]
  297. (if value
  298. (transact-react! repo-url [(kv key value)]
  299. {:key [:kv key]})
  300. (remove-key! repo-url key)))
  301. (defn sub-key-value
  302. ([key]
  303. (sub-key-value (state/get-current-repo) key))
  304. ([repo-url key]
  305. (when (conn/get-conn repo-url)
  306. (let [m (some-> (q repo-url [:kv key] {} key key) react)]
  307. (if-let [result (get m key)]
  308. result
  309. m)))))