Переглянути джерело

Merge branch 'enhance/ios-folder-picker' of https://github.com/logseq/logseq into enhance/ios-folder-picker

charlie 3 роки тому
батько
коміт
43d84ab09b
39 змінених файлів з 498 додано та 705 видалено
  1. 0 2
      deps/db/src/logseq/db/schema.cljs
  2. 1 1
      deps/graph-parser/src/logseq/graph_parser/extract.cljc
  3. 12 5
      deps/graph-parser/src/logseq/graph_parser/util.cljs
  4. 1 1
      src/electron/electron/core.cljs
  5. 8 0
      src/electron/electron/utils.cljs
  6. 1 1
      src/electron/electron/window.cljs
  7. 1 1
      src/main/frontend/components/block.cljs
  8. 1 0
      src/main/frontend/components/block.css
  9. 1 132
      src/main/frontend/components/encryption.cljs
  10. 13 10
      src/main/frontend/components/file_sync.cljs
  11. 2 2
      src/main/frontend/components/header.cljs
  12. 0 10
      src/main/frontend/components/repo.cljs
  13. 1 24
      src/main/frontend/components/settings.cljs
  14. 2 2
      src/main/frontend/config.cljs
  15. 1 1
      src/main/frontend/core.cljs
  16. 0 15
      src/main/frontend/dicts.cljc
  17. 0 83
      src/main/frontend/encrypt.cljs
  18. 21 24
      src/main/frontend/fs.cljs
  19. 3 9
      src/main/frontend/fs/capacitor_fs.cljs
  20. 4 12
      src/main/frontend/fs/nfs.cljs
  21. 4 12
      src/main/frontend/fs/node.cljs
  22. 270 221
      src/main/frontend/fs/sync.cljs
  23. 1 4
      src/main/frontend/fs/watcher_handler.cljs
  24. 1 10
      src/main/frontend/handler/common.cljs
  25. 56 18
      src/main/frontend/handler/events.cljs
  26. 2 2
      src/main/frontend/handler/file.cljs
  27. 9 2
      src/main/frontend/handler/file_sync.cljs
  28. 2 12
      src/main/frontend/handler/metadata.cljs
  29. 1 1
      src/main/frontend/handler/paste.cljs
  30. 20 46
      src/main/frontend/handler/repo.cljs
  31. 7 6
      src/main/frontend/handler/web/nfs.cljs
  32. 1 1
      src/main/frontend/mobile/graph_picker.cljs
  33. 4 4
      src/main/frontend/mobile/intent.cljs
  34. 0 4
      src/main/frontend/state.cljs
  35. 1 1
      src/main/frontend/util.cljc
  36. 1 1
      src/main/frontend/util/fs.cljs
  37. 24 23
      tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx
  38. 2 2
      tldraw/apps/tldraw-logseq/src/styles.css
  39. 19 0
      yarn.lock

+ 0 - 2
deps/db/src/logseq/db/schema.cljs

@@ -9,8 +9,6 @@
    :ast/version     {}
    :db/type         {}
    :db/ident        {:db/unique :db.unique/identity}
-   :db/encrypted?    {}
-   :db/encryption-keys {}
 
    :recent/pages {}
 

+ 1 - 1
deps/graph-parser/src/logseq/graph_parser/extract.cljc

@@ -24,7 +24,7 @@
     (let [result (first (gp-util/split-last "." file-name))
           ext (string/lower-case (gp-util/get-file-ext filepath))]
       (if (or (gp-config/mldoc-support? ext) (= "edn" ext))
-        (js/decodeURIComponent (string/replace result "." "/"))
+        (gp-util/safe-decode-uri-component (string/replace result "." "/"))
         result))))
 
 (defn- get-page-name

+ 12 - 5
deps/graph-parser/src/logseq/graph_parser/util.cljs

@@ -7,16 +7,22 @@
             [clojure.walk :as walk]
             [logseq.graph-parser.log :as log]))
 
+(defn safe-decode-uri-component
+  [uri]
+  (try
+    (js/decodeURIComponent uri)
+    (catch :default _
+      (log/error :decode-uri-component-failed uri)
+      uri)))
+
 (defn safe-url-decode
   [string]
   (if (string/includes? string "%")
-    (try (some-> string str (js/decodeURIComponent))
-         (catch :default _
-           string))
+    (some-> string str safe-decode-uri-component)
     string))
 
 (defn path-normalize
-  "Normalize file path (for reading paths from FS, not required by writting)
+  "Normalize file path (for reading paths from FS, not required by writing)
    Keep capitalization senstivity"
   [s]
   (.normalize s "NFC"))
@@ -220,7 +226,8 @@
 ;; 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]
-  (js/decodeURIComponent (string/replace file-name-body "." "/")))
+  (let [title (string/replace file-name-body "." "/")]
+    (or (safe-decode-uri-component title) title)))
 
 ;; Register sanitization / parsing fns in:
 ;; logseq.graph-parser.util (parsing only)

+ 1 - 1
src/electron/electron/core.cljs

@@ -77,7 +77,7 @@
                       [STATIC_URL js/__dirname])
 
            path' (.-pathname url')
