Browse Source

refactor: add Fs protocol

Tienson Qin 4 years ago
parent
commit
abdad5634a

+ 0 - 1
package.json

@@ -57,7 +57,6 @@
         "codemirror": "^5.58.1",
         "diff": "5.0.0",
         "diff-match-patch": "^1.0.5",
-        "electron-better-ipc": "^1.1.1",
         "fs": "^0.0.1-security",
         "fuzzysort": "^1.1.4",
         "gulp-cached": "^1.1.1",

+ 1 - 2
src/electron/electron/handler.cljs

@@ -1,2 +1 @@
-(ns electron.handler
-  (:require [electron-better-ipc :refer [ipcMain]]))
+(ns electron.handler)

+ 48 - 256
src/main/frontend/fs.cljs

@@ -1,263 +1,78 @@
 (ns frontend.fs
   (:require [frontend.util :as util :refer-macros [profile]]
             [frontend.config :as config]
-            [frontend.state :as state]
             [clojure.string :as string]
-            [frontend.idb :as idb]
-            [frontend.db :as db]
-            [frontend.handler.common :as common-handler]
             [promesa.core :as p]
-            [goog.object :as gobj]
-            [clojure.set :as set]
             [lambdaisland.glogi :as log]
-            ["/frontend/utils" :as utils]))
+            [frontend.fs.protocol :as protocol]
+            [frontend.fs.nfs :as nfs]
+            [frontend.fs.bfs :as bfs]
+            [frontend.fs.node :as node]))
 
-;; We need to cache the file handles in the memory so that
-;; the browser will not keep asking permissions.
-(defonce nfs-file-handles-cache (atom {}))
-
-(defn get-nfs-file-handle
-  [handle-path]
-  (get @nfs-file-handles-cache handle-path))
-
-(defn add-nfs-file-handle!
-  [handle-path handle]
-  (swap! nfs-file-handles-cache assoc handle-path handle))
-
-(defn remove-nfs-file-handle!
-  [handle-path]
-  (swap! nfs-file-handles-cache dissoc handle-path))
-
-;; TODO:
-;; We need to support several platforms:
-;; 1. Chrome native file system API (lighting-fs wip)
-;; 2. IndexedDB (lighting-fs)
-;; 3. NodeJS
-#_(defprotocol Fs
-    (mkdir! [this dir])
-    (readdir! [this dir])
-    (unlink! [this path opts])
-    (rename! [this old-path new-path])
-    (rmdir! [this dir])
-    (read-file [dir path option])
-    (write-file! [dir path content])
-    (stat [dir path]))
+(defonce nfs-record (nfs/->Nfs))
+(defonce bfs-record (bfs/->Bfs))
+(defonce node-record (node/->Node))
 
 (defn local-db?
   [dir]
   (and (string? dir)
        (config/local-db? (subs dir 1))))
 
-(defn mkdir
+(defn get-fs
   [dir]
   (cond
-    (local-db? dir)
-    (let [[root new-dir] (rest (string/split dir "/"))
-          root-handle (str "handle/" root)]
-      (->
-       (p/let [handle (idb/get-item root-handle)
-               _ (when handle (common-handler/verify-permission nil handle true))]
-         (when (and handle new-dir
-                    (not (string/blank? new-dir)))
-           (p/let [handle (.getDirectoryHandle ^js handle new-dir
-                                               #js {:create true})
-                   handle-path (str root-handle "/" new-dir)
-                   _ (idb/set-item! handle-path handle)]
-             (add-nfs-file-handle! handle-path handle)
-             (println "Stored handle: " (str root-handle "/" new-dir)))))
-       (p/catch (fn [error]
-                  (js/console.debug "mkdir error: " error ", dir: " dir)
-                  (throw error)))))
+    (util/electron?)
+    node-record
 
-    (and dir js/window.pfs)
-    (js/window.pfs.mkdir dir)
+    (local-db? dir)
+    nfs-record
 
     :else
-    (println (str "mkdir " dir " failed"))))
+    bfs-record))
 
