Merge branch 'feat/db' into perf/lazy-load-data

pull/10933/head
Tienson Qin 2024-02-01 03:58:04 +08:00
commit c1a15c0708
27 changed files with 351 additions and 271 deletions

View File

@ -14,6 +14,7 @@
:preferred-workflow :now :preferred-workflow :now
;; Exclude directories/files. ;; Exclude directories/files.
;; This is _only_ for file graphs.
;; Example usage: ;; Example usage:
;; :hidden ["/archived" "/test.md" "../assets/archived"] ;; :hidden ["/archived" "/test.md" "../assets/archived"]
:hidden [] :hidden []
@ -144,6 +145,7 @@
;; :whiteboards-directory "whiteboards" ;; :whiteboards-directory "whiteboards"
;; Enabling this option converts ;; Enabling this option converts
;; This is _only_ for file graphs.
;; [[Grant Ideas]] to [[file:./grant_ideas.org][Grant Ideas]] for org-mode. ;; [[Grant Ideas]] to [[file:./grant_ideas.org][Grant Ideas]] for org-mode.
;; For more information, visit https://github.com/logseq/logseq/issues/672 ;; For more information, visit https://github.com/logseq/logseq/issues/672
;; :org-mode/insert-file-link? false ;; :org-mode/insert-file-link? false
@ -289,6 +291,7 @@
;; :journal? false} ; Default value: false ;; :journal? false} ; Default value: false
;; Favorites to list on the left sidebar ;; Favorites to list on the left sidebar
;; This is _only_ for file graphs.
:favorites [] :favorites []
;; Set flashcards interval. ;; Set flashcards interval.
@ -307,19 +310,23 @@
;; :block-hidden-properties #{:public :icon} ;; :block-hidden-properties #{:public :icon}
;; Create a page for all properties. ;; Create a page for all properties.
;; This is _only_ for file graphs.
;; Default value: true ;; Default value: true
:property-pages/enabled? true :property-pages/enabled? true
;; Properties to exclude from having property pages ;; Properties to exclude from having property pages
;; This is _only_ for file graphs.
;; Example usage: ;; Example usage:
;; :property-pages/excludelist #{:duration :author} ;; :property-pages/excludelist #{:duration :author}
;; By default, property value separated by commas will not be treated as ;; By default, property value separated by commas will not be treated as
;; page references. You can add properties to enable it. ;; page references. You can add properties to enable it.
;; This is _only_ for file graphs.
;; Example usage: ;; Example usage:
;; :property/separated-by-commas #{:alias :tags} ;; :property/separated-by-commas #{:alias :tags}
;; Properties that are ignored when parsing property values for references ;; Properties that are ignored when parsing property values for references
;; This is _only_ for file graphs.
;; Example usage: ;; Example usage:
;; :ignored-page-references-keywords #{:author :website} ;; :ignored-page-references-keywords #{:author :website}
@ -398,10 +405,10 @@
;; :list? false} ;; Default value: false ;; :list? false} ;; Default value: false
;; Configure the escaping method for special characters in page titles. ;; Configure the escaping method for special characters in page titles.
;; This is _only_ for file graphs.
;; Warning: ;; Warning:
;; This is a dangerous operation. To modify the setting, ;; This is a dangerous operation. To modify the setting,
;; access the 'Filename format' setting and follow the instructions. ;; you'll need to manually rename all affected files and
;; Otherwise, You may need to manually rename all affected files and
;; re-index them on all clients after synchronization. ;; re-index them on all clients after synchronization.
;; Incorrect handling may result in messy page titles. ;; Incorrect handling may result in messy page titles.
;; Available options: ;; Available options:

View File