-           path' (js/decodeURIComponent path')
+           path' (utils/safe-decode-uri-component path')
            path' (.join path ROOT path')]
 
        (callback #js {:path path'}))))

+ 8 - 0
src/electron/electron/utils.cljs

@@ -154,3 +154,11 @@
 (defn normalize-lc
   [s]
   (normalize (string/lower-case s)))
+
+(defn safe-decode-uri-component
+  [uri]
+  (try
+    (js/decodeURIComponent uri)
+    (catch :default _
+      (println "decodeURIComponent failed: " uri)
+      uri)))

+ 1 - 1
src/electron/electron/window.cljs

@@ -123,7 +123,7 @@
           new-win-handler
           (fn [e url]
             (let [url (if (string/starts-with? url "file:")
-                        (js/decodeURIComponent url) url)
+                        (utils/safe-decode-uri-component url) url)
                   url (if-not win32? (string/replace url "file://" "") url)]
               (logger/info "new-window" url)
               (if (some #(string/includes?

+ 1 - 1
src/main/frontend/components/block.cljs

@@ -836,7 +836,7 @@
   [label]
   (when (and (= 1 (count label))
              (string? (last (first label))))
-    (js/decodeURIComponent (last (first label)))))
+    (gp-util/safe-decode-uri-component (last (first label)))))
 
 (defn- get-page
   [label]

+ 1 - 0
src/main/frontend/components/block.css

@@ -150,6 +150,7 @@
   background-color: transparent;
   position: absolute;
   border-radius: 2px;
+  opacity: 0.6;
 }
 
 .block-children-left-border:hover {

+ 1 - 132
src/main/frontend/components/encryption.cljs

@@ -1,56 +1,15 @@
 (ns frontend.components.encryption
   (:require [clojure.string :as string]
             [frontend.context.i18n :refer [t]]
-            [frontend.encrypt :as encrypt]
-            [frontend.handler.metadata :as metadata-handler]
             [frontend.handler.notification :as notification]
             [frontend.fs.sync :as sync]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.config :as config]
-            [promesa.core :as p]
             [cljs.core.async :as async]
             [rum.core :as rum]))
 
-(rum/defcs encryption-dialog-inner <
-  (rum/local false ::reveal-secret-phrase?)
-  [state repo-url close-fn]
-  (let [reveal-secret-phrase? (get state ::reveal-secret-phrase?)
-        public-key (encrypt/get-public-key repo-url)
-        private-key (encrypt/get-secret-key repo-url)]
-    [:div
-     [:div.sm:flex.sm:items-start
-      [:div.mt-3.text-center.sm:mt-0.sm:text-left
-       [:h3#modal-headline.text-lg.leading-6.font-medium
-        "This graph is encrypted with " [:a {:href "https://age-encryption.org/" :target "_blank" :rel "noopener"} "age-encryption.org/v1"]]]]
-
-     [:div.mt-1
-      [:div.max-w-2xl.rounded-md.shadow-sm.sm:max-w-xl
-       [:div.cursor-pointer.block.w-full.rounded-sm.p-2
-        {:on-click (fn []
-                     (when (not @reveal-secret-phrase?)
-                       (reset! reveal-secret-phrase? true)))}
-        [:div.font-medium "Public Key:"]
-        [:div.font-mono.select-all.break-all public-key]
-        (if @reveal-secret-phrase?
-          [:div
-           [:div.mt-1.font-medium "Private Key:"]
-           [:div.font-mono.select-all.break-all private-key]]
-          [:div.underline "click to view the private key"])]]]
-
-     [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
-      [:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
-       [:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
-        {:type "button"
-         :on-click close-fn}
-        (t :close)]]]]))
-
-(defn encryption-dialog
-  [repo-url]
-  (fn [close-fn]
-    (encryption-dialog-inner repo-url close-fn)))
-
 (rum/defc show-password-cp
   [*show-password?]
   [:div.flex.flex-row.items-center
@@ -101,12 +60,6 @@
 
               :else
               (case type
-                :local
-                (p/let [keys                (encrypt/generate-key-pair-and-save! repo-url)
-                        db-encrypted-secret (encrypt/encrypt-with-passphrase value keys)]
-                  (metadata-handler/set-db-encrypted-secret! db-encrypted-secret)
-                  (close-fn true))
-
                 (:create-pwd-remote :input-pwd-remote)
                 (do
                   (state/set-state! [:ui/loading? :set-graph-password] true)
@@ -116,7 +69,7 @@
                       (if (instance? js/Error persist-r)
                         (js/console.error persist-r)
                         (when (fn? after-input-password)
-                          (async/<! (after-input-password))
+                          (after-input-password @*password)
                           ;; TODO: it's better if based on sync state
                           (when init-graph-keys
                             (js/setTimeout #(state/pub-event! [:file-sync/maybe-onboarding-show :sync-learn]) 10000)))))))))))
@@ -286,87 +239,3 @@
                             (close-fn'))
                        close-fn')]
        (input-password-inner repo-url close-fn' opts)))))
-
-(rum/defcs encryption-setup-dialog-inner
-  [state repo-url close-fn]
-  [:div
-   [:div.sm:flex.sm:items-start
-    [:div.mt-3.text-center.sm:mt-0.sm:text-left
-     [:h3#modal-headline.text-lg.leading-6.font-medium
-      "Do you want to create an encrypted graph?"]]]
-
-   [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
-    [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
-     [:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
-      {:type "button"
-       :on-click (fn []
-                   (state/set-modal!
-                    (input-password repo-url close-fn)
-                    {:center? true :close-btn? false}))}
-      (t :yes)]]
-    [:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
-     [:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
-      {:type "button"
-       :on-click (fn [] (close-fn false))}
-      (t :no)]]]])
-
-(defn encryption-setup-dialog
-  [repo-url close-fn]
-  (fn [close-modal-fn]
-    (let [close-fn (fn [encrypted?]
-                     (close-fn encrypted?)
-                     (close-modal-fn))]
-      (encryption-setup-dialog-inner repo-url close-fn))))
-
-(rum/defcs encryption-input-secret-inner <
-  (rum/local "" ::secret)
-  (rum/local false ::loading)
-  (rum/local false ::show-password?)
-  [state _repo-url db-encrypted-secret close-fn]
-  (let [secret (::secret state)
-        loading (::loading state)
-        *show-password? (::show-password? state)
-        on-click-fn (fn []
-                      (reset! loading true)
-                      (let [value @secret]
-                        (when-not (string/blank? value) ; TODO: length or other checks
-                          (let [repo (state/get-current-repo)]
-                            (p/do!
-                             (-> (encrypt/decrypt-with-passphrase value db-encrypted-secret)
-                                 (p/then (fn [keys]
-                                           (encrypt/save-key-pair! repo keys)
-                                           (close-fn true)
-                                           (state/set-state! :encryption/graph-parsing? false)))
-                                 (p/catch #(notification/show! "The password is not matched." :warning true))
-                                 (p/finally #(reset! loading false))))))))]
-    [:div
-     [:div.sm:flex.sm:items-start
-      [:div.mt-3.text-center.sm:mt-0.sm:text-left
-       [:h3#modal-headline.text-lg.leading-6.font-medium
-        "Enter your password"]]]
-
-     [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
-      {:type (if @*show-password? "text" "password")
-       :auto-focus true
-       :on-change (fn [e]
-                    (reset! secret (util/evalue e)))
-       :on-key-down (fn [e]
-                      (when (= (.-key e) "Enter")
-                        (on-click-fn)))}]
-
-     (show-password-cp *show-password?)
-
-     [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
-      [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
-       [:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
-        {:type "button"
-         :on-click on-click-fn}
-        (if @loading (ui/loading "Decrypting") "Decrypt")]]]]))
-
-(defn encryption-input-secret-dialog
-  [repo-url db-encrypted-secret close-fn]
-  (fn [close-modal-fn]
-    (let [close-fn (fn [encrypted?]
-                     (close-fn encrypted?)
-                     (close-modal-fn))]
-      (encryption-input-secret-inner repo-url db-encrypted-secret close-fn))))

+ 13 - 10
src/main/frontend/components/file_sync.cljs

@@ -29,7 +29,8 @@
             [rum.core :as rum]
             [cljs-time.core :as t]
             [cljs-time.coerce :as tc]
-            [goog.functions :refer [debounce]]))
+            [goog.functions :refer [debounce]]
+            [logseq.graph-parser.util :as gp-util]))
 
 (declare maybe-onboarding-show)
 (declare open-icloud-graph-clone-picker)
@@ -78,7 +79,7 @@
 
      [:div.folder-tip.flex.flex-col.items-center
       [:h3
-       [:span (ui/icon "folder") [:label.pl-0.5 (js/decodeURIComponent graph-name)]]]
+       [:span (ui/icon "folder") [:label.pl-0.5 (gp-util/safe-decode-uri-component graph-name)]]]
       [:h4.px-6 (config/get-string-repo-dir repo)]
 
       (when (not (string/blank? selected-path))
@@ -132,7 +133,7 @@
               (do
                 (state/set-state! [:ui/loading? :graph/create-remote?] true)
                 (when-let [GraphUUID (get (async/<! (file-sync-handler/create-graph graph-name)) 2)]
-                  (async/<! (fs-sync/sync-start))
+                  (async/<! (fs-sync/<sync-start))
                   (state/set-state! [:ui/loading? :graph/create-remote?] false)
                   ;; update existing repo
                   (state/set-repos! (map (fn [r]
@@ -376,9 +377,7 @@
                                                 (second @graphs-txid)
                                                 (fs-sync/graph-sync-off? (second @graphs-txid))
                                                 (async/<! (fs-sync/<check-remote-graph-exists (second @graphs-txid))))
-                                           (do
-                                             (prn "sync start")
-                                             (fs-sync/sync-start))
+                                           (fs-sync/<sync-start)
 
                                            ;; remote graph already has been deleted, clear repos first, then create-remote-graph
                                            synced-file-graph?  ; <check-remote-graph-exists -> false
@@ -441,7 +440,7 @@
 
              (map (fn [f] {:title [:div.file-item
                                    {:key (str "downloading-" f)}
-                                   (js/decodeURIComponent f)]
+                                   (gp-util/safe-decode-uri-component f)]
                            :key   (str "downloading-" f)
                            :icon  (if enabled-progress-panel?
                                     (let [progress (get sync-progress f)
@@ -460,13 +459,17 @@
                                 path (fs-sync/relative-path e)]
                             {:title [:div.file-item
                                      {:key (str "queue-" path)}
-                                     (js/decodeURIComponent path)]
+                                     (try
+                                       (gp-util/safe-decode-uri-component path)
+                                       (catch :default _
+                                         (prn "Wrong path: " path)
+                                         path))]
                              :key   (str "queue-" path)
                              :icon  (ui/icon icon)})) (take 10 queuing-files))
 
              (map (fn [f] {:title [:div.file-item
                                    {:key (str "uploading-" f)}
-                                   (js/decodeURIComponent f)]
+                                   (gp-util/safe-decode-uri-component f)]
                            :key   (str "uploading-" f)
                            :icon  (if enabled-progress-panel?
                                     (let [progress (get sync-progress f)
@@ -489,7 +492,7 @@
                                                         (if page-name
                                                           (rfe/push-state :page {:name page-name})
                                                           (rfe/push-state :file {:path full-path})))}
-                                           [:span.file-sync-item (js/decodeURIComponent (:path f))]
+                                           [:span.file-sync-item (gp-util/safe-decode-uri-component (:path f))]
                                            [:div.opacity-50 (ui/humanity-time-ago (:time f) nil)]]})))
                             (take 10 history-files)))))
 

+ 2 - 2
src/main/frontend/components/header.cljs

@@ -152,6 +152,7 @@
   [{:keys [open-fn current-repo default-home new-block-mode]}]
   (let [repos (->> (state/sub [:me :repos])
                    (remove #(= (:url %) config/local-repo)))
+        _ (state/sub [:user/info :UserGroups])
         electron-mac? (and util/mac? (util/electron?))
         show-open-folder? (and (nfs/supported?)
                                (or (empty? repos)
@@ -196,8 +197,7 @@
              (ui/icon "chevron-left" {:size 26})])))]
 
      [:div.r.flex
-      (when (and sync-enabled?
-                 current-repo
+      (when (and current-repo
                  (not (config/demo-graph? current-repo))
                  (user-handler/alpha-or-beta-user?))
         (fs-sync/indicator))

+ 0 - 10
src/main/frontend/components/repo.cljs

@@ -17,8 +17,6 @@
             [promesa.core :as p]
             [electron.ipc :as ipc]
             [goog.object :as gobj]
-            [frontend.components.encryption :as encryption]
-            [frontend.encrypt :as encrypt]
             [cljs.core.async :as async :refer [go <!]]
             [frontend.handler.file-sync :as file-sync]
             [reitit.frontend.easy :as rfe]))
@@ -60,14 +58,6 @@
                                      (state/pub-event! [:graph/switch url])))
 
      [:div.controls
-      (when (encrypt/encrypted-db? url)
-        [:a.control {:title    "Show encryption information about this graph"
-                     :on-click (fn []
-                                 (if remote?
-                                   (state/pub-event! [:modal/remote-encryption-input-pw-dialog url repo])
-                                   (state/set-modal! (encryption/encryption-dialog url))))}
-         "🔐"])
-
       (let [loading? (state/sub [:ui/loading? :remove/remote-graph GraphUUID])]
         [:div.flex.flex-row.items-center
          (when loading? [:div.ml-2 (ui/loading "")])

+ 1 - 24
src/main/frontend/components/settings.cljs

@@ -410,24 +410,6 @@
 ;;             (let [value (not enable-block-timestamps?)]
 ;;               (config-handler/set-config! :feature/enable-block-timestamps? value)))))
 
-(defn encryption-row [enable-encryption?]
-  (toggle "enable_encryption"
-          (t :settings-page/enable-encryption)
-          enable-encryption?
-          #(let [value (not enable-encryption?)]
-             (config-handler/set-config! :feature/enable-encryption? value)
-             (when value
-               (state/close-modal!)
-               ;; FIXME: Don't send the `(atom false)` ! Should check multi-window! or internal status error happens
-               (js/setTimeout (fn [] (state/pub-event! [:graph/ask-for-re-index (atom false) nil]))
-                              100)))
-          [:p.text-sm.opacity-50 "⚠️ This feature is experimental! "
-           [:span "You can use "]
-           [:a {:href "https://github.com/kanru/logseq-encrypt-ui"
-                :target "_blank"}
-            "logseq-encrypt-ui"]
-           [:span " to decrypt your graph."]]))
-
 (rum/defc keyboard-shortcuts-row [t]
   (row-with-button-action
     {:left-label   (t :settings-page/customize-shortcuts)
@@ -635,9 +617,7 @@
   [enabled?]
   (ui/toggle enabled?
              (fn []
-               (let [value (not enabled?)]
-                 (storage/set :logseq-sync-enabled value)
-                 (state/set-state! :feature/enable-sync? value)))
+               (file-sync-handler/set-sync-enabled! (not enabled?)))
              true))
 
 (defn sync-switcher-row [enabled?]
@@ -662,7 +642,6 @@
   []
   (let [current-repo (state/get-current-repo)
         enable-journals? (state/enable-journals? current-repo)
-        enable-encryption? (state/enable-encryption? current-repo)
         enable-flashcards? (state/enable-flashcards? current-repo)
         enable-sync? (state/enable-sync?)
         enable-whiteboards? (state/enable-whiteboards? current-repo)
@@ -685,8 +664,6 @@
      (when (and (util/electron?) config/enable-plugins?) (plugin-system-switcher-row))
      (flashcards-switcher-row enable-flashcards?)
      (zotero-settings-row)
-     (encryption-row enable-encryption?)
-
      (when-not web-platform?
        [:div.mt-1.sm:mt-0.sm:col-span-2
         [:hr]

+ 2 - 2
src/main/frontend/config.cljs

@@ -359,7 +359,7 @@
                  "Local"))
          (->> (string/split repo-dir "Documents/")
               last
-              js/decodeURIComponent
+              gp-util/safe-decode-uri-component
               (str "/" (string/capitalize app-name) "/")))
     (get-repo-dir repo-dir)))
 
@@ -370,7 +370,7 @@
     path
     (util/node-path.join (get-repo-dir repo-url) path)))
 
-;; FIXME: There is another get-file-path at src/main/frontend/fs/capacitor_fs.cljs
+;; FIXME: There is another normalize-file-protocol-path at src/main/frontend/fs/capacitor_fs.cljs
 (defn get-file-path
   "Normalization happens here"
   [repo-url relative-path]

+ 1 - 1
src/main/frontend/core.cljs

@@ -49,7 +49,7 @@
     (display-welcome-message)
     (persist-var/load-vars)
     (when config/dev?
-      (js/setTimeout #(sync/sync-start) 1000))))
+      (js/setTimeout #(sync/<sync-start) 1000))))
 
 (defn ^:export init []
   ;; init is called ONCE when the page loads

+ 0 - 15
src/main/frontend/dicts.cljc

@@ -218,7 +218,6 @@
         :settings-page/enable-tooltip "Tooltips"
         :settings-page/enable-journals "Journals"
         :settings-page/enable-all-pages-public "All pages public when publishing"
-        :settings-page/enable-encryption "Encryption"
         :settings-page/customize-shortcuts "Keyboard shortcuts"
         :settings-page/shortcut-settings "Customize shortcuts"
         :settings-page/home-default-page "Set the default home page"
@@ -672,7 +671,6 @@
         :settings-page/customize-shortcuts "Tastaturbefehle"
         :settings-page/disable-sentry "Nutzungs- und Diagnostik-Daten an Logseq senden"
         :settings-page/edit-custom-css "custom.css bearbeiten"
-        :settings-page/enable-encryption "Verschlüsselung"
         :settings-page/enable-shortcut-tooltip "Tooltips für Verknüpfungen aktivieren"
         :settings-page/enable-tooltip "Tooltips"
         :settings-page/shortcut-settings "Verknüpfungen anpassen"
@@ -998,7 +996,6 @@
         :settings-page/enable-all-pages-public "Alle pagina's openbaar bij publiceren"
         :settings-page/enable-block-time "Tijdstempel voor blokken inschakelen"
         :settings-page/enable-developer-mode "Ontwikkelaar modus"
-        :settings-page/enable-encryption "Encryptie"
         :settings-page/enable-flashcards "Flashcards"
         :settings-page/enable-journals "Journaals"
         :settings-page/enable-shortcut-tooltip "Snelkoppeling tooltip inschakelen"
@@ -1402,7 +1399,6 @@
         :settings-page/edit-setting "Modifier"
         :settings-page/enable-all-pages-public "Toutes les pages publiques lors de la publication"
         :settings-page/enable-block-time "Horodatage de bloc"
-        :settings-page/enable-encryption "Chiffrement"
         :settings-page/enable-flashcards "Cartes-mémoire"
         :settings-page/enable-journals "Journals"
         :settings-page/enable-shortcut-tooltip "Activer les astuces sur les raccourcis"
@@ -1649,7 +1645,6 @@
            :settings-page/enable-tooltip "开启提示框"
            :settings-page/enable-journals "开启日记"
            :settings-page/enable-all-pages-public "发布所有页面"
-           :settings-page/enable-encryption "激活加密功能"
            :settings-page/enable-whiteboards "激活白板功能"
            :settings-page/customize-shortcuts "自定义快捷键"
            :settings-page/shortcut-settings "快捷键设置"
@@ -2313,7 +2308,6 @@
         :settings-page/enable-tooltip "Habilitar descripción emergente"
         :settings-page/enable-journals "Habilitar diarios"
         :settings-page/enable-all-pages-public "Hacer todas las páginas públicas al publicar"
-        :settings-page/enable-encryption "Habilitar función de cifrado"
         :settings-page/customize-shortcuts "Atajos de teclado"
         :settings-page/shortcut-settings "Personalizar atajos"
         :settings-page/home-default-page "Establecer página de inicio"
@@ -2627,7 +2621,6 @@
            :settings-page/preferred-workflow "Foretrukket arbeidslflyt"
            :settings-page/enable-shortcut-tooltip "Skru på tooltip for snarveier"
            :settings-page/enable-timetracking "Aktiver tidssporing"
-           :settings-page/enable-encryption "Aktiver kryptering"
            :settings-page/enable-tooltip "Aktiver verktøytips"
            :settings-page/enable-journals "Aktiver dagbøker"
            :settings-page/enable-all-pages-public "Aktiver alle sider som offentlige ved publisering"
@@ -2951,7 +2944,6 @@
            :settings-page/enable-tooltip "Ativar dicas de ferramentas"
            :settings-page/enable-journals "Ativar diários"
            :settings-page/enable-all-pages-public "Ativar todas as páginas públicas ao publicar"
-           :settings-page/enable-encryption "Ativar funcionalidade de criptografia"
            :settings-page/customize-shortcuts "Atalhos de teclado"
            :settings-page/shortcut-settings "Personalizar atalhos"
            :settings-page/home-default-page "Definir a página inicial padrão"
@@ -3299,7 +3291,6 @@
            :settings-page/enable-tooltip "Dicas de atalhos"
            :settings-page/enable-journals "Diários"
            :settings-page/enable-all-pages-public "Todas as páginas públicas ao publicar"
-           :settings-page/enable-encryption "Encriptação"
            :settings-page/customize-shortcuts "Atalhos de teclado"
            :settings-page/shortcut-settings "Personalizar atalhos"
            :settings-page/home-default-page "Definir a página inicial predefinida"
@@ -3652,7 +3643,6 @@
         :settings-page/enable-shortcut-tooltip "Всплывающие подсказки горячих клавиш"
         :settings-page/enable-journals "Включить Дневники"
         :settings-page/enable-all-pages-public "Все страницы общедоступны при публикации"
-        :settings-page/enable-encryption "Функции шифрования"
         :settings-page/enable-flashcards "Карточки"
         :settings-page/custom-configuration "Настройки пользователя"
         :settings-page/custom-theme "Тема пользователя"
@@ -3981,7 +3971,6 @@
         :settings-page/enable-tooltip "ツールチップ"
         :settings-page/enable-journals "日誌"
         :settings-page/enable-all-pages-public "パブリッシュ時には全てのページを公開する"
-        :settings-page/enable-encryption "暗号化"
         :settings-page/customize-shortcuts "キーボードショートカット"
         :settings-page/shortcut-settings "ショートカットをカスタマイズ"
         :settings-page/home-default-page "デフォルトのホームページを設定"
@@ -4466,7 +4455,6 @@
 
         :file-sync/other-user-graph "Il grafo locale attuale è associato al grafo remoto di un altro utente. Non è quindi possibile avviare la sincronizzazione."
         :file-sync/graph-deleted "Il grafo attuale è stato eliminato"
-        :settings-page/enable-encryption "Crittografia"
         :settings-page/edit-export-css "Modificare export.css"
         :settings-page/enable-flashcards "Flashcard"
         :settings-page/export-theme "Esporta tema"}
@@ -4640,7 +4628,6 @@
         :settings-page/enable-tooltip "Araç ipuçları"
         :settings-page/enable-journals "Günlük"
         :settings-page/enable-all-pages-public "Yayımlanan tüm sayfaları herkese açık yap"
-        :settings-page/enable-encryption "Şifreleme"
         :settings-page/customize-shortcuts "Klavye kısayolları"
         :settings-page/shortcut-settings "Kısayolları özelleştir"
         :settings-page/home-default-page "Varsayılan ana sayfayı ayarla"
@@ -4972,7 +4959,6 @@
         :settings-page/enable-tooltip "툴팁 활성화"
         :settings-page/enable-journals "일지 활성화"
         :settings-page/enable-all-pages-public "출판할 때 모든 페이지 공개로 설정"
-        :settings-page/enable-encryption "암호화"
         :settings-page/customize-shortcuts "키보드 단축키"
         :settings-page/shortcut-settings "단축키 설정"
         :settings-page/home-default-page "기본 홈 페이지 설정"
@@ -5300,7 +5286,6 @@
         :settings-page/enable-tooltip "Podpowiedzi"
         :settings-page/enable-journals "Dzienniki"
         :settings-page/enable-all-pages-public "Publikuj wszystkie strony"
-        :settings-page/enable-encryption "Szyfrowanie"
         :settings-page/customize-shortcuts "Skróty klawiszowe"
         :settings-page/shortcut-settings "Zmień skróty"
         :settings-page/home-default-page "Ustaw domyślną stronę startową"

+ 0 - 83
src/main/frontend/encrypt.cljs

@@ -1,95 +1,12 @@
 (ns frontend.encrypt
   "Encryption related fns for use with encryption feature and file sync"
   (:require [logseq.graph-parser.utf8 :as utf8]
-            [frontend.db.utils :as db-utils]
             [frontend.util :as util]
-            [frontend.db :as db]
-            [frontend.state :as state]
-            [clojure.string :as str]
-            [cljs.reader :as reader]
             [promesa.core :as p]
             [electron.ipc :as ipc]
             [shadow.loader :as loader]
-            [lambdaisland.glogi :as log]
             [frontend.mobile.util :as mobile-util]))
 
-(defonce age-pem-header-line "-----BEGIN AGE ENCRYPTED FILE-----")
-(defonce age-version-line "age-encryption.org/v1")
-
-(defn content-encrypted?
-  [content]
-  (when content
-    (or (str/starts-with? content age-pem-header-line)
-        (str/starts-with? content age-version-line))))
-
-(defn encrypted-db?
-  [repo-url]
-  (db-utils/get-key-value repo-url :db/encrypted?))
-
-(defn get-key-pair
-  [repo-url]
-  (db-utils/get-key-value repo-url :db/encryption-keys))
-
-(defn save-key-pair!
-  [repo-url keys]
-  (let [keys (if (string? keys) (reader/read-string keys) keys)]
-    (db/set-key-value repo-url :db/encryption-keys keys)
-    (db/set-key-value repo-url :db/encrypted? true)))
-
-(defn generate-key-pair
-  []
-  (p/let [_ (loader/load :age-encryption)
-          lazy-keygen (resolve 'frontend.extensions.age-encryption/keygen)
-          js-keys (lazy-keygen)]
-    (array-seq js-keys)))
-
-(defn generate-key-pair-and-save!
-  [repo-url]
-  (when-not (get-key-pair repo-url)
-    (p/let [keys (generate-key-pair)]
-      (save-key-pair! repo-url keys)
-      (pr-str keys))))
-
-(defn get-public-key
-  [repo-url]
-  (second (get-key-pair repo-url)))
-
-(defn get-secret-key
-  [repo-url]
-  (first (get-key-pair repo-url)))
-
-(defn encrypt
-  ([content]
-   (encrypt (state/get-current-repo) content))
-  ([repo-url content]
-   (cond
-     (encrypted-db? repo-url)
-     (p/let [_ (loader/load :age-encryption)
-             lazy-encrypt-with-x25519 (resolve 'frontend.extensions.age-encryption/encrypt-with-x25519)
-             content (utf8/encode content)
-             public-key (get-public-key repo-url)
-             encrypted (lazy-encrypt-with-x25519 public-key content true)]
-       (utf8/decode encrypted))
-     :else
-     (p/resolved content))))
-
-(defn decrypt
-  ([content]
-   (decrypt (state/get-current-repo) content))
-  ([repo-url content]
-   (cond
-     (and (encrypted-db? repo-url)
-          (content-encrypted? content))
-     (let [content (utf8/encode content)]
-       (if-let [secret-key (get-secret-key repo-url)]
-         (p/let [_ (loader/load :age-encryption)
-                 lazy-decrypt-with-x25519 (resolve 'frontend.extensions.age-encryption/decrypt-with-x25519)
-                 decrypted (lazy-decrypt-with-x25519 secret-key content)]
-           (utf8/decode decrypted))
-         (log/error :encrypt/empty-secret-key (str "Can't find the secret key for repo: " repo-url))))
-     :else
-     (p/resolved content))))
-
 (defn encrypt-with-passphrase
   [passphrase content]
   (cond

+ 21 - 24
src/main/frontend/fs.cljs

@@ -14,7 +14,6 @@
             [promesa.core :as p]
             [frontend.db :as db]
             [clojure.string :as string]
-            [frontend.encrypt :as encrypt]
             [frontend.state :as state]))
 
 (defonce nfs-record (nfs/->Nfs))
@@ -77,29 +76,27 @@
   [repo dir path content opts]
   (when content
     (let [fs-record (get-fs dir)]
-      (p/let [md-or-org? (contains? #{"md" "markdown" "org"} (util/get-file-ext path))
-              content (if-not md-or-org? content (encrypt/encrypt content))]
-        (->
-         (p/let [opts (assoc opts
-                             :error-handler
-                             (fn [error]
-                               (state/pub-event! [:instrument {:type :write-file/failed
-                                                               :payload {:fs (type fs-record)
-                                                                         :user-agent (when js/navigator js/navigator.userAgent)
-                                                                         :path path
-                                                                         :content-length (count content)
-                                                                         :error-str (str error)
-                                                                         :error error}}])))
-                 _ (protocol/write-file! (get-fs dir) repo dir path content opts)]
-           (when (= bfs-record fs-record)
-             (db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
-         (p/catch (fn [error]
-                    (log/error :file/write-failed {:dir dir
-                                                   :path path
-                                                   :error error})
-                    ;; Disable this temporarily
-                    ;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
-                    )))))))
+      (->
+       (p/let [opts (assoc opts
+                           :error-handler
+                           (fn [error]
+                             (state/pub-event! [:instrument {:type :write-file/failed
+                                                             :payload {:fs (type fs-record)
+                                                                       :user-agent (when js/navigator js/navigator.userAgent)
+                                                                       :path path
+                                                                       :content-length (count content)
+                                                                       :error-str (str error)
+                                                                       :error error}}])))
+               _ (protocol/write-file! (get-fs dir) repo dir path content opts)]
+         (when (= bfs-record fs-record)
+           (db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
+       (p/catch (fn [error]
+                  (log/error :file/write-failed {:dir dir
+                                                 :path path
+                                                 :error error})
+                  ;; Disable this temporarily
+                  ;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
+                  ))))))
 
 (defn read-file
   ([dir path]

+ 3 - 9
src/main/frontend/fs/capacitor_fs.cljs

@@ -6,7 +6,6 @@
             [goog.string :as gstring]
             [frontend.config :as config]
             [frontend.db :as db]
-            [frontend.encrypt :as encrypt]
             [frontend.fs.protocol :as protocol]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
@@ -105,10 +104,7 @@
 (defn- contents-matched?
   [disk-content db-content]
   (when (and (string? disk-content) (string? db-content))
-    (if (encrypt/encrypted-db? (state/get-current-repo))
-      (p/let [decrypted-content (encrypt/decrypt disk-content)]
-        (= (string/trim decrypted-content) (string/trim db-content)))
-      (p/resolved (= (string/trim disk-content) (string/trim db-content))))))
+    (p/resolved (= (string/trim disk-content) (string/trim db-content)))))
 
 (def backup-dir "logseq/bak")
 (def version-file-dir "logseq/version-files/local")
@@ -195,7 +191,7 @@
          (not contents-matched?)
          (not (contains? #{"excalidraw" "edn" "css"} ext))
          (not (string/includes? path "/.recycle/")))
-        (p/let [disk-content (encrypt/decrypt disk-content)]
+        (p/let [disk-content disk-content]
           (state/pub-event! [:file/not-matched-from-disk path disk-content content]))
 
         :else
@@ -206,9 +202,7 @@
            (when-not contents-matched?
              (backup-file repo-dir :backup-dir path disk-content))
            (db/set-file-last-modified-at! repo path mtime)
-           (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
-                             (encrypt/decrypt content)
-                             content)]
+           (p/let [content content]
              (db/set-file-content! repo path content))
            (when ok-handler
              (ok-handler repo path result))

+ 4 - 12
src/main/frontend/fs/nfs.cljs

@@ -10,8 +10,7 @@
             [frontend.config :as config]
             [frontend.state :as state]
             [frontend.handler.notification :as notification]
-            ["/frontend/utils" :as utils]
-            [frontend.encrypt :as encrypt]))
+            ["/frontend/utils" :as utils]))
 
 ;; We need to cache the file handles in the memory so that
 ;; the browser will not keep asking permissions.
@@ -58,10 +57,7 @@
 (defn- contents-matched?
   [disk-content db-content]
   (when (and (string? disk-content) (string? db-content))
-    (if (encrypt/encrypted-db? (state/get-current-repo))
-      (p/let [decrypted-content (encrypt/decrypt disk-content)]
-        (= (string/trim decrypted-content) (string/trim db-content)))
-      (p/resolved (= (string/trim disk-content) (string/trim db-content))))))
+    (p/resolved (= (string/trim disk-content) (string/trim db-content)))))
 
 (defrecord ^:large-vars/cleanup-todo Nfs []
   protocol/Fs
@@ -173,16 +169,12 @@
                          (not contents-matched?)
                          (not (contains? #{"excalidraw" "edn" "css"} ext))
                          (not (string/includes? path "/.recycle/")))
-                      (p/let [local-content (encrypt/decrypt local-content)]
-                        (state/pub-event! [:file/not-matched-from-disk path local-content content]))
+                      (state/pub-event! [:file/not-matched-from-disk path local-content content])
                       (p/let [_ (verify-permission repo file-handle true)
                               _ (utils/writeFile file-handle content)
                               file (.getFile file-handle)]
                         (when file
-                          (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
-                                            (encrypt/decrypt content)
-                                            content)]
-                            (db/set-file-content! repo path content))
+                          (db/set-file-content! repo path content)
                           (nfs-saved-handler repo path file))))))
                 (p/catch (fn [e]
                            (js/console.error e))))

+ 4 - 12
src/main/frontend/fs/node.cljs

@@ -9,8 +9,7 @@
             [frontend.util :as util]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
-            [promesa.core :as p]
-            [frontend.encrypt :as encrypt]))
+            [promesa.core :as p]))
 
 (defn concat-path
   [dir path]
@@ -29,10 +28,7 @@
 (defn- contents-matched?
   [disk-content db-content]
   (when (and (string? disk-content) (string? db-content))
-    (if (encrypt/encrypted-db? (state/get-current-repo))
-      (p/let [decrypted-content (encrypt/decrypt disk-content)]
-        (= (string/trim decrypted-content) (string/trim db-content)))
-      (p/resolved (= (string/trim disk-content) (string/trim db-content))))))
+    (p/resolved (= (string/trim disk-content) (string/trim db-content)))))
 
 (defn- write-file-impl!
   [this repo dir path content {:keys [ok-handler error-handler old-content skip-compare?]} stat]
@@ -61,8 +57,7 @@
          (not contents-matched?)
          (not (contains? #{"excalidraw" "edn" "css"} ext))
          (not (string/includes? path "/.recycle/")))
-        (p/let [disk-content (encrypt/decrypt disk-content)]
-          (state/pub-event! [:file/not-matched-from-disk path disk-content content]))
+        (state/pub-event! [:file/not-matched-from-disk path disk-content content])
 
         :else
         (->
@@ -71,10 +66,7 @@
            (when-not contents-matched?
              (ipc/ipc "backupDbFile" (config/get-local-dir repo) path disk-content content))
            (db/set-file-last-modified-at! repo path mtime)
-           (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
-                             (encrypt/decrypt content)
-                             content)]
-             (db/set-file-content! repo path content))
+           (db/set-file-content! repo path content)
            (when ok-handler
              (ok-handler repo path result))
            result)

+ 270 - 221
src/main/frontend/fs/sync.cljs

@@ -20,6 +20,7 @@
             [frontend.mobile.util :as mobile-util]
             [frontend.util :as util]
             [frontend.util.persist-var :as persist-var]
+            [frontend.util.fs :as fs-util]
             [frontend.handler.notification :as notification]
             [frontend.context.i18n :refer [t]]
             [frontend.diff :as diff]
@@ -298,9 +299,9 @@
 (defn <request [api-name & args]
   (let [name (str api-name (.now js/Date))]
     (go (swap! *on-flying-request conj name)
-      (let [r (<! (apply <request* api-name args))]
-        (swap! *on-flying-request disj name)
-        r))))
+        (let [r (<! (apply <request* api-name args))]
+          (swap! *on-flying-request disj name)
+          r))))
 
 (defn- remove-dir-prefix [dir path]
   (let [r (string/replace path (js/RegExp. (str "^" (gstring/regExpEscape dir))) "")]
@@ -339,7 +340,7 @@
   (-stop! [this]))
 (defprotocol IStopped?
   (-stopped? [this]))
-;from-path, to-path is relative path
+                                        ;from-path, to-path is relative path
 (deftype FileTxn [from-path to-path updated? deleted? txid checksum]
   Object
   (renamed? [_]
@@ -384,19 +385,19 @@
   (let [update? (= "update_files" TXType)
         delete? (= "delete_files" TXType)
         update-xf
-                (comp
-                 (remove #(or (empty? (first %))
-                              (empty? (last %))))
-                 (map #(->FileTxn (first %) (first %) update? delete? TXId (last %))))
+        (comp
+         (remove #(or (empty? (first %))
+                      (empty? (last %))))
+         (map #(->FileTxn (first %) (first %) update? delete? TXId (last %))))
         delete-xf
-                (comp
-                 (remove #(empty? (first %)))
-                 (map #(->FileTxn (first %) (first %) update? delete? TXId nil)))
+        (comp
+         (remove #(empty? (first %)))
+         (map #(->FileTxn (first %) (first %) update? delete? TXId nil)))
         rename-xf
-                (comp
-                 (remove #(or (empty? (first %))
-                              (empty? (second %))))
-                 (map #(->FileTxn (second %) (first %) false false TXId nil)))
+        (comp
+         (remove #(or (empty? (first %))
+                      (empty? (second %))))
+         (map #(->FileTxn (second %) (first %) false false TXId nil)))
         xf (case TXType
              "delete_files" delete-xf
              "update_files" update-xf
@@ -462,22 +463,57 @@
    (contains-path? (relative-path path))
    (boolean)))
 
-(defn- diffs->partitioned-filetxns
+(defn- filter-download-files-with-reserved-chars
+  "Skip downloading file paths with reserved chars."
+  [files]
+  (let [reserved-files (filter
+                        #(fs-util/include-reserved-chars? (-relative-path %))
+                        files)]
+    (when (seq reserved-files)
+      (state/pub-event! [:ui/notify-skipped-downloading-files
+                         (map -relative-path reserved-files)])
+      (prn "Skipped downloading those file paths with reserved chars: "
+           (map -relative-path reserved-files))
+      )
+    (remove
+     #(fs-util/include-reserved-chars? (-relative-path %))
+     files)))
+
+(defn- filter-upload-files-with-reserved-chars
+  "Remove upoading file paths with reserved chars."
+  [paths]
+  (let [path-string? (string? (first paths))
+        f (if path-string?
+            fs-util/include-reserved-chars?
+            #(fs-util/include-reserved-chars? (-relative-path %)))
+        reserved-paths (filter f paths)]
+    (when (seq reserved-paths)
+      (let [paths (if path-string? reserved-paths (map -relative-path reserved-paths))]
+        (state/pub-event! [:ui/notify-files-with-reserved-chars paths])
+        (prn "Skipped uploading those file paths with reserved chars: " paths)))
+    (vec (remove f paths))))
+
+(defn- diffs->filetxns
   "transducer.
   1. diff -> `FileTxn` , see also `<get-diff`
   2. distinct redundant update type filetxns
-  3. partition filetxns, each partition contains same type filetxns,
-     for update type, at most N items in each partition
-     for delete & rename type, only 1 item in each partition.
-  4. remove update or rename filetxns if they are deleted in later filetxns.
+  3. remove update or rename filetxns if they are deleted in later filetxns.
   NOTE: this xf should apply on reversed diffs sequence (sort by txid)"
-  [n]
+  []
   (comp
    (map diff->filetxns)
    cat
    (remove ignored?)
    distinct-update-filetxns-xf
-   remove-deleted-filetxns-xf
+   remove-deleted-filetxns-xf))
+
+(defn- diffs->partitioned-filetxns
+  "partition filetxns, each partition contains same type filetxns,
+   for update type, at most N items in each partition
+   for delete & rename type, only 1 item in each partition."
+  [n]
+  (comp
+   (diffs->filetxns)
    (partition-filetxns n)))
 
 (defn- filepath+checksum->diff
@@ -582,21 +618,21 @@
    #{} s1))
 
 (comment
- (defn map->FileMetadata [m]
-   (apply ->FileMetadata ((juxt :size :etag :path :encrypted-path :last-modified :remote? (constantly nil)) m)))
-
- (assert
-  (=
-   #{(map->FileMetadata {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2})}
-   (diff-file-metadata-sets
-    (into #{}
-          (map map->FileMetadata)
-          [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
-           {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2}])
-    (into #{}
-          (map map->FileMetadata)
-          [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
-           {:size 1 :etag 1 :path 2 :encrypted-path 2 :last-modified 1}])))))
+  (defn map->FileMetadata [m]
+    (apply ->FileMetadata ((juxt :size :etag :path :encrypted-path :last-modified :remote? (constantly nil)) m)))
+
+  (assert
+   (=
+    #{(map->FileMetadata {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2})}
+    (diff-file-metadata-sets
+     (into #{}
+           (map map->FileMetadata)
+           [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
+            {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2}])
+     (into #{}
+           (map map->FileMetadata)
+           [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
+            {:size 1 :etag 1 :path 2 :encrypted-path 2 :last-modified 1}])))))
 
 (extend-protocol IChecksum
   FileMetadata
@@ -1030,8 +1066,8 @@
      (go-loop []
        (let [{:keys [val stop]}
              (async/alt!
-              debug-print-sync-events-loop-stop-chan {:stop true}
-              out-ch ([v] {:val v}))]
+               debug-print-sync-events-loop-stop-chan {:stop true}
+               out-ch ([v] {:val v}))]
          (cond
            stop (do (async/unmix-all out-mix)
                     (doseq [[topic ch] topic&chs]
@@ -1047,28 +1083,28 @@
 
 
 (comment
- ;; sub one type event example:
- (def c1 (chan 10))
- (async/sub sync-events-publication :created-local-version-file c1)
- (offer! sync-events-chan {:event :created-local-version-file :data :xxx})
- (poll! c1)
-
- ;; sub multiple type events example:
- ;; sub :created-local-version-file and :finished-remote->local events,
- ;; output into channel c4-out
- (def c2 (chan 10))
- (def c3 (chan 10))
- (def c4-out (chan 10))
- (def mix-out (async/mix c4-out))
- (async/admix mix-out c2)
- (async/admix mix-out c3)
- (async/sub sync-events-publication :created-local-version-file c2)
- (async/sub sync-events-publication :finished-remote->local c3)
- (offer! sync-events-chan {:event :created-local-version-file :data :xxx})
- (offer! sync-events-chan {:event :finished-remote->local :data :xxx})
- (poll! c4-out)
- (poll! c4-out)
- )
+  ;; sub one type event example:
+  (def c1 (chan 10))
+  (async/sub sync-events-publication :created-local-version-file c1)
+  (offer! sync-events-chan {:event :created-local-version-file :data :xxx})
+  (poll! c1)
+
+  ;; sub multiple type events example:
+  ;; sub :created-local-version-file and :finished-remote->local events,
+  ;; output into channel c4-out
+  (def c2 (chan 10))
+  (def c3 (chan 10))
+  (def c4-out (chan 10))
+  (def mix-out (async/mix c4-out))
+  (async/admix mix-out c2)
+  (async/admix mix-out c3)
+  (async/sub sync-events-publication :created-local-version-file c2)
+  (async/sub sync-events-publication :finished-remote->local c3)
+  (offer! sync-events-chan {:event :created-local-version-file :data :xxx})
+  (offer! sync-events-chan {:event :finished-remote->local :data :xxx})
+  (poll! c4-out)
+  (poll! c4-out)
+  )
 
 ;;; sync events ends
 
@@ -1126,27 +1162,27 @@
       (let [file-meta-list      (transient #{})
             encrypted-path-list (transient [])
             exp-r
-                                (<!
-                                 (go-loop [continuation-token nil]
-                                   (let [r (<! (.<request this "get_all_files"
-                                                          (into
-                                                           {}
-                                                           (remove (comp nil? second)
-                                                                   {:GraphUUID graph-uuid :ContinuationToken continuation-token}))))]
-                                     (if (instance? ExceptionInfo r)
-                                       r
-                                       (let [next-continuation-token (:NextContinuationToken r)
-                                             objs                    (:Objects r)]
-                                         (apply conj! encrypted-path-list (map (comp remove-user-graph-uuid-prefix :Key) objs))
-                                         (apply conj! file-meta-list
-                                                (map
-                                                 #(hash-map :checksum (:checksum %)
-                                                            :encrypted-path (remove-user-graph-uuid-prefix (:Key %))
-                                                            :size (:Size %)
-                                                            :last-modified (:LastModified %))
-                                                 objs))
-                                         (when-not (empty? next-continuation-token)
-                                           (recur next-continuation-token)))))))]
+            (<!
+             (go-loop [continuation-token nil]
+               (let [r (<! (.<request this "get_all_files"
+                                      (into
+                                       {}
+                                       (remove (comp nil? second)
+                                               {:GraphUUID graph-uuid :ContinuationToken continuation-token}))))]
+                 (if (instance? ExceptionInfo r)
+                   r
+                   (let [next-continuation-token (:NextContinuationToken r)
+                         objs                    (:Objects r)]
+                     (apply conj! encrypted-path-list (map (comp remove-user-graph-uuid-prefix :Key) objs))
+                     (apply conj! file-meta-list
+                       (map
+                         #(hash-map :checksum (:checksum %)
+                                    :encrypted-path (remove-user-graph-uuid-prefix (:Key %))
+                                    :size (:Size %)
+                                    :last-modified (:LastModified %))
+                         objs))
+                     (when-not (empty? next-continuation-token)
+                       (recur next-continuation-token)))))))]
         (if (instance? ExceptionInfo exp-r)
           exp-r
           (let [file-meta-list*      (persistent! file-meta-list)
@@ -1212,58 +1248,58 @@
           (let [txns-with-encrypted-paths (mapv #(update % :path remove-user-graph-uuid-prefix) (:Transactions r))
                 encrypted-paths           (mapv :path txns-with-encrypted-paths)
                 encrypted-path->path-map
-                                          (zipmap
-                                           encrypted-paths
-                                           (<! (<decrypt-fnames rsapi graph-uuid encrypted-paths)))
+                (zipmap
+                 encrypted-paths
+                 (<! (<decrypt-fnames rsapi graph-uuid encrypted-paths)))
                 txns
-                                          (mapv
-                                           (fn [txn] (update txn :path #(get encrypted-path->path-map %)))
-                                           txns-with-encrypted-paths)]
+                (mapv
+                 (fn [txn] (update txn :path #(get encrypted-path->path-map %)))
+                 txns-with-encrypted-paths)]
             txns)))))
 
   (<get-diff [this graph-uuid from-txid]
-   ;; TODO: path in transactions should be relative path(now s3 key, which includes graph-uuid and user-uuid)
+    ;; TODO: path in transactions should be relative path(now s3 key, which includes graph-uuid and user-uuid)
     (go
       (let [r (<! (.<request this "get_diff" {:GraphUUID graph-uuid :FromTXId from-txid}))]
         (if (instance? ExceptionInfo r)
           r
           (let [txns-with-encrypted-paths (sort-by :TXId (:Transactions r))
                 txns-with-encrypted-paths*
-                                          (mapv
-                                           (fn [txn]
-                                             (assoc txn :TXContent
-                                                    (mapv
-                                                     (fn [[to-path from-path checksum]]
-                                                       [(remove-user-graph-uuid-prefix to-path)
-                                                        (some-> from-path remove-user-graph-uuid-prefix)
-                                                        checksum])
-                                                     (:TXContent txn))))
-                                           txns-with-encrypted-paths)
+                (mapv
+                 (fn [txn]
+                   (assoc txn :TXContent
+                          (mapv
+                           (fn [[to-path from-path checksum]]
+                             [(remove-user-graph-uuid-prefix to-path)
+                              (some-> from-path remove-user-graph-uuid-prefix)
+                              checksum])
+                           (:TXContent txn))))
+                 txns-with-encrypted-paths)
                 encrypted-paths
-                                          (mapcat
-                                           (fn [txn]
-                                             (remove
-                                              #(or (nil? %) (not (string/starts-with? % "e.")))
-                                              (mapcat
-                                               (fn [[to-path from-path _checksum]] [to-path from-path])
-                                               (:TXContent txn))))
-                                           txns-with-encrypted-paths*)
+                (mapcat
+                 (fn [txn]
+                   (remove
+                    #(or (nil? %) (not (string/starts-with? % "e.")))
+                    (mapcat
+                     (fn [[to-path from-path _checksum]] [to-path from-path])
+                     (:TXContent txn))))
+                 txns-with-encrypted-paths*)
                 encrypted-path->path-map
-                                          (zipmap
-                                           encrypted-paths
-                                           (<! (<decrypt-fnames rsapi graph-uuid encrypted-paths)))
+                (zipmap
+                 encrypted-paths
+                 (<! (<decrypt-fnames rsapi graph-uuid encrypted-paths)))
                 txns
-                                          (mapv
-                                           (fn [txn]
-                                             (assoc
-                                               txn :TXContent
-                                               (mapv
-                                                (fn [[to-path from-path checksum]]
-                                                  [(get encrypted-path->path-map to-path to-path)
-                                                   (some->> from-path (get encrypted-path->path-map))
-                                                   checksum])
-                                                (:TXContent txn))))
-                                           txns-with-encrypted-paths*)]
+                (mapv
+                 (fn [txn]
+                   (assoc
+                    txn :TXContent
+                    (mapv
+                     (fn [[to-path from-path checksum]]
+                       [(get encrypted-path->path-map to-path to-path)
+                        (some->> from-path (get encrypted-path->path-map))
+                        checksum])
+                     (:TXContent txn))))
+                 txns-with-encrypted-paths*)]
             [txns
              (:TXId (last txns))
              (:TXId (first txns))])))))
@@ -1399,12 +1435,12 @@
                       (.-deleted? e) :delete-filetxns
                       (.renamed? e)  :rename-filetxns)) filetxns)
         update-file-items (map
-                           (fn [filetxn]
-                             (let [path (relative-path filetxn)]
-                               {:remote->local-type :update
-                                :checksum (-checksum filetxn)
-                                :path path}))
-                           update-filetxns)
+                            (fn [filetxn]
+                              (let [path (relative-path filetxn)]
+                                {:remote->local-type :update
+                                 :checksum (-checksum filetxn)
+                                 :path path}))
+                            update-filetxns)
         rename-file-items (mapcat
                            (fn [^FileTxn filetxn]
                              (let [to-path (relative-path filetxn)
@@ -1417,12 +1453,12 @@
                                  :path from-path}]))
                            rename-filetxns)
         delete-file-items (map
-                           (fn [filetxn]
-                             (let [path (relative-path filetxn)]
-                               {:remote->local-type :delete
-                                :checksum (-checksum filetxn)
-                                :path path}))
-                           delete-filetxns)]
+                            (fn [filetxn]
+                              (let [path (relative-path filetxn)]
+                                {:remote->local-type :delete
+                                 :checksum (-checksum filetxn)
+                                 :path path}))
+                            delete-filetxns)]
     (set (concat update-file-items rename-file-items delete-file-items))))
 
 (defn- apply-filetxns
@@ -1457,8 +1493,8 @@
                      [recent-remote->local-file-item])
               (<! (<delete-local-files rsapi graph-uuid base-path [relative-p*]))
               (go (<! (timeout 5000))
-                (swap! *sync-state sync-state--remove-recent-remote->local-files
-                       [recent-remote->local-file-item])))))
+                  (swap! *sync-state sync-state--remove-recent-remote->local-files
+                         [recent-remote->local-file-item])))))
 
         (let [update-local-files-ch (<update-local-files rsapi graph-uuid base-path (map relative-path filetxns))
               r (<! (<with-pause update-local-files-ch *paused))]
@@ -1510,8 +1546,8 @@
                                                      (not (instance? ExceptionInfo r)))]
           ;; remove these recent-remote->local-file-items 5s later
           (go (<! (timeout 5000))
-            (swap! *sync-state sync-state--remove-recent-remote->local-files
-                   recent-remote->local-file-items))
+              (swap! *sync-state sync-state--remove-recent-remote->local-files
+                     recent-remote->local-file-items))
           (cond
             (instance? ExceptionInfo r) r
             @*paused                    {:pause true}
@@ -1611,7 +1647,7 @@
           path (relative-path e)]
       {:remote->local-type tp
        :checksum (if (= tp :delete) nil
-                                    (val (first (<! (get-local-files-checksum graph-uuid (.-dir e) [path])))))
+                     (val (first (<! (get-local-files-checksum graph-uuid (.-dir e) [path])))))
        :path path})))
 
 (defn- distinct-file-change-events-xf
@@ -1672,8 +1708,8 @@
     (go-loop []
       (let [{:keys [rename-event local-change]}
             (async/alt!
-             rename-page-event-chan ([v] {:rename-event v}) ;; {:repo X :old-path X :new-path}
-             local-changes-chan ([v] {:local-change v}))]
+              rename-page-event-chan ([v] {:rename-event v}) ;; {:repo X :old-path X :new-path}
+              local-changes-chan ([v] {:local-change v}))]
         (cond
           rename-event
           (let [repo-dir (config/get-repo-dir (:repo rename-event))
@@ -1686,7 +1722,7 @@
             (swap! *rename-events conj k1 k2)
             ;; remove rename-events after 2s
             (go (<! (timeout 3000))
-              (swap! *rename-events disj k1 k2))
+                (swap! *rename-events disj k1 k2))
             ;; add 2 simulated file-watcher events
             (>! ch (->FileChangeEvent "unlink" repo-dir (:old-path rename-event*) nil nil))
             (>! ch (->FileChangeEvent "add" repo-dir (:new-path rename-event*)
@@ -1831,8 +1867,10 @@
                                :input-pwd-remote
                                {:GraphUUID graph-uuid
                                 :init-graph-keys init-graph-keys
-                                :after-input-password #(go (<! (restore-pwd! graph-uuid))
-                                                         (offer! <restored-pwd {:graph-uuid graph-uuid :value true}))}])
+                                :after-input-password (fn [pwd]
+                                                        (when pwd
+                                                          (swap! pwd-map assoc-in [graph-uuid :pwd] pwd)
+                                                          (offer! <restored-pwd {:graph-uuid graph-uuid :value true})))}])
             nil)
         pwd))))
 
