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

View File

@ -18,12 +18,6 @@
(log/error :decode-uri-component-failed uri)
uri)))
(defn safe-url-decode
[string]
(if (string/includes? string "%")
(some-> string str safe-decode-uri-component)
string))
(defn path-normalize
"Normalize file path (for reading paths from FS, not required by writing)
Keep capitalization senstivity"
@ -129,12 +123,7 @@
result))
(map string/trim))))
(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 "___" "/"))
(def url-encoded-pattern #"(?i)%[0-9a-f]{2}") ;; (?i) for case-insensitive mode
(defn page-name-sanity
"Sanitize the page-name. Unify different diacritics and other visual differences.
@ -146,24 +135,6 @@
(remove-boundary-slashes)
(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
"Sanitize the query string for a page name (mandate for :block/name)"
[s]
@ -202,22 +173,6 @@
;; default
(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
[path-or-file-name]
(second (re-find #"(?:\.)(\w+)[^.]*$" path-or-file-name)))
@ -245,27 +200,6 @@
(catch :default _
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
([content]
(safe-read-string {} content))

View File

@ -27,7 +27,9 @@
"C:\\Users\\foo\\Documents\\audio.mp3" "mp3"
"/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?
(are [x y]

View File

@ -47,16 +47,6 @@
(assoc :block/type #{"hidden"}
: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
"Builds all the tx needed for property with closed values including
the hidden page and closed value blocks as needed"
@ -76,8 +66,7 @@
(:closed-values property)))
property-schema (assoc (:block/schema property)
:values (mapv :block/uuid closed-value-blocks-tx))
property-tx (merge (sqlite-util/build-new-property
(new-property-tx prop-name property-schema (:block/uuid property)))
property-tx (merge (sqlite-util/build-new-property prop-name property-schema (:block/uuid property))
property-attributes)]
(into [property-tx page-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}
{:icon-id (get default-property-uuids :icon)})
[(sqlite-util/build-new-property
{:block/schema schema
:block/original-name (or original-name k-name)
:block/name (common-util/page-name-sanity-lc k-name)
:block/uuid (get default-property-uuids k-keyword (d/squuid))})])))
(or original-name k-name)
schema
(get default-property-uuids k-keyword (d/squuid)))])))
db-property/built-in-properties)))
(defn build-db-initial-data

View File

@ -35,12 +35,15 @@
(defn build-new-property
"Build a standard new property so that it is is consistent across contexts"
[block]
[prop-name prop-schema prop-uuid]
(block-with-timestamps
(merge {:block/type "property"
:block/journal? false
:block/format :markdown}
block)))
{:block/type "property"
:block/journal? false
:block/format :markdown
: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

View File

@ -27,6 +27,66 @@
(common-util/safe-decode-uri-component (string/replace 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
"Get page name with overridden order of
`title::` property
@ -54,9 +114,9 @@
(and first-block
(string? 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))
(common-util/title-parsing result filename-format)
(title-parsing result filename-format)
result))]
(or property-name
file-name

View File

@ -3,6 +3,44 @@
[logseq.graph-parser.extract :as extract]
[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
[text]
(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",
"@highlightjs/cdn-assets": "10.4.1",
"@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/react-tweet-embed": "1.3.1-1",
"@logseq/sqlite-wasm": "=0.1.0",

View File

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

View File

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

View File

@ -4,6 +4,7 @@
[clojure.core.async :as async]
[clojure.edn :as edn]
[clojure.string :as string]
[datascript.core :as d]
[frontend.components.onboarding.setups :as setups]
[frontend.components.repo :as repo]
[frontend.components.svg :as svg]
@ -11,12 +12,14 @@
[frontend.context.i18n :refer [t]]
[frontend.db :as db]
[frontend.fs :as fs]
[frontend.persist-db.browser :as db-browser]
[frontend.handler.db-based.editor :as db-editor-handler]
[frontend.handler.import :as import-handler]
[frontend.handler.notification :as notification]
[frontend.handler.repo :as repo-handler]
[frontend.handler.route :as route-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.ui :as ui]
[frontend.util :as util]
@ -24,13 +27,13 @@
[goog.functions :refer [debounce]]
[goog.object :as gobj]
[logseq.common.path :as path]
[logseq.common.util :as common-util]
[logseq.db :as ldb]
[logseq.graph-parser :as graph-parser]
[logseq.outliner.core :as outliner-core]
[medley.core :as medley]
[promesa.core :as p]
[borkdude.rewrite-edn :as rewrite]
[rum.core :as rum]
[frontend.handler.repo :as repo-handler]
[frontend.handler.common.config-edn :as config-edn-common-handler]))
[rum.core :as rum]))
;; Can't name this component as `frontend.components.import` since shadow-cljs
;; will complain about it.
@ -214,38 +217,76 @@
(-> (when config-file
(.text config-file))
(p/then (fn [content]
(let [migrated-content (-> (reduce rewrite/dissoc
(rewrite/parse-string (str content))
(keys config-edn-common-handler/file-only-config))
str)]
(let [migrated-content (repo-handler/migrate-db-config content)]
(p/do!
(db-editor-handler/save-file! "logseq/config.edn" 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
[initial-name on-graph-name-confirmed]
(let [[input set-input!] (rum/use-state initial-name)
on-submit #(do (on-graph-name-confirmed input)
(state/close-modal!))]
[:div.container
[:div.sm:flex.sm:items-start
[:div.mt-3.text-center.sm:mt-0.sm:text-left
[:h3#modal-headline.leading-6.font-medium
"Imported new graph name:"]]]
[initial-name on-graph-name-confirmed]
(let [[input set-input!] (rum/use-state initial-name)
on-submit #(do (on-graph-name-confirmed input)
(state/close-modal!))]
[:div.container
[:div.sm:flex.sm:items-start
[:div.mt-3.text-center.sm:mt-0.sm:text-left
[:h3#modal-headline.leading-6.font-medium
"Imported new graph name:"]]]
[:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2.mb-4
{:auto-focus true
:default-value input
:on-change (fn [e]
(set-input! (util/evalue e)))
:on-key-press (fn [e]
(when (= "Enter" (util/ekey e))
(on-submit)))}]
[:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2.mb-4
{:auto-focus true
:default-value input
:on-change (fn [e]
(set-input! (util/evalue e)))
:on-key-press (fn [e]
(when (= "Enter" (util/ekey e))
(on-submit)))}]
[:div.mt-5.sm:mt-4.flex
(ui/button "Confirm"
{:on-click on-submit})]]))
[:div.mt-5.sm:mt-4.flex
(ui/button "Confirm"
{:on-click on-submit})]]))
(defn graph-folder-to-db-import-handler
"Import from a graph folder as a DB-based graph.
@ -277,6 +318,7 @@
(async/<! (p->c (import-config-file! config-file)))
(async/<! (import-from-asset-files! asset-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)
(finished-cb)))))]
(state/set-modal!

View File

@ -209,3 +209,20 @@ a.control-link {
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)
(catch :default e
(prn :debug :error)
(js/console.error e)))))
(js/console.error e tx-data)))))
(getInitialData
[_this repo]

View File

@ -107,7 +107,9 @@ nested keys or positional errors e.g. tuples"
:file/name-format
"is not used in DB graphs"
: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
"Detects config keys that will or have been deprecated"

View File

@ -26,7 +26,7 @@
(defn build-hidden-page-tx-data
[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/format :markdown)))
@ -50,8 +50,7 @@
(let [repo (state/get-current-repo)
conn (db/get-db repo false)
config (state/get-config repo)
_ (worker-page/create! repo conn config title options)
[_ page-name] (worker-page/get-title-and-pagename title)]
[_ page-name] (worker-page/create! repo conn config title options)]
(when redirect?
(route-handler/redirect-to-page! 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)
conn (db/get-db repo false)
config (state/get-config repo)
_ (worker-page/create! repo conn config title options)
[_ page-name] (worker-page/get-title-and-pagename title)]
[p page-name] (worker-page/create! repo conn config title options)
_result p]
(when redirect?
(route-handler/redirect-to-page! page-name))
(let [page (db/entity [:block/name page-name])]

View File

@ -105,12 +105,7 @@
:block/uuid property-uuid
:block/type "property"})]
{:outliner-op :save-block})
(db/transact! repo [(sqlite-util/build-new-property
(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)))]
(db/transact! repo [(sqlite-util/build-new-property k-name schema property-uuid)]
{:outliner-op :insert-blocks}))))
(defn validate-property-value

View File

@ -779,12 +779,7 @@
"Check " [:a {:href "https://docs.logseq.com/#/page/logseq%20file%20and%20folder%20naming%20rules"
:target "_blank"}
"Logseq file and folder naming rules"]
" 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"))]]]]
" for more details."]]]]
:warning
false))

