mirror of
https://github.com/logseq/logseq.git
synced 2026-02-01 22:47:36 +00:00
chore: remove @logseq/capacitor-file-sync and capacitor fs
This commit is contained in:
@@ -111,8 +111,7 @@
|
||||
"@js-joda/core": "3.2.0",
|
||||
"@js-joda/locale_en-us": "3.1.1",
|
||||
"@js-joda/timezone": "2.5.0",
|
||||
"@logseq/capacitor-file-sync": "5.0.2",
|
||||
"@logseq/diff-merge": "0.2.2",
|
||||
"@logseq/diff-merge": "^0.2.2",
|
||||
"@logseq/react-tweet-embed": "1.3.1-1",
|
||||
"@logseq/sqlite-wasm": "=0.1.0",
|
||||
"@radix-ui/colors": "^0.1.8",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
(ns frontend.db.persist
|
||||
"Handles operations to persisting db to disk or indexedDB"
|
||||
(:require [frontend.util :as util]
|
||||
[frontend.idb :as idb]
|
||||
(:require [cljs-bean.core :as bean]
|
||||
[electron.ipc :as ipc]
|
||||
[frontend.config :as config]
|
||||
[frontend.db.conn :as db-conn]
|
||||
[promesa.core :as p]
|
||||
[frontend.idb :as idb]
|
||||
[frontend.persist-db :as persist-db]
|
||||
[cljs-bean.core :as bean]
|
||||
[frontend.config :as config]))
|
||||
[frontend.util :as util]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn get-all-graphs
|
||||
[]
|
||||
@@ -24,7 +24,7 @@
|
||||
(distinct (concat
|
||||
repos'
|
||||
(map (fn [repo-name] {:name repo-name})
|
||||
(concat idb-repos (some-> electron-disk-graphs bean/->clj)))))))
|
||||
(concat idb-repos (some-> electron-disk-graphs bean/->clj)))))))
|
||||
|
||||
(defn delete-graph!
|
||||
[graph]
|
||||
@@ -34,13 +34,3 @@
|
||||
(if (util/electron?)
|
||||
(ipc/ipc "deleteGraph" graph key db-based?)
|
||||
(idb/remove-item! key)))))
|
||||
|
||||
(defn rename-graph!
|
||||
[old-repo new-repo]
|
||||
(let [old-key (db-conn/get-repo-path old-repo)
|
||||
new-key (db-conn/get-repo-path new-repo)]
|
||||
(if (util/electron?)
|
||||
(do
|
||||
(js/console.error "rename-graph! is not supported in electron")
|
||||
(idb/rename-item! old-key new-key))
|
||||
(idb/rename-item! old-key new-key))))
|
||||
|
||||
@@ -1,41 +1,20 @@
|
||||
(ns frontend.encrypt
|
||||
"Encryption related fns for use with encryption feature and file sync"
|
||||
(:require [logseq.graph-parser.utf8 :as utf8]
|
||||
(:require [electron.ipc :as ipc]
|
||||
[frontend.util :as util]
|
||||
[promesa.core :as p]
|
||||
[electron.ipc :as ipc]
|
||||
[frontend.mobile.util :as mobile-util]))
|
||||
[logseq.graph-parser.utf8 :as utf8]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn encrypt-with-passphrase
|
||||
[passphrase content]
|
||||
(cond
|
||||
(util/electron?)
|
||||
(when (util/electron?)
|
||||
(p/let [raw-content (utf8/encode content)
|
||||
encrypted (ipc/ipc "encrypt-with-passphrase" passphrase raw-content)]
|
||||
(utf8/decode encrypted))
|
||||
|
||||
(mobile-util/native-platform?)
|
||||
(p/chain (.encryptWithPassphrase mobile-util/file-sync
|
||||
(clj->js {:passphrase passphrase :content content}))
|
||||
#(js->clj % :keywordize-keys true)
|
||||
:data)
|
||||
|
||||
:else
|
||||
nil))
|
||||
(utf8/decode encrypted))))
|
||||
|
||||
(defn decrypt-with-passphrase
|
||||
[passphrase content]
|
||||
(cond
|
||||
(util/electron?)
|
||||
(when (util/electron?)
|
||||
(p/let [raw-content (utf8/encode content)
|
||||
decrypted (ipc/ipc "decrypt-with-passphrase" passphrase raw-content)]
|
||||
(utf8/decode decrypted))
|
||||
|
||||
(mobile-util/native-platform?)
|
||||
(p/chain (.decryptWithPassphrase mobile-util/file-sync
|
||||
(clj->js {:passphrase passphrase :content content}))
|
||||
#(js->clj % :keywordize-keys true)
|
||||
:data)
|
||||
|
||||
:else
|
||||
nil))
|
||||
(utf8/decode decrypted))))
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
[clojure.string :as string]
|
||||
[electron.ipc :as ipc]
|
||||
[frontend.config :as config]
|
||||
[frontend.fs.capacitor-fs :as capacitor-fs]
|
||||
[frontend.fs.memory-fs :as memory-fs]
|
||||
[frontend.fs.node :as node]
|
||||
[frontend.fs.protocol :as protocol]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[lambdaisland.glogi :as log]
|
||||
@@ -19,17 +17,12 @@
|
||||
|
||||
(defonce memory-backend (memory-fs/->MemoryFs))
|
||||
(defonce node-backend (node/->Node))
|
||||
(defonce mobile-backend (capacitor-fs/->Capacitorfs))
|
||||
|
||||
(defn- get-native-backend
|
||||
"Native FS backend of current platform"
|
||||
[]
|
||||
(cond
|
||||
(util/electron?)
|
||||
node-backend
|
||||
|
||||
(mobile-util/native-platform?)
|
||||
mobile-backend))
|
||||
(when (util/electron?)
|
||||
node-backend))
|
||||
|
||||
(defn get-fs
|
||||
[dir & {:keys [repo rpath]}]
|
||||
@@ -58,7 +51,7 @@
|
||||
node-backend
|
||||
|
||||
:else
|
||||
mobile-backend)))
|
||||
nil)))
|
||||
|
||||
(defn mkdir!
|
||||
[dir]
|
||||
@@ -166,7 +159,7 @@
|
||||
|
||||
:else
|
||||
(let [[old-path new-path]
|
||||
(map #(if (or (util/electron?) (mobile-util/native-platform?))
|
||||
(map #(if (util/electron?)
|
||||
%
|
||||
(str (config/get-repo-dir repo) "/" %))
|
||||
[old-path new-path])
|
||||
@@ -246,9 +239,6 @@
|
||||
(util/electron?)
|
||||
(path/url-to-path path)
|
||||
|
||||
(mobile-util/native-platform?)
|
||||
path
|
||||
|
||||
:else
|
||||
path))
|
||||
|
||||
@@ -258,12 +248,5 @@
|
||||
|
||||
(defn backup-db-file!
|
||||
[repo path db-content disk-content]
|
||||
(cond
|
||||
(util/electron?)
|
||||
(ipc/ipc "backupDbFile" (config/get-local-dir repo) path db-content disk-content)
|
||||
|
||||
(mobile-util/native-platform?)
|
||||
(capacitor-fs/backup-file repo :backup-dir path db-content)
|
||||
|
||||
;; TODO: nfs
|
||||
))
|
||||
(when (util/electron?)
|
||||
(ipc/ipc "backupDbFile" (config/get-local-dir repo) path db-content disk-content)))
|
||||
|
||||
@@ -1,392 +0,0 @@
|
||||
(ns frontend.fs.capacitor-fs
|
||||
"Implementation of fs protocol for mobile"
|
||||
(:require ["@capacitor/filesystem" :refer [Encoding Filesystem]]
|
||||
[cljs-bean.core :as bean]
|
||||
[clojure.string :as string]
|
||||
[frontend.config :as config]
|
||||
[frontend.db :as db]
|
||||
[frontend.fs.protocol :as protocol]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[goog.string :as gstring]
|
||||
[lambdaisland.glogi :as log]
|
||||
[logseq.common.path :as path]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(when (mobile-util/native-ios?)
|
||||
(defn ios-ensure-documents!
|
||||
[]
|
||||
(.ensureDocuments mobile-util/ios-file-container)))
|
||||
|
||||
(when (mobile-util/native-android?)
|
||||
(defn- android-check-permission []
|
||||
(p/let [permission (.checkPermissions Filesystem)
|
||||
permission (-> permission
|
||||
bean/->clj
|
||||
:publicStorage)]
|
||||
(when-not (= permission "granted")
|
||||
(p/do!
|
||||
(.requestPermissions Filesystem))))))
|
||||
|
||||
(defn- <dir-exists?
|
||||
[fpath]
|
||||
(p/catch (p/let [fpath (path/path-normalize fpath)
|
||||
stat (.stat Filesystem (clj->js {:path fpath}))]
|
||||
(-> stat
|
||||
bean/->clj
|
||||
:type
|
||||
(= "directory")))
|
||||
(fn [_error]
|
||||
false)))
|
||||
|
||||
(defn- <write-file-with-utf8
|
||||
[path content]
|
||||
(when-not (string/blank? path)
|
||||
(-> (p/chain (.writeFile Filesystem (clj->js {:path path
|
||||
:data content
|
||||
:encoding (.-UTF8 Encoding)
|
||||
:recursive true}))
|
||||
#(js->clj % :keywordize-keys true))
|
||||
(p/catch (fn [error]
|
||||
(js/console.error "writeFile Error: " path ": " error)
|
||||
nil)))))
|
||||
|
||||
(defn- <read-file-with-utf8
|
||||
[path]
|
||||
(when-not (string/blank? path)
|
||||
(-> (p/chain (.readFile Filesystem (clj->js {:path path
|
||||
:encoding (.-UTF8 Encoding)}))
|
||||
#(js->clj % :keywordize-keys true)
|
||||
#(get % :data nil))
|
||||
(p/catch (fn [error]
|
||||
(js/console.error "readFile Error: " path ": " error)
|
||||
nil)))))
|
||||
|
||||
(defn- <readdir [path]
|
||||
(-> (p/chain (.readdir Filesystem (clj->js {:path path}))
|
||||
#(js->clj % :keywordize-keys true)
|
||||
:files)
|
||||
(p/catch (fn [error]
|
||||
(js/console.error "readdir Error: " path ": " error)
|
||||
nil))))
|
||||
|
||||
(defn- get-file-paths
|
||||
"get all file paths recursively"
|
||||
[path]
|
||||
(p/let [result (p/loop [result []
|
||||
dirs [path]]
|
||||
(if (empty? dirs)
|
||||
result
|
||||
(p/let [d (first dirs)
|
||||
files (<readdir d)
|
||||
files (->> files
|
||||
(remove (fn [{:keys [name type]}]
|
||||
(or (string/starts-with? name ".")
|
||||
(and (= type "directory")
|
||||
(or (= name "bak")
|
||||
(= name "version-files")))))))
|
||||
files-dir (->> files
|
||||
(filterv #(= (:type %) "directory"))
|
||||
(mapv :uri))
|
||||
paths-result (->> files
|
||||
(filterv #(= (:type %) "file"))
|
||||
(mapv :uri))]
|
||||
(p/recur (concat result paths-result)
|
||||
(concat (rest dirs) files-dir)))))]
|
||||
result))
|
||||
|
||||
(defn- get-files
|
||||
"get all files and contents recursively"
|
||||
[path]
|
||||
(p/let [result (p/loop [result []
|
||||
dirs [path]]
|
||||
(if (empty? dirs)
|
||||
result
|
||||
(p/let [d (first dirs)
|
||||
files (<readdir d)
|
||||
files (->> files
|
||||
(remove (fn [{:keys [name type]}]
|
||||
(or (string/starts-with? name ".")
|
||||
(and (= type "directory")
|
||||
(or (= name "bak")
|
||||
(= name "version-files")))))))
|
||||
files-dir (->> files
|
||||
(filterv #(= (:type %) "directory"))
|
||||
(mapv :uri))
|
||||
files-result
|
||||
(p/all
|
||||
(->> files
|
||||
(filter #(= (:type %) "file"))
|
||||
(filter
|
||||
(fn [{:keys [uri]}]
|
||||
(some #(string/ends-with? uri %)
|
||||
[".md" ".markdown" ".org" ".edn" ".css"])))
|
||||
(mapv
|
||||
(fn [{:keys [uri] :as file-info}]
|
||||
(p/chain (<read-file-with-utf8 uri)
|
||||
#(assoc (dissoc file-info :uri)
|
||||
:content %
|
||||
:path uri))))))]
|
||||
(p/recur (concat result files-result)
|
||||
(concat (rest dirs) files-dir)))))]
|
||||
(js->clj result :keywordize-keys true)))
|
||||
|
||||
(defn- <contents-matched?
|
||||
[disk-content db-content]
|
||||
(when (and (string? disk-content) (string? 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")
|
||||
|
||||
(defn- get-backup-dir
|
||||
[repo-dir path bak-dir ext]
|
||||
(let [relative-path (-> path
|
||||
(string/replace (re-pattern (str "^" (gstring/regExpEscape repo-dir)))
|
||||
"")
|
||||
(string/replace (re-pattern (str "(?i)" (gstring/regExpEscape (str "." ext)) "$"))
|
||||
""))]
|
||||
(path/path-join repo-dir bak-dir relative-path)))
|
||||
|
||||
(defn- <truncate-old-versioned-files!
|
||||
"reserve the latest 6 version files"
|
||||
[dir]
|
||||
(-> (p/let [files (.readdir Filesystem (clj->js {:path dir}))
|
||||
|
||||
files (:files (js->clj files :keywordize-keys true))]
|
||||
(drop 6 (reverse (sort-by :mtime files))))
|
||||
(p/then (fn [old-version-files]
|
||||
(p/all (mapv (fn [file]
|
||||
(.deleteFile Filesystem (clj->js {:path (:uri file)})))
|
||||
old-version-files))))
|
||||
(p/catch (fn [_]))))
|
||||
|
||||
;; TODO: move this to FS protocol
|
||||
(defn backup-file
|
||||
"backup CONTENT under DIR :backup-dir or :version-file-dir
|
||||
:backup-dir = `backup-dir`
|
||||
:version-file-dir = `version-file-dir`"
|
||||
[repo dir path content]
|
||||
{:pre [(contains? #{:backup-dir :version-file-dir} dir)]}
|
||||
(let [repo-dir (config/get-local-dir repo)
|
||||
ext (util/get-file-ext path)
|
||||
dir (case dir
|
||||
:backup-dir (get-backup-dir repo-dir path backup-dir ext)
|
||||
:version-file-dir (get-backup-dir repo-dir path version-file-dir ext))
|
||||
new-path (path/path-join dir (str (string/replace (.toISOString (js/Date.)) ":" "_") "." (mobile-util/platform) "." ext))]
|
||||
|
||||
(<write-file-with-utf8 new-path content)
|
||||
(<truncate-old-versioned-files! dir)))
|
||||
|
||||
(defn backup-file-handle-changed!
|
||||
[repo-dir file-path content]
|
||||
(let [divider-schema "://"
|
||||
file-schema (string/split file-path divider-schema)
|
||||
file-schema (if (> (count file-schema) 1) (first file-schema) "")
|
||||
dir-schema? (and (string? repo-dir)
|
||||
(string/includes? repo-dir divider-schema))
|
||||
repo-dir (if-not dir-schema?
|
||||
(str file-schema divider-schema repo-dir) repo-dir)
|
||||
backup-root (path/path-join repo-dir backup-dir)
|
||||
backup-dir-parent (util/node-path.dirname file-path)
|
||||
backup-dir-parent (string/replace backup-dir-parent repo-dir "")
|
||||
backup-dir-name (util/node-path.name file-path)
|
||||
file-extname (.extname util/node-path file-path)
|
||||
file-root (path/path-join backup-root backup-dir-parent backup-dir-name)
|
||||
file-path (path/path-join file-root
|
||||
(str (string/replace (.toISOString (js/Date.)) ":" "_") "." (mobile-util/platform) file-extname))]
|
||||
(<write-file-with-utf8 file-path content)
|
||||
(<truncate-old-versioned-files! file-root)))
|
||||
|
||||
(defn- write-file-impl!
|
||||
[repo dir rpath content {:keys [ok-handler error-handler old-content skip-compare?]} stat]
|
||||
(let [fpath (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
|
||||
(p/let [disk-content (if (not= stat :not-found)
|
||||
(<read-file-with-utf8 fpath)
|
||||
"")
|
||||
disk-content (or disk-content "")
|
||||
repo-dir (config/get-local-dir repo)
|
||||
db-content (or old-content (db/get-file repo rpath) "")
|
||||
contents-matched? (<contents-matched? disk-content db-content)]
|
||||
(->
|
||||
(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 rpath mtime)
|
||||
(db/set-file-content! repo rpath 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
|
||||
"iOS sometimes return paths without the private part."
|
||||
[path]
|
||||
(if (mobile-util/native-ios?)
|
||||
(cond
|
||||
(or (string/includes? path "///private/")
|
||||
;; virtual machine
|
||||
(string/starts-with? path "file:///Users/"))
|
||||
path
|
||||
|
||||
(string/includes? path "///")
|
||||
(let [[prefix others] (string/split path "///")]
|
||||
(str prefix "///private/" others))
|
||||
|
||||
:else
|
||||
path)
|
||||
path))
|
||||
|
||||
(defn- local-container-path?
|
||||
"Check whether `path' is logseq's container `localDocumentsPath' on iOS"
|
||||
[path localDocumentsPath]
|
||||
(string/includes? path localDocumentsPath))
|
||||
|
||||
(rum/defc instruction
|
||||
[]
|
||||
[:div.instruction
|
||||
[:h1.title "Please choose a valid directory!"]
|
||||
[:p.leading-6 "Logseq app can only save or access your graphs stored in a specific directory with a "
|
||||
[:strong "Logseq icon"]
|
||||
" inside, located either in \"iCloud Drive\", \"On My iPhone\" or \"On My iPad\"."]
|
||||
[:p.leading-6 "Please watch the following short instruction video. "
|
||||
[:small.text-gray-500 "(may take few seconds to load...)"]]
|
||||
[:iframe
|
||||
{:src "https://www.loom.com/embed/dae612ae5fd94e508bd0acdf02efb888"
|
||||
:frame-border "0"
|
||||
:position "relative"
|
||||
:allow-full-screen "allowfullscreen"
|
||||
:webkit-allow-full-screen "webkitallowfullscreen"
|
||||
:height "100%"}]])
|
||||
|
||||
(defn- open-dir
|
||||
[dir]
|
||||
(p/let [_ (when (mobile-util/native-android?) (android-check-permission))
|
||||
{:keys [path localDocumentsPath]} (-> (.pickFolder mobile-util/folder-picker
|
||||
(clj->js (when (and dir (mobile-util/native-ios?))
|
||||
{:path dir})))
|
||||
(p/then #(js->clj % :keywordize-keys true))
|
||||
(p/catch (fn [e]
|
||||
(js/alert (str e))
|
||||
nil))) ;; NOTE: If pick folder fails, let it crash
|
||||
_ (when (and (mobile-util/native-ios?)
|
||||
(not (or (local-container-path? path localDocumentsPath)
|
||||
(mobile-util/in-iCloud-container-path? path))))
|
||||
(state/pub-event! [:modal/show-instruction]))
|
||||
exists? (<dir-exists? path)
|
||||
_ (when-not exists?
|
||||
(p/rejected (str "Cannot access selected directory: " path)))
|
||||
_ (when (mobile-util/is-iCloud-container-path? path)
|
||||
(p/rejected (str "Please avoid accessing the top-level iCloud container path: " path)))
|
||||
path (if (mobile-util/native-ios?)
|
||||
(ios-force-include-private path)
|
||||
path)
|
||||
_ (js/console.log "Opening or Creating graph at directory: " path)
|
||||
files (get-files path)]
|
||||
{:path path
|
||||
:files (into [] files)}))
|
||||
|
||||
(defrecord ^:large-vars/cleanup-todo Capacitorfs []
|
||||
protocol/Fs
|
||||
(mkdir! [_this dir]
|
||||
(-> (<dir-exists? dir)
|
||||
(p/then (fn [exists?]
|
||||
(if exists?
|
||||
(p/resolved true)
|
||||
(.mkdir Filesystem
|
||||
(clj->js
|
||||
{:path dir})))))
|
||||
(p/catch (fn [error]
|
||||
(log/error :mkdir! {:path dir
|
||||
:error error})))))
|
||||
(mkdir-recur! [_this dir]
|
||||
(-> (<dir-exists? dir)
|
||||
(p/then (fn [exists?]
|
||||
(if exists?
|
||||
(p/resolved true)
|
||||
(.mkdir Filesystem
|
||||
(clj->js
|
||||
{:path dir
|
||||
:recursive true})))))
|
||||
(p/catch (fn [error]
|
||||
(log/error :mkdir-recur! {:path dir
|
||||
:error error})))))
|
||||
(readdir [_this dir] ; recursive
|
||||
(let [dir (path/path-normalize dir)]
|
||||
(get-file-paths dir)))
|
||||
(unlink! [this repo fpath _opts]
|
||||
(p/let [repo-dir (config/get-local-dir repo)
|
||||
recycle-dir (path/path-join repo-dir config/app-name ".recycle") ;; logseq/.recycle
|
||||
;; convert url to pure path
|
||||
file-name (-> (path/trim-dir-prefix repo-dir fpath)
|
||||
(string/replace "/" "_"))
|
||||
new-path (path/path-join recycle-dir file-name)
|
||||
_ (protocol/mkdir-recur! this recycle-dir)]
|
||||
(protocol/rename! this repo fpath new-path)))
|
||||
(rmdir! [_this _dir]
|
||||
;; Too dangerous!!! We'll never implement this.
|
||||
nil)
|
||||
(read-file [_this dir path _options]
|
||||
(let [fpath (path/path-join dir path)]
|
||||
(->
|
||||
(<read-file-with-utf8 fpath)
|
||||
(p/catch (fn [error]
|
||||
(log/error :read-file-failed error))))))
|
||||
(write-file! [_this repo dir path content opts]
|
||||
(let [fpath (path/path-join dir path)]
|
||||
(p/let [stat (p/catch
|
||||
(.stat Filesystem (clj->js {:path fpath}))
|
||||
(fn [_e] :not-found))]
|
||||
;; `path` is full-path
|
||||
(write-file-impl! repo dir path content opts stat))))
|
||||
(rename! [_this _repo old-fpath new-fpath]
|
||||
(-> (.rename Filesystem
|
||||
(clj->js
|
||||
{:from old-fpath
|
||||
:to new-fpath}))
|
||||
(p/catch (fn [error]
|
||||
(log/error :rename-file-failed error)))))
|
||||
(copy! [_this _repo old-path new-path]
|
||||
(-> (.copy Filesystem
|
||||
(clj->js
|
||||
{:from old-path
|
||||
:to new-path}))
|
||||
(p/catch (fn [error]
|
||||
(log/error :copy-file-failed error)))))
|
||||
(stat [_this fpath]
|
||||
(-> (p/chain (.stat Filesystem (clj->js {:path fpath}))
|
||||
#(js->clj % :keywordize-keys true))
|
||||
(p/catch (fn [error]
|
||||
(let [errstr (if error (.toString error) "")]
|
||||
(when (string/includes? errstr "because you don’t have permission to view it")
|
||||
(state/pub-event! [:notification/show
|
||||
{:content "No permission, please clear cache and re-open graph folder."
|
||||
:status :error}]))
|
||||
(p/rejected error))))))
|
||||
(open-dir [_this dir]
|
||||
(open-dir dir))
|
||||
(get-files [_this dir]
|
||||
(get-files dir))
|
||||
(watch-dir! [_this dir _options]
|
||||
(p/let [_ (.unwatch mobile-util/fs-watcher)]
|
||||
(.watch mobile-util/fs-watcher (clj->js {:path dir}))))
|
||||
(unwatch-dir! [_this _dir]
|
||||
(.unwatch mobile-util/fs-watcher)))
|
||||
@@ -1,7 +1,6 @@
|
||||
(ns frontend.fs.sync
|
||||
"Main ns for providing file sync functionality"
|
||||
(:require ["@capawesome/capacitor-background-task" :refer [BackgroundTask]]
|
||||
["path" :as node-path]
|
||||
(:require ["path" :as node-path]
|
||||
[cljs-http.client :as http]
|
||||
[cljs-time.coerce :as tc]
|
||||
[cljs-time.core :as t]
|
||||
@@ -23,12 +22,10 @@
|
||||
[frontend.diff :as diff]
|
||||
[frontend.encrypt :as encrypt]
|
||||
[frontend.fs :as fs]
|
||||
[frontend.fs.capacitor-fs :as capacitor-fs]
|
||||
[frontend.fs.diff-merge :as diff-merge]
|
||||
[frontend.handler.file-based.file :as file-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.user :as user]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.pubsub :as pubsub]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
@@ -911,149 +908,10 @@
|
||||
(<add-new-version [_this repo path content]
|
||||
(p->c (ipc/ipc "addVersionFile" (config/get-local-dir repo) path content))))
|
||||
|
||||
(deftype ^:large-vars/cleanup-todo CapacitorAPI [^:mutable graph-uuid' ^:mutable private-key ^:mutable public-key']
|
||||
IToken
|
||||
(<get-token [_this]
|
||||
(user/<wrap-ensure-id&access-token
|
||||
(state/get-auth-id-token)))
|
||||
|
||||
IRSAPI
|
||||
(rsapi-ready? [_ graph-uuid] (and (= graph-uuid graph-uuid') private-key public-key'))
|
||||
(<key-gen [_]
|
||||
(go (let [r (<! (p->c (.keygen mobile-util/file-sync #js {})))]
|
||||
(-> r
|
||||
(js->clj :keywordize-keys true)))))
|
||||
(<set-env [_ graph-uuid prod? secret-key public-key]
|
||||
(set! graph-uuid' graph-uuid)
|
||||
(set! private-key secret-key)
|
||||
(set! public-key' public-key)
|
||||
(p->c (.setEnv mobile-util/file-sync (clj->js {:graphUUID graph-uuid
|
||||
:env (if prod? "prod" "dev")
|
||||
:secretKey secret-key
|
||||
:publicKey public-key}))))
|
||||
|
||||
(<get-local-all-files-meta [this graph-uuid base-path]
|
||||
(go
|
||||
(let [r (<! (p->c (.getLocalAllFilesMeta mobile-util/file-sync (clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path}))))]
|
||||
(or (guard-ex r)
|
||||
(<! (<build-local-file-metadatas this graph-uuid (.-result r)))))))
|
||||
|
||||
(<get-local-files-meta [this graph-uuid base-path filepaths]
|
||||
(go
|
||||
(let [r (<! (p->c (.getLocalFilesMeta mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:filePaths filepaths}))))]
|
||||
(assert (not (instance? ExceptionInfo r)) "get-local-files-meta shouldn't return exception")
|
||||
(<! (<build-local-file-metadatas this graph-uuid (.-result r))))))
|
||||
|
||||
(<rename-local-file [_ graph-uuid base-path from to]
|
||||
(p->c (.renameLocalFile mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:from (path-normalize from)
|
||||
:to (path-normalize to)}))))
|
||||
|
||||
(<update-local-files [this graph-uuid base-path filepaths]
|
||||
(go
|
||||
(let [token-or-exp (<! (<get-token this))
|
||||
filepaths' (map path-normalize filepaths)]
|
||||
(or (guard-ex token-or-exp)
|
||||
(<! (p->c (.updateLocalFiles mobile-util/file-sync (clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:filePaths filepaths'
|
||||
:token token-or-exp}))))))))
|
||||
(<fetch-remote-files [this graph-uuid base-path filepaths]
|
||||
(go
|
||||
(let [token-or-exp (<! (<get-token this))]
|
||||
(or (guard-ex token-or-exp)
|
||||
(js->clj
|
||||
(.-value
|
||||
(<! (<retry-rsapi
|
||||
#(p->c (.fetchRemoteFiles mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:filePaths filepaths
|
||||
:token token-or-exp})))))))))))
|
||||
(<download-version-files [this graph-uuid base-path filepaths]
|
||||
(go
|
||||
(let [token-or-exp (<! (<get-token this))]
|
||||
(or (guard-ex token-or-exp)
|
||||
(<! (<retry-rsapi
|
||||
#(p->c (.updateLocalVersionFiles mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:filePaths filepaths
|
||||
:token token-or-exp})))))))))
|
||||
|
||||
(<delete-local-files [_ graph-uuid base-path filepaths]
|
||||
(let [normalized-filepaths (mapv path-normalize filepaths)]
|
||||
(go
|
||||
(let [r (<! (<retry-rsapi #(p->c (.deleteLocalFiles mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:filePaths normalized-filepaths})))))]
|
||||
r))))
|
||||
|
||||
(<update-remote-files [this graph-uuid base-path filepaths local-txid]
|
||||
(let [normalized-filepaths (mapv path-normalize filepaths)]
|
||||
(go
|
||||
(let [token-or-exp (<! (<get-token this))
|
||||
r (or (guard-ex token-or-exp)
|
||||
(<! (p->c (.updateRemoteFiles mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:filePaths normalized-filepaths
|
||||
:txid local-txid
|
||||
:token token-or-exp
|
||||
:fnameEncryption true})))))]
|
||||
(or (guard-ex r)
|
||||
(get (js->clj r) "txid"))))))
|
||||
|
||||
(<delete-remote-files [this graph-uuid base-path filepaths local-txid]
|
||||
(let [normalized-filepaths (mapv path-normalize filepaths)]
|
||||
(go
|
||||
(let [token-or-exp (<! (<get-token this))
|
||||
r (or (guard-ex token-or-exp)
|
||||
(<! (p->c (.deleteRemoteFiles mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:basePath base-path
|
||||
:filePaths normalized-filepaths
|
||||
:txid local-txid
|
||||
:token token-or-exp})))))]
|
||||
(or (guard-ex r)
|
||||
(get (js->clj r) "txid"))))))
|
||||
|
||||
(<encrypt-fnames [_ graph-uuid fnames]
|
||||
(go
|
||||
(let [r (<! (p->c (.encryptFnames mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:filePaths fnames}))))]
|
||||
(or (guard-ex r)
|
||||
(get (js->clj r) "value")))))
|
||||
(<decrypt-fnames [_ graph-uuid fnames]
|
||||
(go (let [r (<! (p->c (.decryptFnames mobile-util/file-sync
|
||||
(clj->js {:graphUUID graph-uuid
|
||||
:filePaths fnames}))))]
|
||||
(if (instance? ExceptionInfo r)
|
||||
(ex-info "decrypt-failed" {:fnames fnames} (ex-cause r))
|
||||
(get (js->clj r) "value")))))
|
||||
(<cancel-all-requests [_]
|
||||
(p->c (.cancelAllRequests mobile-util/file-sync)))
|
||||
(<add-new-version [_this repo path content]
|
||||
(p->c (capacitor-fs/backup-file repo :version-file-dir path content))))
|
||||
|
||||
(def rsapi (cond
|
||||
(util/electron?)
|
||||
(->RSAPI nil nil nil)
|
||||
|
||||
(mobile-util/native-ios?)
|
||||
(->CapacitorAPI nil nil nil)
|
||||
|
||||
(mobile-util/native-android?)
|
||||
(->CapacitorAPI nil nil nil)
|
||||
|
||||
:else
|
||||
nil))
|
||||
|
||||
@@ -3227,9 +3085,7 @@
|
||||
local->remote-syncer (->Local->RemoteSyncer user-uuid graph-uuid
|
||||
base-path
|
||||
repo *sync-state remoteapi-with-stop
|
||||
(if (mobile-util/native-platform?)
|
||||
2000
|
||||
10000)
|
||||
10000
|
||||
*txid *txid-for-get-deletion-log nil (chan) *stopped? *paused?
|
||||
(chan 1) (chan 1))
|
||||
remote->local-syncer (->Remote->LocalSyncer user-uuid graph-uuid base-path
|
||||
@@ -3260,11 +3116,6 @@
|
||||
|
||||
(reset! current-sm-graph-uuid nil)))
|
||||
|
||||
(defn <sync-local->remote-now []
|
||||
(go
|
||||
(when-let [_sm ^SyncManager (state/get-file-sync-manager (state/get-current-file-sync-graph-uuid))]
|
||||
(offer! immediately-local->remote-chan true))))
|
||||
|
||||
(defn sync-need-password!
|
||||
[]
|
||||
(when-let [sm ^SyncManager (state/get-file-sync-manager (state/get-current-file-sync-graph-uuid))]
|
||||
@@ -3410,52 +3261,8 @@
|
||||
(finally
|
||||
(reset! *sync-starting false)))))))
|
||||
|
||||
(defn- restart-if-stopped!
|
||||
[is-active?]
|
||||
(cond
|
||||
(and is-active? (graph-sync-off? (second @graphs-txid)))
|
||||
(<sync-start)
|
||||
|
||||
:else
|
||||
(offer! pause-resume-chan is-active?)))
|
||||
|
||||
(def app-state-changed-cursor (rum/cursor state/state :mobile/app-state-change))
|
||||
|
||||
(def finished-local->remote-chan (chan 1))
|
||||
|
||||
(add-watch app-state-changed-cursor "sync"
|
||||
(fn [_ _ _ {:keys [is-active?]}]
|
||||
(cond
|
||||
(mobile-util/native-android?)
|
||||
(when-not is-active?
|
||||
(<sync-local->remote-now))
|
||||
|
||||
(mobile-util/native-ios?)
|
||||
(let [*task-id (atom nil)]
|
||||
(if is-active?
|
||||
(restart-if-stopped! is-active?)
|
||||
(when (state/get-current-file-sync-graph-uuid)
|
||||
(p/let [task-id (.beforeExit ^js BackgroundTask
|
||||
(fn []
|
||||
(go
|
||||
;; Wait for file watcher events
|
||||
(<! (timeout 2000))
|
||||
(util/drain-chan finished-local->remote-chan)
|
||||
(<! (<sync-local->remote-now))
|
||||
;; wait at most 20s
|
||||
(async/alts! [finished-local->remote-chan (timeout 20000)])
|
||||
(p/let [active? (mobile-util/app-active?)]
|
||||
(when-not active?
|
||||
(offer! pause-resume-chan is-active?)))
|
||||
(<! (timeout 5000))
|
||||
(prn "finish task: " @*task-id)
|
||||
(let [opt #js {:taskId @*task-id}]
|
||||
(.finish ^js BackgroundTask opt)))))]
|
||||
(reset! *task-id task-id)))))
|
||||
|
||||
:else
|
||||
nil)))
|
||||
|
||||
;;; ### some add-watches
|
||||
|
||||
;; TODO: replace this logic by pause/resume state
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.handler.user :as user-handler]
|
||||
[frontend.idb :as idb]
|
||||
[frontend.mobile.core :as mobile]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.modules.instrumentation.core :as instrument]
|
||||
[frontend.modules.shortcut.core :as shortcut]
|
||||
@@ -167,8 +166,6 @@
|
||||
(events/run!)
|
||||
|
||||
(p/do!
|
||||
(when (mobile-util/native-platform?)
|
||||
(mobile/mobile-preinit))
|
||||
(-> (p/let [_ (db-browser/start-db-worker!)
|
||||
repos (repo-handler/get-repos)
|
||||
_ (state/set-repos! repos)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,7 @@
|
||||
core.async channel to handle them. Any part of the system can dispatch
|
||||
one of these events using state/pub-event!"
|
||||
(:refer-clojure :exclude [run!])
|
||||
(:require ["@capacitor/filesystem" :refer [Directory Filesystem]]
|
||||
["@sentry/react" :as Sentry]
|
||||
(:require ["@sentry/react" :as Sentry]
|
||||
[cljs-bean.core :as bean]
|
||||
[clojure.core.async :as async]
|
||||
[clojure.core.async.interop :refer [p->c]]
|
||||
@@ -14,9 +13,7 @@
|
||||
[frontend.date :as date]
|
||||
[frontend.db :as db]
|
||||
[frontend.db.async :as db-async]
|
||||
[frontend.db.conn :as conn]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.db.persist :as db-persist]
|
||||
[frontend.db.transact :as db-transact]
|
||||
[frontend.extensions.fsrs :as fsrs]
|
||||
[frontend.fs :as fs]
|
||||
@@ -30,7 +27,6 @@
|
||||
[frontend.handler.db-based.rtc-flows :as rtc-flows]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.export :as export]
|
||||
[frontend.handler.file-sync :as file-sync-handler]
|
||||
[frontend.handler.graph :as graph-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.page :as page-handler]
|
||||
@@ -49,7 +45,6 @@
|
||||
[frontend.modules.shortcut.core :as st]
|
||||
[frontend.persist-db :as persist-db]
|
||||
[frontend.quick-capture :as quick-capture]
|
||||
[frontend.search :as search]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.persist-var :as persist-var]
|
||||
@@ -85,29 +80,22 @@
|
||||
(when (= (:url repo) current-repo)
|
||||
(file-sync-restart!)))
|
||||
|
||||
;; FIXME(andelf): awful multi-arty function.
|
||||
;; Should use a `-impl` function instead of the awful `skip-ios-check?` param with nested callback.
|
||||
(defn- graph-switch
|
||||
([graph]
|
||||
(graph-switch graph false))
|
||||
([graph skip-ios-check?]
|
||||
(let [db-based? (config/db-based-graph? graph)]
|
||||
(if (and (mobile-util/native-ios?) (not skip-ios-check?))
|
||||
(state/pub-event! [:validate-appId graph-switch graph])
|
||||
(do
|
||||
(state/set-current-repo! graph)
|
||||
(page-handler/init-commands!)
|
||||
[graph]
|
||||
(let [db-based? (config/db-based-graph? graph)]
|
||||
(state/set-current-repo! graph)
|
||||
(page-handler/init-commands!)
|
||||
;; load config
|
||||
(repo-config-handler/restore-repo-config! graph)
|
||||
(when-not (= :draw (state/get-current-route))
|
||||
(route-handler/redirect-to-home!))
|
||||
(when-not db-based?
|
||||
(repo-config-handler/restore-repo-config! graph)
|
||||
(when-not (= :draw (state/get-current-route))
|
||||
(route-handler/redirect-to-home!))
|
||||
(when-not db-based?
|
||||
;; graph-switch will trigger a rtc-start automatically
|
||||
;; (rtc-handler/<rtc-start! graph)
|
||||
(file-sync-restart!))
|
||||
(when-let [dir-name (and (not db-based?) (config/get-repo-dir graph))]
|
||||
(fs/watch-dir! dir-name))
|
||||
(graph-handler/settle-metadata-to-local! {:last-seen-at (js/Date.now)}))))))
|
||||
(file-sync-restart!))
|
||||
(when-let [dir-name (and (not db-based?) (config/get-repo-dir graph))]
|
||||
(fs/watch-dir! dir-name))
|
||||
(graph-handler/settle-metadata-to-local! {:last-seen-at (js/Date.now)})))
|
||||
|
||||
;; Parameters for the `persist-db` function, to show the notification messages
|
||||
(defn- graph-switch-on-persisted
|
||||
@@ -271,72 +259,12 @@
|
||||
(when-let [toolbar (.querySelector main-node "#mobile-editor-toolbar")]
|
||||
(set! (.. toolbar -style -bottom) 0)))))
|
||||
|
||||
(defn- get-ios-app-id
|
||||
[repo-url]
|
||||
(when repo-url
|
||||
(let [app-id (-> (first (string/split repo-url "/Documents"))
|
||||
(string/split "/")
|
||||
last)]
|
||||
app-id)))
|
||||
|
||||
(defmethod handle :validate-appId [[_ graph-switch-f graph]]
|
||||
(when-let [deprecated-repo (or graph (state/get-current-repo))]
|
||||
(if (mobile-util/in-iCloud-container-path? deprecated-repo)
|
||||
;; Installation is not changed for iCloud
|
||||
(when graph-switch-f
|
||||
(graph-switch-f graph true)
|
||||
(state/pub-event! [:graph/ready (state/get-current-repo)]))
|
||||
;; Installation is changed for App Documents directory
|
||||
(p/let [deprecated-app-id (get-ios-app-id deprecated-repo)
|
||||
current-document-url (.getUri Filesystem #js {:path ""
|
||||
:directory (.-Documents Directory)})
|
||||
current-app-id (-> (js->clj current-document-url :keywordize-keys true)
|
||||
get-ios-app-id)]
|
||||
(if (= deprecated-app-id current-app-id)
|
||||
(when graph-switch-f (graph-switch-f graph true))
|
||||
(do
|
||||
(notification/show! [:div "Migrating from previous App installation..."]
|
||||
:warning
|
||||
true)
|
||||
(prn ::migrate-app-id :from deprecated-app-id :to current-app-id)
|
||||
(file-sync-stop!)
|
||||
(.unwatch mobile-util/fs-watcher)
|
||||
(let [current-repo (string/replace deprecated-repo deprecated-app-id current-app-id)
|
||||
current-repo-dir (config/get-repo-dir current-repo)]
|
||||
(try
|
||||
;; replace app-id part of repo url
|
||||
(reset! conn/conns
|
||||
(update-keys @conn/conns
|
||||
(fn [key]
|
||||
(if (string/includes? key deprecated-app-id)
|
||||
(string/replace key deprecated-app-id current-app-id)
|
||||
key))))
|
||||
(db-persist/rename-graph! deprecated-repo current-repo)
|
||||
(search/remove-db! deprecated-repo)
|
||||
(state/add-repo! {:url current-repo :nfs? true})
|
||||
(state/delete-repo! {:url deprecated-repo})
|
||||
(catch :default e
|
||||
(js/console.error e)))
|
||||
(state/set-current-repo! current-repo)
|
||||
(repo-config-handler/restore-repo-config! current-repo)
|
||||
(when graph-switch-f (graph-switch-f current-repo true))
|
||||
(.watch mobile-util/fs-watcher #js {:path current-repo-dir})
|
||||
(file-sync-restart!))))
|
||||
(state/pub-event! [:graph/ready (state/get-current-repo)])))))
|
||||
|
||||
(defmethod handle :plugin/hook-db-tx [[_ {:keys [blocks tx-data] :as payload}]]
|
||||
(when-let [payload (and (seq blocks)
|
||||
(merge payload {:tx-data (map #(into [] %) tx-data)}))]
|
||||
(plugin-handler/hook-plugin-db :changed payload)
|
||||
(plugin-handler/hook-plugin-block-changes payload)))
|
||||
|
||||
(defmethod handle :mobile-file-watcher/changed [[_ ^js event]]
|
||||
(let [type (.-event event)
|
||||
payload (js->clj event :keywordize-keys true)]
|
||||
(fs-watcher/handle-changed! type payload)
|
||||
(when (file-sync-handler/enable-sync?)
|
||||
(sync/file-watch-handler type payload))))
|
||||
|
||||
(defmethod handle :rebuild-slash-commands-list [[_]]
|
||||
(page-handler/rebuild-slash-commands-list!))
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
[frontend.db :as db]
|
||||
[frontend.extensions.fsrs :as fsrs]
|
||||
[frontend.extensions.srs :as srs]
|
||||
[frontend.fs.capacitor-fs :as capacitor-fs]
|
||||
[frontend.fs.sync :as sync]
|
||||
[frontend.handler.db-based.rtc :as rtc-handler]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
@@ -99,12 +98,6 @@
|
||||
{:id :srs
|
||||
:label "flashcards__cp"})))
|
||||
|
||||
(defmethod events/handle :modal/show-instruction [_]
|
||||
(shui/dialog-open!
|
||||
capacitor-fs/instruction
|
||||
{:id :instruction
|
||||
:label "instruction__cp"}))
|
||||
|
||||
(defmethod events/handle :modal/show-themes-modal [[_ classic?]]
|
||||
(if classic?
|
||||
(plugin/open-select-theme!)
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
[frontend.db :as db]
|
||||
[frontend.db.file-based.model :as file-model]
|
||||
[frontend.fs :as fs]
|
||||
[frontend.fs.capacitor-fs :as capacitor-fs]
|
||||
[frontend.handler.common.config-edn :as config-edn-common-handler]
|
||||
[frontend.handler.file-based.reset-file :as reset-file-handler]
|
||||
[frontend.handler.global-config :as global-config-handler]
|
||||
[frontend.handler.repo-config :as repo-config-handler]
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.schema.handler.global-config :as global-config-schema]
|
||||
[frontend.schema.handler.repo-config :as repo-config-schema]
|
||||
[frontend.state :as state]
|
||||
@@ -78,15 +76,8 @@
|
||||
(defn backup-file!
|
||||
"Backup db content to bak directory"
|
||||
[repo-url path db-content content]
|
||||
(cond
|
||||
(util/electron?)
|
||||
(ipc/ipc "backupDbFile" repo-url path db-content content)
|
||||
|
||||
(mobile-util/native-platform?)
|
||||
(capacitor-fs/backup-file-handle-changed! repo-url path db-content)
|
||||
|
||||
:else
|
||||
nil))
|
||||
(when (util/electron?)
|
||||
(ipc/ipc "backupDbFile" repo-url path db-content content)))
|
||||
|
||||
(defn- detect-deprecations
|
||||
[path content]
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
(when (and key @store)
|
||||
(idb-keyval/set key value @store)))
|
||||
|
||||
(defn rename-item!
|
||||
[old-key new-key]
|
||||
(when (and old-key new-key @store)
|
||||
(p/let [value (idb-keyval/get old-key @store)]
|
||||
(when value
|
||||
(idb-keyval/set new-key value @store)
|
||||
(idb-keyval/del old-key @store)))))
|
||||
(comment
|
||||
(defn rename-item!
|
||||
[old-key new-key]
|
||||
(when (and old-key new-key @store)
|
||||
(p/let [value (idb-keyval/get old-key @store)]
|
||||
(when value
|
||||
(idb-keyval/set new-key value @store)
|
||||
(idb-keyval/del old-key @store))))))
|
||||
|
||||
(comment
|
||||
(defn set-batch!
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
(:require ["@capacitor/app" :refer [^js App]]
|
||||
["@capacitor/keyboard" :refer [^js Keyboard]]
|
||||
[clojure.string :as string]
|
||||
[promesa.core :as p]
|
||||
[frontend.fs.capacitor-fs :as capacitor-fs]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.mobile.deeplink :as deeplink]
|
||||
[frontend.mobile.intent :as intent]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[cljs-bean.core :as bean]
|
||||
[frontend.config :as config]))
|
||||
[frontend.util :as util]))
|
||||
|
||||
(def *init-url (atom nil))
|
||||
;; FIXME: `appUrlOpen` are fired twice when receiving a same intent.
|
||||
@@ -21,14 +17,6 @@
|
||||
(def *last-shared-url (atom nil))
|
||||
(def *last-shared-seconds (atom 0))
|
||||
|
||||
(defn mobile-preinit
|
||||
"preinit logic of mobile platforms: setup document folder permission"
|
||||
[]
|
||||
(when (mobile-util/native-ios?)
|
||||
;; Caution: This must be called before any file accessing
|
||||
(capacitor-fs/ios-ensure-documents!)))
|
||||
|
||||
|
||||
(defn mobile-postinit
|
||||
"postinit logic of mobile platforms: handle deeplink and intent"
|
||||
[]
|
||||
@@ -40,30 +28,7 @@
|
||||
(defn- ios-init
|
||||
"Initialize iOS-specified event listeners"
|
||||
[]
|
||||
(p/let [^js paths (capacitor-fs/ios-ensure-documents!)]
|
||||
(when paths
|
||||
(let [paths (-> paths
|
||||
bean/->clj
|
||||
(update-vals capacitor-fs/ios-force-include-private))]
|
||||
(state/set-state! :mobile/container-urls paths)
|
||||
(println "iOS container path: " paths))))
|
||||
|
||||
;; Fix iOS App directory change across installation
|
||||
(when (not (config/demo-graph?))
|
||||
(state/pub-event! [:validate-appId]))
|
||||
|
||||
(mobile-util/check-ios-zoomed-display)
|
||||
|
||||
;; keep this the same logic as src/main/electron/listener.cljs
|
||||
(.addListener mobile-util/file-sync "debug"
|
||||
(fn [event]
|
||||
(let [event (js->clj event :keywordize-keys true)
|
||||
payload (:data event)]
|
||||
(when (or (= (:event event) "download:progress")
|
||||
(= (:event event) "upload:progress"))
|
||||
(state/set-state! [:file-sync/graph-state (:graphUUID payload) :file-sync/progress (:file payload)] payload))))))
|
||||
|
||||
|
||||
(mobile-util/check-ios-zoomed-display))
|
||||
|
||||
(defn- android-init
|
||||
"Initialize Android-specified event listeners"
|
||||
@@ -98,13 +63,7 @@
|
||||
(js/window.history.back)))))
|
||||
|
||||
(.addEventListener js/window "sendIntentReceived"
|
||||
#(intent/handle-received))
|
||||
|
||||
(.addListener mobile-util/file-sync "progress"
|
||||
(fn [event]
|
||||
(js/console.log "🔄" event)
|
||||
(let [event (js->clj event :keywordize-keys true)]
|
||||
(state/set-state! [:file-sync/graph-state (:graphUUID event) :file-sync/progress (:file event)] event)))))
|
||||
#(intent/handle-received)))
|
||||
|
||||
(defn- app-state-change-handler
|
||||
[^js state]
|
||||
@@ -112,8 +71,7 @@
|
||||
(when (state/get-current-repo)
|
||||
(let [is-active? (.-isActive state)]
|
||||
(when-not is-active?
|
||||
(editor-handler/save-current-block!))
|
||||
(state/set-mobile-app-state-change is-active?))))
|
||||
(editor-handler/save-current-block!)))))
|
||||
|
||||
(defn- general-init
|
||||
"Initialize event listeners used by both iOS and Android"
|
||||
@@ -129,10 +87,6 @@
|
||||
(reset! *last-shared-seconds (.getSeconds (js/Date.)))
|
||||
(deeplink/deeplink url))))))
|
||||
|
||||
(.addListener mobile-util/fs-watcher "watcher"
|
||||
(fn [event]
|
||||
(state/pub-event! [:mobile-file-watcher/changed event])))
|
||||
|
||||
(.addListener Keyboard "keyboardWillShow"
|
||||
(fn [^js info]
|
||||
(let [keyboard-height (.-keyboardHeight info)]
|
||||
@@ -147,7 +101,6 @@
|
||||
|
||||
(.addListener App "appStateChange" app-state-change-handler))
|
||||
|
||||
|
||||
(defn init! []
|
||||
(when (mobile-util/native-android?)
|
||||
(android-init))
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
(ns frontend.mobile.util
|
||||
(:require ["@capacitor/core" :refer [Capacitor registerPlugin ^js Plugins]]
|
||||
(:require ["@capacitor/core" :refer [Capacitor registerPlugin]]
|
||||
["@capacitor/splash-screen" :refer [SplashScreen]]
|
||||
["@logseq/capacitor-file-sync" :refer [FileSync]]
|
||||
[clojure.string :as string]
|
||||
[promesa.core :as p]
|
||||
[goog.object :as gobj]))
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn platform []
|
||||
(.getPlatform Capacitor))
|
||||
@@ -25,13 +23,7 @@
|
||||
|
||||
(defonce folder-picker (registerPlugin "FolderPicker"))
|
||||
(when (native-ios?)
|
||||
(defonce ios-utils (registerPlugin "Utils"))
|
||||
(defonce ios-file-container (registerPlugin "FileContainer")))
|
||||
|
||||
;; NOTE: both iOS and android share the same API
|
||||
(when (native-platform?)
|
||||
(defonce file-sync FileSync)
|
||||
(defonce fs-watcher (registerPlugin "FsWatcher")))
|
||||
(defonce ios-utils (registerPlugin "Utils")))
|
||||
|
||||
(defn hide-splash []
|
||||
(.hide SplashScreen))
|
||||
@@ -99,14 +91,10 @@
|
||||
[path]
|
||||
(string/includes? path "/iCloud~com~logseq~logseq/"))
|
||||
|
||||
(defn is-iCloud-container-path?
|
||||
"Check whether `path' is iCloud container path on iOS"
|
||||
[path]
|
||||
(re-matches #"/iCloud~com~logseq~logseq/Documents/?$" path))
|
||||
|
||||
(defn app-active?
|
||||
"Whether the app is active. This function returns a promise."
|
||||
[]
|
||||
(let [app ^js (gobj/get Plugins "App")]
|
||||
(p/let [state (.getState app)]
|
||||
(gobj/get state "isActive"))))
|
||||
(comment
|
||||
(defn app-active?
|
||||
"Whether the app is active. This function returns a promise."
|
||||
[]
|
||||
(let [app ^js (gobj/get Plugins "App")]
|
||||
(p/let [state (.getState app)]
|
||||
(gobj/get state "isActive")))))
|
||||
|
||||
@@ -218,10 +218,6 @@
|
||||
:mobile/show-toolbar? false
|
||||
:mobile/show-recording-bar? false
|
||||
:mobile/show-tabbar? false
|
||||
;;; Used to monitor mobile app status,
|
||||
;;; value spec:
|
||||
;;; {:is-active? bool, :timestamp int}
|
||||
:mobile/app-state-change (atom nil)
|
||||
|
||||
;; plugin
|
||||
:plugin/enabled (and util/plugin-platform?
|
||||
@@ -2228,12 +2224,6 @@ Similar to re-frame subscriptions"
|
||||
(every? not-empty (vals agent-opts)))
|
||||
(str protocol "://" host ":" port))))
|
||||
|
||||
(defn set-mobile-app-state-change
|
||||
[is-active?]
|
||||
(set-state! :mobile/app-state-change
|
||||
{:is-active? is-active?
|
||||
:timestamp (inst-ms (js/Date.))}))
|
||||
|
||||
(defn get-sync-graph-by-id
|
||||
[graph-uuid]
|
||||
(when graph-uuid
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@@ -587,12 +587,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@js-joda/timezone/-/timezone-2.5.0.tgz#b422ff400c25ae311384239c62724eecee2e442b"
|
||||
integrity sha512-HHFVhGUKIOtITiT+sbQRdYuO5Q+a8FDj/vQSGUSxe6+5w2in5JsavfRsAN2tU/NCdBeFx/6q8evHMtOrXfdn2g==
|
||||
|
||||
"@logseq/capacitor-file-sync@5.0.2":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@logseq/capacitor-file-sync/-/capacitor-file-sync-5.0.2.tgz#10c56e35b41b1a0afd293c9b045fbcfe150c3477"
|
||||
integrity sha512-FymsTeRtF66zG+oeO+ohZxWICMQMC8An4n9pdI0zz1WaGLer4oWC/lUghlC2DpztRLA32p0CH28tEzF5+2jARg==
|
||||
|
||||
"@logseq/diff-merge@0.2.2":
|
||||
"@logseq/diff-merge@^0.2.2":
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@logseq/diff-merge/-/diff-merge-0.2.2.tgz#583bd8c8c66d5ff05ea70906475efaa078e839a3"
|
||||
integrity sha512-0WeKNhq8PsjvunOqNEd9aSM4tgiClwhonXgXzrQ4KYj8VoyLaEAyEWWGOAoE7mwR+aqwM+bMB4MxuNFywnUb8A==
|
||||
@@ -9002,6 +8997,11 @@ undertaker@^1.2.1:
|
||||
object.reduce "^1.0.0"
|
||||
undertaker-registry "^1.0.0"
|
||||
|
||||
undici-types@~6.20.0:
|
||||
version "6.20.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
|
||||
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
|
||||
|
||||
undici-types@~6.21.0:
|
||||
version "6.21.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
|
||||
|
||||
Reference in New Issue
Block a user