Merge branch 'feat/db' into fix/multiple-tabs

This commit is contained in:
rcmerci
2025-04-17 13:06:23 +08:00
30 changed files with 463 additions and 296 deletions

View File

@@ -182,6 +182,7 @@
logseq.db.file-based.entity-util file-entity-util
logseq.db.frontend.class db-class
logseq.db.frontend.content db-content
logseq.db.frontend.db db-db
logseq.db.frontend.db-ident db-ident
logseq.db.frontend.entity-plus entity-plus
logseq.db.frontend.entity-util entity-util

View File

@@ -40,7 +40,7 @@
logseq/shui {:local/root "deps/shui"}
metosin/malli {:mvn/version "0.16.1"}
com.cognitect/transit-cljs {:mvn/version "0.8.280"}
missionary/missionary {:mvn/version "b.39"}
missionary/missionary {:mvn/version "b.44"}
meander/epsilon {:mvn/version "0.0.650"}
io.github.open-spaced-repetition/cljc-fsrs {:git/sha "0e70e96a73cf63c85dcc2df4d022edf12806b239"

View File

@@ -16,6 +16,7 @@
logseq.db.common.view db-view
logseq.db.frontend.content db-content
logseq.db.frontend.class db-class
logseq.db.frontend.db db-db
logseq.db.frontend.db-ident db-ident
logseq.db.frontend.inputs db-inputs
logseq.db.frontend.property db-property

View File

@@ -1,19 +1,19 @@
(ns logseq.db
"Main namespace for public db fns. For DB and file graphs.
For shared file graph only fns, use logseq.graph-parser.db"
"Main namespace for db fns that handles DB and file graphs. For db graph only
fns, use logseq.db.frontend.db and for file graph only fns, use
logseq.graph-parser.db"
(:require [clojure.set :as set]
[clojure.string :as string]
[clojure.walk :as walk]
[datascript.core :as d]
[datascript.impl.entity :as de]
[logseq.common.util :as common-util]
[logseq.common.util.namespace :as ns-util]
[logseq.common.util.page-ref :as page-ref]
[logseq.common.uuid :as common-uuid]
[logseq.db.common.delete-blocks :as delete-blocks] ;; Load entity extensions
[logseq.db.common.entity-util :as common-entity-util]
[logseq.db.common.sqlite :as sqlite-common-db]
[logseq.db.frontend.class :as db-class]
[logseq.db.frontend.db :as db-db]
[logseq.db.frontend.entity-plus :as entity-plus]
[logseq.db.frontend.entity-util :as entity-util]
[logseq.db.frontend.property :as db-property]
@@ -412,11 +412,6 @@
[]
(common-uuid/gen-uuid))
(defn get-classes-with-property
"Get classes which have given property as a class property"
[db property-id]
(:logseq.property.class/_properties (d/entity db property-id)))
(defn get-alias-source-page
"return the source page (page-name) of an alias"
[db alias-id]
@@ -492,37 +487,13 @@
e))))))
(def built-in? entity-util/built-in?)
(defn built-in-class-property?
"Whether property a built-in property for the specific class"
[class-entity property-entity]
(and (built-in? class-entity)
(class? class-entity)
(built-in? property-entity)
(contains? (set (get-in (db-class/built-in-classes (:db/ident class-entity)) [:schema :properties]))
(:db/ident property-entity))))
(defn private-built-in-page?
"Private built-in pages should not be navigable or searchable by users. Later it
could be useful to use this for the All Pages view"
[page]
(cond (property? page)
(not (public-built-in-property? page))
(or (class? page) (internal-page? page))
false
;; Default to true for closed value and future internal types.
;; Other types like whiteboard are not considered because they aren't built-in
:else
true))
(def get-classes-with-property db-db/get-classes-with-property)
(def built-in-class-property? db-db/built-in-class-property?)
(def private-built-in-page? db-db/private-built-in-page?)
(def write-transit-str sqlite-util/write-transit-str)
(def read-transit-str sqlite-util/read-transit-str)
(defn build-favorite-tx
"Builds tx for a favorite block in favorite page"
[favorite-uuid]
{:block/link [:block/uuid favorite-uuid]
:block/title ""})
(def build-favorite-tx db-db/build-favorite-tx)
(defn get-key-value
[db key-ident]
@@ -544,87 +515,20 @@
[db]
(when db (get-key-value db :logseq.kv/remote-schema-version)))
(defn get-all-properties
[db]
(->> (d/datoms db :avet :block/tags :logseq.class/Property)
(map (fn [d]
(d/entity db (:e d))))))
(defn get-page-parents
[node & {:keys [node-class?]}]
(when-let [parent (:logseq.property/parent node)]
(loop [current-parent parent
parents' []]
(if (and
current-parent
(if node-class? (class? current-parent) true)
(not (contains? parents' current-parent)))
(recur (:logseq.property/parent current-parent)
(conj parents' current-parent))
(vec (reverse parents'))))))
(defn get-title-with-parents
[entity]
(if (or (entity-util/class? entity) (entity-util/internal-page? entity))
(let [parents' (->> (get-page-parents entity)
(remove (fn [e] (= :logseq.class/Root (:db/ident e))))
vec)]
(string/join
ns-util/parent-char
(map :block/title (conj (vec parents') entity))))
(:block/title entity)))
(defn get-classes-parents
[tags]
(let [tags' (filter class? tags)
result (mapcat #(get-page-parents % {:node-class? true}) tags')]
(set result)))
(defn class-instance?
"Whether `object` is an instance of `class`"
[class object]
(let [tags (:block/tags object)
tags-ids (set (map :db/id tags))]
(or
(contains? tags-ids (:db/id class))
(let [class-parent-ids (set (map :db/id (get-classes-parents tags)))]
(contains? (set/union class-parent-ids tags-ids) (:db/id class))))))
(defn inline-tag?
[block-raw-title tag]
(assert (string? block-raw-title) "block-raw-title should be a string")
(string/includes? block-raw-title (str "#" (page-ref/->page-ref (:block/uuid tag)))))
(defonce node-display-type-classes
#{:logseq.class/Code-block :logseq.class/Math-block :logseq.class/Quote-block})
(defn get-class-ident-by-display-type
[display-type]
(case display-type
:code :logseq.class/Code-block
:math :logseq.class/Math-block
:quote :logseq.class/Quote-block
nil))
(defn get-display-type-by-class-ident
[class-ident]
(case class-ident
:logseq.class/Code-block :code
:logseq.class/Math-block :math
:logseq.class/Quote-block :quote
nil))
(def get-all-properties db-db/get-all-properties)
(def get-page-parents db-db/get-page-parents)
(def get-classes-parents db-db/get-classes-parents)
(def get-title-with-parents db-db/get-title-with-parents)
(def class-instance? db-db/class-instance?)
(def inline-tag? db-db/inline-tag?)
(def node-display-type-classes db-db/node-display-type-classes)
(def get-class-ident-by-display-type db-db/get-class-ident-by-display-type)
(def get-display-type-by-class-ident db-db/get-display-type-by-class-ident)
(def get-recent-updated-pages sqlite-common-db/get-recent-updated-pages)
(def get-latest-journals sqlite-common-db/get-latest-journals)
(defn get-all-namespace-relation
[db]
(d/q '[:find ?page ?parent
:where
[?page :block/namespace ?parent]]
db))
(defn get-pages-relation
[db with-journal?]
(if (entity-plus/db-based-graph? db)

View File

@@ -311,6 +311,7 @@
(= (:db/id block) id)
(= id (:db/id (:block/page block)))
(ldb/hidden? (:block/page block))
(ldb/hidden? block)
(contains? (set (map :db/id (:block/tags block))) (:db/id entity))
(some? (get block (:db/ident entity))))
(or
@@ -331,7 +332,8 @@
distinct))
full-ref-blocks)
(remove nil?)
(frequencies))]
(frequencies)
(sort-by second #(> %1 %2)))]
{:ref-pages-count ref-pages-count
:ref-blocks ref-blocks}))
@@ -564,9 +566,11 @@
[(:block/uuid (first blocks))
(map (fn [b]
{:db/id (:db/id b)
:block/parent (:block/uuid (:block/parent b))}) blocks)])
:block/parent (:block/uuid (:block/parent b))})
(ldb/sort-by-order blocks))])
parent-groups))
(map :db/id entities))]
(->> (sort-entities db sorting entities)
(map :db/id)))]
[by-value' group]))
result)
(map :db/id result))]