-(defn readdir
+(defn mkdir!
   [dir]
-  (cond
-    (local-db? dir)
-    (let [prefix (str "handle/" dir)
-          cached-files (keys @nfs-file-handles-cache)]
-      (p/resolved
-       (->> (filter #(string/starts-with? % (str prefix "/")) cached-files)
-            (map (fn [path]
-                   (string/replace path prefix ""))))))
-
-    (and dir js/window.pfs)
-    (js/window.pfs.readdir dir)
+  (protocol/mkdir! (get-fs dir) dir))
 
-    :else
-    nil))
+(defn readdir
+  [dir]
+  (protocol/readdir (get-fs dir) dir))
 
-(defn unlink
+(defn unlink!
   [path opts]
-  (cond
-    (local-db? path)
-    (let [[dir basename] (util/get-dir-and-basename path)
-          handle-path (str "handle" path)]
-      (->
-       (p/let [handle (idb/get-item (str "handle" dir))
-               _ (idb/remove-item! handle-path)]
-         (when handle
-           (.removeEntry ^js handle basename))
-         (remove-nfs-file-handle! handle-path))
-       (p/catch (fn [error]
-                  (log/error :unlink/path {:path path
-                                           :error error})))))
-
-    :else
-    (p/let [stat (js/window.pfs.stat path)]
-      (if (= (.-type stat) "file")
-        (js/window.pfs.unlink path opts)
-        (p/rejected "Unlinking a directory is not allowed")))))
+  (protocol/unlink! (get-fs path) path opts))
 
-(defn rmdir
-  "Remove the directory recursively."
+(defn rmdir!
+  "Remove the directory recursively.
+   Warning: only run it for browser cache."
   [dir]
-  (cond
-    (local-db? dir)
-    nil
-
-    :else
-    (js/window.workerThread.rimraf dir)))
+  (protocol/rmdir! (get-fs dir) dir))
 
 (defn read-file
-  ([dir path]
-   (read-file dir path (clj->js {:encoding "utf8"})))
-  ([dir path option]
-   (cond
-     (local-db? dir)
-     (let [handle-path (str "handle" dir "/" path)]
-       (p/let [handle (idb/get-item handle-path)
-               local-file (and handle (.getFile handle))]
-         (and local-file (.text local-file))))
-
-     :else
-     (js/window.pfs.readFile (str dir "/" path) option))))
-
-(defn nfs-saved-handler
-  [repo path file]
-  (when-let [last-modified (gobj/get file "lastModified")]
-    ;; TODO: extract
-    (let [path (if (= \/ (first path))
-                 (subs path 1)
-                 path)]
-      (db/set-file-last-modified-at! repo path last-modified))))
-
-(defn write-file
-  ([repo dir path content]
-   (write-file repo dir path content nil))
-  ([repo dir path content {:keys [old-content last-modified-at]}]
-   (->
-    (cond
-      (local-db? dir)
-      (let [parts (string/split path "/")
-            basename (last parts)
-            sub-dir (->> (butlast parts)
-                         (remove string/blank?)
-                         (string/join "/"))
-            sub-dir-handle-path (str "handle/"
-                                     (subs dir 1)
-                                     (if sub-dir
-                                       (str "/" sub-dir)))
-            handle-path (if (= "/" (last sub-dir-handle-path))
-                          (subs sub-dir-handle-path 0 (dec (count sub-dir-handle-path)))
-                          sub-dir-handle-path)
-            basename-handle-path (str handle-path "/" basename)]
-        (p/let [file-handle (idb/get-item basename-handle-path)]
-          (when file-handle
-            (add-nfs-file-handle! basename-handle-path file-handle))
-          (if file-handle
-            (p/let [local-file (.getFile file-handle)
-                    local-content (.text local-file)
-                    local-last-modified-at (gobj/get local-file "lastModified")
-                    current-time (util/time-ms)
-                    new? (> current-time local-last-modified-at)
-                    new-created? (nil? last-modified-at)
-                    not-changed? (= last-modified-at local-last-modified-at)
-                    format (-> (util/get-file-ext path)
-                               (config/get-file-format))
-                    pending-writes (state/get-write-chan-length)]
-              ;; (println {:last-modified-at last-modified-at
-              ;;           :local-last-modified-at local-last-modified-at
-              ;;           :not-changed? not-changed?
-              ;;           :new-created? new-created?
-              ;;           :pending-writes pending-writes
-              ;;           :local-content local-content
-              ;;           :old-content old-content
-              ;;           :new? new?})
-              (if (and local-content old-content new?
-                       (or
-                        (> pending-writes 0)
-                        not-changed?
-                        new-created?))
-                (do
-                  (p/let [_ (common-handler/verify-permission repo file-handle true)
-                          _ (utils/writeFile file-handle content)
-                          file (.getFile file-handle)]
-                    (when file
-                      (nfs-saved-handler repo path file))))
-                (do
-                  (js/alert (str "The file has been modified in your local disk! File path: " path
-                                 ", save your changes and click the refresh button to reload it.")))))
-            ;; create file handle
-            (->
-             (p/let [handle (idb/get-item handle-path)]
-               (if handle
-                 (do
-                   (p/let [_ (common-handler/verify-permission repo handle true)
-                           file-handle (.getFileHandle ^js handle basename #js {:create true})
-                           _ (idb/set-item! basename-handle-path file-handle)
-                           _ (utils/writeFile file-handle content)
-                           file (.getFile file-handle)]
-                     (when file
-                       (nfs-saved-handler repo path file))))
-                 (println "Error: directory handle not exists: " handle-path)))
-             (p/catch (fn [error]
-                        (println "Write local file failed: " {:path path})
-                        (js/console.error error)))))))
-
-      js/window.pfs
-      (js/window.pfs.writeFile (str dir "/" path) content)
-
-      :else
-      nil)
-    (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 rename
+  [dir path]
+  (protocol/read-file (get-fs dir) dir path))
+
+(defn write-file!
+  [repo dir path content opts]
+  (->
+   (protocol/write-file! (get-fs dir) repo dir path content opts)
+   (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 rename!
   [repo old-path new-path]
   (cond
     ; See https://github.com/isomorphic-git/lightning-fs/issues/41
     (= old-path new-path)
     (p/resolved nil)
 
-    (local-db? old-path)
-    ;; create new file
-    ;; delete old file
-    (p/let [[dir basename] (util/get-dir-and-basename old-path)
-            [_ new-basename] (util/get-dir-and-basename new-path)
-            parts (->> (string/split new-path "/")
-                       (remove string/blank?))
-            dir (str "/" (first parts))
-            new-path (->> (rest parts)
-                          (string/join "/"))
-            handle (idb/get-item (str "handle" old-path))
-            file (.getFile handle)
-            content (.text file)
-            _ (write-file repo dir new-path content)]
-      (unlink old-path nil))
-
     :else
-    (js/window.pfs.rename old-path new-path)))
+    (protocol/rename! (get-fs old-path) repo old-path new-path)))
 
 (defn stat
   [dir path]
@@ -267,32 +82,16 @@
                              (subs path 1)
                              path))
                       "")]
-    (cond
-      (local-db? dir)
-      (if-let [file (get-nfs-file-handle (str "handle/"
-                                              (string/replace-first dir "/" "")
-                                              append-path))]
-        (p/let [file (.getFile file)]
-          (let [get-attr #(gobj/get file %)]
-            {:file/last-modified-at (get-attr "lastModified")
-             :file/size (get-attr "size")
-             :file/type (get-attr "type")}))
-        (p/rejected "File not exists"))
-
-      :else
-      (do
-        (js/window.pfs.stat (str dir append-path))))))
+    (protocol/stat (get-fs dir) dir path)))
 
 (defn mkdir-if-not-exists
   [dir]
   (when dir
-    (let [local? (config/local-db? dir)]
-      (when (or local? js/window.pfs)
-        (util/p-handle
-         (stat dir nil)
-         (fn [_stat])
-         (fn [error]
-           (mkdir dir)))))))
+    (util/p-handle
+     (stat dir nil)
+     (fn [_stat])
+     (fn [error]
+       (mkdir! dir)))))
 
 (defn create-if-not-exists
   ([repo dir path]
@@ -306,7 +105,7 @@
         true)
       (p/catch
        (fn [_error]
-         (p/let [_ (write-file repo dir path initial-content)]
+         (p/let [_ (write-file! repo dir path initial-content nil)]
            false)))))))
 
 (defn file-exists?
@@ -315,10 +114,3 @@
    (stat dir path)
    (fn [_stat] true)
    (fn [_e] false)))