@@ -1855,7 +1893,6 @@
               (<! (<get-graph-encrypt-keys-memoize remoteapi graph-uuid))
               init-graph-keys (some-> (ex-data r) :err :status (= 404))
               pwd (<! (<ensure-pwd-exists! repo graph-uuid init-graph-keys))]
-
           (cond
             (not pwd)
             (do (println :debug "waiting password...")
@@ -1884,6 +1921,7 @@
               (if (= :recur next-state)
                 (recur)
                 next-state))
+
             :else
             ;; pwd, public-key, encrypted-private-key all exist
             (do (assert (and pwd public-key encrypted-private-key) {:encrypted-private-key encrypted-private-key
@@ -1893,7 +1931,6 @@
                 (if (get-in @pwd-map [graph-uuid :private-key])
                   (do (when (state/modal-opened?)
                         (state/set-state! [:ui/loading? :set-graph-password] false)
-                        (notification/show! "Password successfully matched" :success)
                         (state/close-modal!))
                       ::idle)
                   ;; bad pwd
@@ -2163,8 +2200,8 @@
           (do
             (swap! *sync-state #(sync-state-reset-full-remote->local-files % []))
             (<! (<update-graphs-txid! latest-txid graph-uuid user-uuid repo))
-              (reset! *txid latest-txid)
-              {:succ true})))))
+            (reset! *txid latest-txid)
+            {:succ true})))))
 
   IRemote->LocalSync
   (stop-remote->local! [_] (vreset! *stopped true))
