handler.cljs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. (ns electron.handler
  2. "This ns starts the event handling for the electron main process and defines
  3. all the application-specific event types"
  4. (:require ["electron" :refer [ipcMain dialog app autoUpdater shell]]
  5. [cljs-bean.core :as bean]
  6. ["fs" :as fs]
  7. ["buffer" :as buffer]
  8. ["fs-extra" :as fs-extra]
  9. ["path" :as path]
  10. ["os" :as os]
  11. ["diff-match-patch" :as google-diff]
  12. ["/electron/utils" :as js-utils]
  13. ["abort-controller" :as AbortController]
  14. [electron.fs-watcher :as watcher]
  15. [electron.configs :as cfgs]
  16. [promesa.core :as p]
  17. [clojure.string :as string]
  18. [electron.utils :as utils]
  19. [electron.logger :as logger]
  20. [electron.state :as state]
  21. [clojure.core.async :as async]
  22. [electron.search :as search]
  23. [electron.git :as git]
  24. [electron.plugin :as plugin]
  25. [electron.window :as win]
  26. [electron.file-sync-rsapi :as rsapi]
  27. [electron.backup-file :as backup-file]
  28. [cljs.reader :as reader]
  29. [electron.find-in-page :as find]))
  30. (defmulti handle (fn [_window args] (keyword (first args))))
  31. (defmethod handle :mkdir [_window [_ dir]]
  32. (fs/mkdirSync dir))
  33. (defmethod handle :mkdir-recur [_window [_ dir]]
  34. (fs/mkdirSync dir #js {:recursive true}))
  35. ;; {encoding: 'utf8', withFileTypes: true}
  36. (defn- readdir
  37. [dir]
  38. (->> (tree-seq
  39. (fn [^js fpath]
  40. (.isDirectory (fs/statSync fpath)))
  41. (fn [dir]
  42. (let [files (fs/readdirSync dir (clj->js {:withFileTypes true}))]
  43. (->> files
  44. (remove #(.isSymbolicLink ^js %))
  45. (remove #(string/starts-with? (.-name ^js %) "."))
  46. (map #(.join path dir (.-name %))))))
  47. dir)
  48. (doall)
  49. (vec)))
  50. (defmethod handle :readdir [_window [_ dir]]
  51. (let [entries (readdir dir)]
  52. (js/console.log entries)
  53. entries))
  54. (defmethod handle :unlink [_window [_ repo-dir path]]
  55. (if (plugin/dotdir-file? path)
  56. (fs/unlinkSync path)
  57. (try
  58. (logger/info ::unlink {:path path})
  59. (let [file-name (-> (string/replace path (str repo-dir "/") "")
  60. (string/replace "/" "_")
  61. (string/replace "\\" "_"))
  62. recycle-dir (str repo-dir "/logseq/.recycle")
  63. _ (fs-extra/ensureDirSync recycle-dir)
  64. new-path (str recycle-dir "/" file-name)]
  65. (fs/renameSync path new-path)
  66. (logger/debug ::unlink "recycle to" new-path))
  67. (catch :default e
  68. (logger/error ::unlink path e)
  69. nil))))
  70. (defonce Diff (google-diff.))
  71. (defn string-some-deleted?
  72. [old new]
  73. (let [result (.diff_main Diff old new)]
  74. (some (fn [a] (= -1 (first a))) result)))
  75. (defmethod handle :backupDbFile [_window [_ repo path db-content new-content]]
  76. (when (and (string? db-content)
  77. (string? new-content)
  78. (string-some-deleted? db-content new-content))
  79. (logger/info ::backup "backup db file" path)
  80. (backup-file/backup-file repo :backup-dir path (path/extname path) db-content)))
  81. (defmethod handle :addVersionFile [_window [_ repo path content]]
  82. (backup-file/backup-file repo :version-file-dir path (path/extname path) content))
  83. (defmethod handle :openFileBackupDir [_window [_ repo path]]
  84. (when (string? path)
  85. (let [dir (backup-file/get-backup-dir repo path)]
  86. (.openPath shell dir))))
  87. (defmethod handle :readFile [_window [_ path]]
  88. (utils/read-file path))
  89. (defn writable?
  90. [path]
  91. (assert (string? path))
  92. (try
  93. (fs/accessSync path (aget fs "W_OK"))
  94. (catch :default _e
  95. false)))
  96. (defmethod handle :writeFile [window [_ repo path content]]
  97. (let [^js Buf (.-Buffer buffer)
  98. ^js content (if (instance? js/ArrayBuffer content)
  99. (.from Buf content)
  100. content)]
  101. (try
  102. (when (and (fs/existsSync path) (not (writable? path)))
  103. (fs/chmodSync path "644"))
  104. (fs/writeFileSync path content)
  105. (fs/statSync path)
  106. (catch :default e
  107. (logger/warn ::write-file path e)
  108. (let [backup-path (try
  109. (backup-file/backup-file repo :backup-dir path (path/extname path) content)
  110. (catch :default e
  111. (logger/error ::write-file "backup file failed:" e)))]
  112. (utils/send-to-renderer window "notification" {:type "error"
  113. :payload (str "Write to the file " path
  114. " failed, "
  115. e
  116. (when backup-path
  117. (str ". A backup file was saved to "
  118. backup-path
  119. ".")))}))))))
  120. (defmethod handle :rename [_window [_ old-path new-path]]
  121. (logger/info ::rename "from" old-path "to" new-path)
  122. (fs/renameSync old-path new-path))
  123. (defmethod handle :stat [_window [_ path]]
  124. (fs/statSync path))
  125. (defonce allowed-formats
  126. #{:org :markdown :md :edn :json :js :css :excalidraw})
  127. (defn get-ext
  128. [p]
  129. (-> (.extname path p)
  130. (subs 1)
  131. keyword))
  132. (defn- get-files
  133. [path]
  134. (let [result (->>
  135. (readdir path)
  136. (remove (partial utils/ignored-path? path))
  137. (filter #(contains? allowed-formats (get-ext %)))
  138. (map (fn [path]
  139. (let [stat (fs/statSync path)]
  140. (when-not (.isDirectory stat)
  141. {:path (utils/fix-win-path! path)
  142. :content (utils/read-file path)
  143. :stat stat}))))
  144. (remove nil?))]
  145. (vec (cons {:path (utils/fix-win-path! path)} result))))
  146. (defn open-dir-dialog []
  147. (p/let [result (.showOpenDialog dialog (bean/->js
  148. {:properties ["openDirectory" "createDirectory" "promptToCreate"]}))
  149. result (get (js->clj result) "filePaths")]
  150. (p/resolved (first result))))
  151. (defmethod handle :openDir [^js _window _messages]
  152. (logger/info ::open-dir "open folder selection dialog")
  153. (p/let [path (open-dir-dialog)]
  154. (logger/debug ::open-dir {:path path})
  155. (if path
  156. (p/resolved (bean/->js (get-files path)))
  157. (p/rejected (js/Error "path empty")))))
  158. (defmethod handle :getFiles [_window [_ path]]
  159. (logger/debug ::get-files {:path path})
  160. (get-files path))
  161. (defn- sanitize-graph-name
  162. [graph-name]
  163. (when graph-name
  164. (-> graph-name
  165. (string/replace "/" "++")
  166. (string/replace ":" "+3A+"))))
  167. (defn- graph-name->path
  168. [graph-name]
  169. (when graph-name
  170. (-> graph-name
  171. (string/replace "+3A+" ":")
  172. (string/replace "++" "/"))))
  173. (defn- get-graphs-dir
  174. []
  175. (let [dir (if utils/ci?
  176. (.resolve path js/__dirname "../tmp/graphs")
  177. (.join path (.homedir os) ".logseq" "graphs"))]
  178. (fs-extra/ensureDirSync dir)
  179. dir))
  180. (defn- get-graphs
  181. "Returns all graph names in the cache directory (strating with `logseq_local_`)"
  182. []
  183. (let [dir (get-graphs-dir)]
  184. (->> (readdir dir)
  185. (remove #{dir})
  186. (map #(path/basename % ".transit"))
  187. (map graph-name->path))))
  188. ;; TODO support alias mechanism
  189. (defn get-graph-name
  190. "Given a graph's name of string, returns the graph's fullname.
  191. E.g., given `cat`, returns `logseq_local_<path_to_directory>/cat`
  192. Returns `nil` if no such graph exists."
  193. [graph-identifier]
  194. (->> (get-graphs)
  195. (some #(when (string/ends-with? (utils/normalize-lc %)
  196. (str "/" (utils/normalize-lc graph-identifier)))
  197. %))))
  198. (defmethod handle :getGraphs [_window [_]]
  199. (get-graphs))
  200. (defn- read-txid-info!
  201. [root]
  202. (try
  203. (let [txid-path (.join path root "logseq/graphs-txid.edn")]
  204. (when (fs/existsSync txid-path)
  205. (when-let [sync-meta (and (not (string/blank? root))
  206. (.toString (.readFileSync fs txid-path)))]
  207. (reader/read-string sync-meta))))
  208. (catch :default e
  209. (js/console.debug "[read txid meta] #" root (.-message e)))))
  210. (defmethod handle :inflateGraphsInfo [_win [_ graphs]]
  211. (if (seq graphs)
  212. (for [{:keys [root] :as graph} graphs]
  213. (if-let [sync-meta (read-txid-info! root)]
  214. (assoc graph
  215. :sync-meta sync-meta
  216. :GraphUUID (second sync-meta))
  217. graph))
  218. []))
  219. (defmethod handle :readGraphTxIdInfo [_win [_ root]]
  220. (read-txid-info! root))
  221. (defn- get-graph-path
  222. [graph-name]
  223. (when graph-name
  224. (let [graph-name (sanitize-graph-name graph-name)
  225. dir (get-graphs-dir)]
  226. (.join path dir (str graph-name ".transit")))))
  227. (defn- get-serialized-graph
  228. [graph-name]
  229. (when graph-name
  230. (when-let [file-path (get-graph-path graph-name)]
  231. (when (fs/existsSync file-path)
  232. (utils/read-file file-path)))))
  233. (defmethod handle :getSerializedGraph [_window [_ graph-name]]
  234. (get-serialized-graph graph-name))
  235. (defmethod handle :saveGraph [_window [_ graph-name value-str]]
  236. ;; NOTE: graph-name is a plain "local" for demo graph.
  237. (when (and graph-name value-str (not (= "local" graph-name)))
  238. (when-let [file-path (get-graph-path graph-name)]
  239. (fs/writeFileSync file-path value-str))))
  240. (defmethod handle :deleteGraph [_window [_ graph-name]]
  241. (when graph-name
  242. (when-let [file-path (get-graph-path graph-name)]
  243. (when (fs/existsSync file-path)
  244. (fs-extra/removeSync file-path)))))
  245. (defmethod handle :persistent-dbs-saved [_window _]
  246. (async/put! state/persistent-dbs-chan true)
  247. true)
  248. (defmethod handle :search-blocks [_window [_ repo q opts]]
  249. (search/search-blocks repo q opts))
  250. (defmethod handle :rebuild-blocks-indice [_window [_ repo data]]
  251. (search/truncate-blocks-table! repo)
  252. ;; unneeded serialization
  253. (search/upsert-blocks! repo (bean/->js data))
  254. (search/write-search-version! repo)
  255. [])
  256. (defmethod handle :transact-blocks [_window [_ repo data]]
  257. (let [{:keys [blocks-to-remove-set blocks-to-add]} data]
  258. (when (seq blocks-to-remove-set)
  259. (search/delete-blocks! repo blocks-to-remove-set))
  260. (when (seq blocks-to-add)
  261. ;; unneeded serialization
  262. (search/upsert-blocks! repo (bean/->js blocks-to-add)))))
  263. (defmethod handle :truncate-blocks [_window [_ repo]]
  264. (search/truncate-blocks-table! repo))
  265. (defmethod handle :remove-db [_window [_ repo]]
  266. (search/delete-db! repo))
  267. (defn clear-cache!
  268. [window]
  269. (let [graphs-dir (get-graphs-dir)]
  270. (fs-extra/removeSync graphs-dir))
  271. (let [path (.getPath ^object app "userData")]
  272. (doseq [dir ["search" "IndexedDB"]]
  273. (let [path (path/join path dir)]
  274. (try
  275. (fs-extra/removeSync path)
  276. (catch :default e
  277. (logger/error "Clear cache:" e)))))
  278. (utils/send-to-renderer window "redirect" {:payload {:to :home}})))
  279. (defmethod handle :clearCache [window _]
  280. (logger/info ::clear-cache)
  281. (search/close!)
  282. (clear-cache! window)
  283. (search/ensure-search-dir!))
  284. (defmethod handle :openDialog [^js _window _messages]
  285. (open-dir-dialog))
  286. (defmethod handle :copyDirectory [^js _window [_ src dest opts]]
  287. (fs-extra/copy src dest opts))
  288. (defmethod handle :getLogseqDotDirRoot []
  289. (utils/get-ls-dotdir-root))
  290. (defmethod handle :testProxyUrl [win [_ url]]
  291. (p/let [_ (utils/fetch url)]
  292. (utils/send-to-renderer win :notification {:type "success" :payload (str "Successfully: " url)})))
  293. (defmethod handle :httpFetchJSON [_win [_ url options]]
  294. (p/let [res (utils/fetch url options)
  295. json (.json res)]
  296. json))
  297. (defmethod handle :getUserDefaultPlugins []
  298. (utils/get-ls-default-plugins))
  299. (defmethod handle :validateUserExternalPlugins [_win [_ urls]]
  300. (zipmap urls (for [url urls]
  301. (try
  302. (and (fs-extra/pathExistsSync url)
  303. (fs-extra/pathExistsSync (path/join url "package.json")))
  304. (catch :default _e false)))))
  305. (defmethod handle :relaunchApp []
  306. (.relaunch app) (.quit app))
  307. (defmethod handle :quitApp []
  308. (.quit app))
  309. (defmethod handle :userAppCfgs [_window [_ k v]]
  310. (let [config (cfgs/get-config)]
  311. (if-not k
  312. config
  313. (if-not (nil? v)
  314. (cfgs/set-item! (keyword k) v)
  315. (cfgs/get-item (keyword k))))))
  316. (defmethod handle :getDirname [_]
  317. js/__dirname)
  318. (defmethod handle :getAppBaseInfo [^js win [_ _opts]]
  319. {:isFullScreen (.isFullScreen win)})
  320. (defmethod handle :getAssetsFiles [^js win [_ {:keys [exts]}]]
  321. (when-let [graph-path (state/get-window-graph-path win)]
  322. (p/let [^js files (js-utils/getAllFiles (.join path graph-path "assets") (clj->js exts))]
  323. files)))
  324. (defn close-watcher-when-orphaned!
  325. "When it's the last window for the directory, close the watcher."
  326. [window graph-path]
  327. (when (not (win/graph-has-other-windows? window graph-path))
  328. (watcher/close-watcher! graph-path)))
  329. (defn set-current-graph!
  330. [window graph-path]
  331. (let [old-path (state/get-window-graph-path window)]
  332. (when (and old-path graph-path (not= old-path graph-path))
  333. (close-watcher-when-orphaned! window old-path))
  334. (swap! state/state assoc :graph/current graph-path)
  335. (swap! state/state assoc-in [:window/graph window] graph-path)
  336. nil))
  337. (defmethod handle :setCurrentGraph [^js window [_ graph-name]]
  338. (when graph-name
  339. (set-current-graph! window (utils/get-graph-dir graph-name))))
  340. (defmethod handle :runGit [_ [_ args]]
  341. (when (seq args)
  342. (git/raw! args)))
  343. (defmethod handle :runGitWithinCurrentGraph [_ [_ args]]
  344. (when (seq args)
  345. (git/init!)
  346. (git/run-git2! (clj->js args))))
  347. (defmethod handle :gitCommitAll [_ [_ message]]
  348. (git/add-all-and-commit! message))
  349. (defmethod handle :installMarketPlugin [_ [_ mft]]
  350. (plugin/install-or-update! mft))
  351. (defmethod handle :updateMarketPlugin [_ [_ pkg]]
  352. (plugin/install-or-update! pkg))
  353. (defmethod handle :uninstallMarketPlugin [_ [_ id]]
  354. (plugin/uninstall! id))
  355. (def *request-abort-signals (atom {}))
  356. (defmethod handle :httpRequest [_ [_ req-id opts]]
  357. (let [{:keys [url abortable method data returnType headers]} opts]
  358. (when-let [[method type] (and (not (string/blank? url))
  359. [(keyword (string/upper-case (or method "GET")))
  360. (keyword (string/lower-case (or returnType "json")))])]
  361. (-> (utils/fetch url
  362. (-> {:method method
  363. :headers (and headers (bean/->js headers))}
  364. (merge (when (and (not (contains? #{:GET :HEAD} method)) data)
  365. ;; TODO: support type of arrayBuffer
  366. {:body (js/JSON.stringify (bean/->js data))})
  367. (when-let [^js controller (and abortable (AbortController.))]
  368. (swap! *request-abort-signals assoc req-id controller)
  369. {:signal (.-signal controller)}))))
  370. (p/then (fn [^js res]
  371. (case type
  372. :json
  373. (.json res)
  374. :arraybuffer
  375. (.arrayBuffer res)
  376. :base64
  377. (-> (.buffer res)
  378. (p/then #(.toString % "base64")))
  379. :text
  380. (.text res))))
  381. (p/catch
  382. (fn [^js e]
  383. ;; TODO: handle special cases
  384. (throw e)))
  385. (p/finally
  386. (fn []
  387. (swap! *request-abort-signals dissoc req-id)))))))
  388. (defmethod handle :httpRequestAbort [_ [_ req-id]]
  389. (when-let [^js controller (get @*request-abort-signals req-id)]
  390. (.abort controller)))
  391. (defmethod handle :quitAndInstall []
  392. (logger/info ::quick-and-install)
  393. (.quitAndInstall autoUpdater))
  394. (defmethod handle :graphUnlinked [^js _win [_ repo]]
  395. (doseq [window (win/get-all-windows)]
  396. (utils/send-to-renderer window "graphUnlinked" (bean/->clj repo))))
  397. (defmethod handle :dbsync [^js _win [_ graph tx-data]]
  398. (let [dir (utils/get-graph-dir graph)]
  399. (doseq [window (win/get-graph-all-windows dir)]
  400. (utils/send-to-renderer window "dbsync"
  401. (bean/->clj {:graph graph
  402. :tx-data tx-data})))))
  403. (defmethod handle :graphHasOtherWindow [^js win [_ graph]]
  404. (let [dir (utils/get-graph-dir graph)]
  405. (win/graph-has-other-windows? win dir)))
  406. (defmethod handle :graphHasMultipleWindows [^js _win [_ graph]]
  407. (let [dir (utils/get-graph-dir graph)
  408. windows (win/get-graph-all-windows dir)]
  409. (> (count windows) 1)))
  410. (defmethod handle :addDirWatcher [^js _window [_ dir options]]
  411. ;; receive dir path (not repo / graph) from frontend
  412. ;; Windows on same dir share the same watcher
  413. ;; Only close file watcher when:
  414. ;; 1. there is no one window on the same dir
  415. ;; 2. reset file watcher to resend `add` event on window refreshing
  416. (when dir
  417. (logger/debug ::watch-dir {:path dir})
  418. (watcher/watch-dir! dir options)
  419. nil))
  420. (defmethod handle :unwatchDir [^js _window [_ dir]]
  421. (when dir
  422. (logger/debug ::unwatch-dir {:path dir})
  423. (watcher/close-watcher! dir)
  424. nil))
  425. (defn open-new-window!
  426. "Persist db first before calling! Or may break db persistency"
  427. []
  428. (let [win (win/create-main-window)]
  429. (win/on-close-actions! win close-watcher-when-orphaned!)
  430. (win/setup-window-listeners! win)
  431. win))
  432. (defmethod handle :openNewWindow [_window [_]]
  433. (logger/info ::open-new-window)
  434. (open-new-window!)
  435. nil)
  436. (defmethod handle :graphReady [window [_ graph-name]]
  437. (when-let [f (:window/once-graph-ready @state/state)]
  438. (f window graph-name)
  439. (state/set-state! :window/once-graph-ready nil)))
  440. (defmethod handle :searchVersionChanged?
  441. [^js _win [_ graph]]
  442. (search/version-changed? graph))
  443. (defmethod handle :reloadWindowPage [^js win]
  444. (logger/warn ::reload-window-page)
  445. (when-let [web-content (.-webContents win)]
  446. (.reload web-content)))
  447. (defmethod handle :setHttpsAgent [^js _win [_ opts]]
  448. (utils/set-fetch-agent opts))
  449. ;;;;;;;;;;;;;;;;;;;;;;;
  450. ;; file-sync-rs-apis ;;
  451. ;;;;;;;;;;;;;;;;;;;;;;;
  452. (defmethod handle :key-gen [_]
  453. (rsapi/key-gen))
  454. (defmethod handle :set-env [_ args]
  455. (apply rsapi/set-env (rest args)))
  456. (defmethod handle :get-local-files-meta [_ args]
  457. (apply rsapi/get-local-files-meta (rest args)))
  458. (defmethod handle :get-local-all-files-meta [_ args]
  459. (apply rsapi/get-local-all-files-meta (rest args)))
  460. (defmethod handle :rename-local-file [_ args]
  461. (apply rsapi/rename-local-file (rest args)))
  462. (defmethod handle :delete-local-files [_ args]
  463. (apply rsapi/delete-local-files (rest args)))
  464. (defmethod handle :update-local-files [_ args]
  465. (apply rsapi/update-local-files (rest args)))
  466. (defmethod handle :download-version-files [_ args]
  467. (apply rsapi/download-version-files (rest args)))
  468. (defmethod handle :delete-remote-files [_ args]
  469. (apply rsapi/delete-remote-files (rest args)))
  470. (defmethod handle :update-remote-files [_ args]
  471. (apply rsapi/update-remote-files (rest args)))
  472. (defmethod handle :decrypt-fnames [_ args]
  473. (apply rsapi/decrypt-fnames (rest args)))
  474. (defmethod handle :encrypt-fnames [_ args]
  475. (apply rsapi/encrypt-fnames (rest args)))
  476. (defmethod handle :encrypt-with-passphrase [_ args]
  477. (apply rsapi/encrypt-with-passphrase (rest args)))
  478. (defmethod handle :decrypt-with-passphrase [_ args]
  479. (apply rsapi/decrypt-with-passphrase (rest args)))
  480. (defmethod handle :default [args]
  481. (logger/error "Error: no ipc handler for:" args))
  482. (defn broadcast-persist-graph!
  483. "Receive graph-name (not graph path)
  484. Sends persist graph event to the renderer contains the target graph.
  485. Returns a promise<void>."
  486. [graph-name]
  487. (p/create (fn [resolve _reject]
  488. (let [graph-path (utils/get-graph-dir graph-name)
  489. windows (win/get-graph-all-windows graph-path)
  490. tar-graph-win (first windows)]
  491. (if tar-graph-win
  492. ;; if no such graph, skip directly
  493. (do (state/set-state! :window/once-persist-done #(resolve nil))
  494. (utils/send-to-renderer tar-graph-win "persistGraph" graph-name))
  495. (resolve nil))))))
  496. (defmethod handle :broadcastPersistGraph [^js _win [_ graph-name]]
  497. (broadcast-persist-graph! graph-name))
  498. (defmethod handle :broadcastPersistGraphDone [^js _win [_]]
  499. ;; main process -> renderer doesn't support promise, so we use a global var to store the callback
  500. (when-let [f (:window/once-persist-done @state/state)]
  501. (f)
  502. (state/set-state! :window/once-persist-done nil)))
  503. (defmethod handle :find-in-page [^js win [_ search option]]
  504. (find/find! win search (bean/->js option)))
  505. (defmethod handle :clear-find-in-page [^js win [_]]
  506. (find/clear! win))
  507. (defn set-ipc-handler! [window]
  508. (let [main-channel "main"]
  509. (.handle ipcMain main-channel
  510. (fn [^js event args-js]
  511. (try
  512. (let [message (bean/->clj args-js)]
  513. ;; Be careful with the return values of `handle` defmethods.
  514. ;; Values that are not non-JS objects will cause this
  515. ;; exception -
  516. ;; https://www.electronjs.org/docs/latest/breaking-changes#behavior-changed-sending-non-js-objects-over-ipc-now-throws-an-exception
  517. (bean/->js (handle (or (utils/get-win-from-sender event) window) message)))
  518. (catch :default e
  519. (when-not (contains? #{"mkdir" "stat"} (nth args-js 0))
  520. (logger/error "IPC error: " {:event event
  521. :args args-js}
  522. e))
  523. e))))
  524. #(.removeHandler ipcMain main-channel)))