View File

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

View File

@ -11,6 +11,7 @@
[frontend.fs.nfs :as nfs]
[frontend.handler.file :as file-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.route :as route-handler]
[frontend.handler.ui :as ui-handler]
@ -35,7 +36,8 @@
[clojure.core.async :as async]
[frontend.mobile.util :as mobile-util]
[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:
;; 1. User changes the config.edn directly in logseq.com (fn: alter-file)
@ -104,7 +106,7 @@
:else
default-content)
file-rpath (path/path-join (config/get-journals-directory) (str file-name "."
(config/get-file-extension format)))
(config/get-file-extension format)))
page-exists? (db/entity repo-url [:block/name (util/page-name-sanity-lc title)])
empty-blocks? (db/page-empty? repo-url (util/page-name-sanity-lc title))]
(when (or empty-blocks? (not page-exists?))
@ -517,13 +519,20 @@
(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))))
(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?]}]
(->
(p/let [_ (persist-db/<new full-graph-name)
_ (start-repo-db-if-not-exists! full-graph-name)
_ (state/add-repo! {:url full-graph-name})
_ (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)
_ (repo-config-handler/set-repo-config-state! full-graph-name config/config-default-content)
;; TODO: handle global graph

View File

@ -13,11 +13,21 @@
[goog.dom :as gdom]
[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]
[:div
[:button.bottom-action
{:on-mouse-down (fn [e]
(util/stop e)
(blur-if-compositing)
(editor-handler/indent-outdent indent?))}
(ui/icon icon {:size ui/icon-size})]])
@ -91,7 +101,13 @@
(command #(if (state/sub :document/mode?)
(editor-handler/insert-new-block! nil)
(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 history/undo! {:icon "rotate" :class "rotate-180"} true)
(command history/redo! {:icon "rotate-clockwise" :class "rotate-180"} true)

View File

@ -311,7 +311,8 @@
:system/info {}
;; Whether block is selected
:ui/select-query-cache (atom {})
:db/async-queries (atom #{})})))
:favorites/updated? (atom 0)
:db/async-queries (atom #{})})))
;; 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))))
(update-state! :history/tx->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,56 +81,57 @@
(date/valid-journal-title? date-formatter title)))
[title page-name] (get-title-and-pagename title)
with-uuid? (if (uuid? uuid) uuid true)] ;; FIXME: prettier validation
(when (ldb/page-empty? @conn page-name)
(let [pages (if split-namespace?
(common-util/split-namespace-pages title)
[title])
format (or format (common-config/get-preferred-format config))
pages (map (fn [page]
with-uuid? (if (uuid? uuid) uuid true)
result (when (ldb/page-empty? @conn page-name)
(let [pages (if split-namespace?
(common-util/split-namespace-pages title)
[title])
format (or format (common-config/get-preferred-format config))
pages (map (fn [page]
;; only apply uuid to the deepest hierarchy of page to create if provided.
(-> (gp-block/page-name->map page (if (= page title) with-uuid? true) @conn true date-formatter)
(assoc :block/format format)))
pages)
txs (->> pages
(-> (gp-block/page-name->map page (if (= page title) with-uuid? true) @conn true date-formatter)
(assoc :block/format format)))
pages)
txs (->> pages
;; for namespace pages, only last page need properties
drop-last
(mapcat #(build-page-tx repo conn config date-formatter format nil % {}))
(remove nil?))
txs (map-indexed (fn [i page]
(if (zero? i)
page
(assoc page :block/namespace
[:block/uuid (:block/uuid (nth txs (dec i)))])))
txs)
page-txs (build-page-tx repo conn config date-formatter format properties (last pages) (select-keys options [:whiteboard? :class? :tags]))
page-txs (if (seq txs)
(update page-txs 0
(fn [p]
(assoc p :block/namespace [:block/uuid (:block/uuid (last txs))])))
page-txs)
first-block-tx (when (and
create-first-block?
(not (or whiteboard? class?))
(ldb/page-empty? @conn (:db/id (d/entity @conn [:block/name page-name])))
drop-last
(mapcat #(build-page-tx repo conn config date-formatter format nil % {}))
(remove nil?))
txs (map-indexed (fn [i page]
(if (zero? i)
page
(assoc page :block/namespace
[:block/uuid (:block/uuid (nth txs (dec i)))])))
txs)
page-txs (build-page-tx repo conn config date-formatter format properties (last pages) (select-keys options [:whiteboard? :class? :tags]))
page-txs (if (seq txs)
(update page-txs 0
(fn [p]
(assoc p :block/namespace [:block/uuid (:block/uuid (last txs))])))
page-txs)
(let [page-id [:block/uuid (:block/uuid (first page-txs))]]
[(sqlite-util/block-with-timestamps
{:block/uuid (ldb/new-block-id)
:block/page page-id
:block/parent page-id
:block/left page-id
:block/content ""
:block/format format})]))
txs (concat
txs
page-txs
first-block-tx)]
(when (seq txs)
(ldb/transact! conn txs (cond-> {:persist-op? persist-op?}
today-journal?
(assoc :create-today-journal? true
:today-journal-name page-name))))))))
first-block-tx (when (and
create-first-block?
(not (or whiteboard? class?))
(ldb/page-empty? @conn (:db/id (d/entity @conn [:block/name page-name])))
page-txs)
(let [page-id [:block/uuid (:block/uuid (first page-txs))]]
[(sqlite-util/block-with-timestamps
{:block/uuid (ldb/new-block-id)
:block/page page-id
:block/parent page-id
:block/left page-id
:block/content ""
:block/format format})]))
txs (concat
txs
page-txs
first-block-tx)]
(when (seq txs)
(ldb/transact! conn txs (cond-> {:persist-op? persist-op?}
today-journal?
(assoc :create-today-journal? true
:today-journal-name page-name))))))] ;; FIXME: prettier validation
[result page-name]))
(defn db-refs->page
"Replace [[page name]] with page name"

View File

@ -302,7 +302,11 @@
(d/pull-many @conn [:db/id])
(filter :db/id)))
(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)))))))
(defn apply-remote-move-ops

View File

@ -1,7 +1,7 @@
(ns frontend.db.name-sanity-test
(:require [cljs.test :refer [deftest testing is]]
[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.util.fs :as fs-util]
[frontend.worker.file.util :as wfu]))
@ -11,7 +11,7 @@
[page-name]
(testing (str "Test sanitization page-name: " 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-double (js/encodeURIComponent url-single)
file-name' (js/decodeURIComponent url-single)

View File

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