@@ -2180,11 +2217,12 @@
                         {:need-remote->local-full-sync true})
 
                     (when (pos-int? latest-txid)
-                      (let [partitioned-filetxns (transduce (diffs->partitioned-filetxns download-batch-size)
+                      (let [filtered-diff-txns (-> (transduce (diffs->filetxns) conj '() (reverse diff-txns))
+                                                   filter-download-files-with-reserved-chars)
+                            partitioned-filetxns (transduce (partition-filetxns download-batch-size)
                                                             (completing (fn [r i] (conj r (reverse i)))) ;reverse
                                                             '()
-                                                            (reverse diff-txns))]
-                        ;; (swap! *sync-state #(sync-state-reset-full-remote->local-files % files))
+                                                            filtered-diff-txns)]
                         (put-sync-event! {:event :start
                                           :data  {:type       :remote->local
                                                   :graph-uuid graph-uuid
@@ -2235,11 +2273,12 @@
                                            :epoch (tc/to-epoch (t/now))}})
                   {:stop true})
               (do (println "[full-sync(remote->local)]" (count sorted-diff-remote-files) "files need to sync")
-                  (swap! *sync-state #(sync-state-reset-full-remote->local-files % sorted-diff-remote-files))
-                  (<! (.sync-files-remote->local!
-                       this (map (juxt relative-path -checksum)
-                                 sorted-diff-remote-files)
-                       latest-txid))))))))))
+                  (let [filtered-files (filter-download-files-with-reserved-chars sorted-diff-remote-files)]
+                    (swap! *sync-state #(sync-state-reset-full-remote->local-files % sorted-diff-remote-files))
+                    (<! (.sync-files-remote->local!
+                         this (map (juxt relative-path -checksum)
+                                filtered-files)
+                         latest-txid)))))))))))
 
 (defn- <file-changed?
   "return true when file changed compared with remote"
@@ -2346,23 +2385,23 @@
          local-files-meta-map))))
 
 (defrecord ^:large-vars/cleanup-todo
- Local->RemoteSyncer [user-uuid graph-uuid base-path repo *sync-state remoteapi
-                      ^:mutable rate *txid ^:mutable remote->local-syncer stop-chan *stopped *paused
+    Local->RemoteSyncer [user-uuid graph-uuid base-path repo *sync-state remoteapi
+                         ^:mutable rate *txid ^:mutable remote->local-syncer stop-chan *stopped *paused
                          ;; control chans
-                      private-immediately-local->remote-chan private-recent-edited-chan]
+                         private-immediately-local->remote-chan private-recent-edited-chan]
   Object
   (filter-file-change-events-fn [_]
     (fn [^FileChangeEvent e]
       (go (and (instance? FileChangeEvent e)
                (if-let [mtime (:mtime (.-stat e))]
-                   ;; if mtime is not nil, it should be after (- now 1min)
-                   ;; ignore events too early
+                 ;; if mtime is not nil, it should be after (- now 1min)
+                 ;; ignore events too early
                  (> (* 1000 mtime) (tc/to-long (t/minus (t/now) (t/minutes 1))))
                  true)
                (or (string/starts-with? (.-dir e) base-path)
                    (string/starts-with? (str "file://" (.-dir e)) base-path)) ; valid path prefix
                (not (ignored? e))     ;not ignored
-                 ;; download files will also trigger file-change-events, ignore them
+               ;; download files will also trigger file-change-events, ignore them
                (not (contains? (:recent-remote->local-files @*sync-state)
                                (<! (<file-change-event=>recent-remote->local-file-item
                                     graph-uuid e))))))))
@@ -2417,7 +2456,8 @@
                 _     (when (not= (count es**) (count es*))
                         (println :debug :filter-too-huge-files
                                  (mapv relative-path (set/difference (set es*) (set es**)))))
-                paths (sequence es->paths-xf es**)
+                paths (-> (sequence es->paths-xf es**)
+                          filter-upload-files-with-reserved-chars)
                 _     (println :sync-local->remote type paths)
                 r     (if (empty? paths)
                         (go @*txid)
@@ -2437,7 +2477,7 @@
                   {:need-sync-remote true})
 
               (need-reset-local-txid? r*) ;; TODO: this cond shouldn't be true,
-                ;; but some potential bugs cause local-txid > remote-txid
+              ;; but some potential bugs cause local-txid > remote-txid
               (let [remote-graph-info-or-ex (<! (<get-remote-graph remoteapi nil graph-uuid))
                     remote-txid             (:TXId remote-graph-info-or-ex)]
                 (if (or (instance? ExceptionInfo remote-graph-info-or-ex) (nil? remote-txid))
@@ -2460,7 +2500,7 @@
               succ?                   ; succ
               (do
                 (println "sync-local->remote! update txid" r*)
-                  ;; persist txid
+                ;; persist txid
                 (<! (<update-graphs-txid! r* graph-uuid user-uuid repo))
                 (reset! *txid r*)
                 {:succ true})
@@ -2507,16 +2547,17 @@
                 change-events
                 (sequence
                  (comp
-                    ;; convert to FileChangeEvent
+                  ;; convert to FileChangeEvent
                   (map #(->FileChangeEvent "change" base-path (.get-normalized-path ^FileMetadata %)
                                            {:size (:size %)} (:etag %)))
                   (remove ignored?))
                  diff-local-files)
-                distinct-change-events (distinct-file-change-events change-events)
+                distinct-change-events (-> (distinct-file-change-events change-events)
+                                           filter-upload-files-with-reserved-chars)
                 _                      (swap! *sync-state #(sync-state-reset-full-local->remote-files % distinct-change-events))
                 change-events-partitions
                 (sequence
-                   ;; partition FileChangeEvents
+                 ;; partition FileChangeEvents
                  (partition-file-change-events upload-batch-size)
                  distinct-change-events)]
             (println "[full-sync(local->remote)]"
@@ -2527,7 +2568,7 @@
                                       :graph-uuid graph-uuid
                                       :full-sync? true
                                       :epoch      (tc/to-epoch (t/now))}})