-
-(defn check-directory-permission!
-  [repo]
-  (when (config/local-db? repo)
-    (p/let [handle (idb/get-item (str "handle/" repo))]
-      (when handle
-        (common-handler/verify-permission repo handle true)))))

+ 31 - 0
src/main/frontend/fs/bfs.cljs

@@ -0,0 +1,31 @@
+(ns frontend.fs.bfs
+  (:require [frontend.fs.protocol :as protocol]
+            [frontend.util :as util]
+            [clojure.string :as string]
+            [promesa.core :as p]))
+
+(defrecord Bfs []
+  protocol/Fs
+  (mkdir! [this dir]
+    (when js/window.pfs
+      (js/window.pfs.mkdir dir)))
+  (readdir [this dir]
+    (when js/window.pfs
+      (js/window.pfs.readdir dir)))
+  (unlink! [this path opts]
+    (when js/window.pfs
+      (p/let [stat (js/window.pfs.stat path)]
+        (if (= (.-type stat) "file")
+          (js/window.pfs.unlink path opts)
+          (p/rejected "Unlinking a directory is not allowed")))))
+  (rmdir! [this dir]
+    (js/window.workerThread.rimraf dir))
+  (read-file [this dir path]
+    (let [option (clj->js {:encoding "utf8"})]
+      (js/window.pfs.readFile (str dir "/" path) option)))
+  (write-file! [this repo dir path content opts]
+    (js/window.pfs.writeFile (str dir "/" path) content))
+  (rename! [this repo old-path new-path]
+    (js/window.pfs.rename old-path new-path))
+  (stat [this dir path]
+    (js/window.pfs.stat (str dir path))))

+ 196 - 0
src/main/frontend/fs/nfs.cljs