113
deps/db/src/logseq/db/frontend/db.cljs vendored Normal file
View File

@@ -0,0 +1,113 @@
(ns logseq.db.frontend.db
"DB graph fns commonly used outside db dep"
(:require [clojure.set :as set]
[clojure.string :as string]
[datascript.core :as d]
[logseq.common.util.namespace :as ns-util]
[logseq.common.util.page-ref :as page-ref]
[logseq.db.frontend.class :as db-class]
[logseq.db.frontend.entity-util :as entity-util]
[logseq.db.frontend.property :as db-property]))
(defn get-classes-with-property
"Get classes which have given property as a class property"
[db property-id]
(:logseq.property.class/_properties (d/entity db property-id)))
(defn built-in-class-property?
"Whether property a built-in property for the specific class"
[class-entity property-entity]
(and (entity-util/built-in? class-entity)
(entity-util/class? class-entity)
(entity-util/built-in? property-entity)
(contains? (set (get-in (db-class/built-in-classes (:db/ident class-entity)) [:schema :properties]))
(:db/ident property-entity))))
(defn private-built-in-page?
"Private built-in pages should not be navigable or searchable by users. Later it
could be useful to use this for the All Pages view"
[page]
(cond (entity-util/property? page)
(not (db-property/public-built-in-property? page))
(or (entity-util/class? page) (entity-util/internal-page? page))
false
;; Default to true for closed value and future internal types.
;; Other types like whiteboard are not considered because they aren't built-in
:else
true))
(defn build-favorite-tx
"Builds tx for a favorite block in favorite page"
[favorite-uuid]
{:block/link [:block/uuid favorite-uuid]
:block/title ""})
(defn get-all-properties
[db]
(->> (d/datoms db :avet :block/tags :logseq.class/Property)
(map (fn [d]
(d/entity db (:e d))))))
(defn get-page-parents
[node & {:keys [node-class?]}]
(when-let [parent (:logseq.property/parent node)]
(loop [current-parent parent
parents' []]
(if (and
current-parent
(if node-class? (entity-util/class? current-parent) true)
(not (contains? parents' current-parent)))
(recur (:logseq.property/parent current-parent)
(conj parents' current-parent))
(vec (reverse parents'))))))
(defn get-title-with-parents
[entity]
(if (or (entity-util/class? entity) (entity-util/internal-page? entity))
(let [parents' (->> (get-page-parents entity)
(remove (fn [e] (= :logseq.class/Root (:db/ident e))))
vec)]
(string/join
ns-util/parent-char
(map :block/title (conj (vec parents') entity))))
(:block/title entity)))
(defn get-classes-parents
[tags]
(let [tags' (filter entity-util/class? tags)
result (mapcat #(get-page-parents % {:node-class? true}) tags')]
(set result)))
(defn class-instance?
"Whether `object` is an instance of `class`"
[class object]
(let [tags (:block/tags object)
tags-ids (set (map :db/id tags))]
(or
(contains? tags-ids (:db/id class))
(let [class-parent-ids (set (map :db/id (get-classes-parents tags)))]
(contains? (set/union class-parent-ids tags-ids) (:db/id class))))))
(defn inline-tag?
[block-raw-title tag]
(assert (string? block-raw-title) "block-raw-title should be a string")
(string/includes? block-raw-title (str "#" (page-ref/->page-ref (:block/uuid tag)))))
(defonce node-display-type-classes
#{:logseq.class/Code-block :logseq.class/Math-block :logseq.class/Quote-block})
(defn get-class-ident-by-display-type
[display-type]
(case display-type
:code :logseq.class/Code-block
:math :logseq.class/Math-block
:quote :logseq.class/Quote-block
nil))
(defn get-display-type-by-class-ident
[class-ident]
(case class-ident
:logseq.class/Code-block :code
:logseq.class/Math-block :math
:logseq.class/Quote-block :quote
nil))

View File

@@ -659,8 +659,10 @@
(if (:logseq.property/classes m)
(update m :logseq.property/classes
(fn [cs]
(mapv #(or (some->> (:db/ident %) class-ident->id (hash-map :db/id))
(throw (ex-info (str "No :db/id found for :db/ident " (pr-str (:db/ident %))) {})))
(mapv #(if (db-class/logseq-class? (:db/ident %))
%
(or (some->> (:db/ident %) class-ident->id (hash-map :db/id))
(throw (ex-info (str "No :db/id found for :db/ident " (pr-str %)) {}))))
cs)))
m))
properties-tx)

View File

@@ -9,11 +9,13 @@
[logseq.db :as ldb]
[logseq.db.frontend.class :as db-class]
[logseq.db.frontend.content :as db-content]
[logseq.db.frontend.db :as db-db]
[logseq.db.frontend.entity-plus :as entity-plus]
[logseq.db.frontend.entity-util :as entity-util]
[logseq.db.frontend.property :as db-property]
[logseq.db.sqlite.build :as sqlite-build]
[medley.core :as medley]))
[medley.core :as medley]
[logseq.db.frontend.property.type :as db-property-type]))
;; Export fns
;; ==========
@@ -40,7 +42,7 @@
(defn- build-pvalue-entity-for-build-page
[pvalue]
(cond (ldb/internal-page? pvalue)
(cond (entity-util/internal-page? pvalue)
;; Should page properties be pulled here?
[:build/page (cond-> (shallow-copy-page pvalue)
(seq (:block/tags pvalue))
@@ -48,7 +50,10 @@
(entity-util/journal? pvalue)
[:build/page {:build/journal (:block/journal-day pvalue)}]))
(defn- build-pvalue-entity-default [ent-properties pvalue options]
(defn- build-pvalue-entity-default [db ent-properties pvalue
{:keys [include-uuid-fn]
:or {include-uuid-fn (constantly false)}
:as options}]
(if (or (seq ent-properties) (seq (:block/tags pvalue)))
(cond-> {:build/property-value :block
:block/title (or (block-title pvalue)
@@ -57,7 +62,17 @@
(assoc :build/tags (->build-tags (:block/tags pvalue)))
(seq ent-properties)
(assoc :build/properties ent-properties)
(assoc :build/properties
;; TODO: Add support for ref properties here and in sqlite.build
(->> ent-properties
(keep (fn [[k v]]
(let [prop-type (:logseq.property/type (d/entity db k))]
(when-not (contains? db-property-type/all-ref-property-types prop-type)
[k v]))))
(into {})))
(include-uuid-fn (:block/uuid pvalue))
(assoc :block/uuid (:block/uuid pvalue) :build/keep-uuid? true)
(:include-timestamps? options)
(merge (select-keys pvalue [:block/created-at :block/updated-at])))
@@ -87,7 +102,7 @@
(medley/filter-keys db-property/internal-property?))
ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*))
(buildable-properties db' ent-properties* properties-config' options'))]
(build-pvalue-entity-default ent-properties pvalue options'))))))]
(build-pvalue-entity-default db ent-properties pvalue options'))))))]
(->> ent-properties
(map (fn [[k v]]
[k
@@ -259,7 +274,8 @@
(mapcat (fn [val-or-vals]
(keep #(when (and (vector? %)
(= :block/uuid (first %))
(::existing-property-value? (meta %))) (second %))
(::existing-property-value? (meta %)))
(second %))
(if (set? val-or-vals) val-or-vals [val-or-vals]))))
set))
@@ -293,7 +309,7 @@
(when-let [prop-ids (seq (map :db/ident (filter entity-util/property? ents)))]
(build-export-properties db prop-ids export-opts))
classes
(when-let [class-ents (seq (filter ldb/class? ents))]
(when-let [class-ents (seq (filter entity-util/class? ents))]
(->> class-ents
(map #(vector (:db/ident %) (build-export-class % export-opts)))
(into {})))]
@@ -328,7 +344,7 @@
(let [class-parent-ents (->> classes-config
(filter #(:build/class-parent (val %)))
(map #(d/entity db (key %)))
ldb/get-classes-parents)
db-db/get-classes-parents)
classes
(->> class-parent-ents
(remove #(db-class/logseq-class? (:db/ident %)))
@@ -670,8 +686,16 @@
(update :pages-and-blocks
(fn [pages-and-blocks]
(mapv (fn [{:keys [page blocks]}]
{:page (remove-uuid-if-not-ref page)
:blocks (sqlite-build/update-each-block blocks remove-uuid-if-not-ref)})
(let [page-map {:page (remove-uuid-if-not-ref page)
:blocks (sqlite-build/update-each-block blocks remove-uuid-if-not-ref)}
;; TODO: Walk data structure via :build/properties instead of slower walk
page-map'
(walk/postwalk (fn [f]
(if (and (map? f) (:build/property-value f))
(remove-uuid-if-not-ref f)
f))
page-map)]
page-map'))
pages-and-blocks))))))
(defn- add-ontology-for-include-namespaces
@@ -750,12 +774,19 @@
undefined))
(defn- find-undefined-uuids [{:keys [classes properties pages-and-blocks]}]
(let [known-uuids
(let [pvalue-known-uuids (atom #{})
_ (walk/postwalk (fn [f]
(if (and (map? f) (:build/property-value f) (:block/uuid f))
(swap! pvalue-known-uuids conj (:block/uuid f))
f))
pages-and-blocks)
known-uuids
(->> (concat (keep :block/uuid (vals classes))
(keep :block/uuid (vals properties))
(keep #(get-in % [:page :block/uuid]) pages-and-blocks)
(mapcat #(sqlite-build/extract-from-blocks (:blocks %) (fn [m] (some-> m :block/uuid vector)))
pages-and-blocks))
pages-and-blocks)
@pvalue-known-uuids)
set)
;; Only looks one-level deep in properties e.g. not inside :build/page
;; Doesn't find :block/link refs

View File

@@ -52,12 +52,7 @@
(merge read-handlers))
reader (transit/reader :json {:handlers read-handlers*})]
(fn read-transit-str* [s]
;; TODO: delete the following pred later
;; https://github.com/logseq/logseq/pull/11790#discussion_r2014120469
(if (and (string? s) (identical? "[" (first s)))
(transit/read reader s)
(do (prn :invalid-transit-string s)
s)))))
(transit/read reader s))))
(defn db-based-graph?
[graph-name]

View File

@@ -59,3 +59,10 @@
[db page-name]
(some-> (ldb/get-page db page-name)
:block/file))
(defn get-all-namespace-relation
[db]
(d/q '[:find ?page ?parent
:where
[?page :block/namespace ?parent]]
db))

View File

@@ -221,7 +221,7 @@
(extend-type Entity
otree/INode
(-save [this *txs-state db repo _date-formatter {:keys [retract-attributes? retract-attributes]
(-save [this *txs-state db repo _date-formatter {:keys [retract-attributes? retract-attributes outliner-op]
:or {retract-attributes? true}}]
(assert (ds/outliner-txs-state? *txs-state)
"db should be satisfied outliner-tx-state?")
@@ -233,12 +233,16 @@
data)
db-based?
(dissoc :block/properties))
m* (-> data'
(dissoc :block/children :block/meta :block/unordered
:block.temp/ast-title :block.temp/ast-body :block/level :block.temp/fully-loaded?)
common-util/remove-nils
block-with-updated-at
(fix-tag-ids db {:db-graph? db-based?}))
collapse-or-expand? (= outliner-op :collapse-expand-blocks)
m* (cond->
(-> data'
(dissoc :block/children :block/meta :block/unordered
:block.temp/ast-title :block.temp/ast-body :block/level :block.temp/fully-loaded?)
common-util/remove-nils
(fix-tag-ids db {:db-graph? db-based?}))
(not collapse-or-expand?)
block-with-updated-at)
db-id (:db/id this)
block-uuid (:block/uuid this)
eid (or db-id (when block-uuid [:block/uuid block-uuid]))
@@ -292,7 +296,8 @@
retract-attributes)))))))
;; Update block's page attributes
(update-page-when-save-block *txs-state block-entity m)
(when-not collapse-or-expand?
(update-page-when-save-block *txs-state block-entity m))
;; Remove orphaned refs from block
(when (and (:block/title m) (not= (:block/title m) (:block/title block-entity)))
(remove-orphaned-refs-when-save db *txs-state block-entity m {:db-graph? db-based?})))

View File

@@ -79,7 +79,7 @@
(let [dark? (= "dark" theme)
relation (ldb/get-pages-relation db journal?)
tagged-pages (ldb/get-all-tagged-pages db)
namespaces (ldb/get-all-namespace-relation db)
namespaces (gp-db/get-all-namespace-relation db)
tags (set (map second tagged-pages))
full-pages (ldb/get-all-pages db)
db-based? (entity-plus/db-based-graph? db)
@@ -178,7 +178,7 @@
tags (set (remove #(= page-id %) tags))
ref-pages (get-page-referenced-pages db page-id)
mentioned-pages (get-pages-that-mentioned-page db page-id show-journal)
namespaces (ldb/get-all-namespace-relation db)
namespaces (gp-db/get-all-namespace-relation db)
links (concat
namespaces
(map (fn [ref-page]
@@ -216,7 +216,7 @@
(if (ldb/page? b) b (:block/page b))))
(remove (fn [node] (= (:db/id block) (:db/id node))))
(common-util/distinct-by :db/id))
namespaces (ldb/get-all-namespace-relation db)
namespaces (gp-db/get-all-namespace-relation db)
links (->> (concat
namespaces
(map (fn [p] [(:db/id block) (:db/id p)]) ref-blocks))

View File

@@ -21,32 +21,55 @@
(m/reductions {} init-value)
(m/latest identity))))
(def delays (reductions * 1000 (repeat 2)))
(def ^:private retry-sentinel (js-obj))
(defn backoff
"Retry task when it throw exception `(get ex-data :missionary/retry)`"
[delays-seq task]
(m/sp
(loop [[delay & rest-delays] (seq delays-seq)]
(let [r (try
(m/? task)
(catch :default e
(if (and (some-> e ex-data :missionary/retry)
(pos-int? delay))
(do (m/? (m/sleep delay))
(println :missionary/retry "after" delay "ms (" (ex-message e) ")")
retry-sentinel)
(throw e))))]
(if (identical? r retry-sentinel)
(recur rest-delays)
r)))))
(defn mix
"Return a flow which is mixed by `flows`"
[& flows]
(m/ap (m/?> (m/?> (count flows) (m/seed flows)))))
(def never-flow (m/ap (m/? m/never)))
(def delays (reductions * 1000 (repeat 2)))
(def ^:private retry-sentinel (js-obj))
(defn backoff
"Retry task when it throw exception `(get ex-data :missionary/retry)`
:delay-seq - retry delay-msecs
:reset-flow - retry immediately when getting value from flow and reset delays to init state"
[{:keys [delay-seq reset-flow]
:or {delay-seq (take 4 delays)
reset-flow never-flow}}
task]
(let [reset-flow* (mix reset-flow never-flow)]
(m/sp
(loop [[delay & rest-delays] (seq delay-seq)]
(let [r (try
(m/? task)
(catch :default e
(if (and (some-> e ex-data :missionary/retry)
(pos-int? delay))
(let [delay-or-reset
(m/? (m/race (m/sleep delay :delay)
(m/reduce (fn [_ r] (when r (reduced :reset))) nil
(->> (continue-flow reset-flow*)
(m/eduction (drop 1) (take 1))))))
rest-delays*
(case delay-or-reset
:delay
(do (println :missionary/retry "after" delay "ms (" (ex-message e) ")")
rest-delays)
:reset
(do (println :missionary/retry "retry now (" (ex-message e) ")")
delay-seq))]
[retry-sentinel rest-delays*])
(throw e))))]
(if (and (vector? r)
(first r) ;; if delete this `(first r)`,
;; the code continues to the next line even if r=0...
;; I suspect it's a bug in missionary.
(identical? retry-sentinel (first r)))
(recur (second r))
r))))))
(defn clock
"Return a flow that emits `value` every `interval-ms`."
([interval-ms]

View File

@@ -1082,3 +1082,9 @@ html.is-mac {
aspect-ratio: 16 / 9;
height: auto;
}
.ls-filters {
div[data-testid='virtuoso-item-list'] button {
@apply mb-2;
}
}

View File

@@ -1270,15 +1270,17 @@
type (:logseq.property/type property)
multiple-values? (db-property/many? property)
v (let [v (get block (:db/ident property))]
(cond
(and multiple-values? (or (set? v) (and (coll? v) (empty? v)) (nil? v)))
v
multiple-values?
#{v}
(set? v)
(first v)
:else
v))
(or
(cond
(and multiple-values? (or (set? v) (and (coll? v) (empty? v)) (nil? v)))
v
multiple-values?
#{v}
(set? v)
(first v)
:else
v)
(:logseq.property/default-value property)))
self-value-or-embedded? (fn [v]
(or (= (:db/id v) (:db/id block))
;; property value self embedded

View File

@@ -5,12 +5,14 @@
[frontend.config :as config]
[frontend.context.i18n :refer [t]]
[frontend.db :as db]
[frontend.db-mixins :as db-mixins]
[frontend.handler.page :as page-handler]
[frontend.search :as search]
[frontend.state :as state]
[frontend.ui :as ui]
[frontend.util :as util]
[logseq.db.common.view :as db-view]
[logseq.shui.hooks :as hooks]
[promesa.core :as p]
[rum.core :as rum]))
@@ -18,50 +20,71 @@
[references]
(sort-by second #(> %1 %2) references))
(defn filtered-refs
[page filters filtered-references*]
[:div.flex.gap-2.flex-wrap.items-center
(let [filtered-references (if (de/entity? (first filtered-references*))
(map (fn [e] [(:block/title e)]) filtered-references*)
filtered-references*)] <
(for [[ref-name ref-count] filtered-references]
(when ref-name
(let [lc-reference (string/lower-case ref-name)]
(ui/button
[:span
ref-name
(when ref-count [:sup " " ref-count])]
:on-click (fn [e]
(let [db-based? (config/db-based-graph? (state/get-current-repo))
includes (set (map :block/name (:included filters)))
excludes (set (map :block/name (:excluded filters)))
included? (includes lc-reference)
not-in-filters? (and (not included?) (not (excludes lc-reference)))
shift? (.-shiftKey e)]
(if db-based?
(page-handler/db-based-save-filter! page (:db/id (db/get-page lc-reference))
{:add? not-in-filters?
:include? (if not-in-filters? (not shift?) included?)})
(let [filters-m (->> (concat (map #(vector % true) includes) (map #(vector % false) excludes))
(into {}))
filters' (if not-in-filters?
(assoc filters-m lc-reference (not shift?))
(dissoc filters-m lc-reference))]
(page-handler/file-based-save-filter! page filters')))))
:small? true
:variant :outline
:key ref-name)))))])
(rum/defc ref-button
[page filters ref-name ref-count]
(let [lc-reference (string/lower-case ref-name)]
(ui/button
[:span
ref-name
(when ref-count [:sup " " ref-count])]
:on-click (fn [e]
(let [db-based? (config/db-based-graph? (state/get-current-repo))
includes (set (map :block/name (:included filters)))
excludes (set (map :block/name (:excluded filters)))
included? (includes lc-reference)
not-in-filters? (and (not included?) (not (excludes lc-reference)))
shift? (.-shiftKey e)]
(if db-based?
(page-handler/db-based-save-filter! page (:db/id (db/get-page lc-reference))
{:add? not-in-filters?
:include? (if not-in-filters? (not shift?) included?)})
(let [filters-m (->> (concat (map #(vector % true) includes) (map #(vector % false) excludes))
(into {}))
filters' (if not-in-filters?
(assoc filters-m lc-reference (not shift?))
(dissoc filters-m lc-reference))]
(page-handler/file-based-save-filter! page filters')))))
:small? true
:variant :outline)))
(rum/defcs filter-dialog < (rum/local "" ::filterSearch) rum/reactive
[state page references]
(let [page-entity (db/sub-block (:db/id page))
filter-search (get state ::filterSearch)
(defn filtered-refs
[page filters filtered-references* virtual?]
(let [filtered-references (if (de/entity? (first filtered-references*))
(map (fn [e] [(:block/title e)]) filtered-references*)
filtered-references*)]
(if (and (> (count filtered-references) 100)
(not (false? virtual?)))
(ui/virtualized-list
{:style {:height 500
:width 500
:max-width 500}
:total-count (count filtered-references)
:compute-item-key (fn [idx]
(str "ref-button-" idx))
:item-content (fn [idx]
(let [[ref-name ref-count] (util/nth-safe filtered-references idx)]
(ref-button page filters ref-name ref-count)))})
[:div.flex.gap-2.flex-wrap.items-center
{:style {:width 500
:max-width 500}}
(for [[ref-name ref-count] filtered-references]
(rum/with-key (ref-button page filters ref-name ref-count)
(str "ref-" ref-name)))])))
(rum/defc filter-dialog-aux
[page-entity references]
(let [[filter-search set-filter-search!] (hooks/use-state "")
[filtered-references set-filtered-references!] (hooks/use-state references)
filters (db-view/get-filters (db/get-db) page-entity)
filtered-references (frequencies-sort
(if (= @filter-search "")
references
(search/fuzzy-search references @filter-search :limit 500 :extract-fn first)))
{:keys [included excluded]} filters]
(hooks/use-effect!
(fn []
(let [references (if (= filter-search "")
references
(->> (search/fuzzy-search references filter-search :limit 100 :extract-fn first)
frequencies-sort))]
(set-filtered-references! references)))
[(hooks/use-debounced-value filter-search 200)])
[:div.ls-filters.filters
[:div.sm:flex.sm:items-start
[:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-gray-200.text-gray-500.sm:mx-0.sm:h-10.sm:w-10
@@ -75,12 +98,12 @@
(when (seq included)
[:div.flex.flex-row.flex-wrap.center-items
[:div.mr-1.font-medium.py-1 (t :linked-references/filter-includes)]
(filtered-refs page-entity filters included)])
(filtered-refs page-entity filters included false)])
(when (seq excluded)
[:div.flex.flex-row.flex-wrap
[:div.mr-1.font-medium.py-1 (t :linked-references/filter-excludes)]
(filtered-refs page-entity filters excluded)])])
(filtered-refs page-entity filters excluded false)])])
[:div.cp__filters-input-panel.flex.focus-within:bg-gray-03
(ui/icon "search")
[:input.cp__filters-input.w-full.bg-transparent
@@ -89,7 +112,7 @@
:ref (fn [^js el] (when el
(-> (p/delay 32) (p/then #(.focus el)))))
:on-change (fn [e]
(reset! filter-search (util/evalue e)))}]]
(set-filter-search! (util/evalue e)))}]]
(let [all-filters (set
(concat (map :block/name included)
(map :block/name excluded)))
@@ -97,4 +120,9 @@
filtered-references)]
(when (seq refs)
[:div.mt-4
(filtered-refs page-entity filters refs)]))]))
(filtered-refs page-entity filters refs true)]))]))
(rum/defc filter-dialog < rum/reactive db-mixins/query
[page references]
(let [page-entity (db/sub-block (:db/id page))]
(filter-dialog-aux page-entity references)))

View File

@@ -215,7 +215,11 @@
{:align :start})
(editor-handler/edit-block! block :max {:container-id :unknown-container}))))))}
(if block
(inline-title (:block/title block))
[:div (inline-title
(some->> (:block/title block)
string/trim
string/split-lines
first))]
[:div])]))
(defn build-columns
@@ -1220,7 +1224,8 @@
db-id (cond (map? item) (:db/id item)
(number? item) item
:else nil)
[item set-item!] (hooks/use-state nil)
block (some-> db-id db/entity)
[item set-item!] (hooks/use-state (when (:block.temp/fully-loaded? block) block))
opts (if list-view?
{:skip-refresh? true
:children? false}
@@ -1251,7 +1256,7 @@
(gdom/getElement "main-content-container"))
:compute-item-key (fn [idx]
(let [block-id (util/nth-safe rows idx)]
(str "table-row-" (:group-idx option) "-" block-id)))
(str "table-row-" block-id)))
:skipAnimationFrameInResizeObserver true
:total-count (count rows)
:context {:scrolling scrolling?}
@@ -1298,8 +1303,8 @@
:compute-item-key (fn [idx]
(let [block-id (util/nth-safe rows idx)]
(str "list-row-" block-id)))
;; :skipAnimationFrameInResizeObserver true
:total-count (count rows)
:skipAnimationFrameInResizeObserver true
:item-content (fn [idx] (lazy-item-render rows idx))})))
breadcrumb (state/get-component :block/breadcrumb)
all-numbers? (every? number? rows)]
@@ -1339,6 +1344,9 @@
{:ref #(reset! *scroller-ref %)
:total-count (count blocks)
:custom-scroll-parent (gdom/getElement "main-content-container")
:skipAnimationFrameInResizeObserver true
:compute-item-key (fn [idx]
(str (:db/id view-entity) "-card-" idx))
:item-content (fn [idx]
(lazy-item (:data table) idx {}
(fn [block]
@@ -1692,52 +1700,45 @@
:add-new-object! add-new-object!}]
(if (and group-by-property-ident (not (number? (first (:rows table)))))
(when (seq (:rows table))
[:div.flex.flex-col.border-t.pt-2
(ui/virtualized-list
{:class (when list-view? "group-list-view")
:custom-scroll-parent (gdom/getElement "main-content-container")
:increase-viewport-by {:top 300 :bottom 300}
:compute-item-key (fn [idx]
(str "table-group" idx))
:skipAnimationFrameInResizeObserver true
:total-count (count (:rows table))
:item-content (fn [idx]
(let [[value group] (nth (:rows table) idx)
add-new-object! (when (fn? add-new-object!)
(fn [_]
(add-new-object! view-entity table
{:properties {(:db/ident group-by-property) (or (and (map? value) (:db/id value)) value)}})))
table' (shui/table-option (-> table-map
(assoc-in [:data-fns :add-new-object!] add-new-object!)
(assoc :data group ; data for this group
)))
readable-property-value #(if (and (map? %) (or (:block/title %) (:logseq.property/value %)))
(db-property/property-value-content %)
(str %))
group-by-page? (or (= :block/page group-by-property-ident)
(and (not db-based?) (contains? #{:linked-references :unlinked-references} display-type)))]
(rum/with-key
(ui/foldable
[:div
(cond
group-by-page?
(if value
(let [c (state/get-component :block/page-cp)]
(c {:disable-preview? true} value))
[:div.text-muted-foreground.text-sm
"Pages"])
[:div.flex.flex-col.border-t.pt-2.gap-2
(map-indexed
(fn [idx [value group]]
(let [add-new-object! (when (fn? add-new-object!)
(fn [_]
(add-new-object! view-entity table
{:properties {(:db/ident group-by-property) (or (and (map? value) (:db/id value)) value)}})))
table' (shui/table-option (-> table-map
(assoc-in [:data-fns :add-new-object!] add-new-object!)
(assoc :data group ; data for this group
)))
readable-property-value #(if (and (map? %) (or (:block/title %) (:logseq.property/value %)))
(db-property/property-value-content %)
(str %))
group-by-page? (or (= :block/page group-by-property-ident)
(and (not db-based?) (contains? #{:linked-references :unlinked-references} display-type)))]
(rum/with-key
(ui/foldable
[:div
(cond
group-by-page?
(if value
(let [c (state/get-component :block/page-cp)]
(c {:disable-preview? true} value))
[:div.text-muted-foreground.text-sm
"Pages"])
(some? value)
(let [icon (pu/get-block-property-value value :logseq.property/icon)]
[:div.flex.flex-row.gap-1.items-center
(when icon (icon-component/icon icon {:color? true}))
(readable-property-value value)])
:else
(str "No " (:block/title group-by-property)))]
(let [render (view-cp view-entity (assoc table' :rows group :group-idx idx) option view-opts)]
(if list-view? [:div.-ml-2 render] render))
{:title-trigger? false})
(str "group-" idx))))})])
(some? value)
(let [icon (pu/get-block-property-value value :logseq.property/icon)]
[:div.flex.flex-row.gap-1.items-center
(when icon (icon-component/icon icon {:color? true}))
(readable-property-value value)])
:else
(str "No " (:block/title group-by-property)))]
(let [render (view-cp view-entity (assoc table' :rows group) option view-opts)]
(if list-view? [:div.-ml-2 render] render))
{:title-trigger? false})
(str (:db/id view-entity) "-group-idx-" idx))))
(:rows table))])
(view-cp view-entity table option view-opts)))]
(merge {:title-trigger? false} foldable-options))]))
@@ -1812,7 +1813,7 @@
[(:db/id view-entity)
(hooks/use-debounced-value input 300)
sorting-filters
(:logseq.property.view/group-by-property view-entity)
(:db/id (:logseq.property.view/group-by-property view-entity))
;; page filters
(:logseq.property.linked-references/includes view-parent)
(:logseq.property.linked-references/excludes view-parent)

View File

@@ -793,7 +793,7 @@ independent of format as format specific heading characters are stripped"
(defn get-all-namespace-relation
[repo]
(ldb/get-all-namespace-relation (conn/get-db repo)))
(gp-db/get-all-namespace-relation (conn/get-db repo)))
(defn get-all-namespace-parents
[repo]

View File

@@ -104,7 +104,9 @@
(defn- handle-connection-change
[e]
(let [online? (= (gobj/get e "type") "online")]
(state/set-online! online?)))
(state/set-online! online?)
(state/<invoke-db-worker :thread-api/update-thread-atom
:thread-atom/online-event online?)))
(defn set-network-watcher!
[]
@@ -155,7 +157,6 @@
(i18n/start)
(instrument/init)
(state/set-online! js/navigator.onLine)
(set-network-watcher!)
(-> (util/indexeddb-check?)
(p/catch (fn [_e]
@@ -177,6 +178,7 @@
_ (if (empty? repos)
(repo-handler/new-db! config/demo-repo)
(restore-and-setup! repo))]
(set-network-watcher!)
(when (util/electron?)
(persist-db/run-export-periodically!))
(when (mobile-util/native-platform?)

View File

@@ -77,8 +77,8 @@
(def edit-block! block-handler/edit-block!)
(defn- outliner-save-block!
[block]
(outliner-op/save-block! block))
[block & {:as opts}]
(outliner-op/save-block! block opts))
(defn get-block-own-order-list-type
[block]
@@ -3642,7 +3642,7 @@
(when-not (= current-value value)
(let [block {:block/uuid block-id
:block/collapsed? value}]
(outliner-save-block! block)))))))
(outliner-save-block! block {:outliner-op :collapse-expand-blocks})))))))
(doseq [block-id block-ids]
(state/set-collapsed-block! block-id value)))))

