reference.cljs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. (ns frontend.components.reference
  2. (:require [frontend.components.block :as block]
  3. [frontend.components.content :as content]
  4. [frontend.components.editor :as editor]
  5. [frontend.components.reference-filters :as filters]
  6. [frontend.config :as config]
  7. [frontend.context.i18n :refer [t]]
  8. [frontend.db :as db]
  9. [logseq.db :as ldb]
  10. [frontend.db-mixins :as db-mixins]
  11. [frontend.db.async :as db-async]
  12. [frontend.db.utils :as db-utils]
  13. [frontend.handler.block :as block-handler]
  14. [frontend.handler.page :as page-handler]
  15. [frontend.modules.outliner.tree :as tree]
  16. [frontend.search :as search]
  17. [frontend.state :as state]
  18. [frontend.ui :as ui]
  19. [frontend.util :as util]
  20. [logseq.shui.ui :as shui]
  21. [promesa.core :as p]
  22. [rum.core :as rum]))
  23. (rum/defc block-linked-references < rum/reactive db-mixins/query
  24. {:init (fn [state]
  25. (when-let [e (db/entity [:block/uuid (first (:rum/args state))])]
  26. (db-async/<get-block-refs (state/get-current-repo) (:db/id e)))
  27. state)}
  28. [block-id]
  29. (when-let [e (db/entity [:block/uuid block-id])]
  30. (when-not (state/sub-async-query-loading (str (:db/id e) "-refs"))
  31. (let [ref-blocks (-> (db/get-referenced-blocks (:db/id e))
  32. db-utils/group-by-page)]
  33. (when (> (count ref-blocks) 0)
  34. (let [ref-hiccup (block/->hiccup ref-blocks
  35. {:id (str block-id)
  36. :ref? true
  37. :breadcrumb-show? true
  38. :group-by-page? true
  39. :editor-box editor/box}
  40. {})]
  41. [:div.references-blocks
  42. (content/content block-id
  43. {:hiccup ref-hiccup})]))))))
  44. (rum/defc references-inner
  45. [page-entity filters filtered-ref-blocks]
  46. (let [*ref (rum/use-ref nil)]
  47. [:div.references-blocks.faster.fade-in {:ref *ref}
  48. (let [ref-hiccup (block/->hiccup filtered-ref-blocks
  49. {:id (str (:block/uuid page-entity))
  50. :ref? true
  51. :breadcrumb-show? true
  52. :group-by-page? true
  53. :editor-box editor/box
  54. :filters filters}
  55. {})]
  56. (content/content (str (:block/uuid page-entity)) {:hiccup ref-hiccup}))]))
  57. (rum/defc references-cp
  58. [page-entity *filters total filter-n filtered-ref-blocks *ref-pages]
  59. (let [filters @*filters
  60. threshold (state/get-linked-references-collapsed-threshold)
  61. default-collapsed? (or (>= total threshold) (ldb/class? page-entity))
  62. *collapsed? (atom nil)]
  63. (ui/foldable
  64. [:div.flex.flex-row.flex-1.justify-between.items-center
  65. [:div.font-medium.opacity-50
  66. (t :linked-references/reference-count (when (or (seq (:included filters))
  67. (seq (:excluded filters))) filter-n) total)]
  68. [:a.filter.fade-link
  69. {:title (t :linked-references/filter-heading)
  70. :on-mouse-over (fn [_e]
  71. (when @*collapsed? ; collapsed
  72. ;; expand
  73. (reset! @*collapsed? false)))
  74. :on-pointer-down (fn [e]
  75. (util/stop-propagation e)
  76. (shui/popup-show! (.-target e)
  77. (fn []
  78. [:div.p-4
  79. (filters/filter-dialog page-entity *filters *ref-pages)])
  80. {:align "end"}))}
  81. (ui/icon "filter" {:class (cond
  82. (and (empty? (:included filters)) (empty? (:excluded filters)))
  83. "opacity-60 hover:opacity-100"
  84. (and (seq (:included filters)) (empty? (:excluded filters)))
  85. "text-success"
  86. (and (empty? (:included filters)) (seq (:excluded filters)))
  87. "text-error"
  88. :else
  89. "text-warning")
  90. :size 22})]]
  91. (fn []
  92. (references-inner page-entity filters filtered-ref-blocks))
  93. {:default-collapsed? default-collapsed?
  94. :title-trigger? true
  95. :init-collapsed (fn [collapsed-atom]
  96. (reset! *collapsed? collapsed-atom))})))
  97. (defn- get-filtered-children
  98. [block parent->blocks]
  99. (let [children (get parent->blocks (:db/id block))]
  100. (set
  101. (loop [blocks children
  102. result (vec children)]
  103. (if (empty? blocks)
  104. result
  105. (let [fb (first blocks)
  106. children (get parent->blocks (:db/id fb))]
  107. (recur
  108. (concat children (rest blocks))
  109. (conj result fb))))))))
  110. (rum/defc references-aux < rum/reactive db-mixins/query
  111. {:should-update (fn [old-state new-state]
  112. ;; Re-render if only filters update
  113. (not= (last (:rum/args old-state))
  114. (last (:rum/args new-state))))}
  115. [state repo page-entity *filters filters]
  116. (let [*ref-pages (::ref-pages state)
  117. page-id (:db/id page-entity)
  118. ref-blocks (db/get-referenced-blocks page-id)
  119. aliases (db/page-alias-set repo page-id)
  120. aliases-exclude-self (set (remove #{page-id} aliases))
  121. top-level-blocks (filter (fn [b] (some aliases (set (map :db/id (:block/refs b))))) ref-blocks)
  122. top-level-blocks-ids (set (map :db/id top-level-blocks))
  123. filtered-ref-blocks (->> (block-handler/filter-blocks ref-blocks filters)
  124. (block-handler/get-filtered-ref-blocks-with-parents ref-blocks))
  125. total (count top-level-blocks)
  126. filtered-top-blocks (filter (fn [b] (top-level-blocks-ids (:db/id b))) filtered-ref-blocks)
  127. filter-n (count filtered-top-blocks)
  128. parent->blocks (group-by (fn [x] (:db/id (x :block/parent))) filtered-ref-blocks)
  129. result (->> (group-by :block/page filtered-top-blocks)
  130. (map (fn [[page blocks]]
  131. (let [blocks (sort-by (fn [b] (not= (:db/id page) (:db/id (:block/parent b)))) blocks)
  132. result (map (fn [block]
  133. (let [filtered-children (get-filtered-children block parent->blocks)
  134. refs (when-not (contains? top-level-blocks-ids (:db/id (:block/parent block)))
  135. (block-handler/get-blocks-refed-pages aliases (cons block filtered-children)))
  136. block' (assoc (tree/block-entity->map block) :block/children filtered-children)]
  137. [block' refs])) blocks)
  138. blocks' (map first result)
  139. page' (if (contains? aliases-exclude-self (:db/id page))
  140. {:db/id (:db/id page)
  141. :block/alias? true
  142. :block/journal-day (:block/journal-day page)}
  143. page)]
  144. [[page' blocks'] (mapcat second result)]))))
  145. filtered-ref-blocks' (map first result)
  146. ref-pages (->>
  147. (mapcat second result)
  148. (map :block/title)
  149. frequencies)]
  150. (reset! *ref-pages ref-pages)
  151. (when (or (seq (:included filters)) (seq (:excluded filters)) (> filter-n 0))
  152. [:div.references.page-linked.flex-1.flex-row
  153. [:div.content.pt-6
  154. (references-cp page-entity *filters total filter-n filtered-ref-blocks' *ref-pages)]])))
  155. (rum/defcs references* < rum/reactive db-mixins/query
  156. (rum/local nil ::ref-pages)
  157. {:init (fn [state]
  158. (let [page (first (:rum/args state))]
  159. (when page (db-async/<get-block-refs (state/get-current-repo) (:db/id page))))
  160. (assoc state ::filters (atom nil)))}
  161. [state block-entity]
  162. (when block-entity
  163. (let [repo (state/get-current-repo)
  164. *filters (::filters state)]
  165. (when block-entity
  166. (when-not (state/sub-async-query-loading (str (:db/id block-entity) "-refs"))
  167. (let [block-entity (db/sub-block (:db/id block-entity))
  168. filters (page-handler/get-filters block-entity)
  169. _ (when-not (= filters @*filters)
  170. (reset! *filters filters))]
  171. (references-aux state repo block-entity *filters filters)))))))
  172. (rum/defc references
  173. [entity]
  174. (ui/catch-error
  175. (ui/component-error (if (config/db-based-graph? (state/get-current-repo))
  176. "Linked References: Unexpected error."
  177. "Linked References: Unexpected error. Please re-index your graph first."))
  178. (references* entity)))
  179. (rum/defcs unlinked-references-aux
  180. < rum/reactive db-mixins/query
  181. {:init
  182. (fn [state]
  183. (let [*result (atom nil)
  184. [page *n-ref] (:rum/args state)]
  185. (p/let [result (search/get-unlinked-refs (:db/id page))]
  186. (reset! *n-ref (count result))
  187. (reset! *result result))
  188. (assoc state ::result *result)))}
  189. [state page _n-ref]
  190. (let [ref-blocks (rum/react (::result state))]
  191. (when (seq ref-blocks)
  192. [:div.references-blocks
  193. (let [ref-hiccup (block/->hiccup ref-blocks
  194. {:id (str (:block/title page) "-unlinked-")
  195. :ref? true
  196. :group-by-page? true
  197. :editor-box editor/box}
  198. {})]
  199. (content/content (:block/name page)
  200. {:hiccup ref-hiccup}))])))
  201. (rum/defcs unlinked-references < rum/reactive
  202. (rum/local nil ::n-ref)
  203. [state page]
  204. (let [n-ref (get state ::n-ref)]
  205. (when page
  206. [:div.references.page-unlinked.mt-6.flex-1.flex-row.faster.fade-in
  207. [:div.content.flex-1
  208. (ui/foldable
  209. [:div.font-medium.opacity-50
  210. (t :unlinked-references/reference-count @n-ref)]
  211. (fn [] (unlinked-references-aux page n-ref))
  212. {:default-collapsed? true
  213. :title-trigger? true})]])))