file_sync.cljs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. (ns frontend.handler.file-sync
  2. "Provides util handler fns for file sync"
  3. (:require ["path" :as node-path]
  4. [cljs-time.coerce :as tc]
  5. [cljs-time.core :as t]
  6. [cljs-time.format :as tf]
  7. [cljs.core.async :as async :refer [go <!]]
  8. [cljs.core.async.interop :refer [p->c]]
  9. [clojure.string :as string]
  10. [frontend.config :as config]
  11. [frontend.db :as db]
  12. [frontend.fs :as fs]
  13. [frontend.fs.sync :as sync]
  14. [frontend.handler.notification :as notification]
  15. [frontend.handler.user :as user]
  16. [frontend.pubsub :as pubsub]
  17. [frontend.state :as state]
  18. [frontend.storage :as storage]
  19. [frontend.util :as util]
  20. [lambdaisland.glogi :as log]
  21. [logseq.common.path :as path]))
  22. (def *beta-unavailable? (volatile! false))
  23. (def refresh-file-sync-component (atom false))
  24. (defn get-current-graph-uuid []
  25. (state/get-current-file-sync-graph-uuid))
  26. (defn enable-sync?
  27. []
  28. (or (state/enable-sync?)
  29. config/dev?))
  30. (defn current-graph-sync-on?
  31. []
  32. (when-let [sync-state (state/sub-file-sync-state (state/get-current-file-sync-graph-uuid))]
  33. (not (sync/sync-state--stopped? sync-state))))
  34. (defn synced-file-graph?
  35. [graph]
  36. (some (fn [item] (and (= graph (:url item))
  37. (:GraphUUID item))) (state/get-repos)))
  38. (defn create-graph
  39. [name]
  40. (go
  41. (let [r* (<! (sync/<create-graph sync/remoteapi name))
  42. user-uuid-or-exp (<! (user/<user-uuid))
  43. r (if (instance? ExceptionInfo r*) r*
  44. (if (instance? ExceptionInfo user-uuid-or-exp)
  45. user-uuid-or-exp
  46. (:GraphUUID r*)))]
  47. (when-not (instance? ExceptionInfo user-uuid-or-exp)
  48. (if (and (not (instance? ExceptionInfo r))
  49. (string? r))
  50. (let [tx-info [0 r user-uuid-or-exp (state/get-current-repo)]]
  51. (<! (apply sync/<update-graphs-txid! tx-info))
  52. (swap! refresh-file-sync-component not)
  53. tx-info)
  54. (do
  55. (state/set-state! [:ui/loading? :graph/create-remote?] false)
  56. (cond
  57. ;; already processed this exception by events
  58. ;; - :file-sync/storage-exceed-limit
  59. ;; - :file-sync/graph-count-exceed-limit
  60. (or (sync/storage-exceed-limit? r)
  61. (sync/graph-count-exceed-limit? r))
  62. nil
  63. (contains? #{400 404} (get-in (ex-data r) [:err :status]))
  64. (notification/show! (str "Create graph failed: already existed graph: " name) :warning true nil 4000 nil)
  65. :else
  66. (notification/show! (str "Create graph failed: " (ex-message r)) :warning true nil 4000 nil))))))))
  67. (defn <delete-graph
  68. [graph-uuid]
  69. (go
  70. (let [same-graph? (= graph-uuid (get-current-graph-uuid))]
  71. (when same-graph?
  72. (<! (sync/<sync-stop)))
  73. (let [r (<! (sync/<delete-graph sync/remoteapi graph-uuid))]
  74. (if (instance? ExceptionInfo r)
  75. (notification/show! (str "Delete graph failed: " graph-uuid) :warning)
  76. (do
  77. (when same-graph?
  78. (sync/clear-graphs-txid! graph-uuid)
  79. (swap! refresh-file-sync-component not))
  80. (notification/show! (str "Graph deleted") :success)))))))
  81. (defn <list-graphs
  82. []
  83. (go
  84. (let [r (<! (sync/<list-remote-graphs sync/remoteapi))]
  85. (if (instance? ExceptionInfo r)
  86. r
  87. (:Graphs r)))))
  88. (defn load-session-graphs
  89. []
  90. (when-not (state/sub [:file-sync/remote-graphs :loading])
  91. (go
  92. (when-not (or util/web-platform? (util/mobile?))
  93. (state/set-state! [:file-sync/remote-graphs :loading] true)
  94. (let [graphs-or-exp (<! (<list-graphs))]
  95. (when-not (instance? ExceptionInfo graphs-or-exp)
  96. (state/set-state! :file-sync/remote-graphs {:loading false :graphs graphs-or-exp})))))))
  97. (defn reset-session-graphs
  98. []
  99. (state/set-state! :file-sync/remote-graphs {:loading false :graphs nil}))
  100. (defn init-graph [graph-uuid]
  101. (go
  102. (let [repo (state/get-current-repo)
  103. user-uuid-or-exp (<! (user/<user-uuid))]
  104. (if (instance? ExceptionInfo user-uuid-or-exp)
  105. (notification/show! (ex-message user-uuid-or-exp) :error)
  106. (do
  107. (state/set-state! :sync-graph/init? true)
  108. (<! (sync/<update-graphs-txid! 0 graph-uuid user-uuid-or-exp repo))
  109. (swap! refresh-file-sync-component not)
  110. (state/pub-event! [:graph/switch repo {:persist? false}]))))))
  111. (defn download-version-file
  112. ([graph-uuid file-uuid version-uuid]
  113. (download-version-file graph-uuid file-uuid version-uuid false))
  114. ([graph-uuid file-uuid version-uuid silent-download?]
  115. (go
  116. (let [key (node-path/join file-uuid version-uuid)
  117. r (<! (sync/<download-version-files
  118. sync/rsapi graph-uuid (config/get-repo-dir (state/get-current-repo)) [key]))]
  119. (if (instance? ExceptionInfo r)
  120. (notification/show! (ex-cause r) :error)
  121. (when-not silent-download?
  122. (notification/show! [:div
  123. [:div "Downloaded version file at: "]
  124. [:div key]] :success false)))
  125. (when-not (instance? ExceptionInfo r)
  126. (path/path-join "logseq" "version-files" key))))))
  127. (defn- <list-file-local-versions
  128. [page]
  129. (go
  130. (when-let [path (-> page :block/file :file/path)]
  131. (let [base-path (config/get-repo-dir (state/get-current-repo))
  132. rel-path (string/replace-first path base-path "")
  133. file-stem (path/file-stem rel-path)
  134. version-files-dir (path/path-join base-path "logseq/version-files/local" (path/dirname rel-path) file-stem)
  135. version-file-paths (<! (p->c (fs/readdir version-files-dir :path-only? true)))]
  136. (when-not (instance? ExceptionInfo version-file-paths)
  137. (when (seq version-file-paths)
  138. (->>
  139. (mapv
  140. (fn [path]
  141. (try
  142. (let [create-time
  143. (-> (node-path/parse path)
  144. (js->clj :keywordize-keys true)
  145. :name
  146. (#(tf/parse (tf/formatter "yyyy-MM-dd'T'HH_mm_ss.SSSZZ") %)))]
  147. {:create-time create-time
  148. :path path
  149. :relative-path (path/relative-path base-path path)})
  150. (catch :default e
  151. (log/error :page-history/parse-format-error e)
  152. nil)))
  153. version-file-paths)
  154. (remove nil?))))))))
  155. (defn <fetch-page-file-versions [graph-uuid page]
  156. []
  157. (let [file-id (:db/id (:block/file page))]
  158. (go
  159. (when-let [path (:file/path (db/entity file-id))]
  160. (let [version-list (:VersionList
  161. (<! (sync/<get-remote-file-versions sync/remoteapi graph-uuid path)))
  162. local-version-list (<! (<list-file-local-versions page))
  163. all-version-list (->> (concat version-list local-version-list)
  164. (sort-by #(or (:CreateTime %)
  165. (:create-time %))
  166. >))]
  167. all-version-list)))))
  168. (defn init-remote-graph
  169. [local-graph-dir graph]
  170. (when (and local-graph-dir graph)
  171. (notification/show!
  172. (str "Start syncing the remote graph "
  173. (:GraphName graph)
  174. " to "
  175. (config/get-string-repo-dir local-graph-dir))
  176. :success)
  177. (init-graph (:GraphUUID graph))
  178. (state/close-modal!)))
  179. (defn setup-file-sync-event-listeners
  180. []
  181. (let [c (async/chan 1)
  182. p pubsub/sync-events-pub
  183. topics [:finished-local->remote :finished-remote->local :start]]
  184. (doseq [topic topics]
  185. (async/sub p topic c))
  186. (async/go-loop []
  187. (let [{:keys [event data]} (async/<! c)]
  188. (case event
  189. (list :finished-local->remote :finished-remote->local)
  190. (when-let [current-uuid (state/get-current-file-sync-graph-uuid)]
  191. (state/clear-file-sync-progress! current-uuid)
  192. (state/set-state! [:file-sync/graph-state current-uuid :file-sync/last-synced-at] (:epoch data))
  193. (when (= event :finished-local->remote)
  194. (async/offer! sync/finished-local->remote-chan true)))
  195. :start
  196. (when-let [current-uuid (state/get-current-file-sync-graph-uuid)]
  197. (state/set-state! [:file-sync/graph-state current-uuid :file-sync/start-time] data))
  198. nil)
  199. (when (and (:file-change-events data)
  200. (= :page (state/get-current-route)))
  201. (state/pub-event! [:file-sync/maybe-onboarding-show :sync-history])))
  202. (recur))
  203. #(doseq [topic topics]
  204. (async/unsub p topic c))))
  205. (defn reset-user-state! []
  206. (vreset! *beta-unavailable? false)
  207. (state/set-state! :file-sync/onboarding-state nil))
  208. (defn calculate-time-left
  209. "This assumes that the network speed is stable which could be wrong sometimes."
  210. [sync-state progressing]
  211. (when-let [start-time (get-in @state/state
  212. [:file-sync/graph-state
  213. (state/get-current-file-sync-graph-uuid)
  214. :file-sync/start-time
  215. :epoch])]
  216. (let [now (tc/to-epoch (t/now))
  217. diff-seconds (- now start-time)
  218. finished (reduce + (map (comp :progress second) progressing))
  219. local->remote-files (:full-local->remote-files sync-state)
  220. remote->local-files (:full-remote->local-files sync-state)
  221. total (if (seq remote->local-files)
  222. (reduce + (map (fn [m] (or (:size m) 0)) remote->local-files))
  223. (reduce + (map #(:size (.-stat %)) local->remote-files)))
  224. mins (int (/ (* (/ total finished) diff-seconds) 60))]
  225. (if (or (zero? total) (zero? finished))
  226. "waiting"
  227. (cond
  228. (zero? mins) "soon"
  229. (= mins 1) "1 min left"
  230. (> mins 30) "calculating..."
  231. :else (str mins " mins left"))))))
  232. (defn set-sync-enabled!
  233. [value]
  234. (storage/set :logseq-sync-enabled value)
  235. (state/set-state! :feature/enable-sync? value))
  236. (defn set-sync-diff-merge-enabled!
  237. [value]
  238. (storage/set :logseq-sync-diff-merge-enabled value)
  239. (state/set-state! :feature/enable-sync-diff-merge? value))