-              ;; 1. delete local files
+            ;; 1. delete local files
             (loop [[f & fs] delete-local-files]
               (when f
                 (let [relative-p (relative-path f)]
@@ -2543,7 +2584,7 @@
                                  [fake-recent-remote->local-file-item])))))
                 (recur fs)))
 
-              ;; 2. upload local files
+            ;; 2. upload local files
             (loop [es-partitions change-events-partitions]
               (if @*stopped
                 {:stop true}
@@ -2560,14 +2601,14 @@
 ;;; ### put all stuff together
 
 (defrecord ^:large-vars/cleanup-todo
-  SyncManager [graph-uuid base-path *sync-state
-               ^Local->RemoteSyncer local->remote-syncer ^Remote->LocalSyncer remote->local-syncer remoteapi
-               ^:mutable ratelimit-local-changes-chan
-               *txid ^:mutable state ^:mutable remote-change-chan ^:mutable *ws *stopped? *paused?
-               ^:mutable ops-chan
-               ;; control chans
-               private-full-sync-chan private-stop-sync-chan private-remote->local-sync-chan
-               private-remote->local-full-sync-chan private-pause-resume-chan]
+    SyncManager [graph-uuid base-path *sync-state
+                 ^Local->RemoteSyncer local->remote-syncer ^Remote->LocalSyncer remote->local-syncer remoteapi
+                 ^:mutable ratelimit-local-changes-chan
+                 *txid ^:mutable state ^:mutable remote-change-chan ^:mutable *ws *stopped? *paused?
+                 ^:mutable ops-chan
+                 ;; control chans
+                 private-full-sync-chan private-stop-sync-chan private-remote->local-sync-chan
+                 private-remote->local-full-sync-chan private-pause-resume-chan]
   Object
   (schedule [this next-state args reason]
     {:pre [(s/valid? ::state next-state)]}
@@ -2608,19 +2649,19 @@
     (go-loop []
       (let [{:keys [stop remote->local remote->local-full-sync local->remote-full-sync local->remote resume pause]}
             (async/alt!
-             private-stop-sync-chan {:stop true}
-             private-remote->local-full-sync-chan {:remote->local-full-sync true}
-             private-remote->local-sync-chan {:remote->local true}
-             private-full-sync-chan {:local->remote-full-sync true}
-             private-pause-resume-chan ([v] (if v {:resume true} {:pause true}))
-             remote-change-chan ([v] (println "remote change:" v) {:remote->local v})
-             ratelimit-local-changes-chan ([v]
-                                           (let [rest-v (util/drain-chan ratelimit-local-changes-chan)
-                                                 vs     (cons v rest-v)]
-                                             (println "local changes:" vs)
-                                             {:local->remote vs}))
-             (timeout (* 20 60 1000)) {:local->remote-full-sync true}
-             :priority true)]
+              private-stop-sync-chan {:stop true}
+              private-remote->local-full-sync-chan {:remote->local-full-sync true}
+              private-remote->local-sync-chan {:remote->local true}
+              private-full-sync-chan {:local->remote-full-sync true}
+              private-pause-resume-chan ([v] (if v {:resume true} {:pause true}))
+              remote-change-chan ([v] (println "remote change:" v) {:remote->local v})
+              ratelimit-local-changes-chan ([v]
+                                            (let [rest-v (util/drain-chan ratelimit-local-changes-chan)
+                                                  vs     (cons v rest-v)]
+                                              (println "local changes:" vs)
+                                              {:local->remote vs}))
+              (timeout (* 20 60 1000)) {:local->remote-full-sync true}
+              :priority true)]
         (cond
           stop
           (do (util/drain-chan ops-chan)
@@ -2813,13 +2854,13 @@
                 (.schedule this ::idle nil nil)))))))
 
   (local->remote [this {local-changes :local}]
-   ;; local-changes:: list of FileChangeEvent
+    ;; local-changes:: list of FileChangeEvent
     (assert (some? local-changes) local-changes)
     (go
       (let [distincted-local-changes (distinct-file-change-events local-changes)
             _ (swap! *sync-state #(sync-state-reset-full-local->remote-files % distincted-local-changes))
             change-events-partitions
-                                     (sequence (partition-file-change-events upload-batch-size) distincted-local-changes)
+            (sequence (partition-file-change-events upload-batch-size) distincted-local-changes)
             _ (put-sync-event! {:event :start
                                 :data  {:type       :local->remote
                                         :graph-uuid graph-uuid
@@ -2927,6 +2968,9 @@
     (reset! current-sm-graph-uuid graph-uuid)
     (sync-manager user-uuid graph-uuid base-path repo txid *sync-state)))
 
+;; Avoid sync reentrancy
+(defonce *sync-entered? (atom false))
+
 (defn <sync-stop []
   (go
     (when-let [sm ^SyncManager (state/get-file-sync-manager (state/get-current-file-sync-graph-uuid))]
@@ -2936,6 +2980,8 @@
 
       (<! (-stop! sm))
 
+      (reset! *sync-entered? false)
+
       (println "[SyncManager" (:graph-uuid sm) "]" "stopped"))
 
     (reset! current-sm-graph-uuid nil)))
@@ -2964,15 +3010,15 @@
   (go
     (let [r (<! (<list-remote-graphs remoteapi))
           result
-            (or
-             ;; if api call failed, assume this remote graph still exists
-             (instance? ExceptionInfo r)
-             (and
-              (contains? r :Graphs)
-              (->> (:Graphs r)
-                   (mapv :GraphUUID)
-                   set
-                   (#(contains? % local-graph-uuid)))))]
+          (or
+           ;; if api call failed, assume this remote graph still exists
+           (instance? ExceptionInfo r)
+           (and
+            (contains? r :Graphs)
+            (->> (:Graphs r)
+                 (mapv :GraphUUID)
+                 set
+                 (#(contains? % local-graph-uuid)))))]
 
       (when-not result
         (notification/show! (t :file-sync/graph-deleted) :warning false))
@@ -2994,26 +3040,28 @@
 
 (declare network-online-cursor)
 
-(defn sync-start
+(defn <sync-start
   []
   (go
-    (let [*sync-state                 (atom (sync-state))
-          current-user-uuid           (user/user-uuid)
-          ;; put @graph-uuid & get-current-repo together,
-          ;; prevent to get older repo dir and current graph-uuid.
-          _                           (<! (p->c (persist-var/-load graphs-txid)))
-          [user-uuid graph-uuid txid] @graphs-txid
-          txid                        (or txid 0)
-          repo                        (state/get-current-repo)]
-      (when (and (graph-sync-off? repo) @network-online-cursor)
-        (when (and user-uuid graph-uuid txid
+    (when (false? @*sync-entered?)
+      (reset! *sync-entered? true)
+      (let [*sync-state                 (atom (sync-state))
+            current-user-uuid           (user/user-uuid)
+            ;; put @graph-uuid & get-current-repo together,
+            ;; prevent to get older repo dir and current graph-uuid.
+            _                           (<! (p->c (persist-var/-load graphs-txid)))
+            [user-uuid graph-uuid txid] @graphs-txid
+            txid                        (or txid 0)
+            repo                        (state/get-current-repo)]
+        (when (and repo
+                   (graph-sync-off? repo) @network-online-cursor
+                   user-uuid graph-uuid txid
                    (user/logged-in?)
-                   repo
                    (not (config/demo-graph? repo)))
           (try
-            (when-some [sm (sync-manager-singleton current-user-uuid graph-uuid
-                                                   (config/get-repo-dir repo) repo
-                                                   txid *sync-state)]
+            (when-let [sm (sync-manager-singleton current-user-uuid graph-uuid
+                                                  (config/get-repo-dir repo) repo
+                                                  txid *sync-state)]
               (when (check-graph-belong-to-current-user current-user-uuid user-uuid)
                 (if-not (<! (<check-remote-graph-exists graph-uuid)) ; remote graph has been deleted
                   (clear-graphs-txid! repo)
@@ -3034,7 +3082,8 @@
                     (offer! full-sync-chan true)))))
             (catch :default e
               (prn "Sync start error: ")
-              (log/error :exception e))))))))
+              (log/error :exception e))))
+        (reset! *sync-entered? false)))))
 
 ;;; ### some add-watches
 
@@ -3047,7 +3096,7 @@
                (<sync-stop)
 
                (and (false? o) (true? n))
-               (sync-start)
+               (<sync-start)
 
                :else
                nil)))
@@ -3085,7 +3134,7 @@
 
 ;;; add-tap
 (comment
- (def *x (atom nil))
- (add-tap (fn [v] (reset! *x v)))
+  (def *x (atom nil))
+  (add-tap (fn [v] (reset! *x v)))
 
- )
+  )

+ 1 - 4
src/main/frontend/fs/watcher_handler.cljs

@@ -15,7 +15,6 @@
             [lambdaisland.glogi :as log]
             [promesa.core :as p]
             [frontend.state :as state]
-            [frontend.encrypt :as encrypt]
             [frontend.fs :as fs]))
 
 ;; all IPC paths must be normalized! (via gp-util/path-normalize)
@@ -56,9 +55,7 @@
           pages-metadata-path (config/get-pages-metadata-path)
           {:keys [mtime]} stat
           db-content (or (db/get-file repo path) "")]
-      (when (and (or content (contains? #{"unlink" "unlinkDir" "addDir"} type))
-                 (not (encrypt/content-encrypted? content))
-                 (not (:encryption/graph-parsing? @state/state)))
+      (when (or content (contains? #{"unlink" "unlinkDir" "addDir"} type))
         (cond
           (and (= "unlinkDir" type) dir)
           (state/pub-event! [:graph/dir-gone dir])

+ 1 - 10
src/main/frontend/handler/common.cljs

@@ -8,8 +8,7 @@
             [frontend.util :as util]
             [frontend.util.property :as property]
             [goog.object :as gobj]
-            ["ignore" :as Ignore]
-            [lambdaisland.glogi :as log]))
+            ["ignore" :as Ignore]))
 
 (defn copy-to-clipboard-without-id-property!
   [format raw-text html]
@@ -59,14 +58,6 @@
         (println error-message-or-handler))
       {})))
 
-(defn read-metadata!
-  [content]
-  (try
-    (reader/read-string content)
-    (catch :default e
-      (log/error :parse/metadata-failed e)
-      {})))
-
 (defn get-page-default-properties
   [page-name]
   {:title page-name

+ 56 - 18
src/main/frontend/handler/events.cljs

@@ -21,7 +21,6 @@
             [frontend.db.conn :as conn]
             [frontend.db.model :as db-model]
             [frontend.db.persist :as db-persist]
-            [frontend.encrypt :as encrypt]
             [frontend.extensions.srs :as srs]
             [frontend.fs :as fs]
             [frontend.fs.capacitor-fs :as capacitor-fs]
@@ -55,6 +54,7 @@
             [frontend.handler.file-sync :as file-sync-handler]
             [frontend.components.file-sync :as file-sync]
             [frontend.components.encryption :as encryption]
+            [frontend.components.conversion :as conversion-component]
             [goog.dom :as gdom]
             [logseq.db.schema :as db-schema]
             [promesa.core :as p]
@@ -68,7 +68,7 @@
 (defn- file-sync-restart! []
   (async/go (async/<! (p->c (persist-var/load-vars)))
             (async/<! (sync/<sync-stop))
-            (some-> (sync/sync-start) async/<!)))
+            (some-> (sync/<sync-start) async/<!)))
 
 (defn- file-sync-stop! []
   (async/go (async/<! (p->c (persist-var/load-vars)))
@@ -86,6 +86,7 @@
           (state/set-state! :user/info result)
           (let [status (if (user-handler/alpha-or-beta-user?) :welcome :unavailable)]
             (when (and (= status :welcome) (user-handler/logged-in?))
+              (file-sync-handler/set-sync-enabled! true)
               (async/<! (file-sync-handler/load-session-graphs))
               (p/let [repos (repo-handler/refresh-repos!)]
                 (when-let [repo (state/get-current-repo)]
@@ -93,7 +94,7 @@
                                     (vector? (:sync-meta %))
                                     (util/uuid-string? (first (:sync-meta %)))
                                     (util/uuid-string? (second (:sync-meta %)))) repos)
-                    (sync/sync-start)))))
+                    (sync/<sync-start)))))
             (ui-handler/re-render-root!)
             (file-sync/maybe-onboarding-show status)))))))
 
