feature: logseq protocol; refactor persistGraph

pull/4922/head
Junyi Du 2022-04-12 20:18:31 +08:00 committed by Tienson Qin
parent f23c7cfd09
commit aa29a00b88
7 changed files with 98 additions and 48 deletions

View File

@ -44,10 +44,22 @@
[win url]
(.info logger "open-url" (str {:url url}))
(let [parsed-url (js/URL. url)]
(when (and (= (str LSP_SCHEME ":") (.-protocol parsed-url))
(= "auth-callback" (.-host parsed-url)))
(send-to-renderer win "loginCallback" (.get (.-searchParams parsed-url) "code")))))
(let [parsed-url (js/URL. url)
url-protocol (.-protocol parsed-url)
url-host (.-host parsed-url)]
(when (= (str LSP_SCHEME ":") url-protocol)
(cond
(= "auth-callback" url-host)
(send-to-renderer win "loginCallback" (.get (.-searchParams parsed-url) "code"))
(= "local" url-host)
(let [[graph page block-id] (utils/get-URL-decoded-params parsed-url ["graph" "page" "block-id"])
graph-name (handler/get-graph-name graph)]
(if graph-name
(utils/send-to-renderer win "openNewWindowOfGraph" graph-name)
(utils/send-to-renderer "notification" {:type "error"
:payload (str "Cannot match graph name " graph
" to any linked graph.")})))))))
(defn setup-interceptor! [^js app]
(.setAsDefaultProtocolClient app LSP_SCHEME)

View File

@ -36,6 +36,7 @@
:stat (fs/statSync path)}))
(defn watch-dir!
"Watch a directory if no such file watcher exists"
[_win dir]
(when (and (fs/existsSync dir)
(not (get @*file-watcher dir)))

View File

@ -209,6 +209,7 @@
dir))
(defn- get-graphs
"Returns all graph names in the cache directory (strating with `logseq_local_`)"
[]
(let [dir (get-graphs-dir)]
(->> (readdir dir)
@ -216,6 +217,15 @@
(map #(path/basename % ".transit"))
(map graph-name->path))))
(defn get-graph-name
"Given a graph's name, returns the graph's fullname.
E.g., given `cat`, returns `logseq_local_<path_to_directory>/cat`
Returns `nil` if no such graph exists."
[graph]
;; FIXME: path-normalize for electron?
(->> (get-graphs)
(some #(when (string/ends-with? % (str "/" graph)) %))))
(defmethod handle :getGraphs [_window [_]]
(get-graphs))
@ -326,18 +336,22 @@
(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)))
[window graph-path]
(when (not (win/graph-has-other-windows? window graph-path))
(watcher/close-watcher! graph-path)))
(defmethod handle :setCurrentGraph [^js win [_ 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)
(defn set-current-graph!
[window graph-path]
(let [old-path (state/get-window-graph-path window)]
(when old-path (close-watcher-when-orphaned! window old-path))
(swap! state/state assoc :graph/current graph-path)
(swap! state/state assoc-in [:window/graph window] graph-path)
nil))
(defmethod handle :setCurrentGraph [^js window [_ graph-name]]
(when graph-name
(set-current-graph! window (utils/get-graph-dir graph-name))))
(defmethod handle :runGit [_ [_ args]]
(when (seq args)
(git/raw! args)))
@ -375,26 +389,30 @@
(defmethod handle :graphHasMultipleWindows [^js _win [_ graph]]
(let [dir (utils/get-graph-dir graph)
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]]
;; receive dir path (not repo / graph) from frontend
;; Windows on same dir share the same watcher
;; Only close file watcher when:
;; 1. there is no one window on the same dir (TODO: check this on a window is closed)
;; 1. there is no one window on the same dir
;; 2. reset file watcher to resend `add` event on window refreshing
(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
;; maintain only one file watcher when multiple windows on the same dir
(close-watcher-when-orphaned! window dir)
(watcher/watch-dir! window dir)))
(defmethod handle :openNewWindow [_window [_]]
(defn open-new-window!
[]
(let [win (win/create-main-window)]
(win/on-close-actions! win close-watcher-when-orphaned!)
(win/setup-window-listeners! win)
nil))
win))
(defmethod handle :openNewWindow [_window [_]]
(open-new-window!)
nil)
(defmethod handle :searchVersionChanged?
[^js _win [_ graph]]
@ -443,21 +461,28 @@
(defmethod handle :default [args]
(println "Error: no ipc handler for: " (bean/->js args)))
(defmethod handle :persistGraph [^js win [_ graph]]
;; 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) ;; 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)
(utils/send-to-renderer win "persistGraphDone" graph)))) ;; if no such graph, skip directly
(defn broadcast-persist-graph!
"Sends persist graph event to the renderer contains the target graph.
Returns a promise."
[graph-name]
(p/create (fn [resolve _reject]
(let [graph-path (utils/get-graph-dir graph-name)
windows (win/get-graph-all-windows graph-path)
tar-graph-win (first windows)]
(if tar-graph-win
;; if no such graph, skip directly
(do (state/set-state! :window/once-persist-done #(resolve nil))
(utils/send-to-renderer tar-graph-win "persistGraph" graph-name))
(resolve nil))))))
(defmethod handle :persistGraphDone [^js _win [_ graph]]
;; when graph is persisted, broadcast it to all windows
(let [windows (win/get-all-windows)]
(doseq [window windows]
(utils/send-to-renderer window "persistGraphDone" graph))))
(defmethod handle :broadcastPersistGraph [^js _win [_ graph-name]]
(broadcast-persist-graph! graph-name))
(defmethod handle :broadcastPersistGraphDone [^js _win [_]]
;; main process -> renderer doesn't support promise, so we use a global var to store the callback
(when-let [f (:window/once-persist-done @state/state)]
(f)
(state/set-state! :window/once-persist-done nil)))
(defn set-ipc-handler! [window]
(let [main-channel "main"]

View File

@ -14,7 +14,10 @@
:graph/current nil
;; window -> current graph
:window/graph {}}))
:window/graph {}
;; job to do when persistGraph is done on renderer
:window/once-persist-done nil}))
(defn set-state!
[path value]

View File

@ -123,3 +123,13 @@
(defn get-graph-dir
[graph-name]
(string/replace graph-name "logseq_local_" ""))
(defn get-URL-decoded-params
"Get decoded URL parameters from parsed js/URL.
`nil` for non-existing keys."
[^js parsed-url keys]
(let [params (.-searchParams parsed-url)]
(map (fn [key]
(when-let [value (.get params key)]
(js/decodeURI value)))
keys)))

View File

@ -78,12 +78,13 @@
(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 "persistGraphDone" repo)
after-f #(ipc/ipc "broadcastPersistGraphDone")
error-f (fn []
(after-f)
(notification/show!
@ -96,7 +97,12 @@
(js/window.apis.on "loginCallback"
(fn [code]
(user/login-callback code))))
(user/login-callback code)))
(js/window.apis.on "openNewWindowOfGraph"
;; Handle open new window in renderer, until the destination graph doesn't rely on setting local storage
(fn [repo]
(ui-handler/open-new-window! nil repo))))
(defn listen!
[]

View File

@ -670,17 +670,10 @@
"Only works for electron
Call backend to handle persisting a specific db on other window
Skip persisting if no other windows is open (controlled by electron)
step 1. [In HERE] a window --persistGraph-----> electron
step 2. electron --persistGraph-----> window holds the graph
step 3. window w/ graph --persistGraphDone-> electron
step 4. [In HERE] electron --persistGraphDone-> all windows"
step 1. [In HERE] a window ---broadcastPersistGraph----> electron
step 2. electron ---------persistGraph-------> window holds the graph
step 3. window w/ graph --broadcastPersistGraphDone-> electron
step 4. [In HERE] a window <---broadcastPersistGraph---- electron"
[graph]
(p/create (fn [resolve _]
(js/window.apis.on "persistGraphDone"
#(let [repo (bean/->clj %)]
(prn "received persistGraphDone" repo)
(when (= graph repo)
;; js/window.apis.once doesn't work
(js/window.apis.removeAllListeners "persistGraphDone")
(resolve repo))))
(ipc/ipc "persistGraph" graph))))
(p/let [_ (ipc/ipc "broadcastPersistGraph" graph)] ;; invoke for chaining promise
nil))