@ -18,12 +18,6 @@
(log/error :decode-uri-component-failed uri) (log/error :decode-uri-component-failed uri)
uri))) uri)))
(defn safe-url-decode
[string]
(if (string/includes? string "%")
(some-> string str safe-decode-uri-component)
string))
(defn path-normalize (defn path-normalize
"Normalize file path (for reading paths from FS, not required by writing) "Normalize file path (for reading paths from FS, not required by writing)
Keep capitalization senstivity" Keep capitalization senstivity"
@ -129,12 +123,7 @@
result)) result))
(map string/trim)))) (map string/trim))))
(defn decode-namespace-underlines (def url-encoded-pattern #"(?i)%[0-9a-f]{2}") ;; (?i) for case-insensitive mode
"Decode namespace underlines to slashed;
If continuous underlines, only decode at start;
Having empty namespace is invalid."
[string]
(string/replace string "___" "/"))
(defn page-name-sanity (defn page-name-sanity
"Sanitize the page-name. Unify different diacritics and other visual differences. "Sanitize the page-name. Unify different diacritics and other visual differences.
@ -146,24 +135,6 @@
(remove-boundary-slashes) (remove-boundary-slashes)
(path-normalize))) (path-normalize)))
(defn make-valid-namespaces
"Remove those empty namespaces from title to make it a valid page name."
[title]
(->> (string/split title "/")
(remove empty?)
(string/join "/")))
(def url-encoded-pattern #"(?i)%[0-9a-f]{2}") ;; (?i) for case-insensitive mode
(defn- tri-lb-title-parsing
"Parsing file name under the new file name format
Avoid calling directly"
[file-name]
(some-> file-name
(decode-namespace-underlines)
(string/replace url-encoded-pattern safe-url-decode)
(make-valid-namespaces)))
(defn page-name-sanity-lc (defn page-name-sanity-lc
"Sanitize the query string for a page name (mandate for :block/name)" "Sanitize the query string for a page name (mandate for :block/name)"
[s] [s]
@ -202,22 +173,6 @@
;; default ;; default
(keyword format))) (keyword format)))
(defn path->file-name
;; Only for internal paths, as they are converted to POXIS already
;; https://github.com/logseq/logseq/blob/48b8e54e0fdd8fbd2c5d25b7f1912efef8814714/deps/graph-parser/src/logseq/graph_parser/extract.cljc#L32
;; Should be converted to POXIS first for external paths
[path]
(if (string/includes? path "/")
(last (split-last "/" path))
path))
(defn path->file-body
[path]
(when-let [file-name (path->file-name path)]
(if (string/includes? file-name ".")
(first (split-last "." file-name))
file-name)))
(defn path->file-ext (defn path->file-ext
[path-or-file-name] [path-or-file-name]
(second (re-find #"(?:\.)(\w+)[^.]*$" path-or-file-name))) (second (re-find #"(?:\.)(\w+)[^.]*$" path-or-file-name)))
@ -245,27 +200,6 @@
(catch :default _ (catch :default _
false))) false)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Keep for backward compatibility ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Rule of dir-ver 0
;; Source: https://github.com/logseq/logseq/blob/e7110eea6790eda5861fdedb6b02c2a78b504cd9/deps/graph-parser/src/logseq/graph_parser/extract.cljc#L35
(defn legacy-title-parsing
[file-name-body]
(let [title (string/replace file-name-body "." "/")]
(or (safe-decode-uri-component title) title)))
;; Register sanitization / parsing fns in:
;; logseq.common.util (parsing only)
;; frontend.util.fs (sanitization only)
(defn title-parsing
"Convert file name in the given file name format to page title"
[file-name-body filename-format]
(case filename-format
:triple-lowbar (tri-lb-title-parsing file-name-body)
(legacy-title-parsing file-name-body)))
(defn safe-read-string (defn safe-read-string
([content] ([content]
(safe-read-string {} content)) (safe-read-string {} content))

View File

@ -27,7 +27,9 @@
"C:\\Users\\foo\\Documents\\audio.mp3" "mp3" "C:\\Users\\foo\\Documents\\audio.mp3" "mp3"
"/root/Documents/audio" nil "/root/Documents/audio" nil
"/root/Documents/audio." nil "/root/Documents/audio." nil
"special/characters/aäääöüß.7z" "7z")) "special/characters/aäääöüß.7z" "7z"
"asldk lakls .lsad" "lsad"
"中文asldk lakls .lsad" "lsad"))
(deftest url? (deftest url?
(are [x y] (are [x y]

View File

@ -47,16 +47,6 @@
(assoc :block/type #{"hidden"} (assoc :block/type #{"hidden"}
:block/format :markdown)))) :block/format :markdown))))
(defn new-property-tx
"Provide attributes for a new built-in property given name, schema and uuid.
TODO: Merge this with sqlite-util/build-new-property once common-util/page-name-sanity-lc
is available to deps/db"
[prop-name prop-schema prop-uuid]
{:block/uuid prop-uuid
:block/schema (merge {:type :default} prop-schema)
:block/original-name (name prop-name)
:block/name (common-util/page-name-sanity-lc (name prop-name))})
(defn build-closed-values (defn build-closed-values
"Builds all the tx needed for property with closed values including "Builds all the tx needed for property with closed values including
the hidden page and closed value blocks as needed" the hidden page and closed value blocks as needed"
@ -76,8 +66,7 @@
(:closed-values property))) (:closed-values property)))
property-schema (assoc (:block/schema property) property-schema (assoc (:block/schema property)
:values (mapv :block/uuid closed-value-blocks-tx)) :values (mapv :block/uuid closed-value-blocks-tx))
property-tx (merge (sqlite-util/build-new-property property-tx (merge (sqlite-util/build-new-property prop-name property-schema (:block/uuid property))
(new-property-tx prop-name property-schema (:block/uuid property)))
property-attributes)] property-attributes)]
(into [property-tx page-tx] (into [property-tx page-tx]
(when-not closed-value-page-uuids? closed-value-blocks-tx)))) (when-not closed-value-page-uuids? closed-value-blocks-tx))))

View File