@@ -322,8 +323,7 @@
                         {:label "diff__cp"}))))
 
 (defmethod handle :modal/display-file-version [[_ path content hash]]
-  (p/let [content (when content (encrypt/decrypt content))]
-    (state/set-modal! #(git-component/file-specific-version path hash content))))
+  (state/set-modal! #(git-component/file-specific-version path hash content)))
 
 ;; Hook on a graph is ready to be shown to the user.
 ;; It's different from :graph/resotred, as :graph/restored is for window reloaded
@@ -579,7 +579,7 @@
       (async/<! (sync/<sync-stop))
       (state/set-state! [:ui/loading? :graph/create-remote?] true)
       (when-let [GraphUUID (get (async/<! (file-sync-handler/create-graph graph-name)) 2)]
-        (async/<! (sync/sync-start))
+        (async/<! (sync/<sync-start))
         (state/set-state! [:ui/loading? :graph/create-remote?] false)
         ;; update existing repo
         (state/set-repos! (map (fn [r]
@@ -621,18 +621,6 @@
                      (state/close-modal!)
                      (state/pub-event! [:graph/re-index])))]])))
 
-;; encryption
-(defmethod handle :modal/encryption-setup-dialog [[_ repo-url close-fn]]
-  (state/set-modal!
-   (encryption/encryption-setup-dialog repo-url close-fn)))
-
-(defmethod handle :modal/encryption-input-secret-dialog [[_ repo-url db-encrypted-secret close-fn]]
-  (state/set-modal!
-   (encryption/encryption-input-secret-dialog
-    repo-url
-    db-encrypted-secret
-    close-fn)))
-
 (defmethod handle :modal/remote-encryption-input-pw-dialog [[_ repo-url remote-graph-info type opts]]
   (state/set-modal!
    (encryption/input-password
@@ -724,6 +712,56 @@
     (when (= dir (config/get-repo-dir repo))
       (fs/watch-dir! dir))))
 
+(defmethod handle :ui/notify-files-with-reserved-chars [[_ paths]]
+  (sync/<sync-stop)
+
+  (notification/show!
+   [:div
+    [:div.mb-4
+     [:div.font-semibold.mb-4.text-xl "It seems that you're using the old filename format."]
+
+     [:div
+      [:p
+       "We suggest you upgrade now to avoid some potential bugs."]
+      [:p
+       "For example, the files below have reserved characters can't be synced on some platforms."]]
+     ]
+    (ui/button
+      "Upgrade filename format"
+      :on-click (fn []
+                  (notification/clear-all!)
+                  (state/set-modal!
+                  (fn [_] (conversion-component/files-breaking-changed))
+                  {:id :filename-format-panel :center? true})))
+    [:ol.my-2
+     (for [path paths]
+       [:li path])]]
+   :warning
+   false))
+
+(defmethod handle :ui/notify-skipped-downloading-files [[_ paths]]
+  (notification/show!
+   [:div
+    [:div.mb-4
+     [:div.font-semibold.mb-4.text-xl "It seems that you're using the old filename format."]
+     [:p
+      "The files below that have reserved characters can't be saved on this device."]
+     [:div.overflow-y-auto.max-h-96
+      [:ol.my-2
+       (for [path paths]
+         [:li path])]]
+
+     [:div
+      [:p
+       "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
+       "To solve this problem, we suggest you upgrade the filename format (on Settings > Advanced > Filename format > click EDIT button) in other devices to avoid more potential bugs."]]]]
+   :warning
+   false))
+
 (defmethod handle :graph/setup-a-repo [[_ opts]]
   (let [opts' (merge {:picked-root-fn #(state/close-modal!)
                       :native-icloud? (not (string/blank? (state/get-icloud-container-root-url)))

+ 2 - 2
src/main/frontend/handler/file.cljs

@@ -221,11 +221,11 @@
       (fs/watch-dir! dir))))
 
 (defn create-metadata-file
-  [repo-url encrypted?]
+  [repo-url]
   (let [repo-dir (config/get-repo-dir repo-url)
         path (str config/app-name "/" config/metadata-file)
         file-path (str "/" path)
-        default-content (if encrypted? "{:db/encrypted? true}" "{}")]
+        default-content "{}"]
     (p/let [_ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir config/app-name))
             file-exists? (fs/create-if-not-exists repo-url repo-dir file-path default-content)]
       (when-not file-exists?

+ 9 - 2
src/main/frontend/handler/file_sync.cljs

@@ -13,7 +13,9 @@
             [frontend.handler.user :as user]
             [frontend.fs :as fs]
             [cljs-time.coerce :as tc]
-            [cljs-time.core :as t]))
+            [cljs-time.core :as t]
+            [frontend.storage :as storage]
+            [logseq.graph-parser.util :as gp-util]))
 
 (def *beta-unavailable? (volatile! false))
 