@@ -0,0 +1,196 @@
+(ns frontend.fs.nfs
+  (:require [frontend.fs.protocol :as protocol]
+            [frontend.util :as util]
+            [clojure.string :as string]
+            [frontend.idb :as idb]
+            [promesa.core :as p]
+            [lambdaisland.glogi :as log]
+            [goog.object :as gobj]
+            [frontend.db :as db]
+            [frontend.config :as config]
+            [frontend.state :as state]
+            ["/frontend/utils" :as utils]))
+
+;; We need to cache the file handles in the memory so that
+;; the browser will not keep asking permissions.
+(defonce nfs-file-handles-cache (atom {}))
+
+(defn get-nfs-file-handle
+  [handle-path]
+  (get @nfs-file-handles-cache handle-path))
+
+(defn add-nfs-file-handle!
+  [handle-path handle]
+  (swap! nfs-file-handles-cache assoc handle-path handle))
+
+(defn remove-nfs-file-handle!
+  [handle-path]
+  (swap! nfs-file-handles-cache dissoc handle-path))
+
+(defn nfs-saved-handler
+  [repo path file]
+  (when-let [last-modified (gobj/get file "lastModified")]
+    ;; TODO: extract
+    (let [path (if (= \/ (first path))
+                 (subs path 1)
+                 path)]
+      ;; Bad code
+      (db/set-file-last-modified-at! repo path last-modified))))
+
+(defn verify-permission
+  [repo handle read-write?]
+  (let [repo (or repo (state/get-current-repo))]
+    (p/then
+     (utils/verifyPermission handle read-write?)
+     (fn []
+       (state/set-state! [:nfs/user-granted? repo] true)
+       true))))
+
+(defn check-directory-permission!
+  [repo]
+  (when (config/local-db? repo)
+    (p/let [handle (idb/get-item (str "handle/" repo))]
+      (when handle
+        (verify-permission repo handle true)))))
+
+(defrecord Nfs []
+  protocol/Fs
+  (mkdir! [this dir]
+    (let [[root new-dir] (rest (string/split dir "/"))
+          root-handle (str "handle/" root)]
+      (->
+       (p/let [handle (idb/get-item root-handle)
+               _ (when handle (verify-permission nil handle true))]
+         (when (and handle new-dir
+                    (not (string/blank? new-dir)))
+           (p/let [handle (.getDirectoryHandle ^js handle new-dir
+                                               #js {:create true})
+                   handle-path (str root-handle "/" new-dir)
+                   _ (idb/set-item! handle-path handle)]
+             (add-nfs-file-handle! handle-path handle)
+             (println "Stored handle: " (str root-handle "/" new-dir)))))
+       (p/catch (fn [error]
+                  (js/console.debug "mkdir error: " error ", dir: " dir)
+                  (throw error))))))
+
+  (readdir [this dir]
+    (let [prefix (str "handle/" dir)
+          cached-files (keys @nfs-file-handles-cache)]
+      (p/resolved
+       (->> (filter #(string/starts-with? % (str prefix "/")) cached-files)
+            (map (fn [path]
+                   (string/replace path prefix "")))))))
+
+  (unlink! [this path opts]
+    (let [[dir basename] (util/get-dir-and-basename path)
+          handle-path (str "handle" path)]
+      (->
+       (p/let [handle (idb/get-item (str "handle" dir))
+               _ (idb/remove-item! handle-path)]
+         (when handle
+           (.removeEntry ^js handle basename))
+         (remove-nfs-file-handle! handle-path))
+       (p/catch (fn [error]
+                  (log/error :unlink/path {:path path
+                                           :error error}))))))
+
+  (rmdir! [this dir]
+    nil)
+
+  (read-file [this dir path]
+    (let [handle-path (str "handle" dir "/" path)]
+      (p/let [handle (idb/get-item handle-path)
+              local-file (and handle (.getFile handle))]
+        (and local-file (.text local-file)))))
+
+  (write-file! [this repo dir path content opts]
+    (let [{:keys [old-content last-modified-at]} opts
+          parts (string/split path "/")
+          basename (last parts)
+          sub-dir (->> (butlast parts)
+                       (remove string/blank?)
+                       (string/join "/"))
+          sub-dir-handle-path (str "handle/"
+                                   (subs dir 1)
+                                   (if sub-dir
+                                     (str "/" sub-dir)))
+          handle-path (if (= "/" (last sub-dir-handle-path))
+                        (subs sub-dir-handle-path 0 (dec (count sub-dir-handle-path)))
+                        sub-dir-handle-path)
+          basename-handle-path (str handle-path "/" basename)]
+      (p/let [file-handle (idb/get-item basename-handle-path)]
+        (when file-handle
+          (add-nfs-file-handle! basename-handle-path file-handle))
+        (if file-handle
+          (p/let [local-file (.getFile file-handle)
+                  local-content (.text local-file)
+                  local-last-modified-at (gobj/get local-file "lastModified")
+                  current-time (util/time-ms)
+                  new? (> current-time local-last-modified-at)
+                  new-created? (nil? last-modified-at)
+                  not-changed? (= last-modified-at local-last-modified-at)
+                  format (-> (util/get-file-ext path)
+                             (config/get-file-format))
+                  pending-writes (state/get-write-chan-length)]
+             ;; (println {:last-modified-at last-modified-at
+             ;;           :local-last-modified-at local-last-modified-at
+             ;;           :not-changed? not-changed?
+             ;;           :new-created? new-created?
+             ;;           :pending-writes pending-writes
+             ;;           :local-content local-content
+             ;;           :old-content old-content
+             ;;           :new? new?})
+            (if (and local-content old-content new?
+                     (or
+                      (> pending-writes 0)
+                      not-changed?
+                      new-created?))
+              (do
+                (p/let [_ (verify-permission repo file-handle true)
+                        _ (utils/writeFile file-handle content)
+                        file (.getFile file-handle)]
+                  (when file
+                    (nfs-saved-handler repo path file))))
+              (do
+                (js/alert (str "The file has been modified in your local disk! File path: " path
+                               ", save your changes and click the refresh button to reload it.")))))
+           ;; create file handle
+          (->
+           (p/let [handle (idb/get-item handle-path)]
+             (if handle
+               (do
+                 (p/let [_ (verify-permission repo handle true)
+                         file-handle (.getFileHandle ^js handle basename #js {:create true})
+                         _ (idb/set-item! basename-handle-path file-handle)
+                         _ (utils/writeFile file-handle content)
+                         file (.getFile file-handle)]
+                   (when file
+                     (nfs-saved-handler repo path file))))
+               (println "Error: directory handle not exists: " handle-path)))
+           (p/catch (fn [error]
+                      (println "Write local file failed: " {:path path})
+                      (js/console.error error))))))))
+
+  (rename! [this repo old-path new-path]
+    (p/let [[dir basename] (util/get-dir-and-basename old-path)
+            [_ new-basename] (util/get-dir-and-basename new-path)
+            parts (->> (string/split new-path "/")
+                       (remove string/blank?))
+            dir (str "/" (first parts))
+            new-path (->> (rest parts)
+                          (string/join "/"))
+            handle (idb/get-item (str "handle" old-path))
+            file (.getFile handle)
+            content (.text file)
+            _ (protocol/write-file! this repo dir new-path content nil)]
+      (protocol/unlink! this old-path nil)))
+  (stat [this dir path]
+    (if-let [file (get-nfs-file-handle (str "handle/"
+                                            (string/replace-first dir "/" "")
+                                            path))]
+      (p/let [file (.getFile file)]
+        (let [get-attr #(gobj/get file %)]
+          {:file/last-modified-at (get-attr "lastModified")
+           :file/size (get-attr "size")
+           :file/type (get-attr "type")}))
+      (p/rejected "File not exists"))))

+ 23 - 0
src/main/frontend/fs/node.cljs

@@ -0,0 +1,23 @@
+(ns frontend.fs.node
+  (:require [frontend.fs.protocol :as protocol]
+            [frontend.util :as util]
+            [clojure.string :as string]))
+
+(defrecord Node []
+  protocol/Fs
+  (mkdir! [this dir]
+    nil)
+  (readdir [this dir]
+    nil)
+  (unlink! [this path opts]
+    nil)
+  (rmdir! [this dir]
+    nil)
+  (read-file [this dir path]
+    nil)
+  (write-file! [this repo dir path content opts]
+    nil)
+  (rename! [this repo old-path new-path]
+    nil)
+  (stat [this dir path]
+    nil))

+ 11 - 0
src/main/frontend/fs/protocol.cljs

@@ -0,0 +1,11 @@
+(ns frontend.fs.protocol)
+
+(defprotocol Fs
+  (mkdir! [this dir])
+  (readdir [this dir])
+  (unlink! [this path opts])
+  (rmdir! [this dir])
+  (read-file [this dir path])
+  (write-file! [this repo dir path content opts])
+  (rename! [this repo old-path new-path])
+  (stat [this dir path]))

+ 28 - 38
src/main/frontend/handler/common.cljs

@@ -68,8 +68,7 @@
             diffs (git/get-diffs repo local-oid remote-oid)]
       (println {:local-oid local-oid
                 :remote-oid remote-oid
-                :diffs diffs})))
-  )
+                :diffs diffs}))))
 
 (defn get-config
   [repo-url]
@@ -91,20 +90,20 @@
   [ok-handler error-handler]
   (let [repos (state/get-repos)
         installation-ids (->> (map :installation_id repos)
-                           (remove nil?)
-                           (distinct))]
+                              (remove nil?)
+                              (distinct))]
     (when (or (seq repos)
-            (seq installation-ids))
+              (seq installation-ids))
       (util/post (str config/api "refresh_github_token")
-        {:installation-ids installation-ids
-         :repos repos}
-        (fn [result]
-          (state/set-github-installation-tokens! result)
-          (when ok-handler (ok-handler)))
-        (fn [error]
-          (log/error :token/http-request-failed error)
-          (js/console.dir error)
-          (when error-handler (error-handler)))))))
+                 {:installation-ids installation-ids
+                  :repos repos}
+                 (fn [result]
+                   (state/set-github-installation-tokens! result)
+                   (when ok-handler (ok-handler)))
+                 (fn [error]
+                   (log/error :token/http-request-failed error)
+                   (js/console.dir error)
+                   (when error-handler (error-handler)))))))
 
 (defn- get-github-token*
   [repo]
@@ -114,7 +113,7 @@
           (state/get-github-token repo)]
       (spec/validate :repos/repo token-state)
       (if (and (map? token-state)
-            (string? expires_at))
+               (string? expires_at))
         (let [expires-at (tf/parse (tf/formatters :date-time-no-ms) expires_at)
               now (t/now)
               expired? (t/after? now expires-at)]
@@ -129,26 +128,17 @@
   ([repo]
    (when-not (config/local-db? repo)
      (js/Promise.
-       (fn [resolve reject]
-         (let [{:keys [expired? token exist?]} (get-github-token* repo)
-               valid-token? (and exist? (not expired?))]
-           (if valid-token?
-             (resolve token)
-             (request-app-tokens!
-               (fn []
-                 (let [{:keys [expired? token exist?] :as token-m} (get-github-token* repo)
-                       valid-token? (and exist? (not expired?))]
-                   (if valid-token?
-                     (resolve token)
-                     (do (log/error :token/failed-get-token token-m)
-                         (reject)))))
-               nil))))))))
-
-(defn verify-permission
-  [repo handle read-write?]
-  (let [repo (or repo (state/get-current-repo))]
-    (p/then
-      (utils/verifyPermission handle read-write?)
-      (fn []
-        (state/set-state! [:nfs/user-granted? repo] true)
-        true))))
+      (fn [resolve reject]
+        (let [{:keys [expired? token exist?]} (get-github-token* repo)
+              valid-token? (and exist? (not expired?))]
+          (if valid-token?
+            (resolve token)
+            (request-app-tokens!
+             (fn []
+               (let [{:keys [expired? token exist?] :as token-m} (get-github-token* repo)
+                     valid-token? (and exist? (not expired?))]
+                 (if valid-token?
+                   (resolve token)
+                   (do (log/error :token/failed-get-token token-m)
+                       (reject)))))
+             nil))))))))

