Sfoglia il codice sorgente

refactor(fs): rewrite url fn

Andelf 2 anni fa
parent
commit
3f467d72ec

+ 10 - 8
src/main/frontend/components/file.cljs

@@ -5,21 +5,22 @@
             [datascript.core :as d]
             [datascript.core :as d]
             [frontend.components.lazy-editor :as lazy-editor]
             [frontend.components.lazy-editor :as lazy-editor]
             [frontend.components.svg :as svg]
             [frontend.components.svg :as svg]
+            [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.date :as date]
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db :as db]
+            [frontend.fs :as fs]
             [frontend.handler.export :as export-handler]
             [frontend.handler.export :as export-handler]
             [frontend.state :as state]
             [frontend.state :as state]
-            [frontend.util :as util]
-            [frontend.fs :as fs]
-            [frontend.config :as config]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
+            [frontend.util :as util]
+            [goog.object :as gobj]
+            [goog.string :as gstring]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util :as gp-util]
-            [goog.object :as gobj]
+            [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
-            [rum.core :as rum]
-            [promesa.core :as p]))
+            [rum.core :as rum]))
 
 
 (defn- get-path
 (defn- get-path
   [state]
   [state]
@@ -29,7 +30,8 @@
 (rum/defc files-all < rum/reactive
 (rum/defc files-all < rum/reactive
   []
   []
   (when-let [current-repo (state/sub :git/current-repo)]
   (when-let [current-repo (state/sub :git/current-repo)]
-    (let [files (db/get-files current-repo)
+    (let [files (db/get-files current-repo) ; [[string]]
+          files (sort-by first gstring/intAwareCompare files)
           mobile? (util/mobile?)]
           mobile? (util/mobile?)]
       [:table.table-auto
       [:table.table-auto
        [:thead
        [:thead
@@ -103,7 +105,7 @@
     [:div.file {:id (str "file-edit-wrapper-" random-id)
     [:div.file {:id (str "file-edit-wrapper-" random-id)
                 :key path}
                 :key path}
      [:h1.title
      [:h1.title
-      [:bdi (js/decodeURI path)]]
+      [:bdi path]]
      (when original-name
      (when original-name
        [:div.text-sm.mb-4.ml-1 "Page: "
        [:div.text-sm.mb-4.ml-1 "Page: "
         [:a.bg-base-2.p-1.ml-1 {:style {:border-radius 4}
         [:a.bg-base-2.p-1.ml-1 {:style {:border-radius 4}

+ 5 - 4
src/main/frontend/components/repo.cljs

@@ -38,7 +38,7 @@
                graph-name (text-util/get-graph-name-from-path local-dir)]
                graph-name (text-util/get-graph-name-from-path local-dir)]
            [:a.flex.items-center {:title    local-dir
            [:a.flex.items-center {:title    local-dir
                                   :on-click #(on-click graph)}
                                   :on-click #(on-click graph)}
-            [:span graph-name (and GraphName [:strong.px-1 "(" GraphName ")"])]
+            [:span graph-name (when GraphName [:strong.px-1 "(" GraphName ")"])]
             (when remote? [:strong.pr-1.flex.items-center (ui/icon "cloud")])])
             (when remote? [:strong.pr-1.flex.items-center (ui/icon "cloud")])])
 
 
          [:a.flex.items-center {:title    GraphUUID
          [:a.flex.items-center {:title    GraphUUID
@@ -47,6 +47,7 @@
           (when remote? [:strong.pl-1.flex.items-center (ui/icon "cloud")])])])))
           (when remote? [:strong.pl-1.flex.items-center (ui/icon "cloud")])])])))
 
 
 (rum/defc repos-inner
 (rum/defc repos-inner
+  "Graph list in `All graphs` page"
   [repos]
   [repos]
   (for [{:keys [url remote? GraphUUID GraphName] :as repo} repos
   (for [{:keys [url remote? GraphUUID GraphName] :as repo} repos
         :let [only-cloud? (and remote? (nil? url))]]
         :let [only-cloud? (and remote? (nil? url))]]
@@ -210,16 +211,16 @@
                              (let [valid-remotes-but-locals? (and (seq repos) (not (some :url repos)))
                              (let [valid-remotes-but-locals? (and (seq repos) (not (some :url repos)))
                                    remote? (when-not valid-remotes-but-locals?
                                    remote? (when-not valid-remotes-but-locals?
                                              (:remote? (first (filter #(= current-repo (:url %)) repos))))
                                              (:remote? (first (filter #(= current-repo (:url %)) repos))))
-                                   repo-path (if-not valid-remotes-but-locals?
+                                   repo-name (if-not valid-remotes-but-locals?
                                                (db/get-repo-name current-repo) "")
                                                (db/get-repo-name current-repo) "")
                                    short-repo-name (if-not valid-remotes-but-locals?
                                    short-repo-name (if-not valid-remotes-but-locals?
-                                                     (db/get-short-repo-name repo-path) "Select a Graph")]
+                                                     (db/get-short-repo-name repo-name) "Select a Graph")]
                                [:a.item.group.flex.items-center.p-2.text-sm.font-medium.rounded-md
                                [:a.item.group.flex.items-center.p-2.text-sm.font-medium.rounded-md
 
 
                                 {:on-click (fn []
                                 {:on-click (fn []
                                              (check-multiple-windows? state)
                                              (check-multiple-windows? state)
                                              (toggle-fn))
                                              (toggle-fn))
-                                 :title    repo-path}       ;; show full path on hover
+                                 :title    repo-name}       ;; show full path on hover
                                 [:span.flex.relative
                                 [:span.flex.relative
                                  {:style {:top 1}}
                                  {:style {:top 1}}
                                  (ui/icon "database" {:size 16 :id "database-icon"})]
                                  (ui/icon "database" {:size 16 :id "database-icon"})]

+ 19 - 12
src/main/frontend/db/conn.cljs

@@ -7,7 +7,9 @@
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.util.text :as text-util]
             [frontend.util.text :as text-util]
             [logseq.graph-parser.text :as text]
             [logseq.graph-parser.text :as text]
-            [logseq.db :as ldb]))
+            [logseq.db :as ldb]
+            [frontend.fs2.path :as fs2-path]
+            [logseq.graph-parser.util :as gp-util]))
 
 
 (defonce conns (atom {}))
 (defonce conns (atom {}))
 
 
@@ -20,24 +22,29 @@
       url)))
       url)))
 
 
 (defn get-repo-name
 (defn get-repo-name
-  [repo]
+  [repo-url]
   (cond
   (cond
     (mobile-util/native-platform?)
     (mobile-util/native-platform?)
-    (text-util/get-graph-name-from-path repo)
+    (text-util/get-graph-name-from-path repo-url)
 
 
-    (config/local-db? repo)
-    (config/get-local-dir repo)
+    (config/local-db? repo-url)
+    (config/get-local-dir repo-url)
 
 
     :else
     :else
-    (get-repo-path repo)))
+    (get-repo-path repo-url)))
 
 
 (defn get-short-repo-name
 (defn get-short-repo-name
-  "repo-path: output of `get-repo-name`"
-  [repo-path]
-  (if (or (util/electron?)
-          (mobile-util/native-platform?))
-    (text/get-file-basename repo-path)
-    repo-path))
+  "repo-name: from get-repo-name. Dir/Name => Name"
+  [repo-name]
+  (cond
+    (util/electron?)
+    (text/get-file-basename repo-name)
+
+    (mobile-util/native-platform?)
+    (gp-util/safe-decode-uri-component (text/get-file-basename repo-name))
+
+    :else
+    repo-name))
 
 
 (defn datascript-db
 (defn datascript-db
   [repo]
   [repo]

+ 9 - 10
src/main/frontend/fs.cljs

@@ -59,11 +59,9 @@
   (p/let [result (protocol/readdir (get-fs dir) dir)
   (p/let [result (protocol/readdir (get-fs dir) dir)
           result (bean/->clj result)]
           result (bean/->clj result)]
     (let [result (if (and path-only? (map? (first result)))
     (let [result (if (and path-only? (map? (first result)))
-                   (map :uri result)
+                   (map :path result)
                    result)]
                    result)]
-      (if (and (map? (first result)) (:uri (first result)))
-        (map #(update % :uri gp-util/path-normalize) result)
-        (map gp-util/path-normalize result)))))
+      (map gp-util/path-normalize result))))
 
 
 (defn unlink!
 (defn unlink!
   "Should move the path to logseq/recycle instead of deleting it."
   "Should move the path to logseq/recycle instead of deleting it."
@@ -180,19 +178,20 @@
   "List all files in the directory, recursively.
   "List all files in the directory, recursively.
    {:path :files []}"
    {:path :files []}"
   [path-or-handle ok-handler]
   [path-or-handle ok-handler]
-  (let [record (get-record)]
+  (let [fs-record (get-record)]
     (when ok-handler
     (when ok-handler
       (js/console.warn "ok-handler not nil"))
       (js/console.warn "ok-handler not nil"))
-    (p/let [result (protocol/list-files record path-or-handle ok-handler)]
+    (p/let [result (protocol/list-files fs-record path-or-handle ok-handler)]
       (prn :t result)
       (prn :t result)
       (if (or (util/electron?)
       (if (or (util/electron?)
               (mobile-util/native-platform?))
               (mobile-util/native-platform?))
-        (let [[dir & paths] result
-              dir (:path dir)
+        (let [files result ;; TODO(andelf): rm first item from electron
+              dir path-or-handle
+              _ (prn ::prepare-rel-path dir)
               files (mapv (fn [entry]
               files (mapv (fn [entry]
-                            (prn ::xx entry)
+                            ;; (prn ::xx entry)
                             (assoc entry :path (fs2-path/relative-path dir (:path entry))))
                             (assoc entry :path (fs2-path/relative-path dir (:path entry))))
-                          paths)]
+                          files)]
           (prn :got files)
           (prn :got files)
           {:path dir :files files})
           {:path dir :files files})
         result))))
         result))))

+ 67 - 54
src/main/frontend/fs/capacitor_fs.cljs

@@ -13,7 +13,8 @@
             [lambdaisland.glogi :as log]
             [lambdaisland.glogi :as log]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]
             [rum.core :as rum]
-            [logseq.graph-parser.util :as gp-util]))
+            [logseq.graph-parser.util :as gp-util]
+            [frontend.fs2.path :as fs2-path]))
 
 
 (when (mobile-util/native-ios?)
 (when (mobile-util/native-ios?)
   (defn ios-ensure-documents!
   (defn ios-ensure-documents!
@@ -97,7 +98,9 @@
                                    (mapv
                                    (mapv
                                     (fn [{:keys [uri] :as file-info}]
                                     (fn [{:keys [uri] :as file-info}]
                                       (p/chain (<read-file-with-utf8 uri)
                                       (p/chain (<read-file-with-utf8 uri)
-                                               #(assoc file-info :content %))))))]
+                                               #(assoc (dissoc file-info :uri)
+                                                       :content %
+                                                       :path uri))))))]
                        (p/recur (concat result files-result)
                        (p/recur (concat result files-result)
                                 (concat (rest dirs) files-dir)))))]
                                 (concat (rest dirs) files-dir)))))]
     (js->clj result :keywordize-keys true)))
     (js->clj result :keywordize-keys true)))
@@ -127,7 +130,7 @@
            files (js->clj files :keywordize-keys true)
            files (js->clj files :keywordize-keys true)
            old-versioned-files (drop 6 (reverse (sort-by :mtime files)))]
            old-versioned-files (drop 6 (reverse (sort-by :mtime files)))]
      (mapv (fn [file]
      (mapv (fn [file]
-             (.deleteFile Filesystem (clj->js {:path (:uri file)})))
+             (.deleteFile Filesystem (clj->js {:path (:path file)})))
            old-versioned-files))
            old-versioned-files))
    (p/catch (fn [_]))))
    (p/catch (fn [_]))))
 
 
