handler.cljs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. (ns frontend.handler
  2. "Main ns that handles application startup. Closest ns that we have to a
  3. system. Contains a couple of small system components"
  4. (:require [cljs.reader :refer [read-string]]
  5. [clojure.string :as string]
  6. [electron.ipc :as ipc]
  7. [electron.listener :as el]
  8. [frontend.components.block :as block]
  9. [frontend.components.editor :as editor]
  10. [frontend.components.page :as page]
  11. [frontend.components.reference :as reference]
  12. [frontend.components.whiteboard :as whiteboard]
  13. [frontend.config :as config]
  14. [frontend.context.i18n :as i18n :refer [t]]
  15. [frontend.db :as db]
  16. [frontend.db.restore :as db-restore]
  17. [frontend.db.conn :as conn]
  18. [frontend.db.persist :as db-persist]
  19. [frontend.db.react :as react]
  20. [frontend.error :as error]
  21. [frontend.extensions.srs :as srs]
  22. [frontend.handler.command-palette :as command-palette]
  23. [frontend.handler.events :as events]
  24. [frontend.handler.file :as file-handler]
  25. [frontend.handler.global-config :as global-config-handler]
  26. [frontend.handler.notification :as notification]
  27. [frontend.handler.page :as page-handler]
  28. [frontend.handler.plugin-config :as plugin-config-handler]
  29. [frontend.handler.repo :as repo-handler]
  30. [frontend.handler.repo-config :as repo-config-handler]
  31. [frontend.handler.ui :as ui-handler]
  32. [frontend.handler.user :as user-handler]
  33. [frontend.idb :as idb]
  34. [frontend.mobile.util :as mobile-util]
  35. [frontend.modules.instrumentation.core :as instrument]
  36. [frontend.modules.outliner.datascript :as outliner-db]
  37. [frontend.modules.outliner.file :as file]
  38. [frontend.modules.shortcut.core :as shortcut]
  39. [frontend.state :as state]
  40. [frontend.ui :as ui]
  41. [frontend.util :as util]
  42. [frontend.util.persist-var :as persist-var]
  43. [goog.object :as gobj]
  44. [lambdaisland.glogi :as log]
  45. [promesa.core :as p]
  46. [frontend.mobile.core :as mobile]
  47. [frontend.db.listener :as db-listener]
  48. [frontend.db.rtc.core :as rtc-core]
  49. [cljs-bean.core :as bean]))
  50. (defn- set-global-error-notification!
  51. []
  52. (set! js/window.onerror
  53. (fn [message, _source, _lineno, _colno, error]
  54. (when-not (error/ignored? message)
  55. (log/error :exception error)
  56. ;; (notification/show!
  57. ;; (str "message=" message "\nsource=" source "\nlineno=" lineno "\ncolno=" colno "\nerror=" error)
  58. ;; :error
  59. ;; ;; Don't auto-hide
  60. ;; false)
  61. ))))
  62. (defn- watch-for-date!
  63. []
  64. (let [f (fn []
  65. #_:clj-kondo/ignore
  66. (let [repo (state/get-current-repo)]
  67. (when (or
  68. (config/db-based-graph? repo)
  69. (and (not (state/nfs-refreshing?))
  70. (not (contains? (:file/unlinked-dirs @state/state)
  71. (config/get-repo-dir repo)))))
  72. ;; Don't create the journal file until user writes something
  73. (page-handler/create-today-journal!))))]
  74. (f)
  75. (js/setInterval f 5000)))
  76. (defn- instrument!
  77. []
  78. (let [total (srs/get-srs-cards-total)]
  79. (state/set-state! :srs/cards-due-count total)))
  80. (defn restore-and-setup!
  81. [repos]
  82. (when-let [repo (or (state/get-current-repo) (:url (first repos)))]
  83. (-> (db-restore/restore-graph! repo)
  84. (p/then
  85. (fn []
  86. (rtc-core/init-rtc-op-db repo)
  87. (db-listener/listen-and-persist! repo)
  88. ;; try to load custom css only for current repo
  89. (ui-handler/add-style-if-exists!)
  90. (->
  91. (p/do! (repo-config-handler/start {:repo repo})
  92. (when (config/global-config-enabled?)
  93. (global-config-handler/start {:repo repo}))
  94. (when (config/plugin-config-enabled?)
  95. (plugin-config-handler/start)))
  96. (p/finally
  97. (fn []
  98. ;; install after config is restored
  99. (shortcut/refresh!)
  100. (cond
  101. (and (not (seq (db/get-files config/local-repo)))
  102. ;; Not native local directory
  103. (not (some config/local-file-based-graph? (map :url repos)))
  104. (not (mobile-util/native-platform?)))
  105. ;; will execute `(state/set-db-restoring! false)` inside
  106. (repo-handler/setup-local-repo-if-not-exists!)
  107. :else
  108. (state/set-db-restoring! false)))))))
  109. (p/then
  110. (fn []
  111. (js/console.log "db restored, setting up repo hooks")
  112. (state/pub-event! [:modal/nfs-ask-permission])
  113. (page-handler/init-commands!)
  114. (watch-for-date!)
  115. (file-handler/watch-for-current-graph-dir!)
  116. (state/pub-event! [:graph/restored (state/get-current-repo)])))
  117. (p/catch (fn [error]
  118. (log/error :exception error))))))
  119. (defn- handle-connection-change
  120. [e]
  121. (let [online? (= (gobj/get e "type") "online")]
  122. (state/set-online! online?)))
  123. (defn set-network-watcher!
  124. []
  125. (js/window.addEventListener "online" handle-connection-change)
  126. (js/window.addEventListener "offline" handle-connection-change))
  127. (defn enable-datalog-console
  128. "Enables datalog console in browser provided by https://github.com/homebaseio/datalog-console"
  129. []
  130. (js/document.documentElement.setAttribute "__datalog-console-remote-installed__" true)
  131. (.addEventListener js/window "message"
  132. (fn [event]
  133. (let [db (conn/get-db)]
  134. (when-let [devtool-message (gobj/getValueByKeys event "data" ":datalog-console.client/devtool-message")]
  135. (let [msg-type (:type (read-string devtool-message))]
  136. (case msg-type
  137. :datalog-console.client/request-whole-database-as-string
  138. (.postMessage js/window #js {":datalog-console.remote/remote-message" (pr-str db)} "*")
  139. nil)))))))
  140. (defn clear-cache!
  141. []
  142. (notification/show! "Clearing..." :warning false)
  143. (p/let [_ (when (util/electron?)
  144. (ipc/ipc "clearCache"))
  145. _ (idb/clear-local-storage-and-idb!)]
  146. (js/setTimeout
  147. (fn [] (if (util/electron?)
  148. (ipc/ipc :reloadWindowPage)
  149. (js/window.location.reload)))
  150. 2000)))
  151. ;; FIXME: Another get-repos implementation at src\main\frontend\handler\repo.cljs
  152. (defn- get-repos
  153. []
  154. (p/let [nfs-dbs (db-persist/get-all-graphs)]
  155. ;; TODO: Better IndexDB migration handling
  156. (cond
  157. (and (mobile-util/native-platform?)
  158. (some #(or (string/includes? % " ")
  159. (string/includes? % "logseq_local_/")) nfs-dbs))
  160. (do (notification/show! ["DB version is not compatible, please clear cache then re-add your graph back."
  161. (ui/button
  162. (t :settings-page/clear-cache)
  163. :class "ui__modal-enter"
  164. :class "text-sm p-1"
  165. :on-click clear-cache!)] :error false)
  166. {:url config/local-repo
  167. :example? true})
  168. (seq nfs-dbs)
  169. (map (fn [db] {:url db :nfs? true}) nfs-dbs)
  170. :else
  171. [{:url config/local-repo
  172. :example? true}])))
  173. (defn- register-components-fns!
  174. []
  175. (state/set-page-blocks-cp! page/page-blocks-cp)
  176. (state/set-component! :block/linked-references reference/block-linked-references)
  177. (state/set-component! :whiteboard/tldraw-preview whiteboard/tldraw-preview)
  178. (state/set-component! :block/single-block block/single-block-cp)
  179. (state/set-component! :editor/box editor/box)
  180. (command-palette/register-global-shortcut-commands))
  181. (reset! db-listener/*db-listener outliner-db/after-transact-pipelines)
  182. (defn- get-system-info
  183. []
  184. (when (util/electron?)
  185. (p/let [info (ipc/ipc :system/info)]
  186. (state/set-state! :system/info (bean/->clj info)))))
  187. (defn start!
  188. [render]
  189. (get-system-info)
  190. (set-global-error-notification!)
  191. (set! js/window.onhashchange #(state/hide-custom-context-menu!)) ;; close context menu when page navs
  192. (register-components-fns!)
  193. (user-handler/restore-tokens-from-localstorage)
  194. (state/set-db-restoring! true)
  195. (when (util/electron?)
  196. (el/listen!))
  197. (render)
  198. (i18n/start)
  199. (instrument/init)
  200. (state/set-online! js/navigator.onLine)
  201. (set-network-watcher!)
  202. (util/indexeddb-check?
  203. (fn [_error]
  204. (notification/show! "Sorry, it seems that your browser doesn't support IndexedDB, we recommend to use latest Chrome(Chromium) or Firefox(Non-private mode)." :error false)
  205. (state/set-indexedb-support! false)))
  206. (idb/start)
  207. (react/run-custom-queries-when-idle!)
  208. (events/run!)
  209. (p/do!
  210. (when (mobile-util/native-platform?)
  211. (mobile/mobile-preinit))
  212. (-> (p/let [repos (get-repos)
  213. _ (state/set-repos! repos)
  214. _ (mobile-util/hide-splash) ;; hide splash as early as ui is stable
  215. _ (restore-and-setup! repos)]
  216. (when (mobile-util/native-platform?)
  217. (state/restore-mobile-theme!)))
  218. (p/catch (fn [e]
  219. (js/console.error "Error while restoring repos: " e)))
  220. (p/finally (fn []
  221. (state/set-db-restoring! false))))
  222. (file/<ratelimit-file-writes!)
  223. (util/<app-wake-up-from-sleep-loop (atom false))
  224. (when config/dev?
  225. (enable-datalog-console))
  226. (persist-var/load-vars)
  227. (js/setTimeout instrument! (* 60 1000))))
  228. (defn stop! []
  229. (prn "stop!"))
  230. (defn quit-and-install-new-version!
  231. []
  232. (p/let [_ (el/persist-dbs!)
  233. _ (ipc/invoke "set-quit-dirty-state" false)]
  234. (ipc/ipc :quitAndInstall)))