reference.cljs 11 KB

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