@ -22,10 +22,9 @@
{:block/schema schema :block/uuid (d/squuid) :closed-values closed-values} {:block/schema schema :block/uuid (d/squuid) :closed-values closed-values}
{:icon-id (get default-property-uuids :icon)}) {:icon-id (get default-property-uuids :icon)})
[(sqlite-util/build-new-property [(sqlite-util/build-new-property
{:block/schema schema (or original-name k-name)
:block/original-name (or original-name k-name) schema
:block/name (common-util/page-name-sanity-lc k-name) (get default-property-uuids k-keyword (d/squuid)))])))
:block/uuid (get default-property-uuids k-keyword (d/squuid))})])))
db-property/built-in-properties))) db-property/built-in-properties)))
(defn build-db-initial-data (defn build-db-initial-data

View File

@ -35,12 +35,15 @@
(defn build-new-property (defn build-new-property
"Build a standard new property so that it is is consistent across contexts" "Build a standard new property so that it is is consistent across contexts"
[block] [prop-name prop-schema prop-uuid]
(block-with-timestamps (block-with-timestamps
(merge {:block/type "property" {:block/type "property"
:block/journal? false :block/journal? false
:block/format :markdown} :block/format :markdown
block))) :block/uuid prop-uuid
:block/schema (merge {:type :default} prop-schema)
:block/original-name (name prop-name)
:block/name (common-util/page-name-sanity-lc (name prop-name))}))
(defn build-new-class (defn build-new-class

View File

@ -27,6 +27,66 @@
(common-util/safe-decode-uri-component (string/replace result "." "/")) (common-util/safe-decode-uri-component (string/replace result "." "/"))
result)))) result))))
(defn- path->file-name
;; Only for internal paths, as they are converted to POXIS already
;; https://github.com/logseq/logseq/blob/48b8e54e0fdd8fbd2c5d25b7f1912efef8814714/deps/graph-parser/src/logseq/graph_parser/extract.cljc#L32
;; Should be converted to POXIS first for external paths
[path]
(if (string/includes? path "/")
(last (common-util/split-last "/" path))
path))
(defn- path->file-body
[path]
(when-let [file-name (path->file-name path)]
(if (string/includes? file-name ".")
(first (common-util/split-last "." file-name))
file-name)))
(defn- safe-url-decode
[string]
(if (string/includes? string "%")
(some-> string str common-util/safe-decode-uri-component)
string))
(defn- decode-namespace-underlines
"Decode namespace underlines to slashed;
If continuous underlines, only decode at start;
Having empty namespace is invalid."
[string]
(string/replace string "___" "/"))
(defn- make-valid-namespaces
"Remove those empty namespaces from title to make it a valid page name."
[title]
(->> (string/split title "/")
(remove empty?)
(string/join "/")))
(defn- tri-lb-title-parsing
"Parsing file name under the new file name format
Avoid calling directly"
[file-name]
(some-> file-name
(decode-namespace-underlines)
(string/replace common-util/url-encoded-pattern safe-url-decode)
(make-valid-namespaces)))
;; Keep for backward compatibility
;; Rule of dir-ver 0
;; Source: https://github.com/logseq/logseq/blob/e7110eea6790eda5861fdedb6b02c2a78b504cd9/deps/graph-parser/src/logseq/graph_parser/extract.cljc#L35
(defn- legacy-title-parsing
[file-name-body]
(let [title (string/replace file-name-body "." "/")]
(or (common-util/safe-decode-uri-component title) title)))
(defn title-parsing
"Convert file name in the given file name format to page title"
[file-name-body filename-format]
(case filename-format
:triple-lowbar (tri-lb-title-parsing file-name-body)
(legacy-title-parsing file-name-body)))
(defn- get-page-name (defn- get-page-name
"Get page name with overridden order of "Get page name with overridden order of
`title::` property `title::` property
@ -54,9 +114,9 @@
(and first-block (and first-block
(string? title) (string? title)
title)) title))
file-name (when-let [result (common-util/path->file-body file)] file-name (when-let [result (path->file-body file)]
(if (common-config/mldoc-support? (common-util/get-file-ext file)) (if (common-config/mldoc-support? (common-util/get-file-ext file))
(common-util/title-parsing result filename-format) (title-parsing result filename-format)
result))] result))]
(or property-name (or property-name
file-name file-name

View File

@ -3,6 +3,44 @@
[logseq.graph-parser.extract :as extract] [logseq.graph-parser.extract :as extract]
[clojure.pprint :as pprint])) [clojure.pprint :as pprint]))
;; This is a copy of frontend.util.fs/multiplatform-reserved-chars for reserved chars testing
(def multiplatform-reserved-chars ":\\*\\?\"<>|\\#\\\\")
;; Stuffs should be parsable (don't crash) when users dump some random files
(deftest page-name-parsing-tests
(is (string? (#'extract/tri-lb-title-parsing "___-_-_-_---___----")))
(is (string? (#'extract/tri-lb-title-parsing "_____///____---___----")))
(is (string? (#'extract/tri-lb-title-parsing "/_/////---/_----")))
(is (string? (#'extract/tri-lb-title-parsing "/\\#*%lasdf\\//__--dsll_____----....-._0x2B")))
(is (string? (#'extract/tri-lb-title-parsing "/\\#*%l;;&&;&\\//__--dsll_____----....-._0x2B")))
(is (string? (#'extract/tri-lb-title-parsing multiplatform-reserved-chars)))
(is (string? (#'extract/tri-lb-title-parsing "dsa&amp&semi;l dsalfjk jkl"))))
(deftest uri-decoding-tests
(is (= (#'extract/safe-url-decode "%*-sd%%%saf%=lks") "%*-sd%%%saf%=lks")) ;; Contains %, but invalid
(is (= (#'extract/safe-url-decode "%2FDownloads%2FCNN%3AIs%5CAll%3AYou%20Need.pdf") "/Downloads/CNN:Is\\All:You Need.pdf"))
(is (= (#'extract/safe-url-decode "asldkflksdaf啦放假啦睡觉啦啊啥的都撒娇浪费dla") "asldkflksdaf啦放假啦睡觉啦啊啥的都撒娇浪费dla")))
(deftest page-name-sanitization-backward-tests
(is (= "abc.def.ghi.jkl" (#'extract/tri-lb-title-parsing "abc.def.ghi.jkl")))
(is (= "abc/def/ghi/jkl" (#'extract/tri-lb-title-parsing "abc%2Fdef%2Fghi%2Fjkl")))
(is (= "abc%/def/ghi/jkl" (#'extract/tri-lb-title-parsing "abc%25%2Fdef%2Fghi%2Fjkl")))
(is (= "abc%2——ef/ghi/jkl" (#'extract/tri-lb-title-parsing "abc%2——ef%2Fghi%2Fjkl")))
(is (= "abc&amp;2Fghi/jkl" (#'extract/tri-lb-title-parsing "abc&amp;2Fghi%2Fjkl")))
(is (= "abc&lt;2Fghi/jkl" (#'extract/tri-lb-title-parsing "abc&lt;2Fghi%2Fjkl")))
(is (= "abc&percnt;2Fghi/jkl" (#'extract/tri-lb-title-parsing "abc&percnt;2Fghi%2Fjkl")))
(is (= "abc&semi;&;2Fghi/jkl" (#'extract/tri-lb-title-parsing "abc&semi;&;2Fghi%2Fjkl")))
;; happens when importing some compatible files on *nix / macOS
(is (= multiplatform-reserved-chars (#'extract/tri-lb-title-parsing multiplatform-reserved-chars))))
(deftest path-utils-tests
(is (= "asldk lakls " (#'extract/path->file-body "/data/app/asldk lakls .lsad")))
(is (= "asldk lakls " (#'extract/path->file-body "asldk lakls .lsad")))
(is (= "asldk lakls" (#'extract/path->file-body "asldk lakls")))
(is (= "asldk lakls" (#'extract/path->file-body "/data/app/asldk lakls")))
(is (= "asldk lakls" (#'extract/path->file-body "file://data/app/asldk lakls.as")))
(is (= "中文asldk lakls" (#'extract/path->file-body "file://中文data/app/中文asldk lakls.as"))))
(defn- extract (defn- extract
[text] [text]
(let [{:keys [blocks]} (extract/extract "a.md" text {:block-pattern "-"}) (let [{:keys [blocks]} (extract/extract "a.md" text {:block-pattern "-"})

View File

@ -1,43 +0,0 @@
(ns logseq.graph-parser.util.file-name-test
(:require [logseq.common.util :as common-util]
[cljs.test :refer [is deftest]]))
;; This is a copy of frontend.util.fs/multiplatform-reserved-chars for reserved chars testing
(def multiplatform-reserved-chars ":\\*\\?\"<>|\\#\\\\")
;; Stuffs should be parsable (don't crash) when users dump some random files
(deftest page-name-parsing-tests
(is (string? (#'common-util/tri-lb-title-parsing "___-_-_-_---___----")))
(is (string? (#'common-util/tri-lb-title-parsing "_____///____---___----")))
(is (string? (#'common-util/tri-lb-title-parsing "/_/////---/_----")))
(is (string? (#'common-util/tri-lb-title-parsing "/\\#*%lasdf\\//__--dsll_____----....-._0x2B")))
(is (string? (#'common-util/tri-lb-title-parsing "/\\#*%l;;&&;&\\//__--dsll_____----....-._0x2B")))
(is (string? (#'common-util/tri-lb-title-parsing multiplatform-reserved-chars)))
(is (string? (#'common-util/tri-lb-title-parsing "dsa&amp&semi;l dsalfjk jkl"))))
(deftest uri-decoding-tests
(is (= (common-util/safe-url-decode "%*-sd%%%saf%=lks") "%*-sd%%%saf%=lks")) ;; Contains %, but invalid
(is (= (common-util/safe-url-decode "%2FDownloads%2FCNN%3AIs%5CAll%3AYou%20Need.pdf") "/Downloads/CNN:Is\\All:You Need.pdf"))
(is (= (common-util/safe-url-decode "asldkflksdaf啦放假啦睡觉啦啊啥的都撒娇浪费dla") "asldkflksdaf啦放假啦睡觉啦啊啥的都撒娇浪费dla")))
(deftest page-name-sanitization-backward-tests
(is (= "abc.def.ghi.jkl" (#'common-util/tri-lb-title-parsing "abc.def.ghi.jkl")))
(is (= "abc/def/ghi/jkl" (#'common-util/tri-lb-title-parsing "abc%2Fdef%2Fghi%2Fjkl")))
(is (= "abc%/def/ghi/jkl" (#'common-util/tri-lb-title-parsing "abc%25%2Fdef%2Fghi%2Fjkl")))
(is (= "abc%2——ef/ghi/jkl" (#'common-util/tri-lb-title-parsing "abc%2——ef%2Fghi%2Fjkl")))
(is (= "abc&amp;2Fghi/jkl" (#'common-util/tri-lb-title-parsing "abc&amp;2Fghi%2Fjkl")))
(is (= "abc&lt;2Fghi/jkl" (#'common-util/tri-lb-title-parsing "abc&lt;2Fghi%2Fjkl")))
(is (= "abc&percnt;2Fghi/jkl" (#'common-util/tri-lb-title-parsing "abc&percnt;2Fghi%2Fjkl")))
(is (= "abc&semi;&;2Fghi/jkl" (#'common-util/tri-lb-title-parsing "abc&semi;&;2Fghi%2Fjkl")))
;; happens when importing some compatible files on *nix / macOS
(is (= multiplatform-reserved-chars (#'common-util/tri-lb-title-parsing multiplatform-reserved-chars))))
(deftest path-utils-tests
(is (= "asldk lakls " (common-util/path->file-body "/data/app/asldk lakls .lsad")))
(is (= "asldk lakls " (common-util/path->file-body "asldk lakls .lsad")))
(is (= "asldk lakls" (common-util/path->file-body "asldk lakls")))
(is (= "asldk lakls" (common-util/path->file-body "/data/app/asldk lakls")))
(is (= "asldk lakls" (common-util/path->file-body "file://data/app/asldk lakls.as")))
(is (= "中文asldk lakls" (common-util/path->file-body "file://中文data/app/中文asldk lakls.as")))
(is (= "lsad" (common-util/path->file-ext "asldk lakls .lsad")))
(is (= "lsad" (common-util/path->file-ext "中文asldk lakls .lsad"))))

View File

@ -100,7 +100,7 @@
"@excalidraw/excalidraw": "0.16.1", "@excalidraw/excalidraw": "0.16.1",
"@highlightjs/cdn-assets": "10.4.1", "@highlightjs/cdn-assets": "10.4.1",
"@isomorphic-git/lightning-fs": "^4.6.0", "@isomorphic-git/lightning-fs": "^4.6.0",
"@logseq/capacitor-file-sync": "5.0.1", "@logseq/capacitor-file-sync": "5.0.2",
"@logseq/diff-merge": "0.2.2", "@logseq/diff-merge": "0.2.2",
"@logseq/react-tweet-embed": "1.3.1-1", "@logseq/react-tweet-embed": "1.3.1-1",
"@logseq/sqlite-wasm": "=0.1.0", "@logseq/sqlite-wasm": "=0.1.0",

View File

@ -176,13 +176,13 @@
:property-attributes :property-attributes
{:db/id (or (property-db-ids (name prop-name)) {:db/id (or (property-db-ids (name prop-name))
(throw (ex-info "No :db/id for property" {:property prop-name})))}}) (throw (ex-info "No :db/id for property" {:property prop-name})))}})
[(sqlite-util/build-new-property [(merge
(merge (db-property-util/new-property-tx prop-name (get-in properties [prop-name :block/schema]) uuid) (sqlite-util/build-new-property prop-name (get-in properties [prop-name :block/schema]) uuid)
{:db/id (or (property-db-ids (name prop-name)) {:db/id (or (property-db-ids (name prop-name))
(throw (ex-info "No :db/id for property" {:property prop-name})))} (throw (ex-info "No :db/id for property" {:property prop-name})))}
(when-let [props (not-empty (get-in properties [prop-name :properties]))] (when-let [props (not-empty (get-in properties [prop-name :properties]))]
{:block/properties (->block-properties-tx props uuid-maps) {:block/properties (->block-properties-tx props uuid-maps)
:block/refs (build-property-refs props property-db-ids)})))])) :block/refs (build-property-refs props property-db-ids)}))]))
property-uuids)) property-uuids))
pages-and-blocks-tx pages-and-blocks-tx
(vec (vec

View File

@ -132,7 +132,8 @@
(rum/defc favorites < rum/reactive (rum/defc favorites < rum/reactive
[t] [t]
(let [favorite-entities (page-handler/get-favorites)] (let [_favorites-updated? (state/sub :favorites/updated?)
favorite-entities (page-handler/get-favorites)]
(nav-content-item (nav-content-item
[:a.flex.items-center.text-sm.font-medium.rounded-md.wrap-th [:a.flex.items-center.text-sm.font-medium.rounded-md.wrap-th
(ui/icon "star" {:size 16}) (ui/icon "star" {:size 16})

View File

@ -4,6 +4,7 @@
[clojure.core.async :as async] [clojure.core.async :as async]
[clojure.edn :as edn] [clojure.edn :as edn]
[clojure.string :as string] [clojure.string :as string]
[datascript.core :as d]
[frontend.components.onboarding.setups :as setups] [frontend.components.onboarding.setups :as setups]
[frontend.components.repo :as repo] [frontend.components.repo :as repo]
[frontend.components.svg :as svg] [frontend.components.svg :as svg]
@ -11,12 +12,14 @@
[frontend.context.i18n :refer [t]] [frontend.context.i18n :refer [t]]
[frontend.db :as db] [frontend.db :as db]
[frontend.fs :as fs] [frontend.fs :as fs]
[frontend.persist-db.browser :as db-browser]
[frontend.handler.db-based.editor :as db-editor-handler] [frontend.handler.db-based.editor :as db-editor-handler]
[frontend.handler.import :as import-handler] [frontend.handler.import :as import-handler]
[frontend.handler.notification :as notification] [frontend.handler.notification :as notification]
[frontend.handler.repo :as repo-handler]
[frontend.handler.route :as route-handler] [frontend.handler.route :as route-handler]
[frontend.handler.ui :as ui-handler] [frontend.handler.ui :as ui-handler]
[frontend.modules.outliner.ui :as ui-outliner-tx]
[frontend.persist-db.browser :as db-browser]
[frontend.state :as state] [frontend.state :as state]
[frontend.ui :as ui] [frontend.ui :as ui]
[frontend.util :as util] [frontend.util :as util]
@ -24,13 +27,13 @@
[goog.functions :refer [debounce]] [goog.functions :refer [debounce]]
[goog.object :as gobj] [goog.object :as gobj]
[logseq.common.path :as path] [logseq.common.path :as path]
[logseq.common.util :as common-util]
[logseq.db :as ldb]
[logseq.graph-parser :as graph-parser] [logseq.graph-parser :as graph-parser]
[logseq.outliner.core :as outliner-core]
[medley.core :as medley] [medley.core :as medley]
[promesa.core :as p] [promesa.core :as p]
[borkdude.rewrite-edn :as rewrite] [rum.core :as rum]))
[rum.core :as rum]
[frontend.handler.repo :as repo-handler]
[frontend.handler.common.config-edn :as config-edn-common-handler]))
;; Can't name this component as `frontend.components.import` since shadow-cljs ;; Can't name this component as `frontend.components.import` since shadow-cljs
;; will complain about it. ;; will complain about it.
@ -214,14 +217,52 @@
(-> (when config-file (-> (when config-file
(.text config-file)) (.text config-file))
(p/then (fn [content] (p/then (fn [content]
(let [migrated-content (-> (reduce rewrite/dissoc (let [migrated-content (repo-handler/migrate-db-config content)]
(rewrite/parse-string (str content))
(keys config-edn-common-handler/file-only-config))
str)]
(p/do! (p/do!
(db-editor-handler/save-file! "logseq/config.edn" migrated-content)) (db-editor-handler/save-file! "logseq/config.edn" migrated-content))
(edn/read-string migrated-content)))))) (edn/read-string migrated-content))))))
(defn- build-hidden-favorites-page-blocks
[page-block-uuid-coll]
(map
(fn [uuid]
{:block/link [:block/uuid uuid]
:block/content ""
:block/format :markdown})
page-block-uuid-coll))
(def hidden-favorites-page-name "$$$favorites")
(def hidden-favorites-page-tx
{:block/uuid (d/squuid)
:block/name hidden-favorites-page-name
:block/original-name hidden-favorites-page-name
:block/journal? false
:block/type #{"hidden"}
:block/format :markdown})
(defn- import-favorites-from-config-edn!
[db-conn repo config-file]
(let [now (inst-ms (js/Date.))]
(p/do!
(ldb/transact! repo [(assoc hidden-favorites-page-tx
:block/created-at now
:block/updated-at now)])
(p/let [content (when config-file (.text config-file))]
(when-let [content-edn (try (edn/read-string content)
(catch :default _ nil))]
(when-let [favorites (seq (:favorites content-edn))]
(when-let [page-block-uuid-coll
(seq
(keep (fn [page-name]
(some-> (d/entity @db-conn [:block/name (common-util/page-name-sanity-lc page-name)])
:block/uuid))
favorites))]
(let [page-entity (d/entity @db-conn [:block/name hidden-favorites-page-name])]
(ui-outliner-tx/transact!
{:outliner-op :insert-blocks}
(outliner-core/insert-blocks! repo db-conn (build-hidden-favorites-page-blocks page-block-uuid-coll)
page-entity {}))))))))))
(rum/defc confirm-graph-name-dialog (rum/defc confirm-graph-name-dialog
[initial-name on-graph-name-confirmed] [initial-name on-graph-name-confirmed]
@ -277,6 +318,7 @@
(async/<! (p->c (import-config-file! config-file))) (async/<! (p->c (import-config-file! config-file)))
(async/<! (import-from-asset-files! asset-files)) (async/<! (import-from-asset-files! asset-files))
(async/<! (import-from-doc-files! db-conn repo doc-files)) (async/<! (import-from-doc-files! db-conn repo doc-files))
(async/<! (p->c (import-favorites-from-config-edn! db-conn repo config-file)))
(state/set-state! :graph/importing nil) (state/set-state! :graph/importing nil)
(finished-cb)))))] (finished-cb)))))]
(state/set-modal! (state/set-modal!

View File

@ -209,3 +209,20 @@ a.control-link {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
} }
.property-select {
.menu-link:hover, a.menu-link.chosen {
.iconTablerExtInProgress50,
.iconTablerExtBacklog,
.iconTablerExtTodo,
.iconTablerExtDoing,
.iconTablerExtInReview,
.iconTablerExtDone {
@apply text-accent-09;
}
.iconTablerExtCancelled {
@apply text-red-rx-09;
}
}
}

View File

@ -357,7 +357,7 @@
nil) nil)
(catch :default e (catch :default e
(prn :debug :error) (prn :debug :error)
(js/console.error e))))) (js/console.error e tx-data)))))
(getInitialData (getInitialData
[_this repo] [_this repo]

View File

@ -107,7 +107,9 @@ nested keys or positional errors e.g. tuples"
:file/name-format :file/name-format
"is not used in DB graphs" "is not used in DB graphs"
:feature/enable-block-timestamps? :feature/enable-block-timestamps?
"is not used in DB graphs as it is always enabled"}) "is not used in DB graphs as it is always enabled"
:favorites
"is not stored in config for DB graphs"})
(defn detect-deprecations (defn detect-deprecations
"Detects config keys that will or have been deprecated" "Detects config keys that will or have been deprecated"

View File

@ -26,7 +26,7 @@
(defn build-hidden-page-tx-data (defn build-hidden-page-tx-data
[page-name] [page-name]
(let [page-name* (str "$$$" page-name)] (let [page-name* (str "$$$" page-name)]
(assoc (block/page-name->map page-name* false false) (assoc (block/page-name->map page-name* true false)
:block/type #{"hidden"} :block/type #{"hidden"}
:block/format :markdown))) :block/format :markdown)))
@ -50,8 +50,7 @@
(let [repo (state/get-current-repo) (let [repo (state/get-current-repo)
conn (db/get-db repo false) conn (db/get-db repo false)
config (state/get-config repo) config (state/get-config repo)
_ (worker-page/create! repo conn config title options) [_ page-name] (worker-page/create! repo conn config title options)]
[_ page-name] (worker-page/get-title-and-pagename title)]
(when redirect? (when redirect?
(route-handler/redirect-to-page! page-name)) (route-handler/redirect-to-page! page-name))
(when-let [first-block (first (:block/_left (db/entity [:block/name page-name])))] (when-let [first-block (first (:block/_left (db/entity [:block/name page-name])))]
@ -67,8 +66,8 @@
(p/let [repo (state/get-current-repo) (p/let [repo (state/get-current-repo)
conn (db/get-db repo false) conn (db/get-db repo false)
config (state/get-config repo) config (state/get-config repo)
_ (worker-page/create! repo conn config title options) [p page-name] (worker-page/create! repo conn config title options)
[_ page-name] (worker-page/get-title-and-pagename title)] _result p]
(when redirect? (when redirect?
(route-handler/redirect-to-page! page-name)) (route-handler/redirect-to-page! page-name))
(let [page (db/entity [:block/name page-name])] (let [page (db/entity [:block/name page-name])]

View File

@ -105,12 +105,7 @@
:block/uuid property-uuid :block/uuid property-uuid
:block/type "property"})] :block/type "property"})]
{:outliner-op :save-block}) {:outliner-op :save-block})
(db/transact! repo [(sqlite-util/build-new-property (db/transact! repo [(sqlite-util/build-new-property k-name schema property-uuid)]
(cond-> {:block/original-name k-name
:block/name (util/page-name-sanity-lc k-name)
:block/uuid property-uuid}
(seq schema)
(assoc :block/schema schema)))]
{:outliner-op :insert-blocks})))) {:outliner-op :insert-blocks}))))
(defn validate-property-value (defn validate-property-value

View File

@ -779,12 +779,7 @@
"Check " [:a {:href "https://docs.logseq.com/#/page/logseq%20file%20and%20folder%20naming%20rules" "Check " [:a {:href "https://docs.logseq.com/#/page/logseq%20file%20and%20folder%20naming%20rules"
:target "_blank"} :target "_blank"}
"Logseq file and folder naming rules"] "Logseq file and folder naming rules"]
" for more details."] " for more details."]]]]
[:p
(util/format "To solve this problem, we suggest you quit Logseq and update the filename format (on Settings > Advanced > Filename format > click EDIT button)%s to avoid more potential bugs."
(if (and util/mac? (not (mobile-util/native-ios?)))
""
" in other devices"))]]]]
:warning :warning
false)) false))

View File

@ -49,21 +49,25 @@
(defn <unfavorite-page! (defn <unfavorite-page!
[page-name] [page-name]
(p/do!
(let [repo (state/get-current-repo)] (let [repo (state/get-current-repo)]
(if (config/db-based-graph? repo) (if (config/db-based-graph? repo)
(let [db (conn/get-db)] (let [db (conn/get-db)]
(when-let [page-block-uuid (:block/uuid (d/entity db [:block/name (common-util/page-name-sanity-lc page-name)]))] (when-let [page-block-uuid (:block/uuid (d/entity db [:block/name (common-util/page-name-sanity-lc page-name)]))]
(page-common-handler/<unfavorite-page!-v2 page-block-uuid))) (page-common-handler/<unfavorite-page!-v2 page-block-uuid)))
(page-common-handler/unfavorite-page! page-name)))) (page-common-handler/unfavorite-page! page-name)))
(state/update-favorites-updated!)))
(defn <favorite-page! (defn <favorite-page!
[page-name] [page-name]
(p/do!
(let [repo (state/get-current-repo)] (let [repo (state/get-current-repo)]
(if (config/db-based-graph? repo) (if (config/db-based-graph? repo)
(let [db (conn/get-db)] (let [db (conn/get-db)]
(when-let [page-block-uuid (:block/uuid (d/entity db [:block/name (common-util/page-name-sanity-lc page-name)]))] (when-let [page-block-uuid (:block/uuid (d/entity db [:block/name (common-util/page-name-sanity-lc page-name)]))]
(page-common-handler/<favorite-page!-v2 page-block-uuid))) (page-common-handler/<favorite-page!-v2 page-block-uuid)))
(page-common-handler/favorite-page! page-name)))) (page-common-handler/favorite-page! page-name)))
(state/update-favorites-updated!)))
(defn favorited? (defn favorited?
[page-name] [page-name]
@ -145,13 +149,14 @@
favorites) favorites)
current-blocks (ldb/sort-by-left (ldb/get-page-blocks @conn page-common-handler/favorites-page-name {}) current-blocks (ldb/sort-by-left (ldb/get-page-blocks @conn page-common-handler/favorites-page-name {})
favorites-page-entity)] favorites-page-entity)]
(prn :favorite-page-block-db-id-coll favorite-page-block-db-id-coll) (p/do!
(ui-outliner-tx/transact! (ui-outliner-tx/transact!
{} {}
(doseq [[page-block-db-id block] (zipmap favorite-page-block-db-id-coll current-blocks)] (doseq [[page-block-db-id block] (zipmap favorite-page-block-db-id-coll current-blocks)]
(when (not= page-block-db-id (:db/id (:block/link block))) (when (not= page-block-db-id (:db/id (:block/link block)))
(outliner-core/save-block! repo conn (state/get-date-formatter) (outliner-core/save-block! repo conn (state/get-date-formatter)
(assoc block :block/link page-block-db-id))))))))) (assoc block :block/link page-block-db-id)))))
(state/update-favorites-updated!))))))
(defn has-more-journals? (defn has-more-journals?
[] []

View File

@ -11,6 +11,7 @@
[frontend.fs.nfs :as nfs] [frontend.fs.nfs :as nfs]
[frontend.handler.file :as file-handler] [frontend.handler.file :as file-handler]
[frontend.handler.repo-config :as repo-config-handler] [frontend.handler.repo-config :as repo-config-handler]
[frontend.handler.common.config-edn :as config-edn-common-handler]
[frontend.handler.common.file :as file-common-handler] [frontend.handler.common.file :as file-common-handler]
[frontend.handler.route :as route-handler] [frontend.handler.route :as route-handler]
[frontend.handler.ui :as ui-handler] [frontend.handler.ui :as ui-handler]
@ -35,7 +36,8 @@
[clojure.core.async :as async] [clojure.core.async :as async]
[frontend.mobile.util :as mobile-util] [frontend.mobile.util :as mobile-util]
[medley.core :as medley] [medley.core :as medley]
[logseq.common.path :as path])) [logseq.common.path :as path]
[borkdude.rewrite-edn :as rewrite]))
;; Project settings should be checked in two situations: ;; Project settings should be checked in two situations:
;; 1. User changes the config.edn directly in logseq.com (fn: alter-file) ;; 1. User changes the config.edn directly in logseq.com (fn: alter-file)
@ -517,13 +519,20 @@
(let [full-graph-name (string/lower-case (str config/db-version-prefix graph-name))] (let [full-graph-name (string/lower-case (str config/db-version-prefix graph-name))]
(some #(= (some-> (:url %) string/lower-case) full-graph-name) (state/get-repos)))) (some #(= (some-> (:url %) string/lower-case) full-graph-name) (state/get-repos))))
(defn migrate-db-config
[content]
(-> (reduce rewrite/dissoc
(rewrite/parse-string (str content))
(keys config-edn-common-handler/file-only-config))
str))
(defn- create-db [full-graph-name {:keys [file-graph-import?]}] (defn- create-db [full-graph-name {:keys [file-graph-import?]}]
(-> (->
(p/let [_ (persist-db/<new full-graph-name) (p/let [_ (persist-db/<new full-graph-name)
_ (start-repo-db-if-not-exists! full-graph-name) _ (start-repo-db-if-not-exists! full-graph-name)
_ (state/add-repo! {:url full-graph-name}) _ (state/add-repo! {:url full-graph-name})
_ (when-not file-graph-import? (route-handler/redirect-to-home!)) _ (when-not file-graph-import? (route-handler/redirect-to-home!))
initial-data (sqlite-create-graph/build-db-initial-data config/config-default-content) initial-data (sqlite-create-graph/build-db-initial-data (migrate-db-config config/config-default-content))
_ (db/transact! full-graph-name initial-data) _ (db/transact! full-graph-name initial-data)
_ (repo-config-handler/set-repo-config-state! full-graph-name config/config-default-content) _ (repo-config-handler/set-repo-config-state! full-graph-name config/config-default-content)
;; TODO: handle global graph ;; TODO: handle global graph

View File

@ -13,11 +13,21 @@
[goog.dom :as gdom] [goog.dom :as gdom]
[rum.core :as rum])) [rum.core :as rum]))
(defn- blur-if-compositing
"Call blur on the textarea if it is in composition mode, let the IME commit the composing text"
[]
(when-let [edit-input-id (and (state/editor-in-composition?)
(state/get-edit-input-id))]
(let [textarea-el (gdom/getElement edit-input-id)]
(.blur textarea-el))))
(rum/defc indent-outdent [indent? icon] (rum/defc indent-outdent [indent? icon]
[:div [:div
[:button.bottom-action [:button.bottom-action
{:on-mouse-down (fn [e] {:on-mouse-down (fn [e]
(util/stop e) (util/stop e)
(blur-if-compositing)
(editor-handler/indent-outdent indent?))} (editor-handler/indent-outdent indent?))}
(ui/icon icon {:size ui/icon-size})]]) (ui/icon icon {:size ui/icon-size})]])
@ -91,7 +101,13 @@
(command #(if (state/sub :document/mode?) (command #(if (state/sub :document/mode?)
(editor-handler/insert-new-block! nil) (editor-handler/insert-new-block! nil)
(commands/simple-insert! parent-id "\n" {})) {:icon "arrow-back"}) (commands/simple-insert! parent-id "\n" {})) {:icon "arrow-back"})
(command editor-handler/cycle-todo! {:icon "checkbox"} true) ;; On mobile devies, some IME(keyboard) uses composing mode.
;; The composing text can be committed by losing focus.
;; 100ms is enough to commit the composing text to db.
(command #(do
(blur-if-compositing)
(editor-handler/cycle-todo!))
{:icon "checkbox"} true)
(command #(mobile-camera/embed-photo parent-id) {:icon "camera"} true) (command #(mobile-camera/embed-photo parent-id) {:icon "camera"} true)
(command history/undo! {:icon "rotate" :class "rotate-180"} true) (command history/undo! {:icon "rotate" :class "rotate-180"} true)
(command history/redo! {:icon "rotate-clockwise" :class "rotate-180"} true) (command history/redo! {:icon "rotate-clockwise" :class "rotate-180"} true)

View File

@ -311,6 +311,7 @@
:system/info {} :system/info {}
;; Whether block is selected ;; Whether block is selected
:ui/select-query-cache (atom {}) :ui/select-query-cache (atom {})
:favorites/updated? (atom 0)
:db/async-queries (atom #{})}))) :db/async-queries (atom #{})})))
;; Block ast state ;; Block ast state
@ -2366,3 +2367,7 @@ Similar to re-frame subscriptions"
(when (and max-tx-id (nil? (:after (get @(:history/tx->editor-cursor @state) max-tx-id)))) (when (and max-tx-id (nil? (:after (get @(:history/tx->editor-cursor @state) max-tx-id))))
(update-state! :history/tx->editor-cursor (update-state! :history/tx->editor-cursor
(fn [m] (assoc-in m [max-tx-id :after] editor-cursor)))))) (fn [m] (assoc-in m [max-tx-id :after] editor-cursor))))))
(defn update-favorites-updated!
[]
(update-state! :favorites/updated? inc))

View File

@ -81,8 +81,8 @@
(date/valid-journal-title? date-formatter title))) (date/valid-journal-title? date-formatter title)))
[title page-name] (get-title-and-pagename title) [title page-name] (get-title-and-pagename title)
with-uuid? (if (uuid? uuid) uuid true)] ;; FIXME: prettier validation with-uuid? (if (uuid? uuid) uuid true)
(when (ldb/page-empty? @conn page-name) result (when (ldb/page-empty? @conn page-name)
(let [pages (if split-namespace? (let [pages (if split-namespace?
(common-util/split-namespace-pages title) (common-util/split-namespace-pages title)
[title]) [title])
@ -130,7 +130,8 @@
(ldb/transact! conn txs (cond-> {:persist-op? persist-op?} (ldb/transact! conn txs (cond-> {:persist-op? persist-op?}
today-journal? today-journal?
(assoc :create-today-journal? true (assoc :create-today-journal? true
:today-journal-name page-name)))))))) :today-journal-name page-name))))))] ;; FIXME: prettier validation
[result page-name]))
(defn db-refs->page (defn db-refs->page
"Replace [[page name]] with page name" "Replace [[page name]] with page name"

View File

@ -302,7 +302,11 @@
(d/pull-many @conn [:db/id]) (d/pull-many @conn [:db/id])
(filter :db/id))) (filter :db/id)))
(contains? key-set :properties) (assoc :block/properties (contains? key-set :properties) (assoc :block/properties
(transit/read transit-r (:properties op-value))))] (transit/read transit-r (:properties op-value)))
(contains? key-set :link) (assoc :block/link (some->> (:link op-value)
(vector :block/uuid)
(d/pull @conn [:db/id])
:db/id)))]
(transact-db! :save-block repo conn date-formatter new-block))))))) (transact-db! :save-block repo conn date-formatter new-block)))))))
(defn apply-remote-move-ops (defn apply-remote-move-ops

View File

@ -1,7 +1,7 @@
(ns frontend.db.name-sanity-test (ns frontend.db.name-sanity-test
(:require [cljs.test :refer [deftest testing is]] (:require [cljs.test :refer [deftest testing is]]
[clojure.string :as string] [clojure.string :as string]
[logseq.common.util :as common-util] [logseq.graph-parser.extract :as extract]
[frontend.worker.handler.page.rename :as worker-page-rename] [frontend.worker.handler.page.rename :as worker-page-rename]
[frontend.util.fs :as fs-util] [frontend.util.fs :as fs-util]
[frontend.worker.file.util :as wfu])) [frontend.worker.file.util :as wfu]))
@ -11,7 +11,7 @@
[page-name] [page-name]
(testing (str "Test sanitization page-name: " page-name) (testing (str "Test sanitization page-name: " page-name)
(let [file-name (#'wfu/tri-lb-file-name-sanity page-name) (let [file-name (#'wfu/tri-lb-file-name-sanity page-name)
page-name' (#'common-util/tri-lb-title-parsing file-name) page-name' (#'extract/tri-lb-title-parsing file-name)
url-single (js/encodeURIComponent file-name) url-single (js/encodeURIComponent file-name)
url-double (js/encodeURIComponent url-single) url-double (js/encodeURIComponent url-single)
file-name' (js/decodeURIComponent url-single) file-name' (js/decodeURIComponent url-single)

View File

@ -537,10 +537,10 @@
"@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
"@logseq/capacitor-file-sync@5.0.1": "@logseq/capacitor-file-sync@5.0.2":
version "5.0.1" version "5.0.2"
resolved "https://registry.yarnpkg.com/@logseq/capacitor-file-sync/-/capacitor-file-sync-5.0.1.tgz#e154f715597785518ccd7d058f353acb67fbc2b8" resolved "https://registry.yarnpkg.com/@logseq/capacitor-file-sync/-/capacitor-file-sync-5.0.2.tgz#10c56e35b41b1a0afd293c9b045fbcfe150c3477"
integrity sha512-C1fLSS53orxsUWBsNb6LKwuOdlEU9ZhxkweMjNKG9VaSkLFTFqpjFG36OTso23WQ7hC5e45jjXq79aoWuqJaKA== integrity sha512-FymsTeRtF66zG+oeO+ohZxWICMQMC8An4n9pdI0zz1WaGLer4oWC/lUghlC2DpztRLA32p0CH28tEzF5+2jARg==
"@logseq/diff-merge@0.2.2": "@logseq/diff-merge@0.2.2":
version "0.2.2" version "0.2.2"