| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658 | 
							- (ns frontend.db.model
 
-   "Core db functions."
 
-   ;; TODO: Remove this config once how repos are passed to this ns are standardized
 
-   {:clj-kondo/config {:linters {:unused-binding {:level :off}}}}
 
-   (:require [clojure.set :as set]
 
-             [clojure.string :as string]
 
-             [clojure.walk :as walk]
 
-             [datascript.core :as d]
 
-             [frontend.config :as config]
 
-             [frontend.date :as date]
 
-             [logseq.db.schema :as db-schema]
 
-             [frontend.db.conn :as conn]
 
-             [frontend.db.react :as react]
 
-             [frontend.db.utils :as db-utils]
 
-             [frontend.state :as state]
 
-             [frontend.util :as util :refer [react]]
 
-             [logseq.graph-parser.util :as gp-util]
 
-             [logseq.db.rules :refer [rules]]
 
-             [logseq.db.default :as default-db]
 
-             [frontend.util.drawer :as drawer]))
 
- ;; lazy loading
 
- (def initial-blocks-length 50)
 
- (def step-loading-blocks 25)
 
- ;; TODO: extract to specific models and move data transform logic to the
 
- ;; corresponding handlers.
 
- ;; Use it as an input argument for datalog queries
 
- (def block-attrs
 
-   '[:db/id
 
-     :block/uuid
 
-     :block/parent
 
-     :block/left
 
-     :block/collapsed?
 
-     :block/format
 
-     :block/refs
 
-     :block/_refs
 
-     :block/path-refs
 
-     :block/tags
 
-     :block/content
 
-     :block/marker
 
-     :block/priority
 
-     :block/properties
 
-     :block/pre-block?
 
-     :block/scheduled
 
-     :block/deadline
 
-     :block/repeated?
 
-     :block/created-at
 
-     :block/updated-at
 
-     :block/file
 
-     :block/heading-level
 
-     {:block/page [:db/id :block/name :block/original-name :block/journal-day]}
 
-     {:block/_parent ...}])
 
- (defn pull-block
 
-   [id]
 
-   (let [repo (state/get-current-repo)]
 
-     (when (conn/get-db repo)
 
-       (->
 
-        (react/q repo [:frontend.db.react/block id] {}
 
-                 '[:find [(pull ?block ?block-attrs) ...]
 
-                   :in $ ?id ?block-attrs
 
-                   :where
 
-                   [?block :block/uuid ?id]]
 
-                 id
 
-                 block-attrs)
 
-        react
 
-        first))))
 
- (defn get-original-name
 
-   [page-entity]
 
-   (or (:block/original-name page-entity)
 
-       (:block/name page-entity)))
 
- (defn get-tag-pages
 
-   [repo tag-name]
 
-   (when tag-name
 
-     (d/q '[:find ?original-name ?name
 
-            :in $ ?tag
 
-            :where
 
-            [?e :block/name ?tag]
 
-            [?page :block/tags ?e]
 
-            [?page :block/original-name ?original-name]
 
-            [?page :block/name ?name]]
 
-          (conn/get-db repo)
 
-          (util/page-name-sanity-lc tag-name))))
 
- (defn get-all-tagged-pages
 
-   [repo]
 
-   (d/q '[:find ?page-name ?tag
 
-          :where
 
-          [?page :block/tags ?e]
 
-          [?e :block/name ?tag]
 
-          [?page :block/name ?page-name]]
 
-        (conn/get-db repo)))
 
- (defn get-all-namespace-relation
 
-   [repo]
 
-   (d/q '[:find ?page-name ?parent
 
-          :where
 
-          [?page :block/name ?page-name]
 
-          [?page :block/namespace ?e]
 
-          [?e :block/name ?parent]]
 
-        (conn/get-db repo)))
 
- (defn get-pages
 
-   [repo]
 
-   (->> (d/q
 
-         '[:find ?page-original-name
 
-           :where
 
-           [?page :block/name ?page-name]
 
-           [(get-else $ ?page :block/original-name ?page-name) ?page-original-name]]
 
-         (conn/get-db repo))
 
-        (map first)))
 
- (defn get-all-pages
 
-   [repo]
 
-   (d/q
 
-    '[:find [(pull ?page [*]) ...]
 
-      :where
 
-      [?page :block/name]]
 
-    (conn/get-db repo)))
 
- (defn get-page-alias
 
-   [repo page-name]
 
-   (when-let [db (and repo (conn/get-db repo))]
 
-     (some->> (d/q '[:find ?alias
 
-                     :in $ ?page-name
 
-                     :where
 
-                     [?page :block/name ?page-name]
 
-                     [?page :block/alias ?alias]]
 
-                   db
 
-                   (util/page-name-sanity-lc page-name))
 
-              db-utils/seq-flatten
 
-              distinct)))
 
- (defn get-alias-source-page
 
-   [repo alias]
 
-   (when-let [db (and repo (conn/get-db repo))]
 
-     (let [alias (util/page-name-sanity-lc alias)
 
-           pages (->>
 
-                  (d/q '[:find (pull ?p [*])
 
-                         :in $ ?alias
 
-                         :where
 
-                         [?a :block/name ?alias]
 
-                         [?p :block/alias ?a]]
 
-                       db
 
-                       alias)
 
-                  (db-utils/seq-flatten))]
 
-       (when (seq pages)
 
-         (some (fn [page]
 
-                 (let [aliases (->> (get-in page [:block/properties :alias])
 
-                                    (map util/page-name-sanity-lc)
 
-                                    set)]
 
-                   (when (contains? aliases alias)
 
-                     page)))
 
-               pages)))))
 
- (defn get-files
 
-   [repo]
 
-   (when-let [db (conn/get-db repo)]
 
-     (->> (d/q
 
-           '[:find ?path
 
-              ;; ?modified-at
 
-             :where
 
-             [?file :file/path ?path]
 
-              ;; [?file :file/last-modified-at ?modified-at]
 
-             ]
 
-           db)
 
-          (seq)
 
-          ;; (sort-by last)
 
-          (reverse))))
 
- (defn get-files-v2
 
-   [repo]
 
-   (when-let [db (conn/get-db repo)]
 
-     (->> (d/q
 
-           '[:find ?file ?path
 
-             ;; ?modified-at
 
-             :where
 
-             [?file :file/path ?path]
 
-             ;; [?file :file/last-modified-at ?modified-at]
 
-             ]
 
-           db)
 
-          (seq)
 
-          ;; (sort-by last)
 
-          (reverse))))
 
- (defn get-files-blocks
 
-   [repo-url paths]
 
-   (let [paths (set paths)
 
-         pred (fn [_db e]
 
-                (contains? paths e))]
 
-     (-> (d/q '[:find ?block
 
-                :in $ ?pred
 
-                :where
 
-                [?file :file/path ?path]
 
-                [(?pred $ ?path)]
 
-                [?p :block/file ?file]
 
-                [?block :block/page ?p]]
 
-              (conn/get-db repo-url) pred)
 
-         db-utils/seq-flatten)))
 
- (defn get-file-blocks
 
-   [repo-url path]
 
-   (-> (d/q '[:find ?block
 
-              :in $ ?path
 
-              :where
 
-              [?file :file/path ?path]
 
-              [?p :block/file ?file]
 
-              [?block :block/page ?p]]
 
-            (conn/get-db repo-url) path)
 
-       db-utils/seq-flatten))
 
- (defn set-file-last-modified-at!
 
-   [repo path last-modified-at]
 
-   (when (and repo path last-modified-at)
 
-     (when-let [conn (conn/get-db repo false)]
 
-       (d/transact! conn
 
-                    [{:file/path path
 
-                      :file/last-modified-at last-modified-at}]
 
-                    {:skip-refresh? true}))))
 
- (defn get-file-last-modified-at
 
-   [repo path]
 
-   (when (and repo path)
 
-     (when-let [db (conn/get-db repo)]
 
-       (-> (d/entity db [:file/path path])
 
-           :file/last-modified-at))))
 
- (defn file-exists?
 
-   [repo path]
 
-   (when (and repo path)
 
-     (when-let [db (conn/get-db repo)]
 
-       (d/entity db [:file/path path]))))
 