@@ -150,7 +152,7 @@
     (when-let [path (:file/path (db/entity file-id))]
       (let [base-path (config/get-repo-dir (state/get-current-repo))
             base-path (if (string/starts-with? base-path "file://")
-                        (js/decodeURIComponent base-path)
+                        (gp-util/safe-decode-uri-component base-path)
                         base-path)
             path*     (string/replace-first (string/replace-first path base-path "") #"^/" "")]
         (go
@@ -234,3 +236,8 @@
           (= mins 1) "1 min left"
           (> mins 30) "calculating..."
           :else (str mins " mins left"))))))
+
+(defn set-sync-enabled!
+  [value]
+  (storage/set :logseq-sync-enabled value)
+  (state/set-state! :feature/enable-sync? value))

+ 2 - 12
src/main/frontend/handler/metadata.cljs

@@ -2,7 +2,6 @@
   "System-component-like ns that manages writing to pages-metadata.edn"
   (:require [cljs.reader :as reader]
             [cljs.pprint]
-            [clojure.string :as string]
             [datascript.db :as ddb]
             [frontend.config :as config]
             [frontend.db :as db]
@@ -17,10 +16,9 @@
 (defn set-metadata!
   [k v]
   (when-let [repo (state/get-current-repo)]
-    (let [encrypted? (= k :db/encrypted-secret)
-          path (config/get-metadata-path)
+    (let [path (config/get-metadata-path)
           file-content (db/get-file path)]
-      (p/let [_ (file-handler/create-metadata-file repo false)]
+      (p/let [_ (file-handler/create-metadata-file repo)]
         (let [metadata-str (or file-content default-metadata-str)
               metadata (try
                          (reader/read-string metadata-str)
@@ -34,9 +32,6 @@
                              :else
                              (let [ks (if (vector? k) k [k])]
                                (assoc-in metadata ks v)))
-              new-metadata (if encrypted?
-                             (assoc new-metadata :db/encrypted? true)
-                             new-metadata)
               new-content (pr-str new-metadata)]
           (file-handler/set-file-content! repo path new-content))))))
 
@@ -58,11 +53,6 @@
                           new-content
                           {}))))))
 