+ 5 - 5
src/main/frontend/handler/draw.cljs

@@ -49,10 +49,10 @@
   [repo]
   (when repo
     (let [repo-dir (util/get-repo-dir repo)]
-     (util/p-handle
-      (fs/mkdir (str repo-dir (str "/" config/default-draw-directory)))
-      (fn [_result] nil)
-      (fn [_error] nil)))))
+      (util/p-handle
+       (fs/mkdir! (str repo-dir (str "/" config/default-draw-directory)))
+       (fn [_result] nil)
+       (fn [_error] nil)))))
 
 (defn save-excalidraw!
   [file data ok-handler]
@@ -63,7 +63,7 @@
         (->
          (p/do!
           (create-draws-directory! repo)
-          (fs/write-file repo repo-dir path data)
+          (fs/write-file! repo repo-dir path data nil)
           (git-handler/git-add repo path)
           (ok-handler file)
           (let [modified-at (tc/to-long (t/now))]

+ 6 - 6
src/main/frontend/handler/editor.cljs

@@ -609,10 +609,10 @@
              ;; fix editing template with multiple headings
              (when (> (count blocks) 1)
                (let [new-value (-> (text/remove-level-spaces (:block/content (first blocks)) (:block/format (first blocks)))
-                                  (string/trim-newline))
-                    edit-input-id (state/get-edit-input-id)]
-                (when edit-input-id
-                  (state/set-edit-content! edit-input-id new-value))))
+                                   (string/trim-newline))
+                     edit-input-id (state/get-edit-input-id)]
+                 (when edit-input-id
+                   (state/set-edit-content! edit-input-id new-value))))
 
              (when (or (seq retract-refs) pre-block?)
                (ui-handler/re-render-root!))
