Browse Source

fix: only unregister main window on quitting to ensure fs watching integrity

Junyi Du 3 years ago
parent
commit
2b9c995d69

+ 10 - 2
src/electron/electron/core.cljs

@@ -224,18 +224,26 @@
                (@*setup-fn)
 
                ;; main window events
+               ;; TODO merge with window/on-close-actions!
+               ;; TODO elimilate the difference between main and non-main windows
                (.on win "close" (fn [e]
-                                  (when @*quit-dirty?
+                                  (when @*quit-dirty? ;; when not updating
                                     (.preventDefault e)
-                                    (state/close-window! win)
                                     (let [web-contents (. win -webContents)]
                                       (.send web-contents "persistent-dbs"))
                                     (async/go
                                       (let [_ (async/<! state/persistent-dbs-chan)]
                                         (if (or @win/*quitting? (not mac?))
+                                          ;; only cmd+q quitting will trigger actual closing on mac
+                                          ;; otherwise, it's just hiding - don't do any actuall closing in that case
+                                          ;; except saving transit
                                           (when-let [win @*win]
+                                            (when-let [dir (state/get-window-graph-path win)]
+                                              (handler/close-watcher-when-orphaned! win dir))
+                                            (state/close-window! win)
                                             (win/destroy-window! win)
                                             (reset! *win nil))
+                                          ;; Just hiding - don't do any actuall closing operation
                                           (do (.preventDefault ^js/Event e)
                                               (if (and mac? (.isFullScreen win))
                                                 (do (.once win "leave-full-screen" #(.hide win))

+ 11 - 9
src/electron/electron/fs_watcher.cljs

@@ -18,13 +18,15 @@
 (defn- send-file-watcher! [dir type payload]
   ;; Should only send to one window; then dbsync will do his job
   ;; If no window is on this graph, just ignore
-  (some (fn [^js win]
-          (when-not (.isDestroyed win)
-            (.. win -webContents
-                (send file-watcher-chan
-                      (bean/->js {:type type :payload payload})))
-            true)) ;; break some loop on success
-        (window/get-graph-all-windows dir)))
+  (let [sent? (some (fn [^js win]
+                      (when-not (.isDestroyed win)
+                        (.. win -webContents
+                            (send file-watcher-chan
+                                  (bean/->js {:type type :payload payload})))
+                        true)) ;; break some loop on success
+                    (window/get-graph-all-windows dir))]
+    (when-not sent? (prn "unhandled file event will cause uncatched file modifications!.
+                          target:" dir))))
 
 (defn- publish-file-event!
   [dir path event]
@@ -76,7 +78,7 @@
 
 (defn close-watcher!
   "If no `dir` provided, close all watchers;
-   Otherwise, close the specific watcher"
+   Otherwise, close the specific watcher if exists"
   ([]
    (doseq [[watcher watcher-del-f] (vals @*file-watcher)]
      (.close watcher)
@@ -84,7 +86,7 @@
    (reset! *file-watcher {}))
   ([dir]
    (let [[watcher watcher-del-f] (get @*file-watcher dir)]
-     (when watcher ;; when there are multiple windows, 
+     (when watcher
        (.close watcher)
        (.removeListener app "quit" watcher-del-f)
        (swap! *file-watcher dissoc dir)))))

+ 20 - 19
src/electron/electron/handler.cljs

@@ -247,12 +247,6 @@
       (when (fs/existsSync file-path)
         (fs-extra/removeSync file-path)))))
 
-(defmethod handle :openNewWindow [_window [_]]
-  (let [win (win/create-main-window)]
-    (win/on-close-save! win)
-    (win/setup-window-listeners! win)
-    nil))
-
 (defmethod handle :persistent-dbs-saved [_window _]
   (async/put! state/persistent-dbs-chan true)
   true)
@@ -329,8 +323,16 @@
 (defmethod handle :getAppBaseInfo [^js win [_ _opts]]
   {:isFullScreen (.isFullScreen win)})
 
+(defn close-watcher-when-orphaned!
+  "When it's the last window for the directory, close the watcher."
+  [window dir]
+  (when (not (win/graph-has-other-windows? window dir))
+    (watcher/close-watcher! dir)))
+
 (defmethod handle :setCurrentGraph [^js win [_ path]]
-  (let [path (when path (utils/get-graph-dir path))]
+  (let [path (when path (utils/get-graph-dir path))
+        old-path (state/get-window-graph-path win)]
+    (when old-path (close-watcher-when-orphaned! win old-path))
     (swap! state/state assoc :graph/current path)
     (swap! state/state assoc-in [:window/graph win] path)
     nil))
@@ -365,19 +367,13 @@
                               (bean/->clj {:graph graph
                                            :tx-data tx-data})))))
 
-(defn- graph-has-other-windows? [win graph]
-  (let [dir (utils/get-graph-dir graph)
-        windows (win/get-graph-all-windows dir)
-        windows (filter #(.isVisible %) windows)]
-    (boolean (some (fn [^js window] (not= (.-id win) (.-id window))) windows))))
-
 (defmethod handle :graphHasOtherWindow [^js win [_ graph]]
-  (graph-has-other-windows? win graph))
+  (win/graph-has-other-windows? win graph))
 
 (defmethod handle :graphHasMultipleWindows [^js _win [_ graph]]
   (let [dir (utils/get-graph-dir graph)
-        windows (win/get-graph-all-windows dir)
-        windows (filter #(.isVisible %) windows)]
+        windows (win/get-graph-all-windows dir)]
+        ;; windows (filter #(.isVisible %) windows) ;; for mac .hide windows. such windows should also included
     (> (count windows) 1)))
 
 (defmethod handle :addDirWatcher [^js window [_ dir]]
@@ -388,10 +384,15 @@
   (when dir
     ;; adding dir watcher when the window has watcher already - must be cmd + r refreshing
     ;; TODO: handle duplicated adding dir watcher when multiple windows
-    (when (not (graph-has-other-windows? window dir))
-      (watcher/close-watcher! dir))
+    (close-watcher-when-orphaned! window dir)
     (watcher/watch-dir! window dir)))
 
+(defmethod handle :openNewWindow [_window [_]]
+  (let [win (win/create-main-window)]
+    (win/on-close-actions! win close-watcher-when-orphaned!)
+    (win/setup-window-listeners! win)
+    nil))
+
 (defmethod handle :searchVersionChanged?
   [^js _win [_ graph]]
   (search/version-changed? graph))
@@ -410,7 +411,7 @@
   ;; call a window holds the specific graph to persist
   (let [dir (utils/get-graph-dir graph)
         windows (win/get-graph-all-windows dir)
-        windows (filter #(.isVisible %) windows)
+        ;; windows (filter #(.isVisible %) windows) ;; for mac .hide windows. such windows should also included
         tar-graph-win (first windows)]
     (if tar-graph-win
       (utils/send-to-renderer tar-graph-win "persistGraph" graph)

+ 5 - 0
src/electron/electron/state.cljs

@@ -43,6 +43,11 @@
   []
   (:graph/current @state))
 
+(defn get-window-graph-path
+  "Get the path of the graph of a window (might be `nil`)"
+  [window]
+  (get (:window/graph @state) window))
+
 (defn close-window!
   [window]
   (swap! state medley/dissoc-in [:window/graph window]))

+ 12 - 3
src/electron/electron/window.cljs

@@ -1,6 +1,6 @@
 (ns electron.window
   (:require ["electron-window-state" :as windowStateKeeper]
-            [electron.utils :refer [mac? win32? linux? dev? logger open]]
+            [electron.utils :refer [mac? win32? linux? dev? logger open] :as utils]
             [electron.configs :as cfgs]
             [electron.context-menu :as context-menu]
             ["electron" :refer [BrowserWindow app session shell] :as electron]
@@ -68,10 +68,13 @@
   [^js win]
   (.destroy win))
 
-(defn on-close-save!
-  [^js win]
+(defn on-close-actions!
+  ;; TODO merge with the on close in core
+  [^js win close-watcher-f] ;; injected watcher related func
   (.on win "close" (fn [e]
                      (.preventDefault e)
+                     (when-let [dir (state/get-window-graph-path win)]
+                       (close-watcher-f win dir))
                      (state/close-window! win)
                      (let [web-contents (. win -webContents)]
                        (.send web-contents "persistent-dbs"))
@@ -91,6 +94,12 @@
        (#(get % graph-path))
        (map first)))
 
+(defn graph-has-other-windows? [win graph]
+  (let [dir (utils/get-graph-dir graph)
+        windows (get-graph-all-windows dir)]
+        ;; windows (filter #(.isVisible %) windows) ;; for mac .hide windows. such windows should also included
+    (boolean (some (fn [^js window] (not= (.-id win) (.-id window))) windows))))
+
 (defn- open-default-app!
   [url default-open]
   (let [URL (.-URL URL)

+ 1 - 1
src/main/electron/listener.cljs

@@ -15,7 +15,7 @@
 (defn persist-dbs!
   []
   ;; only persist current db!
-  ;; TODO rename to persist-db
+  ;; TODO rename the function and event to persist-db
   (repo-handler/persist-db! {:before     #(notification/show!
                                            (ui/loading (t :graph/persist))
                                            :warning)