(ns frontend.handler.file-sync "Provides util handler fns for file sync" (:require ["path" :as path] [cljs-time.format :as tf] [cljs.core.async :as async :refer [go c]] [clojure.string :as string] [frontend.config :as config] [frontend.db :as db] [frontend.fs.sync :as sync] [frontend.handler.notification :as notification] [frontend.state :as state] [frontend.handler.user :as user] [frontend.fs :as fs] [cljs-time.coerce :as tc] [cljs-time.core :as t] [frontend.storage :as storage] [logseq.graph-parser.util :as gp-util] [lambdaisland.glogi :as log])) (def *beta-unavailable? (volatile! false)) (def refresh-file-sync-component (atom false)) (defn get-current-graph-uuid [] (state/get-current-file-sync-graph-uuid)) (defn enable-sync? [] (or (state/enable-sync?) config/dev?)) (defn current-graph-sync-on? [] (when-let [sync-state (state/sub-file-sync-state (state/get-current-file-sync-graph-uuid))] (not (sync/sync-state--stopped? sync-state)))) (defn synced-file-graph? [graph] (some (fn [item] (and (= graph (:url item)) (:GraphUUID item))) (state/get-repos))) (defn create-graph [name] (go (let [r* ( page :block/file :file/path)] (let [base-path (config/get-repo-dir (state/get-current-repo)) rel-path (string/replace-first path base-path "") version-files-dir (->> (path/join "logseq/version-files/local" rel-path) path/parse (#(js->clj % :keywordize-keys true)) ((juxt :dir :name)) (apply path/join base-path)) version-file-paths (->> (c (fs/readdir version-files-dir :path-only? true))) (remove #{version-files-dir}))] (when-not (instance? ExceptionInfo version-file-paths) (when (seq version-file-paths) (->> (mapv (fn [path] (try (let [create-time (-> (path/parse path) (js->clj :keywordize-keys true) :name (#(tf/parse (tf/formatter "yyyy-MM-dd'T'HH_mm_ss.SSSZZ") %)))] {:create-time create-time :path path :relative-path (string/replace-first path base-path "")}) (catch :default e (log/error :page-history/parse-format-error e) nil))) version-file-paths) (remove nil?)))))))) (defn fetch-page-file-versions [graph-uuid page] [] (let [file-id (:db/id (:block/file page))] (when-let [path (:file/path (db/entity file-id))] (let [base-path (config/get-repo-dir (state/get-current-repo)) base-path (if (string/starts-with? base-path "file://") (gp-util/safe-decode-uri-component base-path) base-path) path* (string/replace-first (string/replace-first path base-path "") #"^/" "")] (go (let [version-list (:VersionList (> (concat version-list local-version-list) (sort-by #(or (:CreateTime %) (:create-time %)) >))] all-version-list)))))) (defn init-remote-graph [local graph] (when (and local graph) (notification/show! (str "Start syncing the remote graph " (:GraphName graph) " to " (config/get-string-repo-dir (config/get-local-dir local))) :success) (init-graph (:GraphUUID graph)) (state/close-modal!))) (defn setup-file-sync-event-listeners [] (let [c (async/chan 1) p sync/sync-events-publication topics [:finished-local->remote :finished-remote->local :start]] (doseq [topic topics] (async/sub p topic c)) (async/go-loop [] (let [{:keys [event data]} (async/remote :finished-remote->local) (when-let [current-uuid (state/get-current-file-sync-graph-uuid)] (state/clear-file-sync-progress! current-uuid) (state/set-state! [:file-sync/graph-state current-uuid :file-sync/last-synced-at] (:epoch data)) (when (= event :finished-local->remote) (async/offer! sync/finished-local->remote-chan true))) :start (when-let [current-uuid (state/get-current-file-sync-graph-uuid)] (state/set-state! [:file-sync/graph-state current-uuid :file-sync/start-time] data)) nil) (when (and (:file-change-events data) (= :page (state/get-current-route))) (state/pub-event! [:file-sync/maybe-onboarding-show :sync-history]))) (recur)) #(doseq [topic topics] (async/unsub p topic c)))) (defn reset-user-state! [] (vreset! *beta-unavailable? false) (state/set-state! :file-sync/onboarding-state nil)) (defn calculate-time-left "This assumes that the network speed is stable which could be wrong sometimes." [sync-state progressing] (when-let [start-time (get-in @state/state [:file-sync/graph-state (state/get-current-file-sync-graph-uuid) :file-sync/start-time :epoch])] (let [now (tc/to-epoch (t/now)) diff-seconds (- now start-time) finished (reduce + (map (comp :progress second) progressing)) local->remote-files (:full-local->remote-files sync-state) remote->local-files (:full-remote->local-files sync-state) total (if (seq remote->local-files) (reduce + (map (fn [m] (or (:size m) 0)) remote->local-files)) (reduce + (map #(:size (.-stat %)) local->remote-files))) mins (int (/ (* (/ total finished) diff-seconds) 60))] (if (or (zero? total) (zero? finished)) "waiting" (cond (zero? mins) "soon" (= mins 1) "1 min left" (> mins 30) "calculating..." :else (str mins " mins left")))))) (defn set-sync-enabled! [value] (storage/set :logseq-sync-enabled value) (state/set-state! :feature/enable-sync? value))