View File

@@ -195,7 +195,7 @@
(p/let [result (export-common-handler/<get-debug-datoms repo)
filename (file-name (str repo "-debug-datoms") :transit)
data-str (str "data:text/transit;charset=utf-8,"
(js/encodeURIComponent result))]
(js/encodeURIComponent (ldb/write-transit-str result)))]
(when-let [anchor (gdom/getElement "download-as-transit-debug")]
(.setAttribute anchor "href" data-str)
(.setAttribute anchor "download" filename)

View File

@@ -544,7 +544,13 @@
(boolean
(some
;; check if there's any entity reference this `block` except the view-entity
(fn [ref] (not= id (:db/id (:logseq.property/view-for ref))))
(fn [ref]
(not
(or (= id (:db/id (:logseq.property/view-for ref)))
(ldb/hidden? (:block/page ref))
(ldb/hidden? ref)
(contains? (set (map :db/id (:block/tags ref))) id)
(some? (get ref (:db/ident block))))))
(:block/_refs block)))))))
(def-thread-api :thread-api/get-block-parents

View File

@@ -65,6 +65,7 @@
(if (and (contains? #{:block/title :block/name} a)
(let [entity (d/entity @conn e)]
(and (not (:db/ident entity))
(not (ldb/journal? entity)))))
(not (ldb/journal? entity))
(not (:logseq.property/built-in? entity)))))
(d/datom e a (str "debug " e) t)
(d/datom e a v t))))))