- (defn get-files-full
 
-   [repo]
 
-   (when-let [db (conn/get-db repo)]
 
-     (->>
 
-      (d/q
 
-       '[:find (pull ?file [*])
 
-         :where
 
-         [?file :file/path]]
 
-       db)
 
-      (flatten))))
 
- (defn get-file
 
-   ([path]
 
-    (get-file (state/get-current-repo) path))
 
-   ([repo path]
 
-    (when (and repo path)
 
-      (when-let [db (conn/get-db repo)]
 
-        (:file/content (d/entity db [:file/path path]))))))
 
- (defn get-custom-css
 
-   []
 
-   (when-let [repo (state/get-current-repo)]
 
-     (get-file (config/get-file-path repo "logseq/custom.css"))))
 
- (defn get-block-by-uuid
 
-   [id]
 
-   (db-utils/entity [:block/uuid (if (uuid? id) id (uuid id))]))
 
- (defn query-block-by-uuid
 
-   [id]
 
-   (db-utils/pull [:block/uuid (if (uuid? id) id (uuid id))]))
 
- (defn get-page-format
 
-   [page-name]
 
-   (or
 
-    (let [page (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page-name)])]
 
-      (or
 
-       (:block/format page)
 
-       (when-let [file (:block/file page)]
 
-         (when-let [path (:file/path (db-utils/entity (:db/id file)))]
 
-           (gp-util/get-format path)))))
 
-    (state/get-preferred-format)
 
-    :markdown))
 
- (defn page-alias-set
 
-   [repo-url page]
 
-   (when-let [page-id (:db/id (db-utils/entity repo-url [:block/name (util/safe-page-name-sanity-lc page)]))]
 
-     (->>
 
-      (d/q '[:find ?e
 
-             :in $ ?page-name %
 
-             :where
 
-             [?page :block/name ?page-name]
 
-             (alias ?page ?e)]
 
-           (conn/get-db repo-url)
 
-           (util/safe-page-name-sanity-lc page)
 
-           '[[(alias ?e2 ?e1)
 
-              [?e2 :block/alias ?e1]]
 
-             [(alias ?e2 ?e1)
 
-              [?e1 :block/alias ?e2]]
 
-             [(alias ?e1 ?e3)
 
-              [?e1 :block/alias ?e2]
 
-              [?e2 :block/alias ?e3]]
 
-             [(alias ?e3 ?e1)
 
-              [?e1 :block/alias ?e2]
 
-              [?e2 :block/alias ?e3]]])
 
-      db-utils/seq-flatten
 
-      (set)
 