@@ -168,50 +171,53 @@
     (truncate-old-versioned-files! file-root)))
     (truncate-old-versioned-files! file-root)))
 
 
 (defn- write-file-impl!
 (defn- write-file-impl!
-  [_this repo _dir path content {:keys [ok-handler error-handler old-content skip-compare?]} stat]
-  (if (or (string/blank? repo) skip-compare?)
-    (p/catch
-     (p/let [result (<write-file-with-utf8 path content)]
-       (when ok-handler
-         (ok-handler repo path result)))
-     (fn [error]
-       (if error-handler
-         (error-handler error)
-         (log/error :write-file-failed error))))
+  [repo dir rpath content {:keys [ok-handler error-handler old-content skip-compare?]} stat]
+  (let [fpath (fs2-path/path-join dir rpath)]
+    (if (or (string/blank? repo) skip-compare?)
+      (p/catch
+       (p/let [result (<write-file-with-utf8 fpath content)]
+         (when ok-handler
+           (ok-handler repo fpath result)))
+       (fn [error]
+         (if error-handler
+           (error-handler error)
+           (log/error :write-file-failed error))))
 
 
     ;; Compare with disk content and backup if not equal
     ;; Compare with disk content and backup if not equal
-    (p/let [disk-content (<read-file-with-utf8 path)
-            disk-content (or disk-content "")
-            repo-dir (config/get-local-dir repo)
-            ext (util/get-file-ext path)
-            db-content (or old-content (db/get-file repo path) "")
-            contents-matched? (contents-matched? disk-content db-content)]
-      (cond
-        (and
-         (not= stat :not-found)   ; file on the disk was deleted
-         (not contents-matched?)
-         (not (contains? #{"excalidraw" "edn" "css"} ext))
-         (not (string/includes? path "/.recycle/")))
-        (p/let [disk-content disk-content]
-          (state/pub-event! [:file/not-matched-from-disk path disk-content content]))
+      (p/let [disk-content (<read-file-with-utf8 fpath)
+              disk-content (or disk-content "")
+              repo-dir (config/get-local-dir repo)
+              ext (util/get-file-ext rpath)
+              db-content (or old-content (db/get-file repo rpath) "")
+              contents-matched? (contents-matched? disk-content db-content)]
+        (prn ::before-write contents-matched? )
+        (prn disk-content db-content)
+        (cond
+          (and
+           (not= stat :not-found)   ; file on the disk was deleted
+           (not contents-matched?)
+           (not (contains? #{"excalidraw" "edn" "css"} ext))
+           (not (string/includes? fpath "/.recycle/")))
+          (p/let [disk-content disk-content]
+            (state/pub-event! [:file/not-matched-from-disk rpath disk-content content]))
 
 
-        :else
-        (->
-         (p/let [result (<write-file-with-utf8 path content)
-                 mtime (-> (js->clj stat :keywordize-keys true)
-                           :mtime)]
-           (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 content]
-             (db/set-file-content! repo path content))
-           (when ok-handler
-             (ok-handler repo path result))
-           result)
-         (p/catch (fn [error]
-                    (if error-handler
-                      (error-handler error)
-                      (log/error :write-file-failed error)))))))))
+          :else
+          (->
+           (p/let [result (<write-file-with-utf8 fpath content)
+                   mtime (-> (js->clj stat :keywordize-keys true)
+                             :mtime)]
+             (when-not contents-matched?
+               (backup-file repo-dir :backup-dir fpath disk-content))
+             (db/set-file-last-modified-at! repo fpath mtime)
+             (p/let [content content]
+               (db/set-file-content! repo fpath content))
+             (when ok-handler
+               (ok-handler repo fpath result))
+             result)
+           (p/catch (fn [error]
+                      (if error-handler
+                        (error-handler error)
+                        (log/error :write-file-failed error))))))))))
 
 
 (defn ios-force-include-private
 (defn ios-force-include-private
   "iOS sometimes return paths without the private part."
   "iOS sometimes return paths without the private part."
@@ -232,6 +238,8 @@
     path))
     path))
 
 
 (defn normalize-file-protocol-path [dir path]
 (defn normalize-file-protocol-path [dir path]
+  (prn ::fuck-DO-NOT-CALL-THIS)
+  (js/console.trace)
   (let [dir             (some-> dir (string/replace #"/+$" ""))
   (let [dir             (some-> dir (string/replace #"/+$" ""))
         dir             (if (and (not-empty dir) (string/starts-with? dir "/"))
         dir             (if (and (not-empty dir) (string/starts-with? dir "/"))
                           (do
                           (do
@@ -339,7 +347,7 @@
                 dir)]
                 dir)]
       (readdir dir)))
       (readdir dir)))
   (unlink! [this repo path _opts]
   (unlink! [this repo path _opts]
-    (p/let [path (normalize-file-protocol-path nil path)
+    (p/let [_ (prn ::unlink path)
             repo-url (config/get-local-dir repo)
             repo-url (config/get-local-dir repo)
             recycle-dir (util/safe-path-join repo-url config/app-name ".recycle") ;; logseq/.recycle
             recycle-dir (util/safe-path-join repo-url config/app-name ".recycle") ;; logseq/.recycle
             ;; convert url to pure path
             ;; convert url to pure path
@@ -353,20 +361,23 @@
     ;; Too dangerous!!! We'll never implement this.
     ;; Too dangerous!!! We'll never implement this.
     nil)
     nil)
   (read-file [_this dir path _options]
   (read-file [_this dir path _options]
-    (let [path (normalize-file-protocol-path dir path)]
+    (prn ::read-file dir path)
+    (let [fpath (fs2-path/path-join dir path)]
       (->
       (->
-       (<read-file-with-utf8 path)
+       (<read-file-with-utf8 fpath)
        (p/catch (fn [error]
        (p/catch (fn [error]
                   (log/error :read-file-failed error))))))
                   (log/error :read-file-failed error))))))
-  (write-file! [this repo dir path content opts]
-    (let [path (normalize-file-protocol-path dir path)]
+  (write-file! [_this repo dir path content opts]
+    (prn ::write-file dir path)
+    (let [fpath (fs2-path/path-join dir path)]
       (p/let [stat (p/catch
       (p/let [stat (p/catch
-                    (.stat Filesystem (clj->js {:path path}))
+                    (.stat Filesystem (clj->js {:path fpath}))
                     (fn [_e] :not-found))]
                     (fn [_e] :not-found))]
         ;; `path` is full-path
         ;; `path` is full-path
-        (write-file-impl! this repo dir path content opts stat))))
+        (write-file-impl! repo dir path content opts stat))))
   (rename! [_this _repo old-path new-path]
   (rename! [_this _repo old-path new-path]
-    (let [[old-path new-path] (map #(normalize-file-protocol-path "" %) [old-path new-path])]
+    (let []
+      (prn ::rename old-path new-path)
       (p/catch
       (p/catch
        (p/let [_ (.rename Filesystem
        (p/let [_ (.rename Filesystem
                           (clj->js
                           (clj->js
@@ -375,7 +386,8 @@
        (fn [error]
        (fn [error]
          (log/error :rename-file-failed error)))))
          (log/error :rename-file-failed error)))))
   (copy! [_this _repo old-path new-path]
   (copy! [_this _repo old-path new-path]
-    (let [[old-path new-path] (map #(normalize-file-protocol-path "" %) [old-path new-path])]
+    (let []
+      (prn ::copy old-path new-path)
       (p/catch
       (p/catch
        (p/let [_ (.copy Filesystem
        (p/let [_ (.copy Filesystem
                         (clj->js
                         (clj->js
@@ -384,12 +396,13 @@
        (fn [error]
        (fn [error]
          (log/error :copy-file-failed error)))))
          (log/error :copy-file-failed error)))))
   (stat [_this dir path]
   (stat [_this dir path]
-    (let [path (normalize-file-protocol-path dir path)]
+    (let [path (fs2-path/path-join dir path)]
       (p/chain (.stat Filesystem (clj->js {:path path}))
       (p/chain (.stat Filesystem (clj->js {:path path}))
                #(js->clj % :keywordize-keys true))))
                #(js->clj % :keywordize-keys true))))
   (open-dir [_this dir _ok-handler]
   (open-dir [_this dir _ok-handler]
     (open-dir dir))
     (open-dir dir))
   (list-files [_this dir _ok-handler]
   (list-files [_this dir _ok-handler]
+    (prn ::readdir dir)
     (readdir dir))
     (readdir dir))
   (watch-dir! [_this dir _options]
   (watch-dir! [_this dir _options]
     (p/do!
     (p/do!

+ 13 - 9
src/main/frontend/fs/watcher_handler.cljs

@@ -57,7 +57,9 @@
           ;; on the client to correctly identify it
           ;; on the client to correctly identify it
           repo (if global-dir (state/get-current-repo) (config/get-local-repo dir))
           repo (if global-dir (state/get-current-repo) (config/get-local-repo dir))
           {:keys [mtime]} stat
           {:keys [mtime]} stat
-          db-content (or (db/get-file repo path) "")]
+          db-content (or (db/get-file repo path) "")
+          _ (prn ::read-out-db-cont db-content content)]
+      
       (when (or content (contains? #{"unlink" "unlinkDir" "addDir"} type))
       (when (or content (contains? #{"unlink" "unlinkDir" "addDir"} type))
         (cond
         (cond
           (and (= "unlinkDir" type) dir)
           (and (= "unlinkDir" type) dir)
@@ -80,7 +82,7 @@
 
 
           (and (= "change" type)
           (and (= "change" type)
                (not= (string/trim content) (string/trim db-content))
                (not= (string/trim content) (string/trim db-content))
-               (not (gp-config/local-asset? (string/replace-first path dir ""))))
+               (not (gp-config/local-asset? path)))
           (when-not (and
           (when-not (and
                      (string/includes? path (str "/" (config/get-journals-directory) "/"))
                      (string/includes? path (str "/" (config/get-journals-directory) "/"))
                      (or
                      (or
@@ -88,6 +90,7 @@
                          (string/trim (or (state/get-default-journal-template) "")))
                          (string/trim (or (state/get-default-journal-template) "")))
                       (= (string/trim content) "-")
                       (= (string/trim content) "-")
                       (= (string/trim content) "*")))
                       (= (string/trim content) "*")))
+            (prn ::fuck!!)
             (handle-add-and-change! repo path content db-content mtime (not global-dir))) ;; no backup for global dir
             (handle-add-and-change! repo path content db-content mtime (not global-dir))) ;; no backup for global dir
 
 
           (and (= "unlink" type)
           (and (= "unlink" type)
@@ -122,7 +125,7 @@
                         (map first)
                         (map first)
                         (filter #(string/starts-with? % (config/get-repo-dir graph))))]
                         (filter #(string/starts-with? % (config/get-repo-dir graph))))]
       (p/let [files (fs/readdir dir :path-only? true)
       (p/let [files (fs/readdir dir :path-only? true)
-              files (map #(fs2-path/relative-path dir %) files)       ;; FIXME: readdir returns full paths
+              files (map #(fs2-path/relative-path dir %) files)       ;; FIXME(andelf): readdir returns full paths
               files (remove #(fs-util/ignored-path? dir %) files)]
               files (remove #(fs-util/ignored-path? dir %) files)]
         (let [deleted-files (set/difference (set db-files) (set files))]
         (let [deleted-files (set/difference (set db-files) (set files))]
           (when (seq deleted-files)
           (when (seq deleted-files)
@@ -130,17 +133,18 @@
                                       (concat (db/delete-blocks graph deleted-files nil))
                                       (concat (db/delete-blocks graph deleted-files nil))
                                       (remove nil?))]
                                       (remove nil?))]
               (db/transact! graph delete-tx-data {:delete-files? true})))
               (db/transact! graph delete-tx-data {:delete-files? true})))
-          (doseq [file files]
-            (when-let [_ext (util/get-file-ext file)]
+          (doseq [file-rpath files]
+            (prn ::init-watcher file-rpath)
+            (when-let [_ext (util/get-file-ext file-rpath)]
               (->
               (->
-               (p/let [content (fs/read-file dir file)
-                       stat (fs/stat dir file)
-                       type (if (db/file-exists? graph file)
+               (p/let [content (fs/read-file dir file-rpath)
+                       stat (fs/stat dir file-rpath)
+                       type (if (db/file-exists? graph file-rpath)
                               "change"
                               "change"
                               "add")]
                               "add")]
                  (handle-changed! type
                  (handle-changed! type
                                   {:dir dir
                                   {:dir dir
-                                   :path file
+                                   :path file-rpath
                                    :content content
                                    :content content
                                    :stat stat}))
                                    :stat stat}))
                (p/catch (fn [error]
                (p/catch (fn [error]

+ 65 - 33
src/main/frontend/fs2/path.cljs

@@ -9,13 +9,12 @@
 (defn is-file-url
 (defn is-file-url
   [s]
   [s]
   (and (string? s)
   (and (string? s)
-       (or (string/starts-with? s "file://")
-           (string/starts-with? s "content://")
+       (or (string/starts-with? s "file://") ;; mobile platform
+           (string/starts-with? s "content://") ;; android only
            (string/starts-with? s "logseq://") ;; reserved for future fs protocl
            (string/starts-with? s "logseq://") ;; reserved for future fs protocl
            (string/starts-with? s "s3://"))))
            (string/starts-with? s "s3://"))))
 
 
 
 
-
 (defn filename
 (defn filename
   "File name of a path or URL.
   "File name of a path or URL.
    Returns nil when it's a path."
    Returns nil when it's a path."
@@ -65,7 +64,8 @@
   '..' and '.' normalization."
   '..' and '.' normalization."
   [& segments]
   [& segments]
   (let [segments (remove nil? segments) ;; handle (path-join nil path)
   (let [segments (remove nil? segments) ;; handle (path-join nil path)
-        _ (prn ::seg segments)
+        _ (prn ::join-seg segments)
+        ;; _ (js/console.trace)
         segments (map #(string/replace % #"[/\\]+" "/") segments)
         segments (map #(string/replace % #"[/\\]+" "/") segments)
         ;; a fix for clojure.string/split
         ;; a fix for clojure.string/split
         split-fn (fn [s]
         split-fn (fn [s]
@@ -100,17 +100,57 @@
                  [])
                  [])
          (join-fn))))
          (join-fn))))
 
 
+(defn- uri-path-join-internal
+  "Joins the given URI path segments into a single path, handling relative paths,
+  '..' and '.' normalization."
+  [& segments]
+  (let [segments (remove nil? segments) ;; handle (path-join nil path)
+        _ (prn ::uri-join-seg segments)
+        ; _ (js/console.trace)
+        segments (map #(string/replace % #"[/\\]+" "/") segments)
+        ;; a fix for clojure.string/split
+        split-fn (fn [s]
+                   (if (= s "/")
+                     [""]
+                     (string/split s #"/")))
+        join-fn (fn [segs]
+                  (case segs
+                    []   "."
+                    [""] "/"
+                    #_{:clj-kondo/ignore [:path-invalid-construct/string-join]}
+                    (string/join "/" segs)))]
+    (->> (filter not-empty segments)
+         (mapcat split-fn)
+         (map #(js/encodeURIComponent %))
+         (reduce (fn [acc segment]
+                   (cond
+                     (= "" segment)
+                     [segment]
+
+                     (= ".." segment)
+                     (case (last acc)
+                       ".." (conj acc segment)
+                       ""   acc
+                       nil  [".."]
+                       (pop acc))
+
+                     (= "." segment)
+                     acc
+
+                     :else
+                     (conj acc segment)))
+                 [])
+         (join-fn))))
+
 (defn url-join
 (defn url-join
   "Segments are not URL-ecoded"
   "Segments are not URL-ecoded"
   [base-url & segments]
   [base-url & segments]
-  (let [^js url (.parse Uri base-url)
-        scheme (.getScheme url)
-        domain (.getDomain url)
-        path (.getPath url)
-        new-path (apply path-join-internal path segments)
-        ;; opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query, opt_fragment, opt_ignoreCase
-        new-url (.create Uri scheme nil domain nil new-path nil nil nil)]
-    (.toString new-url)))
+  (let [^js url (js/URL. base-url)
+        scheme (.-protocol url)
+        domain (or (not-empty (.-host url)) "")
+        path (gp-util/safe-decode-uri-component (.-pathname url))
+        encoded-new-path (apply uri-path-join-internal path segments)]
+    (str scheme "//" domain encoded-new-path)))
 
 
 
 
 (defn path-join
 (defn path-join
@@ -128,15 +168,13 @@
 
 
 
 
 (defn url-normalize
 (defn url-normalize
-  [url]
-  (let [^js uri (.parse Uri url)
-        scheme (.getScheme uri)
-        domain (.getDomain uri)
-        path (.getPath uri)
-        new-path (path-normalize-internal path)
-        ;; opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query, opt_fragment, opt_ignoreCase
-        new-uri (.create Uri scheme nil domain nil new-path nil nil nil)]
-    (.toString new-uri)))
+  [origin-url]
+  (let [^js url (js/URL. origin-url)
+        scheme (.-protocol url)
+        domain (or (not-empty (.-host url)) "")
+        path (gp-util/safe-decode-uri-component (.-pathname url))
+        encoded-new-path (uri-path-join-internal path)]
+    (str scheme "//" domain encoded-new-path)))
 
 
 (defn path-normalize
 (defn path-normalize
   "Normalize path or URL"
   "Normalize path or URL"
@@ -164,11 +202,15 @@
   "Get relative path from base path.
   "Get relative path from base path.
    Works for both path and URL."
    Works for both path and URL."
   [base-path sub-path]
   [base-path sub-path]
+    (prn :rel-path base-path sub-path)
   (let [base-path (path-normalize base-path)
   (let [base-path (path-normalize base-path)
         sub-path (path-normalize sub-path)
         sub-path (path-normalize sub-path)
         is-url? (is-file-url base-path)]
         is-url? (is-file-url base-path)]
+    (prn :rel-path base-path sub-path)
     (if (string/starts-with? sub-path base-path)
     (if (string/starts-with? sub-path base-path)
-      (trim-dir-prefix base-path sub-path) ;; FIXME(andelf): speedup
+      (if is-url?
+        (gp-util/safe-decode-uri-component (string/replace (subs sub-path (count base-path)) #"^/+", ""))
+        (string/replace (subs sub-path (count base-path)) #"^/+", ""))
        ;; append as many .. 
        ;; append as many .. 
       (let [base-segs (string/split base-path #"/" -1)
       (let [base-segs (string/split base-path #"/" -1)
             path-segs (string/split sub-path #"/" -1)
             path-segs (string/split sub-path #"/" -1)
@@ -182,18 +224,8 @@
           (str base-prefix (string/join "/" remain-segs)))))))
           (str base-prefix (string/join "/" remain-segs)))))))
 
 
 
 
-(defn decoded-relative-uri
-  "Get relative uri from base url, url-decoded"
-  [base-url path-url]
-  (let [base-url (url-normalize base-url)
-        path-url (url-normalize path-url)]
-    (if (string/starts-with? path-url base-url)
-      (gp-util/safe-decode-uri-component (string/replace (subs path-url (count base-url)) #"^/+", ""))
-      (do
-        (js/console.error "unhandled relative path" base-url path-url)
-        path-url))))
-
 (defn parent
 (defn parent
+  "Parent, containing directory"
   [path]
   [path]
   ;; ugly but works
   ;; ugly but works
   (path-normalize (str path "/..")))
   (path-normalize (str path "/..")))

+ 23 - 20
src/main/frontend/handler/web/nfs.cljs

@@ -47,18 +47,19 @@
       (p/resolved files))))
       (p/resolved files))))
 
 
 (defn- ->db-files
 (defn- ->db-files
-  [mobile-native? electron? dir-name result]
+  [dir-name result]
   (->>
   (->>
    (cond
    (cond
-     mobile-native?
-     (map (fn [{:keys [uri content size mtime]}]
-            {:file/path             (gp-util/path-normalize uri)
+     ;; TODO(andelf): use the same structure for both fields
+     (mobile-util/native-platform?)
+     (map (fn [{:keys [path content size mtime]}]
+            {:file/path             (gp-util/path-normalize path)
              :file/last-modified-at mtime
              :file/last-modified-at mtime
              :file/size             size
              :file/size             size
              :file/content content})
              :file/content content})
           result)
           result)
 
 
-     electron?
+     (util/electron?)
      (map (fn [{:keys [path stat content]}]
      (map (fn [{:keys [path stat content]}]
             (let [{:keys [mtime size]} stat]
             (let [{:keys [mtime size]} stat]
               {:file/path             (gp-util/path-normalize path)
               {:file/path             (gp-util/path-normalize path)
@@ -151,9 +152,7 @@
               dir-name (if nfs?
               dir-name (if nfs?
                          (gobj/get root-handle "name")
                          (gobj/get root-handle "name")
                          root-handle)
                          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)
               repo (str config/local-db-prefix dir-name)
               _ (state/set-loading-files! repo true)
               _ (state/set-loading-files! repo true)
               _ (when-not (or (state/home?) (state/setups-picker?))
               _ (when-not (or (state/home?) (state/setups-picker?))
@@ -165,7 +164,7 @@
                       (idb/set-item! root-handle-path root-handle)
                       (idb/set-item! root-handle-path root-handle)
                       (nfs/add-nfs-file-handle! root-handle-path root-handle))
                       (nfs/add-nfs-file-handle! root-handle-path root-handle))
                   files (:files result)
                   files (:files result)
-                  files (-> (->db-files mobile-native? electron? dir-name files)
+                  files (-> (->db-files dir-name files)
                             (remove-ignore-files dir-name nfs?))
                             (remove-ignore-files dir-name nfs?))
                   _ (when nfs?
                   _ (when nfs?
                       ;; only for browserfs
                       ;; only for browserfs
@@ -206,15 +205,18 @@
                               (do
                               (do
                                 (prn ::prepare-load-new-repo files)
                                 (prn ::prepare-load-new-repo files)
                                 (repo-handler/start-repo-db-if-not-exists! repo)
                                 (repo-handler/start-repo-db-if-not-exists! repo)
-                                (async/go
-                                  (let [_finished? (async/<! (repo-handler/load-new-repo-to-db! repo
-                                                                                            {:new-graph?   true
-                                                                                             :empty-graph? (nil? (seq markup-files))
-                                                                                             :file-objs    files}))]
-                                    (state/add-repo! {:url repo :nfs? true})
-                                    (state/set-loading-files! repo false)
-                                    (when ok-handler (ok-handler {:url repo}))
-                                    (db/persist-if-idle! repo))))))))
+                                (prn ::dd (nil? (seq markup-files)))
+                                (let [_finished? (repo-handler/load-new-repo-to-db! repo
+                                                                                    {:new-graph?   true
+                                                                                     :empty-graph? (nil? (seq markup-files))
+                                                                                     :file-objs    files})
+                                      _ (prn ::fuck _finished?)]
+                                  (prn ::debug-2.5)
+                                  (state/add-repo! {:url repo :nfs? true})
+                                  (prn ::debug-33333)
+                                  (state/set-loading-files! repo false)
+                                  (when ok-handler (ok-handler {:url repo}))
+                                  (db/persist-if-idle! repo)))))))
                 (p/catch (fn [error]
                 (p/catch (fn [error]
                            (log/error :nfs/load-files-error repo)
                            (log/error :nfs/load-files-error repo)
                            (log/error :exception error)))))))
                            (log/error :exception error)))))))
@@ -228,6 +230,7 @@
                    (throw error))))
                    (throw error))))
       (p/finally
       (p/finally
         (fn []
         (fn []
+          (prn ::set-loading-files false)
           (state/set-loading-files! @*repo false)))))))
           (state/set-loading-files! @*repo false)))))))
 
 
 (defn ls-dir-files-with-path!
 (defn ls-dir-files-with-path!
@@ -349,7 +352,7 @@
                                   (fn [path handle]
                                   (fn [path handle]
                                     (when nfs?
                                     (when nfs?
                                       (swap! path-handles assoc path handle))))
                                       (swap! path-handles assoc path handle))))
-                   new-local-files (-> (->db-files mobile-native? electron? dir-name (:files local-files-result))
+                   new-local-files (-> (->db-files dir-name (:files local-files-result))
                                        (remove-ignore-files dir-name nfs?))
                                        (remove-ignore-files dir-name nfs?))
                   ;; new-global-files (if (and (config/global-config-enabled?)
                   ;; new-global-files (if (and (config/global-config-enabled?)
                    ;;                          ;; Hack until we better understand failure in frontend.handler.file/alter-file
                    ;;                          ;; Hack until we better understand failure in frontend.handler.file/alter-file
@@ -357,7 +360,7 @@
                      ;;                 (p/let [global-files-result (fs/list-files
                      ;;                 (p/let [global-files-result (fs/list-files
                       ;;                                             (global-config-handler/global-config-dir)
                       ;;                                             (global-config-handler/global-config-dir)
                        ;;                                            (constantly nil))
                        ;;                                            (constantly nil))
-                        ;;                      global-files (-> (->db-files mobile-native? electron? (global-config-handler/global-config-dir) global-files-result)
+                        ;;                      global-files (-> (->db-files (global-config-handler/global-config-dir) global-files-result)
                          ;;                                      (remove-ignore-files (global-config-handler/global-config-dir) nfs?))]
                          ;;                                      (remove-ignore-files (global-config-handler/global-config-dir) nfs?))]
                           ;;              global-files)
                           ;;              global-files)
                            ;;           (p/resolved []))
                            ;;           (p/resolved []))

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

@@ -10,7 +10,8 @@
             [frontend.fs :as fs]
             [frontend.fs :as fs]
             [frontend.config :as config]
             [frontend.config :as config]
             [promesa.core :as p]
             [promesa.core :as p]
-            [cljs.reader :as reader]))
+            [cljs.reader :as reader]
+            [frontend.fs2.path :as fs2-path]))
 
 
 ;; NOTE: This is not the same ignored-path? as src/electron/electron/utils.cljs.
 ;; NOTE: This is not the same ignored-path? as src/electron/electron/utils.cljs.
 ;;       The assets directory is ignored.
 ;;       The assets directory is ignored.
@@ -173,6 +174,8 @@
             (string/replace #"/" url-encode)
             (string/replace #"/" url-encode)
             (string/replace "*" "%2A"))))
             (string/replace "*" "%2A"))))
 
 
+
+
 ;; Register sanitization / parsing fns in:
 ;; Register sanitization / parsing fns in:
 ;; logseq.graph-parser.util (parsing only)
 ;; logseq.graph-parser.util (parsing only)
 ;; frontend.util.fs         (sanitization only)
 ;; frontend.util.fs         (sanitization only)
@@ -186,6 +189,7 @@
        :triple-lowbar (tri-lb-file-name-sanity title)
        :triple-lowbar (tri-lb-file-name-sanity title)
        ;; The earliest file name rule (before May 2022). For file name check in the conversion logic only. Don't allow users to use this or show up in config, as it's not handled.
        ;; The earliest file name rule (before May 2022). For file name check in the conversion logic only. Don't allow users to use this or show up in config, as it's not handled.
        :legacy-dot    (legacy-dot-file-name-sanity title)
        :legacy-dot    (legacy-dot-file-name-sanity title)
+      ;;  :v3            ()
        (legacy-url-file-name-sanity title)))))
        (legacy-url-file-name-sanity title)))))
 
 
 (defn create-title-property?
 (defn create-title-property?