diff --git a/src/main/electron/listener.cljs b/src/main/electron/listener.cljs index 45fa95a38..1d47d4974 100644 --- a/src/main/electron/listener.cljs +++ b/src/main/electron/listener.cljs @@ -21,6 +21,11 @@ [frontend.ui :as ui] [promesa.core :as p])) +(defn- safe-api-call + "Force the callback result to be nil, otherwise, ipc calls could lead to + window crash." + [k f] + (js/window.apis.on k (fn [data] (f data) nil))) (defn persist-dbs! [] @@ -35,153 +40,148 @@ (defn listen-persistent-dbs! [] - ;; TODO: move "file-watcher" to electron.ipc.channels - (js/window.apis.on - "persistent-dbs" - (fn [_req] - (persist-dbs!)))) + (safe-api-call "persistent-dbs" (fn [_data] (persist-dbs!)))) (defn ^:large-vars/cleanup-todo listen-to-electron! [] ;; TODO: move "file-watcher" to electron.ipc.channels - (js/window.apis.on "file-watcher" + (safe-api-call "file-watcher" (fn [data] (let [{:keys [type payload]} (bean/->clj data)] (watcher-handler/handle-changed! type payload) (when (file-sync-handler/enable-sync?) (sync/file-watch-handler type payload))))) - (js/window.apis.on "file-sync-progress" - (fn [data] - (let [payload (bean/->clj data)] - (state/set-state! [:file-sync/graph-state (:graphUUID payload) :file-sync/progress (:file payload)] payload)))) + (safe-api-call "file-sync-progress" + (fn [data] + (let [payload (bean/->clj data)] + (state/set-state! [:file-sync/graph-state (:graphUUID payload) :file-sync/progress (:file payload)] payload)))) - (js/window.apis.on "notification" - (fn [data] - (let [{:keys [type payload]} (bean/->clj data) - type (keyword type) - comp [:div (str payload)]] - (notification/show! comp type false)))) + (safe-api-call "notification" + (fn [data] + (let [{:keys [type payload]} (bean/->clj data) + type (keyword type) + comp [:div (str payload)]] + (notification/show! comp type false)))) - (js/window.apis.on "graphUnlinked" - (fn [data] - (let [repo (bean/->clj data)] - (repo-handler/remove-repo! repo)))) + (safe-api-call "graphUnlinked" + (fn [data] + (let [repo (bean/->clj data)] + (repo-handler/remove-repo! repo)))) - (js/window.apis.on "setGitUsernameAndEmail" - (fn [] - (state/pub-event! [:modal/set-git-username-and-email]))) + (safe-api-call "setGitUsernameAndEmail" + (fn [] + (state/pub-event! [:modal/set-git-username-and-email]))) - (js/window.apis.on "getCurrentGraph" - (fn [] - (when-let [graph (state/get-current-repo)] - (ipc/ipc "setCurrentGraph" graph)))) + (safe-api-call "getCurrentGraph" + (fn [] + (when-let [graph (state/get-current-repo)] + (ipc/ipc "setCurrentGraph" graph)))) - (js/window.apis.on "redirect" - (fn [data] - (let [{:keys [payload]} (bean/->clj data) - payload (update payload :to keyword)] - (route-handler/redirect! payload)))) + (safe-api-call "redirect" + (fn [data] + (let [{:keys [payload]} (bean/->clj data) + payload (update payload :to keyword)] + (route-handler/redirect! payload)))) - (js/window.apis.on "redirectWhenExists" - ;; Redirect to the given page or block when the provided page or block exists. - ;; Either :page-name or :block-id is required. - ;; :page-name : the original-name of the page. - ;; :block-id : uuid. - (fn [data] - (let [{:keys [page-name block-id file]} (bean/->clj data)] - (cond - page-name - (let [db-page-name (db-model/get-redirect-page-name page-name) - whiteboard? (db-model/whiteboard-page? db-page-name)] - ;; No error handling required, as a page name is always valid - ;; Open new page if the page does not exist - (if whiteboard? - (route-handler/redirect-to-whiteboard! page-name {:block-id block-id}) - (editor-handler/insert-first-page-block-if-not-exists! db-page-name))) + (safe-api-call "redirectWhenExists" + ;; Redirect to the given page or block when the provided page or block exists. + ;; Either :page-name or :block-id is required. + ;; :page-name : the original-name of the page. + ;; :block-id : uuid. + (fn [data] + (let [{:keys [page-name block-id file]} (bean/->clj data)] + (cond + page-name + (let [db-page-name (db-model/get-redirect-page-name page-name) + whiteboard? (db-model/whiteboard-page? db-page-name)] + ;; No error handling required, as a page name is always valid + ;; Open new page if the page does not exist + (if whiteboard? + (route-handler/redirect-to-whiteboard! page-name {:block-id block-id}) + (editor-handler/insert-first-page-block-if-not-exists! db-page-name))) - block-id - (if (db-model/get-block-by-uuid block-id) - (route-handler/redirect-to-page! block-id) - (notification/show! (str "Open link failed. Block-id `" block-id "` doesn't exist in the graph.") :error false)) + block-id + (if (db-model/get-block-by-uuid block-id) + (route-handler/redirect-to-page! block-id) + (notification/show! (str "Open link failed. Block-id `" block-id "` doesn't exist in the graph.") :error false)) - file - (if-let [db-page-name (db-model/get-file-page file false)] - (route-handler/redirect-to-page! db-page-name) - (notification/show! (str "Open link failed. File `" file "` doesn't exist in the graph.") :error false)))))) + file + (if-let [db-page-name (db-model/get-file-page file false)] + (route-handler/redirect-to-page! db-page-name) + (notification/show! (str "Open link failed. File `" file "` doesn't exist in the graph.") :error false)))))) - (js/window.apis.on "dbsync" - (fn [data] - (let [{:keys [graph tx-data]} (bean/->clj data) - tx-data (db/string->db (:data tx-data))] - (when-let [conn (db/get-db graph false)] - (d/transact! conn tx-data {:dbsync? true})) - (ui-handler/re-render-root!)))) + (safe-api-call "dbsync" + (fn [data] + (let [{:keys [graph tx-data]} (bean/->clj data) + tx-data (db/string->db (:data tx-data))] + (when-let [conn (db/get-db graph false)] + (d/transact! conn tx-data {:dbsync? true})) + (ui-handler/re-render-root!)))) - (js/window.apis.on "persistGraph" - ;; electron is requesting window for persisting a graph in it's db - ;; fire back "broadcastPersistGraphDone" on done - (fn [data] - (let [repo (bean/->clj data) - before-f #(notification/show! - (ui/loading (t :graph/persist)) - :warning) - after-f #(ipc/ipc "broadcastPersistGraphDone") - error-f (fn [] - (after-f) - (notification/show! - (t :graph/persist-error) - :error)) - handlers {:before before-f - :on-success after-f - :on-error error-f}] - (repo-handler/persist-db! repo handlers)))) + (safe-api-call "persistGraph" + ;; electron is requesting window for persisting a graph in it's db + ;; fire back "broadcastPersistGraphDone" on done + (fn [data] + (let [repo (bean/->clj data) + before-f #(notification/show! + (ui/loading (t :graph/persist)) + :warning) + after-f #(ipc/ipc "broadcastPersistGraphDone") + error-f (fn [] + (after-f) + (notification/show! + (t :graph/persist-error) + :error)) + handlers {:before before-f + :on-success after-f + :on-error error-f}] + (repo-handler/persist-db! repo handlers)))) - (js/window.apis.on "foundInPage" - (fn [data] - (let [data' (bean/->clj data)] - (state/set-state! [:ui/find-in-page :matches] data') - (dom/remove-style! (dom/by-id "search-in-page-input") :visibility) - (dom/set-text! (dom/by-id "search-in-page-placeholder") "") - (ui/focus-element "search-in-page-input") - true))) + (safe-api-call "foundInPage" + (fn [data] + (let [data' (bean/->clj data)] + (state/set-state! [:ui/find-in-page :matches] data') + (dom/remove-style! (dom/by-id "search-in-page-input") :visibility) + (dom/set-text! (dom/by-id "search-in-page-placeholder") "") + (ui/focus-element "search-in-page-input")))) - (js/window.apis.on "loginCallback" - (fn [code] - (user/login-callback code))) + (safe-api-call "loginCallback" + (fn [code] + (user/login-callback code))) - (js/window.apis.on "quickCapture" - (fn [args] - (state/pub-event! [:editor/quick-capture args]))) + (safe-api-call "quickCapture" + (fn [args] + (state/pub-event! [:editor/quick-capture args]))) - (js/window.apis.on "openNewWindowOfGraph" - ;; Handle open new window in renderer, until the destination graph doesn't rely on setting local storage - ;; No db cache persisting ensured. Should be handled by the caller - (fn [repo] - (ui-handler/open-new-window! repo))) + (safe-api-call "openNewWindowOfGraph" + ;; Handle open new window in renderer, until the destination graph doesn't rely on setting local storage + ;; No db cache persisting ensured. Should be handled by the caller + (fn [repo] + (ui-handler/open-new-window! repo))) - (js/window.apis.on "invokeLogseqAPI" - (fn [^js data] - (let [sync-id (.-syncId data) - method (.-method data) - args (.-args data) - ret-fn! #(ipc/invoke (str :electron.server/sync! sync-id) %)] + (safe-api-call "invokeLogseqAPI" + (fn [^js data] + (let [sync-id (.-syncId data) + method (.-method data) + args (.-args data) + ret-fn! #(ipc/invoke (str :electron.server/sync! sync-id) %)] - (try - (println "invokeLogseqAPI:" method) - (let [^js apis (aget js/window.logseq "api")] - (when-not (aget apis method) - (throw (js/Error. (str "MethodNotExist: " method)))) - (-> (p/promise (apply js-invoke apis method args)) - (p/then #(ret-fn! %)) - (p/catch #(ret-fn! {:error %})))) - (catch js/Error e - (ret-fn! {:error (.-message e)})))))) + (try + (println "invokeLogseqAPI:" method) + (let [^js apis (aget js/window.logseq "api")] + (when-not (aget apis method) + (throw (js/Error. (str "MethodNotExist: " method)))) + (-> (p/promise (apply js-invoke apis method args)) + (p/then #(ret-fn! %)) + (p/catch #(ret-fn! {:error %})))) + (catch js/Error e + (ret-fn! {:error (.-message e)})))))) - (js/window.apis.on "syncAPIServerState" - (fn [^js data] - (state/set-state! :electron/server (bean/->clj data))))) + (safe-api-call "syncAPIServerState" + (fn [^js data] + (state/set-state! :electron/server (bean/->clj data))))) (defn listen! [] diff --git a/src/main/frontend/handler/ui.cljs b/src/main/frontend/handler/ui.cljs index 94a4c0a5e..82d7bc13c 100644 --- a/src/main/frontend/handler/ui.cljs +++ b/src/main/frontend/handler/ui.cljs @@ -92,11 +92,13 @@ (re-render-root! {})) ([{:keys [clear-all-query-state?] :or {clear-all-query-state? false}}] + {:post [(nil? %)]} (when-let [component (state/get-root-component)] (if clear-all-query-state? (db/clear-query-state!) (db/clear-query-state-without-refs-and-embeds!)) - (rum/request-render component)))) + (rum/request-render component)) + nil)) (defn highlight-element! [fragment]