-      (set/union #{page-id}))))
 
- (defn get-entities-by-ids
 
-   ([ids]
 
-    (get-entities-by-ids (state/get-current-repo) ids))
 
-   ([repo ids]
 
-    (when repo
 
-      (db-utils/pull-many repo '[*] ids))))
 
- (defn get-page-names-by-ids
 
-   ([ids]
 
-    (get-page-names-by-ids (state/get-current-repo) ids))
 
-   ([repo ids]
 
-    (when repo
 
-      (->> (db-utils/pull-many repo '[:block/name] ids)
 
-           (map :block/name)))))
 
- (defn get-page-alias-names
 
-   [repo page-name]
 
-   (let [alias-ids (page-alias-set repo page-name)]
 
-     (when (seq alias-ids)
 
-       (let [names (->> (get-page-names-by-ids repo alias-ids)
 
-                        distinct
 
-                        (remove #(= (util/page-name-sanity-lc %) (util/page-name-sanity-lc page-name))))
 
-             lookup-refs (map (fn [name]
 
-                                [:block/name (util/page-name-sanity-lc name)]) names)]
 
-         (->> (db-utils/pull-many repo '[:block/name :block/original-name] lookup-refs)
 
-              (map (fn [m]
 
-                     (or (:block/original-name m) (:block/name m)))))))))
 
- (defn with-pages
 
-   [blocks]
 
-   (let [pages-ids (->> (map (comp :db/id :block/page) blocks)
 
-                        (remove nil?))
 
-         pages (when (seq pages-ids)
 
-                 (db-utils/pull-many '[:db/id :block/name :block/original-name :block/journal-day] pages-ids))
 
-         pages-map (reduce (fn [acc p] (assoc acc (:db/id p) p)) {} pages)
 
-         blocks (map
 
-                 (fn [block]
 
-                   (assoc block :block/page
 
-                          (get pages-map (:db/id (:block/page block)))))
 
-                 blocks)]
 
-     blocks))
 
- (defn get-page-properties
 
-   [page]
 
-   (when-let [page (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)])]
 
-     (:block/properties page)))
 
- ;; FIXME: alert
 
- (defn sort-by-left
 
-   ([blocks parent]
 
-    (sort-by-left blocks parent {:check? true}))
 
-   ([blocks parent {:keys [check?]}]
 
-    (when check?
 
-      (when (not= (count blocks) (count (set (map :block/left blocks))))
 
-        (let [duplicates (->> (map (comp :db/id :block/left) blocks)
 
-                              frequencies
 
-                              (filter (fn [[_k v]] (> v 1)))
 
-                              (map (fn [[k _v]]
 
-                                     (let [left (db-utils/pull k)]
 
-                                       {:left left
 
-                                        :duplicates (->>
 
-                                                     (filter (fn [block]
 
-                                                               (= k (:db/id (:block/left block))))
 
-                                                             blocks)
 
-                                                     (map #(select-keys % [:db/id :block/level :block/content :block/file])))}))))]
 
-          #_(util/pprint duplicates)))
 
-      (assert (= (count blocks) (count (set (map :block/left blocks)))) "Each block should have a different left node"))
 
-    (let [left->blocks (reduce (fn [acc b] (assoc acc (:db/id (:block/left b)) b)) {} blocks)]
 
-      (loop [block parent
 
-             result []]
 
-        (if-let [next (get left->blocks (:db/id block))]
 
-          (recur next (conj result next))
 
-          (vec result))))))
 
- (defn sort-by-left-recursive
 
-   [form]
 
-   (walk/postwalk (fn [f]
 
-                    (if (and (map? f)
 
-                             (:block/_parent f))
 
-                      (let [children (:block/_parent f)]
 
-                        (-> f
 
-                            (dissoc :block/_parent)
 
-                            (assoc :block/children (sort-by-left children f))))
 
-                      f))
 
-                  form))
 
- (defn get-sorted-page-block-ids
 
-   [page-id]
 
-   (let [root (db-utils/entity page-id)]
 
-     (loop [result []
 
-            children (sort-by-left (:block/_parent root) root)]
 
-       (if (seq children)
 
-         (let [child (first children)]
 
-           (recur (conj result (:db/id child))
 
-                  (concat
 
-                   (sort-by-left (:block/_parent child) child)
 
-                   (rest children))))
 
-         result))))
 
- (defn sort-page-random-blocks
 
-   "Blocks could be non consecutive."
 
-   [blocks]
 
-   (assert (every? #(= (:block/page %) (:block/page (first blocks))) blocks) "Blocks must to be in a same page.")
 
-   (let [page-id (:db/id (:block/page (first blocks)))
 
-         ;; TODO: there's no need to sort all the blocks
 
-         sorted-ids (get-sorted-page-block-ids page-id)
 
-         blocks-map (zipmap (map :db/id blocks) blocks)]
 
-     (keep blocks-map sorted-ids)))
 
- (defn has-children?
 
-   ([block-id]
 
-    (has-children? (conn/get-db) block-id))
 
-   ([db block-id]
 
-    (some? (:block/_parent (d/entity db [:block/uuid block-id])))))
 
- (defn- collapsed-and-has-children?
 
-   [db block]
 
-   (and (:block/collapsed? block) (has-children? db (:block/uuid block))))
 
- (defn get-by-parent-&-left
 
-   [db parent-id left-id]
 
-   (when (and parent-id left-id)
 
-     (let [lefts (:block/_left (d/entity db left-id))]
 
-       (some (fn [node] (when (and (= parent-id (:db/id (:block/parent node)))
 
-                                   (not= parent-id (:db/id node)))
 
-                          node)) lefts))))
 
- (defn- get-next-outdented-block
 
-   "Get the next outdented block of the block that has the `id`.
 
-   e.g.
 
-   - a
 
-     - b
 
-       - c
 
-   - d
 
-   The next outdented block of `c` is `d`."
 
-   [db id]
 
-   (when-let [block (d/entity db id)]
 
-     (let [parent (:block/parent block)]
 
-       (if-let [parent-sibling (get-by-parent-&-left db
 
-                                                     (:db/id (:block/parent parent))
 
-                                                     (:db/id parent))]
 
-         parent-sibling
 
-         (get-next-outdented-block db (:db/id parent))))))
 
- (defn get-block-parent
 
-   ([block-id]
 
-    (get-block-parent (state/get-current-repo) block-id))
 
-   ([repo block-id]
 
-    (when-let [db (conn/get-db repo)]
 
-      (when-let [block (d/entity db [:block/uuid block-id])]
 
-        (:block/parent block)))))
 
- (defn top-block?
 
-   [block]
 
-   (= (:db/id (:block/parent block))
 
-      (:db/id (:block/page block))))
 
- ;; non recursive query
 
- (defn get-block-parents
 
-   ([repo block-id]
 
-    (get-block-parents repo block-id 100))
 
-   ([repo block-id depth]
 
-    (loop [block-id block-id
 
-           parents (list)
 
-           d 1]
 
-      (if (> d depth)
 
-        parents
 
-        (if-let [parent (get-block-parent repo block-id)]
 
-          (recur (:block/uuid parent) (conj parents parent) (inc d))
 
-          parents)))))
 
- (comment
 
-   (defn get-immediate-children-v2
 
-     [repo block-id]
 
-     (d/pull (conn/get-db repo)
 
-             '[:block/_parent]
 
-             [:block/uuid block-id])))
 
- ;; Use built-in recursive
 
- (defn get-block-parents-v2
 
-   [repo block-id]
 
-   (d/pull (conn/get-db repo)
 
-           '[:db/id :block/collapsed? :block/properties {:block/parent ...}]
 
-           [:block/uuid block-id]))
 
- (defn get-next-open-block
 
-   ([db block]
 
-    (get-next-open-block db block nil))
 
-   ([db block scoped-block-id]
 
-    (let [block-id (:db/id block)
 
-          block-parent-id (:db/id (:block/parent block))
 
-          next-block (or
 
-                      (if (and (collapsed-and-has-children? db block)
 
-                               (not= block-id scoped-block-id)) ; skips children
 
-                        ;; Sibling
 
-                        (get-by-parent-&-left db block-parent-id block-id)
 
-                        (or
 
-                         ;; Child
 
-                         (get-by-parent-&-left db block-id block-id)
 
-                         ;; Sibling
 
-                         (get-by-parent-&-left db block-parent-id block-id)))
 
-                      ;; Next outdented block
 
-                      (get-next-outdented-block db block-id))]
 
-      (if (and scoped-block-id next-block)
 
-        (let [parents (->> (get-block-parents (state/get-current-repo) (:block/uuid next-block))
 
-                           (map :db/id)
 
-                           (set))]
 
-          (when (contains? parents scoped-block-id)
 
-            next-block))
 
-        next-block))))
 
- (defn get-paginated-blocks-no-cache
 
-   "Result should be sorted."
 
-   [db start-id {:keys [limit include-start? scoped-block-id end-id]}]
 
-   (when-let [start (d/entity db start-id)]
 
-     (let [scoped-block-parents (when scoped-block-id
 
-                                  (let [block (d/entity db scoped-block-id)]
 
-                                    (->> (get-block-parents (state/get-current-repo) (:block/uuid block))
 
-                                         (map :db/id)
 
-                                         (set))))
 
-           result (loop [block start
 
-                         result []]
 
-                    (if (and limit (>= (count result) limit))
 
-                      result
 
-                      (let [next-block (get-next-open-block db block scoped-block-id)]
 
-                        (if next-block
 
-                          (cond
 
-                            (and (seq scoped-block-parents)
 
-                                 (contains? scoped-block-parents (:db/id (:block/parent next-block))))
 
-                            result
 
-                            (and end-id (= end-id (:db/id next-block)))
 
-                            (conj result next-block)
 
-                            :else
 
-                            (recur next-block (conj result next-block)))
 
-                          result))))]
 
-       (if include-start?
 
-         (cons start result)
 
-         result))))
 
- (defn get-block-last-direct-child
 
-   "Notice: if `not-collapsed?` is true, will skip searching for any collapsed block."
 
-   ([db db-id]
 
-    (get-block-last-direct-child db db-id true))
 
-   ([db db-id not-collapsed?]
 
-    (when-let [block (d/entity db db-id)]
 
-      (when (if not-collapsed?
 
-              (not (collapsed-and-has-children? db block))
 
-              true)
 
-        (let [children (:block/_parent block)
 
-              all-left (set (concat (map (comp :db/id :block/left) children) [db-id]))
 
-              all-ids (set (map :db/id children))]
 
-          (first (set/difference all-ids all-left)))))))
 
- (defn get-block-last-child
 
-   [db db-id]
 
-   (let [last-child (get-block-last-direct-child db db-id)]
 
-     (loop [prev last-child
 
-            last-child last-child]
 
-       (if last-child
 
-         (recur last-child (get-block-last-direct-child db last-child))
 
-         prev))))
 
- (defn get-prev-open-block
 
-   [db id]
 
-   (let [block (d/entity db id)
 
-         left (:block/left block)
 
-         left-id (:db/id left)]
 
-     (if (= (:db/id left) (:db/id (:block/parent block)))
 
-       left-id
 
-       (if (util/collapsed? left)
 
-         left-id
 
-         (or (get-block-last-child db (:db/id left)) left-id)))))
 
- (defn recursive-child?
 
-   [repo child-id parent-id]
 
-   (let [*last-node (atom nil)]
 
-     (loop [node (db-utils/entity repo child-id)]
 
-       (when-not (= @*last-node node)
 
-         (reset! *last-node node)
 
-         (if node
 
-           (let [parent (:block/parent node)]
 
-             (if (= (:db/id parent) parent-id)
 
-               true
 
-               (recur parent)))
 
-           false)))))
 
- (defn get-prev-sibling
 
-   [db id]
 
-   (when-let [e (d/entity db id)]
 
-     (let [left (:block/left e)]
 
-       (when (not= (:db/id left) (:db/id (:block/parent e)))
 
-         left))))
 
- (defn get-right-sibling
 
-   [db db-id]
 
-   (when-let [block (d/entity db db-id)]
 
-     (get-by-parent-&-left db
 
-                           (:db/id (:block/parent block))
 
-                           db-id)))
 
- (defn last-child-block?
 
-   "The child block could be collapsed."
 
-   [db parent-id child-id]
 
-   (when-let [child (d/entity db child-id)]
 
-     (cond
 
-       (= parent-id child-id)
 
-       true
 
-       (get-right-sibling db child-id)
 
-       false
 
-       :else
 
-       (last-child-block? db parent-id (:db/id (:block/parent child))))))
 
- (defn- consecutive-block?
 
-   [block-1 block-2]
 
-   (let [db (conn/get-db)
 
-         aux-fn (fn [block-1 block-2]
 
-                  (and (= (:block/page block-1) (:block/page block-2))
 
-                       (or
 
-                        ;; sibling or child
 
-                        (= (:db/id (:block/left block-2)) (:db/id block-1))
 
-                        (when-let [prev-sibling (get-prev-sibling db (:db/id block-2))]
 
-                          (last-child-block? db (:db/id prev-sibling) (:db/id block-1))))))]
 
-     (or (aux-fn block-1 block-2) (aux-fn block-2 block-1))))
 
- (defn get-non-consecutive-blocks
 
-   [blocks]
 
-   (vec
 
-    (keep-indexed
 
-     (fn [i _block]
 
-       (when (< (inc i) (count blocks))
 
-         (when-not (consecutive-block? (nth blocks i)
 
-                                       (nth blocks (inc i)))
 
-           (nth blocks i))))
 
-     blocks)))
 
- (defn- get-start-id-for-pagination-query
 
-   [repo-url current-db {:keys [db-before tx-meta] :as tx-report}
 
-    result outliner-op page-id block-id tx-block-ids]
 
-   (let [db-before (or db-before current-db)
 
-         cached-ids (map :db/id @result)
 
-         cached-ids-set (set (conj cached-ids page-id))
 
-         first-changed-id (cond
 
-                            (= (:real-outliner-op tx-meta) :indent-outdent)
 
-                            (if (state/logical-outdenting?)
 
-                              (first (:move-blocks tx-meta))
 
-                              (last (:move-blocks tx-meta)))
 
-                            (= outliner-op :move-blocks)
 
-                            (let [{:keys [move-blocks target from-page to-page]} tx-meta]
 
-                              (cond
 
-                                (= page-id target) ; move to the first block
 
-                                nil
 
-                                (and from-page to-page (not= from-page to-page))
 
-                                (if (= page-id from-page)
 
-                                  (first move-blocks)
 
-                                  target)
 
-                                :else
 
-                                ;; same page, get the most top block before dragging
 
-                                (let [match-ids (set (conj move-blocks target))]
 
-                                  (loop [[id & others] cached-ids]
 
-                                    (if id
 
-                                      (if (contains? match-ids id)
 
-                                        id
 
-                                        (when (seq others)
 
-                                          (recur others)))
 
-                                      nil)))))
 
-                            :else
 
-                            (let [insert? (= :insert-blocks outliner-op)]
 
-                              (some #(when (and (or (and insert? (not (contains? cached-ids-set %)))
 
-                                                    true)
 
-                                                (recursive-child? repo-url % block-id))
 
-                                       %) tx-block-ids)))]
 
-     (when first-changed-id
 
-       (or (get-prev-open-block db-before first-changed-id)
 
-           (get-prev-open-block current-db first-changed-id)))))
 
- (defn- build-paginated-blocks-from-cache
 
-   "Notice: tx-report could be nil."
 
-   [repo-url tx-report result outliner-op page-id block-id tx-block-ids scoped-block-id]
 
-   (let [{:keys [tx-meta]} tx-report
 
-         current-db (conn/get-db repo-url)]
 
-     (cond
 
-       (and (or (:undo? tx-meta) (:redo? tx-meta)) @result)
 
-       (let [blocks-range (:pagination-blocks-range tx-meta)
 
-             [start-block-id end-block-id] (:new blocks-range)]
 
-         (get-paginated-blocks-no-cache current-db start-block-id
 
-                                        {:end-id end-block-id
 
-                                         :include-start? true
 
-                                         :scoped-block-id scoped-block-id}))
 
-       (contains? #{:save-block :delete-blocks} outliner-op)
 
-       @result
 
-       (contains? #{:insert-blocks :collapse-expand-blocks :move-blocks} outliner-op)
 
-       (when-let [start-id (get-start-id-for-pagination-query
 
-                            repo-url current-db tx-report result outliner-op page-id block-id tx-block-ids)]
 
-         (let [start-page? (:block/name (db-utils/entity start-id))]
 
-           (when-not start-page?
 
-             (let [previous-blocks (take-while (fn [b] (not= start-id (:db/id b))) @result)
 
-                   limit (-> (max (- initial-blocks-length (count previous-blocks))
 
-                                  (count tx-block-ids))
 
-                             (+ 25))
 
-                   more (get-paginated-blocks-no-cache current-db start-id {:limit limit
 
-                                                                            :include-start? true
 
-                                                                            :scoped-block-id scoped-block-id})]
 
-               (concat previous-blocks more)))))
 
-       :else
 
-       nil)))
 
- (defn get-paginated-blocks
 
-   "Get paginated blocks for a page or a specific block.
 
-    `scoped-block-id`: if specified, returns its children only."
 
-   ([repo-url block-id]
 
-    (get-paginated-blocks repo-url block-id {}))
 
-   ([repo-url block-id {:keys [pull-keys start-block limit use-cache? scoped-block-id]
 
-                        :or {pull-keys '[* :block/_refs]
 
-                             limit initial-blocks-length
 
-                             use-cache? true
 
-                             scoped-block-id nil}}]
 
-    (when block-id
 
-      (assert (integer? block-id) (str "wrong block-id: " block-id))
 
-      (let [entity (db-utils/entity repo-url block-id)
 
-            page? (some? (:block/name entity))
 
-            page-entity (if page? entity (:block/page entity))
 
-            page-id (:db/id page-entity)
 
-            bare-page-map {:db/id page-id
 
-                           :block/name (:block/name page-entity)
 
-                           :block/original-name (:block/original-name page-entity)
 
-                           :block/journal-day (:block/journal-day page-entity)}
 
-            query-key (if page?
 
-                        :frontend.db.react/page-blocks
 
-                        :frontend.db.react/block-and-children)]
 
-        (some->
 
-         (react/q repo-url [query-key block-id]
 
-                  {:use-cache? use-cache?
 
-                   :query-fn (fn [db tx-report result]
 
-                               (let [tx-data (:tx-data tx-report)
 
-                                     refs (some->> (filter #(= :block/refs (:a %)) tx-data)
 
-                                                   (map :v))
 
-                                     tx-block-ids (distinct (-> (map :e tx-data)
 
-                                                                (concat refs)))
 
-                                     [tx-id->block cached-id->block] (when (and tx-report result)
 
-                                                                       (let [blocks (->> (db-utils/pull-many repo-url pull-keys tx-block-ids)
 
-                                                                                         (remove nil?))]
 
-                                                                         [(zipmap (mapv :db/id blocks) blocks)
 
-                                                                          (zipmap (mapv :db/id @result) @result)]))
 
-                                     limit (if (and result @result)
 
-                                             (max (+ (count @result) 5) limit)
 
-                                             limit)
 
-                                     outliner-op (get-in tx-report [:tx-meta :outliner-op])
 
-                                     blocks (build-paginated-blocks-from-cache repo-url tx-report result outliner-op page-id block-id tx-block-ids scoped-block-id)
 
-                                     blocks (or blocks
 
-                                                (get-paginated-blocks-no-cache (conn/get-db repo-url) block-id {:limit limit
 
-                                                                                                                :include-start? (not page?)
 
-                                                                                                                :scoped-block-id scoped-block-id}))
 
-                                     block-eids (map :db/id blocks)
 
-                                     blocks (if (and (seq tx-id->block)
 
-                                                     (not (contains? #{:move-blocks} outliner-op)))
 
-                                              (map (fn [id]
 
-                                                     (or (get tx-id->block id)
 
-                                                         (get cached-id->block id)
 
-                                                         (db-utils/pull repo-url pull-keys id))) block-eids)
 
-                                              (db-utils/pull-many repo-url pull-keys block-eids))
 
-                                     blocks (remove (fn [b] (nil? (:block/content b))) blocks)]
 
-                                 (map (fn [b] (assoc b :block/page bare-page-map)) blocks)))}
 
-                  nil)
 
-         react)))))
 
- (defn get-page-blocks-no-cache
 
-   ([page]
 
-    (get-page-blocks-no-cache (state/get-current-repo) page nil))
 
-   ([repo-url page]
 
-    (get-page-blocks-no-cache repo-url page nil))
 
-   ([repo-url page {:keys [pull-keys]
 
-                    :or {pull-keys '[*]}}]
 
-    (when page
 
-      (let [page (util/page-name-sanity-lc page)
 
-            page-id (:db/id (db-utils/entity repo-url [:block/name page]))
 
-            db (conn/get-db repo-url)]
 
-        (when page-id
 
-          (let [datoms (d/datoms db :avet :block/page page-id)
 
-                block-eids (mapv :e datoms)]
 
-            (db-utils/pull-many repo-url pull-keys block-eids)))))))
 
- (defn get-page-blocks-count
 
-   [repo page-id]
 
-   (when-let [db (conn/get-db repo)]
 
-     (count (d/datoms db :avet :block/page page-id))))
 
- (defn page-exists?
 
-   "Whether a page exists."
 
-   [page-name]
 
-   (when page-name
 
-     (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)])))
 
- (defn page-empty?
 
-   "Whether a page is empty. Does it has a non-page block?
 
-   `page-id` could be either a string or a db/id."
 
-   [repo page-id]
 
-   (when-let [db (conn/get-db repo)]
 
-     (let [page-id (if (string? page-id)
 
-                     [:block/name (util/safe-page-name-sanity-lc page-id)]
 
-                     page-id)
 
-           page (d/entity db page-id)]
 
-       (nil? (:block/_left page)))))
 
- (defn page-empty-or-dummy?
 
-   [repo page-id]
 
-   (or
 
-    (page-empty? repo page-id)
 
-    (when-let [db (conn/get-db repo)]
 
-      (let [datoms (d/datoms db :avet :block/page page-id)]
 
-        (and (= (count datoms) 1)
 
-             (= "" (:block/content (db-utils/pull (:e (first datoms))))))))))
 
- (defn parents-collapsed?
 
-   [repo block-id]
 
-   (when-let [block (:block/parent (get-block-parents-v2 repo block-id))]
 
-     (->> (tree-seq map? (fn [x] [(:block/parent x)]) block)
 
-          (some util/collapsed?))))
 
- (defn get-block-page
 
-   [repo block-id]
 
-   (when-let [block (db-utils/entity repo [:block/uuid block-id])]
 
-     (db-utils/entity repo (:db/id (:block/page block)))))
 
- (defn get-pages-by-name-partition
 
-   [repo partition]
 
-   (when-let [db (conn/get-db repo)]
 
-     (when-not (string/blank? partition)
 
-       (let [partition (util/page-name-sanity-lc (string/trim partition))
 
-             ids (->> (d/datoms db :aevt :block/name)
 
-                      (filter (fn [datom]
 
-                                (let [page (:v datom)]
 
-                                  (string/includes? page partition))))
 
-                      (map :e))]
 
-         (when (seq ids)
 
-           (db-utils/pull-many repo
 
-                               '[:db/id :block/name :block/original-name]
 
-                               ids))))))
 
- (defn get-block-children-ids
 
-   [repo block-uuid]
 
-   (when-let [db (conn/get-db repo)]
 
-     (let [eid (:db/id (db-utils/entity repo [:block/uuid block-uuid]))]
 
-       (->> (d/q
 
-             '[:find ?id
 
-               :in $ ?p %
 
-               :where
 
-               (child ?p ?c)
 
-               [?c :block/uuid ?id]]
 
-             db
 
-             eid
 
-             rules)
 
-            (apply concat)))))
 
- (defn get-block-immediate-children
 
-   "Doesn't include nested children."
 
-   [repo block-uuid]
 
-   (when-let [db (conn/get-db repo)]
 
-     (-> (d/q
 
-          '[:find [(pull ?b [*]) ...]
 
-            :in $ ?parent-id
 
-            :where
 
-            [?parent :block/uuid ?parent-id]
 
-            [?b :block/parent ?parent]]
 
-          db
 
-          block-uuid)
 
-         (sort-by-left (db-utils/entity [:block/uuid block-uuid])))))
 
- (defn sub-block-direct-children
 
-   "Doesn't include nested children."
 
-   [repo block-uuid]
 
-   (when-let [db (conn/get-db repo)]
 
-     (-> (react/q repo [:frontend.db.react/block-direct-children block-uuid] {}
 
-                  '[:find [(pull ?b [*]) ...]
 
-                    :in $ ?parent-id
 
-                    :where
 
-                    [?parent :block/uuid ?parent-id]
 
-                    [?b :block/parent ?parent]]
 
-                  block-uuid)
 
-         react
 
-         (sort-by-left (db-utils/entity [:block/uuid block-uuid])))))
 
- (defn get-block-children
 
-   "Including nested children."
 
-   [repo block-uuid]
 
-   (let [ids (get-block-children-ids repo block-uuid)
 
-         ids (map (fn [id] [:block/uuid id]) ids)]
 
-     (when (seq ids)
 
-       (db-utils/pull-many repo '[*] ids))))
 
- ;; TODO: use the tree directly
 
- (defn- flatten-tree
 
-   [blocks-tree]
 
-   (if-let [children (:block/_parent blocks-tree)]
 
-     (cons (dissoc blocks-tree :block/_parent) (mapcat flatten-tree children))
 
-     [blocks-tree]))
 
- (defn get-block-and-children
 
-   [repo block-uuid]
 
-   (some-> (d/q
 
-            '[:find [(pull ?block ?block-attrs) ...]
 
-              :in $ ?id ?block-attrs
 
-              :where
 
-              [?block :block/uuid ?id]]
 
-            (conn/get-db repo)
 
-            block-uuid
 
-            block-attrs)
 
-           first
 
-           flatten-tree))
 
- (defn get-file-page
 
-   ([file-path]
 
-    (get-file-page file-path true))
 
-   ([file-path original-name?]
 
-    (when-let [repo (state/get-current-repo)]
 
-      (when-let [db (conn/get-db repo)]
 
-        (some->
 
-         (d/q
 
-          (if original-name?
 
-            '[:find ?page-name
 
-              :in $ ?path
 
-              :where
 
-              [?file :file/path ?path]
 
-              [?page :block/file ?file]
 
-              [?page :block/original-name ?page-name]]
 
-            '[:find ?page-name
 
-              :in $ ?path
 
-              :where
 
-              [?file :file/path ?path]
 
-              [?page :block/file ?file]
 
-              [?page :block/name ?page-name]])
 
-          db file-path)
 
-         db-utils/seq-flatten
 
-         first)))))
 
- (defn get-page-file
 
-   ([page-name]
 
-    (get-page-file (state/get-current-repo) page-name))
 
-   ([repo page-name]
 
-    (some-> (or (db-utils/entity repo [:block/name page-name])
 
-                (db-utils/entity repo [:block/original-name page-name]))
 
-            :block/file)))
 
- (defn get-block-file-path
 
-   [block]
 
-   (when-let [page-id (:db/id (:block/page block))]
 
-     (:file/path (:block/file (db-utils/entity page-id)))))
 
- (defn get-file-page-id
 
-   [file-path]
 
-   (when-let [repo (state/get-current-repo)]
 
-     (when-let [db (conn/get-db repo)]
 
-       (some->
 
-        (d/q
 
-         '[:find ?page
 
-           :in $ ?path
 
-           :where
 
-           [?file :file/path ?path]
 
-           [?page :block/name]
 
-           [?page :block/file ?file]]
 
-         db file-path)
 
-        db-utils/seq-flatten
 
-        first))))
 
- (defn get-page
 
-   [page-name]
 
-   (if-let [id (parse-uuid page-name)]
 
-     (db-utils/entity [:block/uuid id])
 
-     (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)])))
 
- (defn get-redirect-page-name
 
-   "Given any readable page-name, return the exact page-name in db. If page
 
-    doesn't exists yet, will return the passed `page-name`. Accepts both
 
-    sanitized or unsanitized names.
 
-    alias?: if true, alias is allowed to be returned; otherwise, it would be deref."
 
-   ([page-name] (get-redirect-page-name page-name false))
 
-   ([page-name alias?]
 
-    (when page-name
 
-      (let [page-name' (util/page-name-sanity-lc page-name)
 
-            page-entity (db-utils/entity [:block/name page-name'])]
 
-        (cond
 
-          alias?
 
-          page-name'
 
-          (nil? page-entity)
 
-          page-name
 
-          (page-empty-or-dummy? (state/get-current-repo) (:db/id page-entity))
 
-          (let [source-page (get-alias-source-page (state/get-current-repo) page-name')]
 
-            (or (when source-page (:block/name source-page))
 
-                page-name'))
 
-          :else
 
-          page-name')))))
 
- (defn get-page-original-name
 
-   [page-name]
 
-   (when (string? page-name)
 
-     (let [page (db-utils/pull [:block/name (util/page-name-sanity-lc page-name)])]
 
-       (or (:block/original-name page)
 
-           (:block/name page)))))
 
- (defn get-journals-length
 
-   []
 
-   (let [today (db-utils/date->int (js/Date.))]
 
-     (d/q '[:find (count ?page) .
 
-            :in $ ?today
 
-            :where
 
-            [?page :block/journal? true]
 
-            [?page :block/journal-day ?journal-day]
 
-            [(<= ?journal-day ?today)]]
 
-          (conn/get-db (state/get-current-repo))
 
-          today)))
 
- (defn get-latest-journals
 
-   ([n]
 
-    (get-latest-journals (state/get-current-repo) n))
 
-   ([repo-url n]
 
-    (when (conn/get-db repo-url)
 
-      (let [date (js/Date.)
 
-            _ (.setDate date (- (.getDate date) (dec n)))
 
-            today (db-utils/date->int (js/Date.))]
 
-        (->>
 
-         (react/q repo-url [:frontend.db.react/journals] {:use-cache? false}
 
-                  '[:find [(pull ?page [*]) ...]
 
-                    :in $ ?today
 
-                    :where
 
-                    [?page :block/name ?page-name]
 
-                    [?page :block/journal? true]
 
-                    [?page :block/journal-day ?journal-day]
 
-                    [(<= ?journal-day ?today)]]
 
-                  today)
 
-         (react)
 
-         (sort-by :block/journal-day)
 
-         (reverse)
 
-         (take n))))))
 
- ;; get pages that this page referenced
 
- (defn get-page-referenced-pages
 
-   [repo page]
 
-   (when-let [db (conn/get-db repo)]
 
-     (let [page-name (util/safe-page-name-sanity-lc page)
 
-           pages (page-alias-set repo page)
 
-           page-id (:db/id (db-utils/entity [:block/name page-name]))
 
-           ref-pages (d/q
 
-                      '[:find [?ref-page-name ...]
 
-                        :in $ ?pages
 
-                        :where
 
-                        [(untuple ?pages) [?page ...]]
 
-                        [?block :block/page ?page]
 
-                        [?block :block/refs ?ref-page]
 
-                        [?ref-page :block/name ?ref-page-name]]
 
-                      db
 
-                      pages)]
 
-       (mapv (fn [page] [page (get-page-alias repo page)]) ref-pages))))
 
- (defn get-page-linked-refs-refed-pages
 
-   [repo page]
 
-   (when-let [db (conn/get-db repo)]
 
-     (->
 
-      (d/q
 
-       '[:find [?ref-page ...]
 
-         :in $ % ?page
 
-         :where
 
-         [?p :block/name ?page]
 
-         [?b :block/path-refs ?p]
 
-         [?b :block/refs ?other-p]
 
-         [(not= ?p ?other-p)]
 
-         [?other-p :block/original-name ?ref-page]]
 
-       db
 
-       rules
 
-       (util/safe-page-name-sanity-lc page))
 
-      (distinct))))
 
- ;; Ignore files with empty blocks for now
 
- (defn get-pages-relation
 
-   [repo with-journal?]
 
-   (when-let [db (conn/get-db repo)]
 
-     (let [q (if with-journal?
 
-               '[:find ?page ?ref-page-name
 
-                 :where
 
-                 [?p :block/name ?page]
 
-                 [?block :block/page ?p]
 
-                 [?block :block/refs ?ref-page]
 
-                 [?ref-page :block/name ?ref-page-name]]
 
-               '[:find ?page ?ref-page-name
 
-                 :where
 
-                 [?p :block/journal? false]
 
-                 [?p :block/name ?page]
 
-                 [?block :block/page ?p]
 
-                 [?block :block/refs ?ref-page]
 
-                 [?ref-page :block/name ?ref-page-name]])]
 
-       (->>
 
-        (d/q q db)
 
-        (map (fn [[page ref-page-name]]
 
-               [page ref-page-name]))))))
 
- ;; get pages who mentioned this page
 
- ;; TODO: use :block/_refs
 
- (defn get-pages-that-mentioned-page
 
-   [repo page include-journals]
 
-   (when (conn/get-db repo)
 
-     (let [page-id (:db/id (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)]))
 
-           pages (page-alias-set repo page)
 
-           query-base '[:find ?mentioned-page-name
 
-                        :in $ ?pages ?page-name
 
-                        :where
 
-                        [?block :block/refs ?p]
 
-                        [(contains? ?pages ?p)]
 
-                        [?block :block/page ?mentioned-page]
 
-                        [?mentioned-page :block/name ?mentioned-page-name]]
 
-           query  (if include-journals
 
-                    query-base
 
-                    (conj query-base '[?mentioned-page :block/journal? false]))
 
-           mentioned-pages (->> (react/q repo [:frontend.db.react/page<-pages page-id] {:use-cache? false}
 
-                                         query
 
-                                         pages
 
-                                         page)
 
-                                react
 
-                                db-utils/seq-flatten)]
 
-       (mapv (fn [page] [page (get-page-alias repo page)]) mentioned-pages))))
 
- (defn get-page-referenced-blocks-no-cache
 
-   [page-id]
 
-   (when-let [repo (state/get-current-repo)]
 
-     (->>
 
-      (d/q '[:find (pull ?b [*])
 
-             :in $ ?page-id
 
-             :where
 
-             [?b :block/refs ?page-id]]
 
-           (conn/get-db repo)
 
-           page-id)
 
-      (flatten))))
 
- (defn get-page-referenced-blocks
 
-   ([page]
 
-    (get-page-referenced-blocks (state/get-current-repo) page {:filter? false}))
 
-   ([page options]
 
-    (get-page-referenced-blocks (state/get-current-repo) page options))
 
-   ([repo page options]
 
-    (when repo
 
-      (when (conn/get-db repo)
 
-        (let [page-id (:db/id (db-utils/entity [:block/name (util/safe-page-name-sanity-lc page)]))
 
-              pages (page-alias-set repo page)
 
-              aliases (set/difference pages #{page-id})
 
-              query-result (react/q repo
 
-                                    [:frontend.db.react/page<-blocks-or-block<-blocks page-id]
 
-                                    {}
 
-                                    '[:find [(pull ?block ?block-attrs) ...]
 
-                                      :in $ [?ref-page ...] ?block-attrs
 
-                                      :where
 
-                                      [?block :block/refs ?ref-page]]
 
-                                    pages
 
-                                    (butlast block-attrs))
 
-              result (if (not (options :filter?)) (->> query-result
 
-                                            react
 
-                                            (remove (fn [block]
 
-                                                      (= page-id (:db/id (:block/page block)))))
 
-                                            (sort-by-left-recursive)
 
-                                            db-utils/group-by-page
 
-                                            (map (fn [[k blocks]]
 
-                                                   (let [k (if (contains? aliases (:db/id k))
 
-                                                             (assoc k :block/alias? true)
 
-                                                             k)]
 
-                                                     [k blocks]))))
 
-                         (map (comp :block/original-name :block/page) (->> query-result
 
-                                                                           react
 
-                                                                           (remove (fn [block]
 
-                                                                                     (= page-id (:db/id (:block/page block))))) (sort-by-left-recursive))))]
 
-          result)))))
 
- (defn get-page-referenced-blocks-ids
 
-   "Faster and can be used for pagination later."
 
-   ([page]
 
-    (get-page-referenced-blocks-ids (state/get-current-repo) page))
 
-   ([repo page]
 
-    (when repo
 
-      (when-let [db (conn/get-db repo)]
 
-        (let [pages (page-alias-set repo page)]
 
-          (d/q
 
-           '[:find ?block
 
-             :in $ [?ref-page ...]
 
-             :where
 
-             [?block :block/refs ?ref-page]]
 
-           db
 
-           pages))))))
 
- (defn get-date-scheduled-or-deadlines
 
-   [journal-title]
 
-   (when-let [date (date/journal-title->int journal-title)]
 
-     (let [future-days (state/get-scheduled-future-days)]
 
-       (when-let [repo (state/get-current-repo)]
 
-         (->> (react/q repo [:custom :scheduled-deadline journal-title] {}
 
-                       '[:find [(pull ?block ?block-attrs) ...]
 
-                         :in $ ?day ?future ?block-attrs
 
-                         :where
 
-                         (or
 
-                          [?block :block/scheduled ?d]
 
-                          [?block :block/deadline ?d])
 
-                         [(get-else $ ?block :block/repeated? false) ?repeated]
 
-                         [(get-else $ ?block :block/marker "NIL") ?marker]
 
-                         [(not= ?marker "DONE")]
 
-                         [(not= ?marker "CANCELED")]
 
-                         [(not= ?marker "CANCELLED")]
 
-                         [(<= ?d ?future)]
 
-                         (or-join [?repeated ?d ?day]
 
-                                  [(true? ?repeated)]
 
-                                  [(>= ?d ?day)])]
 
-                       date
 
-                       (+ date future-days)
 
-                       block-attrs)
 
-              react
 
-              (sort-by-left-recursive)
 
-              db-utils/group-by-page)))))
 
- (defn- pattern [name]
 
-   (re-pattern (str "(?i)(^|[^\\[#0-9a-zA-Z]|((^|[^\\[])\\[))"
 
-                    (util/regex-escape name)
 
-                    "($|[^0-9a-zA-Z])")))
 
- (defn get-page-unlinked-references
 
-   [page]
 
-   (when-let [repo (state/get-current-repo)]
 
-     (when (conn/get-db repo)
 
-       (let [page (util/safe-page-name-sanity-lc page)
 
-             page-id     (:db/id (db-utils/entity [:block/name page]))
 
-             alias-names (get-page-alias-names repo page)
 
-             patterns    (->> (conj alias-names page)
 
-                              (map pattern))
 
-             filter-fn   (fn [datom]
 
-                           (some (fn [p]
 
-                                   (re-find p (->> (:v datom)
 
-                                                   (drawer/remove-logbook))))
 
-                                 patterns))]
 
-         (->> (react/q repo [:frontend.db.react/page-unlinked-refs page-id]
 
-                       {:query-fn (fn [db _tx-report _result]
 
-                                    (let [ids
 
-                                          (->> (d/datoms db :aevt :block/content)
 
-                                               (filter filter-fn)
 
-                                               (map :e))
 
-                                          result (d/pull-many db block-attrs ids)]
 
-                                      (remove (fn [block] (= page-id (:db/id (:block/page block)))) result)))}
 
-                       nil)
 
-              react
 
-              (sort-by-left-recursive)
 
-              db-utils/group-by-page)))))
 
- ;; TODO: Replace recursive queries with datoms index implementation
 
- ;; see https://github.com/tonsky/datascript/issues/130#issuecomment-169520434
 
- (defn get-block-referenced-blocks
 
-   ([block-uuid]
 
-    (get-block-referenced-blocks block-uuid {:filter? false}))
 
-   ([block-uuid options]
 
-    (when-let [repo (state/get-current-repo)]
 
-      (when (conn/get-db repo)
 
-        (let [block (db-utils/entity [:block/uuid block-uuid])
 
-              query-result (->> (react/q repo [:frontend.db.react/page<-blocks-or-block<-blocks
 
-                                               (:db/id block)]
 
-                                         {:use-cache? false}
 
-                                         '[:find [(pull ?ref-block ?block-attrs) ...]
 
-                                           :in $ ?block-uuid ?block-attrs
 
-                                           :where
 
-                                           [?block :block/uuid ?block-uuid]
 
-                                           [?ref-block :block/refs ?block]]
 
-                                         block-uuid
 
-                                         block-attrs)
 
-                                react
 
-                                (sort-by-left-recursive))]
 
-          (if (options :filter?)
 
-            (map (comp :block/original-name :block/page) query-result)
 
-            (db-utils/group-by-page query-result)))))))
 
- (defn get-block-referenced-blocks-ids
 
-   [block-uuid]
 
-   (when-let [repo (state/get-current-repo)]
 
-     (let [block (db-utils/entity [:block/uuid block-uuid])]
 
-       (->> (react/q repo [:frontend.db.react/block<-block-ids
 
-                           (:db/id block)] {}
 
-                     '[:find ?ref-block
 
-                       :in $ ?block-uuid ?block-attrs
 
-                       :where
 
-                       [?block :block/uuid ?block-uuid]
 
-                       [?ref-block :block/refs ?block]]
 
-                     block-uuid
 
-                     block-attrs)
 
-            react))))
 
- (defn get-referenced-blocks-ids
 
-   [page-name-or-block-uuid]
 
-   (if-let [id (parse-uuid (str page-name-or-block-uuid))]
 
-     (get-block-referenced-blocks-ids id)
 
-     (get-page-referenced-blocks-ids page-name-or-block-uuid)))
 
- (defn journal-page?
 
-   "sanitized page-name only"
 
-   [page-name]
 
-   (:block/journal? (db-utils/entity [:block/name page-name])))
 
- (defn get-public-pages
 
-   [db]
 
-   (-> (d/q
 
-        '[:find ?p
 
-          :where
 
-          [?p :block/name]
 
-          [?p :block/properties ?properties]
 
-          [(get ?properties :public) ?pub]
 
-          [(= true ?pub)]]
 
-        db)
 
-       (db-utils/seq-flatten)))
 
- (defn get-public-false-pages
 
-   [db]
 
-   (-> (d/q
 
-        '[:find ?p
 
-          :where
 
-          [?p :block/name]
 
-          [?p :block/properties ?properties]
 
-          [(get ?properties :public) ?pub]
 
-          [(= false ?pub)]]
 
-        db)
 
-       (db-utils/seq-flatten)))
 
- (defn get-public-false-block-ids
 
-   [db]
 
-   (-> (d/q
 
-        '[:find ?b
 
-          :where
 
-          [?p :block/name]
 
-          [?p :block/properties ?properties]
 
-          [(get ?properties :public) ?pub]
 
-          [(= false ?pub)]
 
-          [?b :block/page ?p]]
 
-        db)
 
-       (db-utils/seq-flatten)))
 
- (defn get-all-templates
 
-   []
 
-   (let [pred (fn [_db properties]
 
-                (some? (:template properties)))]
 
-     (->> (d/q
 
-           '[:find ?b ?p
 
-             :in $ ?pred
 
-             :where
 
-             [?b :block/properties ?p]
 
-             [(?pred $ ?p)]]
 
-           (conn/get-db)
 
-           pred)
 
-          (map (fn [[e m]]
 
-                 [(get m :template) e]))
 
-          (into {}))))
 
- (defn get-all-properties
 
-   []
 
-   (let [properties (d/q
 
-                      '[:find [?p ...]
 
-                        :where
 
-                        [_ :block/properties ?p]]
 
-                      (conn/get-db))
 
-         properties (remove (fn [m] (empty? m)) properties)]
 
-     (->> (map keys properties)
 
-          (apply concat)
 
-          distinct
 
-          (remove #{:id})
 
-          sort)))
 
- (defn get-property-values
 
-   [property]
 
-   (let [pred (fn [_db properties]
 
-                (get properties property))]
 
-     (->>
 
-      (d/q
 
-        '[:find [?property-val ...]
 
-          :in $ ?pred
 
-          :where
 
-          [_ :block/properties ?p]
 
-          [(?pred $ ?p) ?property-val]]
 
-        (conn/get-db)
 
-        pred)
 
-      (map (fn [x] (if (coll? x) x [x])))
 
-      (apply concat)
 
-      (map str)
 
-      (remove string/blank?)
 
-      (distinct)
 
-      (sort))))
 
- (defn get-template-by-name
 
-   [name]
 
-   (when (string? name)
 
-     (->> (d/q
 
-           '[:find [(pull ?b [*]) ...]
 
-             :in $ ?name
 
-             :where
 
-             [?b :block/properties ?p]
 
-             [(get ?p :template) ?t]
 
-             [(= ?t ?name)]]
 
-           (conn/get-db)
 
-           name)
 
-          (sort-by :block/name)
 
-          (first))))
 
- (defonce blocks-count-cache (atom nil))
 
- (defn blocks-count
 
-   ([]
 
-    (blocks-count true))
 
-   ([cache?]
 
-    (if (and cache? @blocks-count-cache)
 
-      @blocks-count-cache
 
-      (when-let [db (conn/get-db)]
 
-        (let [n (count (d/datoms db :avet :block/uuid))]
 
-          (reset! blocks-count-cache n)
 
-          n)))))
 
- (defn get-all-referenced-blocks-uuid
 
-   "Get all uuids of blocks with any back link exists."
 
-   []
 
-   (when-let [db (conn/get-db)]
 
-     (d/q '[:find [?refed-uuid ...]
 
-            :where
 
-            ;; ?referee-b is block with ref towards ?refed-b
 
-            [?refed-b   :block/uuid ?refed-uuid]
 
-            [?referee-b :block/refs ?refed-b]] db)))
 
- ;; block/uuid and block/content
 
- (defn get-all-block-contents
 
-   []
 
-   (when-let [db (conn/get-db)]
 
-     (->> (d/datoms db :avet :block/uuid)
 
-          (map :v)
 
-          (map (fn [id]
 
-                 (let [e (db-utils/entity [:block/uuid id])]
 
-                   (when (and (not (:block/name e))
 
-                              (not (string/blank? (:block/content e))))
 
-                     {:db/id (:db/id e)
 
-                      :block/uuid id
 
-                      :block/page (:db/id (:block/page e))
 
-                      :block/content (:block/content e)
 
-                      :block/format (:block/format e)}))))
 
-          (remove nil?))))
 
- (defn get-assets
 
-   [datoms]
 
-   (keep
 
-    (fn [datom]
 
-      (when (= :block/content (:a datom))
 
-        (let [matched (re-seq #"\([./]*/assets/([^)]+)\)" (:v datom))
 
-              matched (get (into [] matched) 0)
 
-              path (get matched 1)]
 
-          (when (and (string? path)
 
-                     (not (string/ends-with? path ".js")))
 
-            path))))
 
-    datoms))
 
- (defn clean-export!
 
-   [db]
 
-   (let [remove? #(contains? #{"me" "recent" "file"} %)
 
-         non-public-pages (get-public-false-pages db)
 
-         non-public-datoms (get-public-false-block-ids db)
 
-         non-public-datom-ids (set (concat non-public-pages non-public-datoms))
 
-         filtered-db (d/filter db
 
-                               (fn [_db datom]
 
-                                 (let [ns (namespace (:a datom))]
 
-                                   (and (not (remove? ns))
 
-                                        (not (contains? #{:block/file} (:a datom)))
 
-                                        (not (contains? non-public-datom-ids (:e datom)))))))
 
-         datoms (d/datoms filtered-db :eavt)
 
-         assets (get-assets datoms)]
 
-     [@(d/conn-from-datoms datoms db-schema/schema) assets]))
 
- (defn filter-only-public-pages-and-blocks
 
-   [db]
 
-   (let [public-pages (get-public-pages db)]
 
-     (when (seq public-pages)
 
-       (let [public-pages (set public-pages)
 
-             exported-namespace? #(contains? #{"block" "me" "recent"} %)
 
-             filtered-db (d/filter db
 
-                                   (fn [db datom]
 
-                                     (let [ns (namespace (:a datom))]
 
-                                       (and
 
-                                        (not (contains? #{:block/file} (:a datom)))
 
-                                        (not= ns "file")
 
-                                        (or
 
-                                         (not (exported-namespace? ns))
 
-                                         (and (= ns "block")
 
-                                              (or
 
-                                               (contains? public-pages (:e datom))
 
-                                               (contains? public-pages (:db/id (:block/page (d/entity db (:e datom))))))))))))
 
-             datoms (d/datoms filtered-db :eavt)
 
-             assets (get-assets datoms)]
 
-         [@(d/conn-from-datoms datoms db-schema/schema) assets]))))
 
- (defn delete-blocks
 
-   [repo-url files _delete-page?]
 
-   (when (seq files)
 
-     (let [blocks (get-files-blocks repo-url files)]
 
-       (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks))))
 
- (defn delete-files
 
-   [files]
 
-   (mapv (fn [path] [:db.fn/retractEntity [:file/path path]]) files))
 
- (defn delete-file-blocks!
 
-   [repo-url path]
 
-   (let [blocks (get-file-blocks repo-url path)]
 
-     (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks)))
 
- (defn delete-page-blocks
 
-   [repo-url page]
 
-   (when page
 
-     (when-let [db (conn/get-db repo-url)]
 
-       (let [page (db-utils/pull [:block/name (util/page-name-sanity-lc page)])]
 
-         (when page
 
-           (let [datoms (d/datoms db :avet :block/page (:db/id page))
 
-                 block-eids (mapv :e datoms)]
 
-             (mapv (fn [eid] [:db.fn/retractEntity eid]) block-eids)))))))
 
- (defn delete-pages-by-files
 
-   [files]
 
-   (let [pages (->> (mapv get-file-page files)
 
-                    (remove nil?))]
 
-     (when (seq pages)
 
-       (mapv (fn [page] [:db.fn/retractEntity [:block/name page]]) (map util/page-name-sanity-lc pages)))))
 
- (defn set-file-content!
 
-   ([repo path content]
 
-    (set-file-content! repo path content {}))
 
-   ([repo path content opts]
 
-    (when (and repo path)
 
-      (let [tx-data {:file/path path
 
-                     :file/content content}]
 
-        (db-utils/transact! repo [tx-data] (merge opts {:skip-refresh? true}))))))
 
- (defn get-pre-block
 
-   [repo page-id]
 
-   (-> (d/q '[:find (pull ?b [*])
 
-              :in $ ?page
 
-              :where
 
-              [?b :block/page ?page]
 
-              [?b :block/pre-block? true]]
 
-            (conn/get-db repo)
 
-            page-id)
 
-       ffirst))
 
- (defn get-namespace-pages
 
-   "Accepts both sanitized and unsanitized namespaces"
 
-   [repo namespace]
 
-   (assert (string? namespace))
 
-   (let [namespace (util/page-name-sanity-lc namespace)]
 
-     (d/q
 
-      '[:find [(pull ?c [:db/id :block/name :block/original-name
 
-                         :block/namespace
 
-                         {:block/file [:db/id :file/path]}]) ...]
 
-        :in $ % ?namespace
 
-        :where
 
-        [?p :block/name ?namespace]
 
-        (namespace ?p ?c)]
 
-      (conn/get-db repo)
 
-      rules
 
-      namespace)))
 
- (defn- tree [flat-col root]
 
-   (let [sort-fn #(sort-by :block/name %)
 
-         children (group-by :block/namespace flat-col)
 
-         namespace-children (fn namespace-children [parent-id]
 
-                              (map (fn [m]
 
-                                     (assoc m :namespace/children
 
-                                            (sort-fn (namespace-children {:db/id (:db/id m)}))))
 
-                                   (sort-fn (get children parent-id))))]
 
-     (namespace-children root)))
 
- (defn get-namespace-hierarchy
 
-   "Unsanitized namespaces"
 
-   [repo namespace]
 
-   (let [children (get-namespace-pages repo namespace)
 
-         namespace-id (:db/id (db-utils/entity [:block/name (util/page-name-sanity-lc namespace)]))
 
-         root {:db/id namespace-id}
 
-         col (conj children root)]
 
-     (tree col root)))
 
- (defn get-page-namespace
 
-   [repo page]
 
-   (:block/namespace (db-utils/entity repo [:block/name (util/page-name-sanity-lc page)])))
 
- (defn get-page-namespace-routes
 
-   [repo page]
 
-   (assert (string? page))
 
-   (when-let [db (conn/get-db repo)]
 
-     (when-not (string/blank? page)
 
-       (let [page (util/page-name-sanity-lc (string/trim page))
 
-             page-exist? (db-utils/entity repo [:block/name page])
 
-             ids (if page-exist?
 
-                   '()
 
-                   (->> (d/datoms db :aevt :block/name)
 
-                        (filter (fn [datom]
 
-                                  (string/ends-with? (:v datom) (str "/" page))))
 
-                        (map :e)))]
 
-         (when (seq ids)
 
-           (db-utils/pull-many repo
 
-                               '[:db/id :block/name :block/original-name
 
-                                 {:block/file [:db/id :file/path]}]
 
-                               ids))))))
 
- (defn get-orphaned-pages
 
-   [{:keys [repo pages empty-ref-f]
 
-     :or {repo (state/get-current-repo)
 
-          empty-ref-f (fn [page] (zero? (count (:block/_refs page))))}}]
 
-   (let [pages (->> (or pages (get-pages repo))
 
-                    (remove nil?))
 
-         built-in-pages (set (map string/lower-case default-db/built-in-pages-names))
 
-         orphaned-pages (->>
 
-                         (map
 
-                          (fn [page]
 
-                            (let [name (util/page-name-sanity-lc page)]
 
-                              (when-let [page (db-utils/entity [:block/name name])]
 
-                                (and
 
-                                 (empty-ref-f page)
 
-                                 (or
 
-                                  (page-empty? repo (:db/id page))
 
-                                  (let [first-child (first (:block/_left page))
 
-                                        children (:block/_page page)]
 
-                                    (and
 
-                                     first-child
 
-                                     (= 1 (count children))
 
-                                     (contains? #{"" "-" "*"} (string/trim (:block/content first-child))))))
 
-                                 (not (contains? built-in-pages name))
 
-                                 (not (:block/_namespace page))
 
-                                  ;; a/b/c might be deleted but a/b/c/d still exists (for backward compatibility)
 
-                                 (not (and (string/includes? name "/")
 
-                                           (not (:block/journal? page))))
 
-                                 page))))
 
-                          pages)
 
-                         (remove false?)
 
-                         (remove nil?))]
 
-     orphaned-pages))
 
 
  |