react.cljs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 [frontend.db.conn :as conn]
  8. [frontend.db.utils :as db-utils]
  9. [frontend.db.model :as model]
  10. [frontend.state :as state]
  11. [frontend.date :as date]
  12. [frontend.util :as util :refer-macros [profile] :refer [react]]
  13. [clojure.string :as string]
  14. [frontend.config :as config]
  15. [frontend.format :as format]
  16. [cljs-time.core :as t]
  17. [cljs-time.coerce :as tc]
  18. [frontend.utf8 :as utf8]
  19. [datascript.core :as d]
  20. [lambdaisland.glogi :as log]))
  21. ;; TODO: Extract several parts to handlers
  22. (defn get-current-page
  23. []
  24. (let [match (:route-match @state/state)
  25. route-name (get-in match [:data :name])
  26. tag? (= route-name :tag)
  27. page (case route-name
  28. :page
  29. (get-in match [:path-params :name])
  30. :file
  31. (get-in match [:path-params :path])
  32. :tag
  33. (get-in match [:path-params :name])
  34. (date/journal-name))]
  35. (when page
  36. (let [page-name (util/url-decode (string/lower-case page))]
  37. (model/entity (if tag?
  38. [:tag/name page-name]
  39. [:page/name page-name]))))))
  40. (defn get-current-priority
  41. []
  42. (let [match (:route-match @state/state)
  43. route-name (get-in match [:data :name])]
  44. (when (= route-name :page)
  45. (when-let [page-name (get-in match [:path-params :name])]
  46. (and (contains? #{"a" "b" "c"} (string/lower-case page-name))
  47. (string/upper-case page-name))))))
  48. (defn get-current-marker
  49. []
  50. (let [match (:route-match @state/state)
  51. route-name (get-in match [:data :name])]
  52. (when (= route-name :page)
  53. (when-let [page-name (get-in match [:path-params :name])]
  54. (and (util/marker? page-name)
  55. (string/upper-case page-name))))))
  56. (defn get-handler-keys
  57. [{:keys [key data]}]
  58. (cond
  59. (coll? key)
  60. [key]
  61. :else
  62. (case key
  63. (:block/change :block/insert)
  64. (when-let [blocks (seq data)]
  65. (let [pre-block? (:block/pre-block? (first blocks))
  66. current-priority (get-current-priority)
  67. current-marker (get-current-marker)
  68. current-page-id (:db/id (get-current-page))
  69. {:block/keys [page]} (first blocks)
  70. handler-keys (->>
  71. (util/concat-without-nil
  72. (mapcat
  73. (fn [block]
  74. (when-let [page-id (:db/id (:block/page block))]
  75. [[:blocks (:block/uuid block)]
  76. [:page/blocks page-id]
  77. [:page/ref-pages page-id]]))
  78. blocks)
  79. (when pre-block?
  80. [[:contents]
  81. [:page/published]])
  82. ;; affected priority
  83. (when current-priority
  84. [[:priority/blocks current-priority]])
  85. (when current-marker
  86. [[:marker/blocks current-marker]])
  87. (when current-page-id
  88. [[:page/ref-pages current-page-id]
  89. [:page/refed-blocks current-page-id]
  90. [:page/mentioned-pages current-page-id]])
  91. ;; refed-pages
  92. (apply concat
  93. (for [{:block/keys [ref-pages]} blocks]
  94. (map (fn [page]
  95. (when-let [page (model/entity [:page/name (:page/name page)])]
  96. [:page/refed-blocks (:db/id page)]))
  97. ref-pages)))
  98. ;; refed-blocks
  99. (apply concat
  100. (for [{:block/keys [ref-blocks]} blocks]
  101. (map (fn [ref-block]
  102. [:block/refed-blocks (last ref-block)])
  103. ref-blocks))))
  104. (distinct))
  105. refed-pages (map
  106. (fn [[k page-id]]
  107. (if (= k :page/refed-blocks)
  108. [:page/ref-pages page-id]))
  109. handler-keys)
  110. custom-queries (some->>
  111. (filter (fn [v]
  112. (and (= (first v) (state/get-current-repo))
  113. (= (second v) :custom)))
  114. (keys @model/query-state))
  115. (map (fn [v]
  116. (vec (drop 1 v)))))
  117. block-blocks (some->>
  118. (filter (fn [v]
  119. (and (= (first v) (state/get-current-repo))
  120. (= (second v) :block/block)))
  121. (keys @model/query-state))
  122. (map (fn [v]
  123. (vec (drop 1 v)))))]
  124. (->>
  125. (util/concat-without-nil
  126. handler-keys
  127. refed-pages
  128. custom-queries
  129. block-blocks)
  130. distinct)))
  131. [[key]])))
  132. (defn transact-react!
  133. [repo-url tx-data {:keys [key data files-db?] :as handler-opts
  134. :or {files-db? false}}]
  135. (when-not config/publishing?
  136. (try
  137. (let [repo-url (or repo-url (state/get-current-repo))
  138. tx-data (->> (util/remove-nils tx-data)
  139. (remove nil?))
  140. get-conn (fn [] (if files-db?
  141. (conn/get-files-conn repo-url)
  142. (conn/get-conn repo-url false)))]
  143. (when (and (seq tx-data) (get-conn))
  144. (let [tx-result (profile "Transact!" (d/transact! (get-conn) (vec tx-data)))
  145. db (:db-after tx-result)
  146. handler-keys (get-handler-keys handler-opts)]
  147. (doseq [handler-key handler-keys]
  148. (let [handler-key (vec (cons repo-url handler-key))]
  149. (when-let [cache (get @model/query-state handler-key)]
  150. (let [{:keys [query inputs transform-fn query-fn inputs-fn]} cache]
  151. (when (or query query-fn)
  152. (let [new-result (->
  153. (cond
  154. query-fn
  155. (profile
  156. "Query:"
  157. (doall (query-fn db)))
  158. inputs-fn
  159. (let [inputs (inputs-fn)]
  160. (apply d/q query db inputs))
  161. (keyword? query)
  162. (model/get-key-value repo-url query)
  163. (seq inputs)
  164. (apply d/q query db inputs)
  165. :else
  166. (d/q query db))
  167. transform-fn)]
  168. (model/set-new-result! handler-key new-result))))))))))
  169. (catch js/Error e
  170. ;; FIXME: check error type and notice user
  171. (log/error :db/transact! e)))))
  172. (defn set-key-value
  173. [repo-url key value]
  174. (if value
  175. (transact-react! repo-url [(model/kv key value)]
  176. {:key [:kv key]})
  177. (model/remove-key! repo-url key)))
  178. (defn set-file-content!
  179. [repo path content]
  180. (when (and repo path)
  181. (let [tx-data {:file/path path
  182. :file/content content
  183. :file/last-modified-at (util/time-ms)}
  184. tx-data (if (config/local-db? repo)
  185. (dissoc tx-data :file/last-modified-at)
  186. tx-data)]
  187. (transact-react!
  188. repo
  189. [tx-data]
  190. {:key [:file/content path]
  191. :files-db? true}))))