View File

@@ -0,0 +1,13 @@
(ns frontend.worker.flows
"common flows in worker thread"
(:require [frontend.worker.state :as worker-state]
[missionary.core :as m]))
(def online-event-flow
(->> (m/watch (get @worker-state/*state :thread-atom/online-event))
(m/eduction
(drop-while nil?)
(filter true?))))
(comment
((m/reduce (fn [_ x] (prn :xxx x)) online-event-flow) prn prn))

View File

@@ -3,6 +3,7 @@
(:require [clojure.string :as string]
[datascript.core :as d]
[frontend.common.missionary :as c.m]
[frontend.worker.flows :as worker-flows]
[frontend.worker.rtc.branch-graph :as r.branch-graph]
[frontend.worker.rtc.client-op :as client-op]
[frontend.worker.rtc.exception :as r.ex]
@@ -64,7 +65,10 @@
(let [{:keys [max-remote-schema-version]}
(m/?
(c.m/backoff
(take 5 (drop 2 c.m/delays)) ;retry 5 times if remote-graph is creating (4000 8000 16000 32000 64000)
{:delay-seq
;retry 5 times if remote-graph is creating (4000 8000 16000 32000 64000)
(take 5 (drop 2 c.m/delays))
:reset-flow worker-flows/online-event-flow}
(new-task--register-graph-updates get-ws-create-task graph-uuid major-schema-version repo)))]
(when max-remote-schema-version
(add-log-fn :rtc.log/higher-remote-schema-version-exists

View File

@@ -4,6 +4,7 @@
https://github.com/ReilySiegel/missionary-websocket/blob/master/src/com/reilysiegel/missionary/websocket.cljs"
(:require [cljs-http-missionary.client :as http]
[frontend.common.missionary :as c.m]
[frontend.worker.flows :as worker-flows]
[frontend.worker.rtc.exception :as r.ex]
[frontend.worker.rtc.malli-schema :as rtc-schema]
[missionary.core :as m]))
@@ -88,7 +89,8 @@
(pos-int? open-ws-timeout))
[retry-count open-ws-timeout])
(c.m/backoff
(take retry-count c.m/delays)
{:delay-seq (take retry-count c.m/delays)
:reset-flow worker-flows/online-event-flow}
(m/sp
(try
(if-let [ws (m/? (m/timeout (create-mws* url) open-ws-timeout))]

View File

@@ -36,7 +36,11 @@
:auth/access-token nil
:auth/refresh-token nil
:rtc/downloading-graph? false}))
:rtc/downloading-graph? false
;; thread atoms, these atoms' value are syncing from ui-thread
:thread-atom/online-event (atom nil)
}))
(defonce *rtc-ws-url (atom nil))

View File

@@ -0,0 +1,12 @@
(ns frontend.worker.thread-atom
"atoms from ui-thread"
(:require [frontend.common.thread-api :as thread-api :refer [def-thread-api]]
[frontend.worker.state :as worker-state]))
(def-thread-api :thread-api/update-thread-atom
[atom-key new-value]
(assert (and (keyword? atom-key)
(identical? "thread-atom" (namespace atom-key))))
(when-let [a (get @worker-state/*state atom-key)]
(reset! a new-value)
nil))

View File

@@ -44,7 +44,7 @@
(is (nil? r)))
(m/?
(c.m/backoff
(take 4 c.m/delays)
{}
(m/sp
(let [conn (helper/get-downloaded-test-conn)
page1 (d/pull @conn '[*] [:block/uuid const/page1-uuid])
@@ -72,7 +72,7 @@
(m/? (helper/new-task--wait-all-client-ops-sent))))
:client2
(c.m/backoff
(take 4 c.m/delays)
{}
(m/sp
(let [conn (helper/get-downloaded-test-conn)
page (d/pull @conn '[*] [:block/uuid const/page2-uuid])]
@@ -107,7 +107,7 @@
(m/? (helper/new-task--wait-all-client-ops-sent))))
:client2
(c.m/backoff
(take 4 c.m/delays)
{}
(m/sp
(let [conn (helper/get-downloaded-test-conn)
block1 (d/pull @conn
@@ -231,7 +231,7 @@ client2:
(m/? (helper/new-task--client2-sync-barrier-1->2 "step6"))
(m/?
(c.m/backoff
(take 4 c.m/delays)
{}
(m/sp
(let [page (d/pull @conn '[*] [:block/uuid const/step6-page-uuid])
page-blocks (when-let [page-id (:db/id page)]

View File

@@ -25,7 +25,7 @@
(defn new-task--wait-creating-graph
[graph-uuid]
(c.m/backoff
(take 4 c.m/delays)
{}
(m/sp
(let [graphs (m/? (rtc.core/new-task--get-graphs const/test-token))
graph (some (fn [graph] (when (= graph-uuid (:graph-uuid graph)) graph)) graphs)]
@@ -47,7 +47,7 @@
(def new-task--get-remote-example-graph-uuid
(c.m/backoff
(take 5 c.m/delays)
{}
(m/sp
(let [graphs (m/? (rtc.core/new-task--get-graphs const/test-token))
graph
@@ -94,11 +94,11 @@
#_:clj-kondo/ignore
(me/find
client-op
[?op-type _ {:block-uuid ?block-uuid :av-coll [[!a !v _ !add] ...]}]
[?op-type ?block-uuid (map vector !a !v !add)]
[?op-type _ {:block-uuid ?block-uuid :av-coll [[!a !v _ !add] ...]}]
[?op-type ?block-uuid (map vector !a !v !add)]
[?op-type _ {:block-uuid ?block-uuid}]
[?op-type ?block-uuid]))
[?op-type _ {:block-uuid ?block-uuid}]
[?op-type ?block-uuid]))
(defn new-task--wait-all-client-ops-sent
[& {:keys [timeout] :or {timeout 10000}}]
@@ -144,7 +144,7 @@
"Return a task that return message from other client"
[block-title-pred-fn & {:keys [retry-message retry-count] :or {retry-count 4}}]
(c.m/backoff
(take retry-count c.m/delays)
{:delay-seq (take retry-count c.m/delays)}
(m/sp
(let [conn (get-downloaded-test-conn)
message-page-id (:db/id (ldb/get-page @conn const/message-page-uuid))