Browse Source

feat(sync): add basic ui

rcmerci 3 years ago
parent
commit
882c7f7434

+ 55 - 21
src/main/frontend/components/header.cljs

@@ -1,25 +1,32 @@
 (ns frontend.components.header
-  (:require [frontend.components.export :as export]
+  (:require ["path" :as path]
+            [cljs-bean.core :as bean]
+            [frontend.components.export :as export]
+            [frontend.components.page-menu :as page-menu]
             [frontend.components.plugins :as plugins]
             [frontend.components.repo :as repo]
-            [frontend.components.page-menu :as page-menu]
             [frontend.components.right-sidebar :as sidebar]
             [frontend.components.svg :as svg]
+            [frontend.components.widgets :as widgets]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
+            [frontend.fs.sync :as fs-sync]
             [frontend.handler :as handler]
+            [frontend.handler.file-sync :as file-sync-handler]
+            [frontend.handler.page :as page-handler]
             [frontend.handler.plugin :as plugin-handler]
-            [frontend.handler.user :as user-handler]
             [frontend.handler.route :as route-handler]
+            [frontend.handler.user :as user-handler]
             [frontend.handler.web.nfs :as nfs]
+            [frontend.handler.web.nfs :as nfs-handler]
+            [frontend.mobile.util :as mobile-util]
+            [frontend.modules.shortcut.core :as shortcut]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
-            [cljs-bean.core :as bean]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]
-            [frontend.mobile.util :as mobile-util]
-            [frontend.fs.sync :as fs-sync]))
+            [cljs.core.async :as a]))
 
 (rum/defc home-button []
   (ui/with-shortcut :go/home "left"
@@ -33,8 +40,7 @@
 
 (rum/defc login < rum/reactive
   []
-  (let [_ (state/sub :auth/id-token)])
-  (rum/with-context [[t] i18n/*tongue-context*]
+  (let [_ (state/sub :auth/id-token)]
     (when-not config/publishing?
       (if (user-handler/logged?)
         [:span.text-sm.font-medium (user-handler/email)]
@@ -43,20 +49,49 @@
                                                           (js/window.open "https://logseq-test.auth.us-east-2.amazoncognito.com/oauth2/authorize?client_id=4fi79en9aurclkb92e25hmu9ts&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback"))}
          [:span (t :login)]]))))
 
-(rum/defc file-sync < rum/reactive
-  []
+(rum/defcs file-sync <
+  rum/reactive
+  (rum/local nil ::existed-graphs)
+  [state]
   (let [_ (state/sub :auth/id-token)
-        _ (state/sub :file-sync/sync-state)]
+        _ (state/sub :file-sync/sync-state)
+        ^fs-sync/SyncState sync-state (state/get-file-sync-state-manager)
+        not-syncing? (or (nil? sync-state) (.stopped? sync-state))
+        *existed-graphs (::existed-graphs state)
+        _ (rum/react file-sync-handler/refresh-file-sync-component)
+        graph-txid-exists? (file-sync-handler/graph-txid-exists?)]
     (when-not config/publishing?
       (when (user-handler/logged?)
-        (if (as-> (state/get-file-sync-state-manager) ^fs-sync/SyncState sync-state
-              (or (nil? sync-state) (.stopped? sync-state)))
-          [:a.button
-           {:on-click fs-sync/sync-start}
-           (ui/icon "cloud-off" {:style {:fontSize ui/icon-size}})]
-          [:a.button
-           {:on-click fs-sync/sync-stop}
-           (ui/icon "cloud" {:style {:fontSize ui/icon-size}})])))))
+        (when-not (file-sync-handler/graph-txid-exists?)
+          (a/go
+            (let [graphs (a/<! (file-sync-handler/list-graphs))]
+              (reset! *existed-graphs graphs))))
+        (ui/dropdown-with-links
+         (fn [{:keys [toggle-fn]}]
+           (if not-syncing?
+             [:a.button
+              {:on-click toggle-fn}
+              (ui/icon "cloud-off" {:style {:fontSize ui/icon-size}})]
+             [:a.button
+              {:on-click toggle-fn}
+              (ui/icon "cloud" {:style {:fontSize ui/icon-size}})]))
+         (cond-> []
+           (not graph-txid-exists?)
+           (concat (->> @*existed-graphs
+                        (filterv #(and (:GraphName %) (:GraphUUID %)))
+                        (mapv (fn [graph]
+                                {:title (:GraphName graph)
+                                 :options {:on-click #(file-sync-handler/switch-graph (:GraphUUID graph))}})))
+                   [{:hr true}
+                    {:title "create graph"
+                     :options {:on-click #(file-sync-handler/create-graph (path/basename (state/get-current-repo)))}}])
+           graph-txid-exists?
+           (conj {:title "toggle file sync"
+                  :options {:on-click #(if not-syncing? (fs-sync/sync-start) (fs-sync/sync-stop))}}))
+
+         (cond-> {}
+           (not graph-txid-exists?) (assoc :links-header [:div.font-medium.text-sm.opacity-60.px-4.pt-2
+                                                          "Switch to:"])))))))
 
 (rum/defc left-menu-button < rum/reactive
   [{:keys [on-click]}]
@@ -225,8 +260,7 @@
         [:a.text-sm.font-medium.button {:href (rfe/href :graph)}
          (t :graph)])
 
-      (dropdown-menu {:me           me
-                      :t            t
+      (dropdown-menu {:t            t
                       :current-repo current-repo
                       :default-home default-home})
 

+ 32 - 16
src/main/frontend/fs/sync.cljs

@@ -12,12 +12,11 @@
             [frontend.handler.user :as user]
             [frontend.state :as state]
             [frontend.util :as util]
-            [frontend.util.persist-var :as persist-var]))
+            [frontend.util.persist-var :as persist-var]
+            [rum.core :as rum]))
 
-;;; TODO: watch `user/*login-notify` to start/stop sync loop
 ;;; TODO: add some spec validate
 
-
 (def ws-addr "wss://og96xf1si7.execute-api.us-east-2.amazonaws.com/production?graphuuid=%s")
 
 
@@ -527,13 +526,18 @@
   IRelativePath
   (-relative-path [_] (remove-dir-prefix dir path))
 
+  IEquiv
+  (-equiv [_ other]
+    (and (= dir (.-dir other))
+         (= type (.-type other))
+         (= path (.-path other))))
+
   IPrintWithWriter
   (-pr-writer [coll w opts]
     (write-all w (str {:type type :base-path dir :path path}))))
 
 (defn file-watch-handler
   [type {:keys [dir path _content stat] :as payload}]
-  (prn "file-watch-handler" type (:path payload) (get-in payload [:stat :mtime]))
   (go (>! local-changes-chan (->FileChangeEvent type dir path stat))))
 
 
@@ -543,6 +547,7 @@
 
 (defprotocol ILocal->RemoteSync
   (get-ignore-files [this] "ignored-files won't be synced to remote")
+  ;; TODO: get-monitored-dirs
   (ratelimit [this from-chan] "get watched local file-change events from FROM-CHAN,
   return chan returning events with rate limited")
   (sync-local->remote! [this ^FileChangeEvent e])
@@ -567,7 +572,7 @@
                        (when-not (instance? ExceptionInfo apply-result)
                          (reset! *txid latest-txid)
                          ;; persist txid
-                         (.reset_value! graphs-txid [graph-uuid latest-txid] repo)
+                         (persist-var/-reset-value! graphs-txid [graph-uuid latest-txid] repo)
                          (persist-var/persist-save graphs-txid))
                        apply-result)))))]
         (if (instance? ExceptionInfo r)
@@ -606,13 +611,13 @@
     (let [c (.filtered-chan this 10000)]
       (go-loop [timeout-c (timeout rate)
                 tcoll (transient [])]
-        (let [{:keys [timeout e]}
+        (let [{:keys [timeout ^FileChangeEvent e]}
               (async/alt! timeout-c {:timeout true}
                           from-chan ([e] {:e e}))]
           (cond
             timeout
             (do
-              (<! (async/onto-chan! c (persistent! tcoll) false))
+              (<! (async/onto-chan! c (distinct (persistent! tcoll)) false))
               (recur (async/timeout rate) (transient [])))
 
             (some? e)
@@ -658,7 +663,7 @@
                     (do
                       (println "sync-local->remote! update txid" r*)
                       ;; persist txid
-                      (.reset_value! graphs-txid [graph-uuid r*] repo)
+                      (persist-var/-reset-value! graphs-txid [graph-uuid r*] repo)
                       (persist-var/persist-save graphs-txid)
                       (reset! *txid r*)
                       {:succ true})
@@ -838,7 +843,7 @@
         local->remote-syncer (->Local->RemoteSyncer graph-uuid
                                                     base-path
                                                     repo sync-state
-                                                    10000
+                                                    20000
                                                     *txid nil)
         remote->local-syncer (->Remote->LocalSyncer graph-uuid
                                                     base-path
@@ -885,6 +890,12 @@
 (def remote->local-sync-chan (chan))
 (def local->remote-sync-chan (chan))
 
+
+(defn sync-stop []
+  (when-let [sm (state/get-file-sync-manager)]
+    (println "stopping sync-manager")
+    (.stop sm)))
+
 (defn sync-start []
   (let [graph-uuid (first @graphs-txid)
         txid (second @graphs-txid)
@@ -899,10 +910,15 @@
 
     (.start sm)
     (state/set-file-sync-state-manager sync-state)
-    (state/set-file-sync-manager sm)))
-
-
-(defn sync-stop []
-  (when-let [sm (state/get-file-sync-manager)]
-    (println "stopping sync-manager")
-    (.stop sm)))
+    (state/set-file-sync-manager sm)
+
+    ;; watch :network/online?
+    (add-watch (rum/cursor state/state :network/online?) "sync-manage"
+               (fn [k r o n]
+                 (when (false? n)
+                   (sync-stop))))
+    ;; watch :auth/id-token
+    (add-watch (rum/cursor state/state :auth/id-token) "sync-manage"
+               (fn [k r o n]
+                 (when (nil? n)
+                   (sync-stop))))))

+ 44 - 0
src/main/frontend/handler/file_sync.cljs

@@ -0,0 +1,44 @@
+(ns frontend.handler.file-sync
+  (:require [cljs.core.async :as async :refer [go timeout go-loop offer! poll! chan <! >!]]
+            [frontend.fs.macro :refer [exception-> exception->>]]
+            [frontend.fs.sync :as sync]
+            [frontend.state :as state]
+            [frontend.util.persist-var :as persist-var]
+            [frontend.handler.notification :as notification]))
+
+
+(def refresh-file-sync-component (atom false))
+
+(defn graph-txid-exists?
+  []
+  (let [[graph-uuid txid] @sync/graphs-txid]
+    (some? graph-uuid)))
+
+
+(defn create-graph
+  [name]
+  (go
+    (let [r (exception->
+             (<! (sync/create-graph sync/remoteapi name))
+             :GraphUUID)]
+      (if (and (not (instance? ExceptionInfo r))
+               (string? r))
+        (do
+          (persist-var/-reset-value! sync/graphs-txid [r 0] (state/get-current-repo))
+          (persist-var/persist-save sync/graphs-txid)
+          (swap! refresh-file-sync-component not))
+        (if (= 404 (get-in (ex-data r) [:err :status]))
+          (notification/show! (str "create graph failed: already existed graph: " name) :warning)
+          (notification/show! (str "create graph failed: " r) :warning))))))
+
+
+(defn list-graphs
+  []
+  (go
+    (:Graphs (<! (sync/list-remote-graphs sync/remoteapi)))))
+
+
+(defn switch-graph [graph-uuid]
+  (persist-var/-reset-value! sync/graphs-txid [graph-uuid 0] (state/get-current-repo))
+  (persist-var/persist-save sync/graphs-txid)
+  (swap! refresh-file-sync-component not))

+ 2 - 9
src/main/frontend/handler/user.cljs

@@ -101,11 +101,6 @@
 
 ;;; userinfo, token, login/logout, ...
 
-(def *login-notify
-  "used to notify other parts that login/logout happened,
-  true when login, false when logout"
-  (atom false))
-
 (defn- parse-jwt [jwt]
   (some-> jwt
           (string/split ".")
@@ -150,8 +145,7 @@
 (defn- clear-tokens []
   (state/set-auth-id-token nil)
   (state/set-auth-access-token nil)
-  (state/set-auth-refresh-token nil)
-  (reset! *login-notify false))
+  (state/set-auth-refresh-token nil))
 
 (defn- set-tokens!
   ([id-token access-token]
@@ -171,8 +165,7 @@
               (:body)
               (js/JSON.parse)
               (js->clj :keywordize-keys true)
-              (as-> $ (set-tokens! (:id_token $) (:access_token $) (:refresh_token $))))
-          (reset! *login-notify true))
+              (as-> $ (set-tokens! (:id_token $) (:access_token $) (:refresh_token $)))))
         (debug/pprint "login-callback" resp)))))
 
 (defn refresh-id-token&access-token []

+ 6 - 3
src/main/frontend/util/persist_var.cljs

@@ -16,9 +16,12 @@
 (defprotocol ISave
   (-save [this]))
 
+(defprotocol IResetValue
+  (-reset-value! [this new graph]))
+
 (deftype PersistVar [*value location]
-  Object
-  (reset-value! [_ new graph]
+  IResetValue
+  (-reset-value! [_ new graph]
     (reset! *value (assoc-in @*value [graph :value] new)))
 
   ILoad
@@ -50,7 +53,7 @@
     (get-in @*value [(state/get-current-repo) :value]))
 
   IReset
-  (-reset!                              ;    Deprecated - use (.reset-value! o) instead.
+  (-reset!            ;    Deprecated - use (.reset-value! o) instead.
     [o new-value]
     (swap! *value (fn [o] (assoc-in @*value [(state/get-current-repo) :value] new-value))))