file_sync.cljs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. (ns frontend.handler.file-sync
  2. (:require ["path" :as path]
  3. [cljs-time.coerce :as tc]
  4. [cljs-time.format :as tf]
  5. [cljs.core.async :as async :refer [go <!]]
  6. [cljs.core.async.interop :refer [p->c]]
  7. [clojure.string :as string]
  8. [clojure.set :as set]
  9. [frontend.config :as config]
  10. [frontend.db :as db]
  11. [frontend.fs.sync :as sync]
  12. [frontend.handler.notification :as notification]
  13. [frontend.state :as state]
  14. [frontend.util :as util]
  15. [frontend.handler.user :as user]
  16. [frontend.fs :as fs]))
  17. (def hiding-login&file-sync (not config/dev?))
  18. (def refresh-file-sync-component (atom false))
  19. (defn graph-txid-exists?
  20. []
  21. (let [[_user-uuid graph-uuid _txid] @sync/graphs-txid]
  22. (some? graph-uuid)))
  23. (defn create-graph
  24. [name]
  25. (go
  26. (let [r* (<! (sync/create-graph sync/remoteapi name))
  27. r (if (instance? ExceptionInfo r*) r* (:GraphUUID r*))]
  28. (if (and (not (instance? ExceptionInfo r))
  29. (string? r))
  30. (do
  31. (sync/update-graphs-txid! 0 r (user/user-uuid) (state/get-current-repo))
  32. (swap! refresh-file-sync-component not))
  33. (if (= 404 (get-in (ex-data r) [:err :status]))
  34. (notification/show! (str "Create graph failed: already existed graph: " name) :warning)
  35. (notification/show! (str "Create graph failed: " r) :warning))))))
  36. (defn delete-graph
  37. [graph-uuid]
  38. (sync/sync-stop)
  39. (go
  40. (let [r (<! (sync/delete-graph sync/remoteapi graph-uuid))]
  41. (if (instance? ExceptionInfo r)
  42. (notification/show! (str "Delete graph failed: " graph-uuid) :warning)
  43. (let [[_ local-graph-uuid _] @sync/graphs-txid]
  44. (when (= graph-uuid local-graph-uuid)
  45. (sync/clear-graphs-txid! (state/get-current-repo))
  46. (swap! refresh-file-sync-component not))
  47. (notification/show! (str "Graph deleted") :success))))))
  48. (defn list-graphs
  49. []
  50. (go (:Graphs (<! (sync/list-remote-graphs sync/remoteapi)))))
  51. (defn download-all-files
  52. [repo graph-uuid user-uuid base-path]
  53. (go
  54. (state/reset-file-sync-download-init-state!)
  55. (state/set-file-sync-download-init-state! {:total js/NaN :finished 0 :downloading? true})
  56. (let [remote-all-files-meta (<! (sync/get-remote-all-files-meta sync/remoteapi graph-uuid))
  57. local-all-files-meta (<! (sync/get-local-all-files-meta sync/rsapi graph-uuid base-path))
  58. diff-remote-files (set/difference remote-all-files-meta local-all-files-meta)
  59. latest-txid (:TXId (<! (sync/get-remote-graph sync/remoteapi nil graph-uuid)))
  60. partitioned-filetxns
  61. (sequence (sync/filepaths->partitioned-filetxns 10 graph-uuid user-uuid)
  62. (map sync/relative-path diff-remote-files))]
  63. (state/set-file-sync-download-init-state! {:total (count diff-remote-files) :finished 0})
  64. (let [r (<! (sync/apply-filetxns-partitions
  65. nil user-uuid graph-uuid base-path partitioned-filetxns repo nil (atom false)
  66. (fn [filetxns]
  67. (state/set-file-sync-download-init-state!
  68. {:downloading-files (mapv sync/relative-path filetxns)}))
  69. (fn [filetxns]
  70. (state/set-file-sync-download-init-state!
  71. {:finished (+ (count filetxns)
  72. (or (:finished (state/get-file-sync-download-init-state)) 0))}))))]
  73. (if (instance? ExceptionInfo r)
  74. ;; TODO: add re-download button
  75. (notification/show! (str "Download graph failed: " (ex-cause r)) :warning)
  76. (do (state/reset-file-sync-download-init-state!)
  77. (sync/update-graphs-txid! latest-txid graph-uuid user-uuid repo)))))))
  78. (defn switch-graph [graph-uuid]
  79. (let [repo (state/get-current-repo)
  80. base-path (config/get-repo-dir repo)
  81. user-uuid (user/user-uuid)]
  82. (sync/update-graphs-txid! 0 graph-uuid user-uuid repo)
  83. (download-all-files repo graph-uuid user-uuid base-path)
  84. (swap! refresh-file-sync-component not)))
  85. (defn- download-version-file [graph-uuid file-uuid version-uuid]
  86. (go
  87. (let [key (path/join "version-files" file-uuid version-uuid)
  88. r (<! (sync/update-local-files
  89. sync/rsapi graph-uuid (config/get-repo-dir (state/get-current-repo)) [key]))]
  90. (if (instance? ExceptionInfo r)
  91. (notification/show! (ex-cause r) :error)
  92. (notification/show! [:div
  93. [:div "Downloaded version file at: "]
  94. [:div key]] :success false))
  95. (when-not (instance? ExceptionInfo r)
  96. key))))
  97. (defn- list-file-local-versions
  98. [page]
  99. (go
  100. (when-let [path (-> page :block/file :file/path)]
  101. (let [base-path (config/get-repo-dir (state/get-current-repo))
  102. rel-path (string/replace-first path base-path "")
  103. version-files-dir (->> (path/join "version-files/local" rel-path)
  104. path/parse
  105. (#(js->clj % :keywordize-keys true))
  106. ((juxt :dir :name))
  107. (apply path/join base-path))
  108. version-file-paths* (<! (p->c (fs/readdir version-files-dir)))]
  109. (when-not (instance? ExceptionInfo version-file-paths*)
  110. (let [version-file-paths
  111. (filterv
  112. ;; filter dir
  113. (fn [dir-or-file]
  114. (-> (path/parse dir-or-file)
  115. (js->clj :keywordize-keys true)
  116. :ext
  117. seq))
  118. (js->clj (<! (p->c (fs/readdir version-files-dir)))))]
  119. (mapv
  120. (fn [path]
  121. (let [create-time
  122. (-> (path/parse path)
  123. (js->clj :keywordize-keys true)
  124. :name
  125. (#(tf/parse (tf/formatter "yyyy-MM-dd'T'HH_mm_ss.SSSZZ") %)))]
  126. {:create-time create-time :path path :relative-path (string/replace-first path base-path "")}))
  127. version-file-paths)))))))
  128. (defn list-file-versions [graph-uuid page]
  129. (let [file-id (:db/id (:block/file page))]
  130. (when-let [path (:file/path (db/entity file-id))]
  131. (let [base-path (config/get-repo-dir (state/get-current-repo))
  132. path* (string/replace-first path base-path "")]
  133. (go
  134. (let [version-list (:VersionList
  135. (<! (sync/get-remote-file-versions sync/remoteapi graph-uuid path*)))
  136. local-version-list (<! (list-file-local-versions page))
  137. all-version-list (->> (concat version-list local-version-list)
  138. (sort-by #(or (tc/from-string (:CreateTime %))
  139. (:create-time %))
  140. >))]
  141. (notification/show! [:div
  142. [:div.font-bold "File history - " path*]
  143. [:hr.my-2]
  144. (for [version all-version-list]
  145. (let [version-uuid (or (:VersionUUID version) (:relative-path version))
  146. local? (some? (:relative-path version))]
  147. [:div.my-4 {:key version-uuid}
  148. [:div
  149. [:a.text-xs.inline
  150. {:on-click #(if local?
  151. (js/window.apis.openPath (:path version))
  152. (go
  153. (let [relative-path
  154. (<! (download-version-file graph-uuid
  155. (:FileUUID version)
  156. (:VersionUUID version)))]
  157. (js/window.apis.openPath (path/join base-path relative-path)))))}
  158. version-uuid]
  159. (when-not local?
  160. [:div.opacity-70 (str "Size: " (:Size version))])]
  161. [:div.opacity-50
  162. (util/time-ago (or (tc/from-string (:CreateTime version))
  163. (:create-time version)))]]))]
  164. :success false)))))))
  165. (defn get-current-graph-uuid [] (second @sync/graphs-txid))
  166. (defn get-current-graph-txid [] (nth @sync/graphs-txid 2 nil))