@@ -1565,7 +1565,7 @@
             filename (str (gen-filename index file) ext)
             filename (str path "/" filename)]
         ;(js/console.debug "Write asset #" filename file)
-        (p/then (fs/write-file repo dir filename (.stream file))
+        (p/then (fs/write-file! repo dir filename (.stream file) nil)
                 #(p/resolved [filename file])))))))
 
 (defonce *assets-url-cache (atom {}))
@@ -1595,7 +1595,7 @@
     (save-block! repo block content)
     (when local?
       ;; FIXME: should be relative to current block page path
-      (fs/unlink (str (util/get-repo-dir repo) (string/replace href #"^../" "/")) nil))))
+      (fs/unlink! (str (util/get-repo-dir repo) (string/replace href #"^../" "/")) nil))))
 
 (defn upload-image
   [id files format uploading? drop-or-paste?]

+ 12 - 10
src/main/frontend/handler/file.cljs

@@ -2,6 +2,7 @@
   (:refer-clojure :exclude [load-file])
   (:require [frontend.util :as util :refer-macros [profile]]
             [frontend.fs :as fs]
+            [frontend.fs.nfs :as nfs]
             [promesa.core :as p]
             [frontend.state :as state]
             [frontend.db :as db]
@@ -161,8 +162,8 @@
         (reset-file! repo path content))
       (db/set-file-content! repo path content))
     (util/p-handle
-     (fs/write-file repo (util/get-repo-dir repo) path content {:old-content original-content
-                                                                :last-modified-at (db/get-file-last-modified-at repo path)})
+     (fs/write-file! repo (util/get-repo-dir repo) path content {:old-content original-content
+                                                                 :last-modified-at (db/get-file-last-modified-at repo path)})
      (fn [_]
        (git-handler/git-add repo path update-status?)
        (when (= path (str config/app-name "/" config/config-file))
@@ -220,10 +221,10 @@
                     reset? false}} file->content]
   (let [write-file-f (fn [[path content]]
                        (let [original-content (get file->content path)]
-                         (-> (p/let [_ (fs/check-directory-permission! repo)]
-                               (fs/write-file repo (util/get-repo-dir repo) path content
-                                              {:old-content original-content
-                                               :last-modified-at (db/get-file-last-modified-at repo path)}))
+                         (-> (p/let [_ (nfs/check-directory-permission! repo)]
+                               (fs/write-file! repo (util/get-repo-dir repo) path content
+                                               {:old-content original-content
+                                                :last-modified-at (db/get-file-last-modified-at repo path)}))
                              (p/catch (fn [error]
                                         (log/error :write-file/failed {:path path
                                                                        :content content
@@ -263,10 +264,11 @@
   (when-not (string/blank? file)
     (->
      (p/let [_ (git/remove-file repo file)
-             result (fs/unlink (str (util/get-repo-dir repo)
-                                    "/"
-                                    file)
-                               nil)]
+             result (fs/unlink! (str
+                                 (util/get-repo-dir repo)
+                                 "/"
+                                 file)
+                                nil)]
        (when-let [file (db/entity repo [:file/path file])]
          (common-handler/check-changed-files-status)
          (let [file-id (:db/id file)

+ 1 - 2
src/main/frontend/handler/image.cljs

@@ -35,8 +35,7 @@
                      path)]
           (util/p-handle
            (fs/read-file (util/get-repo-dir (state/get-current-repo))
-                           path
-                           {})
+                         path)
            (fn [blob]
              (let [blob (js/Blob. (array blob) (clj->js {:type "image"}))
                    img-url (image/create-object-url blob)]

+ 8 - 8
src/main/frontend/handler/page.cljs

@@ -50,7 +50,7 @@
          journal-page? (date/valid-journal-title? title)
          directory (get-directory journal-page?)]
      (when dir
-       (p/let [_ (-> (fs/mkdir (str dir "/" directory))
+       (p/let [_ (-> (fs/mkdir! (str dir "/" directory))
                      (p/catch (fn [_e])))]
          (let [format (name (state/get-preferred-format))
                page (string/lower-case title)
@@ -273,10 +273,10 @@
               ;; remove file
               (->
                (p/let [_ (git/remove-file repo file-path)
-                       _ (fs/unlink (str (util/get-repo-dir repo)
-                                         "/"
-                                         file-path)
-                                    nil)]
+                       _ (fs/unlink! (str (util/get-repo-dir repo)
+                                          "/"
+                                          file-path)
+                                     nil)]
                  (common-handler/check-changed-files-status)
                  (repo-handler/push-if-auto-enabled! repo))
                (p/catch (fn [err]
@@ -310,9 +310,9 @@
         (d/transact! conn [{:db/id (:db/id file)
                             :file/path new-path}])))
     (->
-     (p/let [_ (fs/rename repo
-                          (str (util/get-repo-dir repo) "/" old-path)
-                          (str (util/get-repo-dir repo) "/" new-path))
+     (p/let [_ (fs/rename! repo
+                           (str (util/get-repo-dir repo) "/" old-path)
+                           (str (util/get-repo-dir repo) "/" new-path))
              _ (when-not (config/local-db? repo)
                  (git/rename repo old-path new-path))]
        (common-handler/check-changed-files-status)

+ 6 - 6
src/main/frontend/handler/repo.cljs

@@ -2,6 +2,7 @@
   (:refer-clojure :exclude [clone])
   (:require [frontend.util :as util :refer-macros [profile]]
             [frontend.fs :as fs]
+            [frontend.fs.nfs :as nfs]
             [promesa.core :as p]
             [lambdaisland.glogi :as log]
             [frontend.state :as state]
@@ -136,7 +137,7 @@
            empty-blocks? (empty? (db/get-page-blocks-no-cache repo-url (string/lower-case title)))]
        (when (or empty-blocks?
                  (not page-exists?))
-         (p/let [_ (fs/check-directory-permission! repo-url)
+         (p/let [_ (nfs/check-directory-permission! repo-url)
                  _ (fs/mkdir-if-not-exists (str repo-dir "/" config/default-journals-directory))
                  file-exists? (fs/create-if-not-exists repo-url repo-dir file-path content)]
            (when-not file-exists?
@@ -319,7 +320,7 @@
                                      (util/get-block-idx-inside-container block-element))]
        (when (and idx container)
          (state/set-state! :editor/last-edit-block {:block edit-block
-                                               :idx idx
+                                                    :idx idx
                                                     :container (gobj/get container "id")})))
 
      (db/transact-react!
@@ -331,8 +332,7 @@
          (when (seq children-tx)
            (db/transact! repo children-tx))))
      (when (seq files)
-       (file-handler/alter-files repo files opts))
-     )))
+       (file-handler/alter-files repo files opts)))))
 
 (declare push)
 
@@ -539,7 +539,7 @@
                       (db/remove-conn! url)
                       (db/remove-db! url)
                       (db/remove-files-db! url)
-                      (fs/rmdir (util/get-repo-dir url))
+                      (fs/rmdir! (util/get-repo-dir url))
                       (state/delete-repo! repo))]
     (if (config/local-db? url)
       (p/let [_ (idb/clear-local-db! url)] ; clear file handles
@@ -645,7 +645,7 @@
     (db/clear-query-state!)
     (-> (p/do! (db/remove-db! url)
                (db/remove-files-db! url)
-               (fs/rmdir (util/get-repo-dir url))
+               (fs/rmdir! (util/get-repo-dir url))
                (clone-and-load-db url))
         (p/catch (fn [error]
                    (prn "Delete repo failed, error: " error))))))

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

@@ -16,6 +16,7 @@
             [clojure.set :as set]
             [frontend.ui :as ui]
             [frontend.fs :as fs]
+            [frontend.fs.nfs :as nfs]
             [frontend.db :as db]
             [frontend.db.model :as db-model]
             [frontend.config :as config]
@@ -86,7 +87,7 @@
                          [handle-path handle]))
                      handles)]
     (doseq [[path handle] handles]
-      (fs/add-nfs-file-handle! path handle))
+      (nfs/add-nfs-file-handle! path handle))
     (set-files-aux! handles)))
 
 (defn ls-dir-files
@@ -103,7 +104,7 @@
              repo (str config/local-db-prefix dir-name)
              root-handle-path (str config/local-handle-prefix dir-name)
              _ (idb/set-item! root-handle-path root-handle)
-             _ (fs/add-nfs-file-handle! root-handle-path root-handle)
+             _ (nfs/add-nfs-file-handle! root-handle-path root-handle)
              result (nth result 1)
              files (-> (->db-files dir-name result)
                        remove-ignore-files)
@@ -164,7 +165,7 @@
      (ui/button
       "Grant"
       :on-click (fn []
-                  (fs/check-directory-permission! repo)
+                  (nfs/check-directory-permission! repo)
                   (close-fn)))]))
 
 (defn ask-permission-if-local? []
@@ -205,7 +206,7 @@
        (->
         (p/let [handle (idb/get-item handle-path)]
           (when handle
-            (p/let [_ (when handle (common-handler/verify-permission repo handle true))
+            (p/let [_ (when handle (nfs/verify-permission repo handle true))
                     files-result (utils/getFiles handle true
                                                  (fn [path handle]
                                                    (swap! path-handles assoc path handle)))
@@ -232,7 +233,7 @@
                           (p/all (map (fn [path]
                                         (let [handle-path (str handle-path path)]
                                           (idb/remove-item! handle-path)
-                                          (fs/remove-nfs-file-handle! handle-path))) deleted))))
+                                          (nfs/remove-nfs-file-handle! handle-path))) deleted))))
                     added-or-modified (set (concat added modified))
                     _ (when (seq added-or-modified)
                         (p/all (map (fn [path]
@@ -289,4 +290,4 @@
 
 (defn supported?
   []
-  (utils/nfsSupported))
+  (utils/nfsSupported))

+ 6 - 0
src/main/frontend/util.cljc

@@ -50,6 +50,12 @@
       (when-not node-test?
         (re-find #"Mobi" js/navigator.userAgent))))
 
+#?(:cljs
+   (defn electron?
+     []
+     (let [ua (string/lower-case js/navigator.userAgent)]
+       (string/includes? ua " electron"))))
+
 (defn format
   [fmt & args]
   #?(:cljs (apply gstring/format fmt args)

+ 1 - 15
yarn.lock

@@ -1746,13 +1746,6 @@ each-props@^1.3.0:
     is-plain-object "^2.0.1"
     object.defaults "^1.1.0"
 
-electron-better-ipc@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/electron-better-ipc/-/electron-better-ipc-1.1.1.tgz#bbb1aa567b97a0cdcf61bcc464a35df4ec6fd664"
-  integrity sha512-Y8Ty54HJtzT6aWNTyE092W/7q8y3+yJz0XvRpOKGIsLDL2H7oWIFkUegmB+6h5SszjtBiQFpmNmHOKfkDZ8HbQ==
-  dependencies:
-    serialize-error "^5.0.0"
-
 electron-to-chromium@^1.3.621:
   version "1.3.625"
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.625.tgz#a7bd18da4dc732c180b2e95e0e296c0bf22f3bd6"
@@ -5228,13 +5221,6 @@ semver@^7.3.2:
   dependencies:
     lru-cache "^6.0.0"
 
-serialize-error@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac"
-  integrity sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==
-  dependencies:
-    type-fest "^0.8.0"
-
 set-blocking@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -5939,7 +5925,7 @@ type-fest@^0.6.0:
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
   integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
 
-type-fest@^0.8.0, type-fest@^0.8.1:
+type-fest@^0.8.1:
   version "0.8.1"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
   integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==