-(defn set-db-encrypted-secret!
-  [encrypted-secret]
-  (when-not (string/blank? encrypted-secret)
-    (set-metadata! :db/encrypted-secret encrypted-secret)))
-
 (defn- handler-properties!
   [all-properties properties-tx]
   (reduce

+ 1 - 1
src/main/frontend/handler/paste.cljs

@@ -69,7 +69,7 @@
 (defn- get-whiteboard-tldr-from-text
   [text]
   (when-let [matched-text (util/safe-re-find #"<whiteboard-tldr>(.*)</whiteboard-tldr>" text)]
-    (try-parse-as-json (js/decodeURIComponent (second matched-text)))))
+    (try-parse-as-json (gp-util/safe-decode-uri-component (second matched-text)))))
 
 (defn- get-whiteboard-shape-refs-text
   [text]

+ 20 - 46
src/main/frontend/handler/repo.cljs

@@ -32,7 +32,6 @@
             [electron.ipc :as ipc]
             [cljs-bean.core :as bean]
             [clojure.core.async :as async]
-            [frontend.encrypt :as encrypt]
             [frontend.mobile.util :as mobile-util]
             [medley.core :as medley]))
 
@@ -123,19 +122,17 @@
             (ui-handler/re-render-root!)))))))
 
 (defn create-default-files!
-  ([repo-url]
-   (create-default-files! repo-url false))
-  ([repo-url encrypted?]
-   (spec/validate :repos/url repo-url)
-   (let [repo-dir (config/get-repo-dir repo-url)]
-     (p/let [_ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir config/app-name))
-             _ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir (str config/app-name "/" config/recycle-dir)))
-             _ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir (config/get-journals-directory)))
-             _ (file-handler/create-metadata-file repo-url encrypted?)
-             _ (repo-config-handler/create-config-file-if-not-exists repo-url)
-             _ (create-contents-file repo-url)
-             _ (create-custom-theme repo-url)]
-       (state/pub-event! [:page/create-today-journal repo-url])))))
+  [repo-url]
+  (spec/validate :repos/url repo-url)
+  (let [repo-dir (config/get-repo-dir repo-url)]
+    (p/let [_ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir config/app-name))
+            _ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir (str config/app-name "/" config/recycle-dir)))
+            _ (fs/mkdir-if-not-exists (util/safe-path-join repo-dir (config/get-journals-directory)))
+            _ (file-handler/create-metadata-file repo-url)
+            _ (repo-config-handler/create-config-file-if-not-exists repo-url)
+            _ (create-contents-file repo-url)
+            _ (create-custom-theme repo-url)]
+      (state/pub-event! [:page/create-today-journal repo-url]))))
 
 (defn- load-pages-metadata!
   "force?: if set true, skip the metadata timestamp range check"
@@ -205,13 +202,10 @@
       nil)))
 
 (defn- after-parse
-  [repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan]
+  [repo-url files file-paths re-render? re-render-opts opts graph-added-chan]
   (load-pages-metadata! repo-url file-paths files true)
   (when (or (:new-graph? opts) (not (:refresh? opts)))
-    (if (and (not db-encrypted?) (state/enable-encryption? repo-url))
-      (state/pub-event! [:modal/encryption-setup-dialog repo-url
-                         #(create-default-files! repo-url %)])
-      (create-default-files! repo-url db-encrypted?)))
+    (create-default-files! repo-url))
   (when re-render?
     (ui-handler/re-render-root! re-render-opts))
   (state/pub-event! [:graph/added repo-url opts])
@@ -223,7 +217,7 @@
   (async/offer! graph-added-chan true))
 
 (defn- parse-files-and-create-default-files-inner!
-  [repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts]
+  [repo-url files delete-files delete-blocks file-paths re-render? re-render-opts opts]
   (let [supported-files (graph-parser/filter-files files)
         delete-data (->> (concat delete-files delete-blocks)
                          (remove nil?))
@@ -245,7 +239,7 @@
           (parse-and-load-file! repo-url file (assoc
                                                (select-keys opts [:new-graph? :verbose])
                                                :skip-db-transact? false)))
-        (after-parse repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan))
+        (after-parse repo-url files file-paths re-render? re-render-opts opts graph-added-chan))
       (async/go-loop [tx []]
         (if-let [item (async/<! chan)]
           (let [[idx file] item
@@ -273,38 +267,18 @@
               (recur tx')))
           (do
             (when (seq tx) (db/transact! repo-url tx {:from-disk? true}))
-            (after-parse repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan)))))
+            (after-parse repo-url files file-paths re-render? re-render-opts opts graph-added-chan)))))
     graph-added-chan))
 
 (defn- parse-files-and-create-default-files!
-  [repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts]
-  (if db-encrypted?
-    (p/let [files (p/all
-                   (map (fn [file]
-                          (p/let [content (encrypt/decrypt (:file/content file))]
-                            (assoc file :file/content content)))
-                     files))]
-      (parse-files-and-create-default-files-inner! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts))
-    (parse-files-and-create-default-files-inner! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts)))
+  [repo-url files delete-files delete-blocks file-paths re-render? re-render-opts opts]
+  (parse-files-and-create-default-files-inner! repo-url files delete-files delete-blocks file-paths re-render? re-render-opts opts))
 
 (defn parse-files-and-load-to-db!
   [repo-url files {:keys [delete-files delete-blocks re-render? re-render-opts _refresh?] :as opts
                    :or {re-render? true}}]
-  (let [file-paths (map :file/path files)
-        metadata-file (config/get-metadata-path)
-        metadata-content (some #(when (= (:file/path %) metadata-file)
-                                  (:file/content %)) files)
-        metadata (when metadata-content
-                   (common-handler/read-metadata! metadata-content))
-        db-encrypted? (:db/encrypted? metadata)
-        db-encrypted-secret (if db-encrypted? (:db/encrypted-secret metadata) nil)]
-    (if db-encrypted?
-      (let [close-fn #(parse-files-and-create-default-files! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts)]
-        (state/set-state! :encryption/graph-parsing? true)
-        (state/pub-event! [:modal/encryption-input-secret-dialog repo-url
-                           db-encrypted-secret
-                           close-fn]))
-      (parse-files-and-create-default-files! repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts))))
+  (let [file-paths (map :file/path files)]
+    (parse-files-and-create-default-files! repo-url files delete-files delete-blocks file-paths re-render? re-render-opts opts)))
 
 (defn load-repo-to-db!
   [repo-url {:keys [diffs nfs-files refresh? new-graph? empty-graph?]}]

+ 7 - 6
src/main/frontend/handler/web/nfs.cljs

@@ -7,7 +7,6 @@
             [clojure.string :as string]
             [frontend.config :as config]
             [frontend.db :as db]
-            [frontend.encrypt :as encrypt]
             [frontend.fs :as fs]
             [frontend.fs.nfs :as nfs]
             [frontend.handler.common :as common-handler]
@@ -24,7 +23,8 @@
             [lambdaisland.glogi :as log]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.util :as gp-util]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [frontend.fs.capacitor-fs :as capacitor-fs]))
 
 (defn remove-ignore-files
   [files dir-name nfs?]
@@ -153,6 +153,9 @@
               dir-name (if nfs?
                          (gobj/get root-handle "name")
                          root-handle)
+              dir-name (if (mobile-util/native-platform?)
+                         (capacitor-fs/normalize-file-protocol-path "" dir-name)
+                         dir-name)
               repo (str config/local-db-prefix dir-name)
               _ (state/set-loading-files! repo true)
               _ (when-not (or (state/home?) (state/setups-picker?))
@@ -188,8 +191,7 @@
             (-> (p/all (map (fn [file]
                               (p/let [content (if nfs?
                                                 (.text (:file/file file))
-                                                (:file/content file))
-                                      content (encrypt/decrypt content)]
+                                                (:file/content file))]
                                 (assoc file :file/content content))) markup-files))
                 (p/then (fn [result]
                           (p/let [files (map #(dissoc % :file/file) result)
@@ -295,8 +297,7 @@
                       (when-let [file (get-file-f path new-files)]
                         (p/let [content (if nfs?
                                           (.text (:file/file file))
-                                          (:file/content file))
-                                content (encrypt/decrypt content)]
+                                          (:file/content file))]
                           (assoc file :file/content content)))) added-or-modified))
         (p/then (fn [result]
                   (let [files (map #(dissoc % :file/file :file/handle) result)

+ 1 - 1
src/main/frontend/mobile/graph_picker.cljs

@@ -59,7 +59,7 @@
                                                  (state/get-local-container-root-url))]
                                  (-> (validate-graph-dirname root graph-name)
                                      (p/then (fn [graph-path]
-                                               (-> (fs/mkdir! graph-path)
+                                               (-> (fs/mkdir-if-not-exists graph-path)
                                                    (p/then
                                                     (fn []
                                                       (web-nfs/ls-dir-files-with-path!

+ 4 - 4
src/main/frontend/mobile/intent.cljs

@@ -77,12 +77,12 @@
     (-> (string/replace template "{time}" time)
         (string/replace "{url}" (or url "")))))
 
-(defn- embed-text-file 
-  "Store external content with url into Logseq repo" 
+(defn- embed-text-file
+  "Store external content with url into Logseq repo"
   [url title]
   (p/let [time (date/get-current-time)
           title (some-> (or title (path/basename url))
-                        js/decodeURIComponent
+                        gp-util/safe-decode-uri-component
                         util/node-path.name
                         ;; make the title more user friendly
                         gp-util/page-name-sanity)
@@ -148,7 +148,7 @@
 
                       :else
                       (if (mobile-util/native-ios?)
-                        (js/decodeURIComponent v)
+                        (gp-util/safe-decode-uri-component v)
                         v))])))
 
 (defn handle-result [result]

+ 0 - 4
src/main/frontend/state.cljs

@@ -649,10 +649,6 @@ Similar to re-frame subscriptions"
   []
   (:editor/logical-outdenting? (sub-config)))
 
-(defn enable-encryption?
-  [repo]
-  (:feature/enable-encryption? (sub-config repo)))
-
 (defn doc-mode-enter-for-new-line?
   []
   (and (document-mode?)

+ 1 - 1
src/main/frontend/util.cljc

@@ -509,7 +509,7 @@
    (defn safe-path-join [prefix & paths]
      (let [path (apply node-path.join (cons prefix paths))]
        (if (and (electron?) (gstring/caseInsensitiveStartsWith path "file://"))
-         (js/decodeURIComponent (subs path 7))
+         (gp-util/safe-decode-uri-component (subs path 7))
          path))))
 
 (defn trim-safe

+ 1 - 1
src/main/frontend/util/fs.cljs

@@ -74,7 +74,7 @@
   (re-pattern (str "[" multiplatform-reserved-chars "]+")))
 
 (defn include-reserved-chars?
-  "Includes reserved charcters that would broken FS"
+  "Includes reserved characters that would broken FS"
   [s]
   (util/safe-re-find reserved-chars-pattern s))
 

+ 24 - 23
tldraw/apps/tldraw-logseq/src/lib/shapes/LogseqPortalShape.tsx

@@ -83,7 +83,10 @@ const LogseqPortalShapeHeader = observer(
     opacity: number
     children: React.ReactNode
   }) => {
-    const bgColor = getComputedColor(fill, 'background')
+    const bgColor =
+      fill !== 'var(--ls-secondary-background-color)'
+        ? getComputedColor(fill, 'background')
+        : 'var(--ls-tertiary-background-color)'
 
     return (
       <div
@@ -95,12 +98,7 @@ const LogseqPortalShapeHeader = observer(
           className="absolute inset-0 tl-logseq-portal-header-bg"
           style={{
             opacity,
-            background:
-              type === 'P'
-                ? bgColor
-                : `linear-gradient(0deg, var(--ls-highlight-color-${
-                    fill ? fill : 'default'
-                  }), ${bgColor}`,
+            background: type === 'P' ? bgColor : `linear-gradient(0deg, transparent, ${bgColor}`,
           }}
         ></div>
         <div className="relative">{children}</div>
@@ -736,26 +734,29 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
     }, [this.initialHeightCalculated])
 
     return (
-      <div
-        ref={cpRefContainer}
-        className="relative tl-logseq-cp-container"
-        style={{ overflow: this.props.isAutoResizing ? 'visible' : 'auto' }}
-      >
+      <>
         <div
           className="absolute inset-0 tl-logseq-cp-container-bg"
           style={{
-            background: fill
-              ? `var(--ls-highlight-color-${fill})`
-              : 'var(--ls-secondary-background-color)',
+            background:
+              fill && fill !== 'var(--ls-secondary-background-color)'
+                ? `var(--ls-highlight-color-${fill})`
+                : 'var(--ls-secondary-background-color)',
             opacity,
           }}
         ></div>
-        {this.props.blockType === 'B' && this.props.compact ? (
-          <Block blockId={pageId} />
-        ) : (
-          <Page pageName={pageId} />
-        )}
-      </div>
+        <div
+          ref={cpRefContainer}
+          className="relative tl-logseq-cp-container"
+          style={{ overflow: this.props.isAutoResizing ? 'visible' : 'auto' }}
+        >
+          {this.props.blockType === 'B' && this.props.compact ? (
+            <Block blockId={pageId} />
+          ) : (
+            <Page pageName={pageId} />
+          )}
+        </div>
+      </>
     )
   })
 
@@ -938,7 +939,7 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
       <>
         <rect
           fill={
-            this.props.fill
+            this.props.fill && this.props.fill !== 'var(--ls-secondary-background-color)'
               ? `var(--ls-highlight-color-${this.props.fill})`
               : 'var(--ls-secondary-background-color)'
           }
@@ -953,7 +954,7 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
         {!this.props.compact && (
           <rect
             fill={
-              this.props.fill
+              this.props.fill && this.props.fill !== 'var(--ls-secondary-background-color)'
                 ? getComputedColor(this.props.fill, 'background')
                 : 'var(--ls-tertiary-background-color)'
             }

+ 2 - 2
tldraw/apps/tldraw-logseq/src/styles.css

@@ -700,7 +700,7 @@ button.tl-select-input-trigger {
 
   height: 40px;
   flex-shrink: 0;
-
+  z-index: 1;
   color: var(--ls-title-text-color);
   border-top-left-radius: 8px;
   border-top-right-radius: 8px;
@@ -926,7 +926,7 @@ html[data-theme='dark'] {
 }
 
 .tl-color-bg {
-  @apply w-full h-full rounded-sm;
+  @apply flex justify-center items-center w-full h-full rounded-sm;
 }
 
 .tl-slider-root {

+ 19 - 0
yarn.lock

@@ -6447,6 +6447,13 @@ safer-buffer@^2.1.0:
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
 
[email protected]:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378"
+  integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==
+  dependencies:
+    truncate-utf8-bytes "^1.0.0"
+
 [email protected]:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.4.tgz#74b6d33c9ae1e001510f179a91168588f1aedaa9"
@@ -7280,6 +7287,13 @@ trough@^1.0.0:
   resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
   integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
 
+truncate-utf8-bytes@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b"
+  integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==
+  dependencies:
+    utf8-byte-length "^1.0.1"
+
 tslib@^1.9.3:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
@@ -7525,6 +7539,11 @@ use@^3.1.0:
   resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
   integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
 
+utf8-byte-length@^1.0.1:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
+  integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==
+
 util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"