state.cljs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  1. (ns frontend.state
  2. (:require [frontend.storage :as storage]
  3. [rum.core :as rum]
  4. [frontend.util :as util :refer-macros [profile]]
  5. [clojure.string :as string]
  6. [cljs-bean.core :as bean]
  7. [medley.core :as medley]
  8. [goog.object :as gobj]
  9. [goog.dom :as gdom]
  10. [dommy.core :as dom]
  11. [cljs.core.async :as async]
  12. [lambdaisland.glogi :as log]
  13. [cljs-time.core :as t]
  14. [cljs-time.format :as tf]))
  15. (defonce ^:private state
  16. (atom
  17. {:route-match nil
  18. :today nil
  19. :db/batch-txs (async/chan 100)
  20. :file/writes (async/chan 100)
  21. :notification/show? false
  22. :notification/content nil
  23. :repo/cloning? false
  24. :repo/loading-files? nil
  25. :repo/importing-to-db? nil
  26. :repo/sync-status {}
  27. :repo/changed-files nil
  28. :nfs/user-granted? {}
  29. :nfs/refreshing? nil
  30. ;; TODO: how to detect the network reliably?
  31. :network/online? true
  32. :indexeddb/support? true
  33. :me nil
  34. :git/current-repo (storage/get :git/current-repo)
  35. :git/status {}
  36. :format/loading {}
  37. :draw? false
  38. :db/restoring? nil
  39. :journals-length 1
  40. :search/q ""
  41. :search/result nil
  42. ;; custom shortcuts
  43. :shortcuts {:editor/new-block "enter"}
  44. ;; right sidebar
  45. :ui/sidebar-open? false
  46. :ui/left-sidebar-open? false
  47. :ui/theme (or (storage/get :ui/theme) "dark")
  48. ;; :show-all, :hide-block-body, :hide-block-children
  49. :ui/cycle-collapse :show-all
  50. :ui/collapsed-blocks {}
  51. :ui/sidebar-collapsed-blocks {}
  52. :ui/root-component nil
  53. :ui/file-component nil
  54. :ui/custom-query-components {}
  55. :ui/show-recent? false
  56. :ui/developer-mode? (or (= (storage/get "developer-mode") "true")
  57. false)
  58. :document/mode? (or (storage/get :document/mode?) false)
  59. :github/contents {}
  60. :config {}
  61. :editor/show-page-search? false
  62. :editor/show-page-search-hashtag? false
  63. :editor/show-date-picker? false
  64. ;; With label or other data
  65. :editor/show-input nil
  66. :editor/last-saved-cursor nil
  67. :editor/editing? nil
  68. :editor/last-edit-block-id nil
  69. :editor/in-composition? false
  70. :editor/pos 0
  71. :editor/content {}
  72. :editor/block nil
  73. :editor/block-dom-id nil
  74. :editor/set-timestamp-block nil
  75. :editor/last-input-time nil
  76. :db/last-transact-time {}
  77. :db/last-persist-transact-ids {}
  78. ;; whether database is persisted
  79. :db/persisted? {}
  80. :db/latest-txs (or (storage/get-transit :db/latest-txs) {})
  81. :cursor-range nil
  82. :selection/mode false
  83. :selection/blocks []
  84. :selection/start-block nil
  85. :custom-context-menu/show? false
  86. :custom-context-menu/links nil
  87. ;; pages or blocks in the right sidebar
  88. ;; It is a list of `[repo db-id block-type block-data]` 4-tuple
  89. :sidebar/blocks '()
  90. :preferred-language (storage/get :preferred-language)
  91. ;; electron
  92. :electron/updater-pending? false
  93. :electron/updater {}
  94. ;; all notification contents as k-v pairs
  95. :notification/contents {}
  96. :graph/syncing? false}))
  97. (defn get-route-match
  98. []
  99. (:route-match @state))
  100. (defn get-current-route
  101. []
  102. (get-in (get-route-match) [:data :name]))
  103. (defn home?
  104. []
  105. (= :home (get-current-route)))
  106. (defn get-current-page
  107. []
  108. (and
  109. (= :page (get-current-route))
  110. (get-in (get-route-match)
  111. [:path-params :name])))
  112. (defn route-has-p?
  113. []
  114. (get-in (get-route-match) [:query-params :p]))
  115. (defn sub
  116. [ks]
  117. (if (coll? ks)
  118. (util/react (rum/cursor-in state ks))
  119. (util/react (rum/cursor state ks))))
  120. (defn set-state!
  121. [path value]
  122. (if (vector? path)
  123. (swap! state assoc-in path value)
  124. (swap! state assoc path value)))
  125. (defn update-state!
  126. [path f]
  127. (if (vector? path)
  128. (swap! state update-in path f)
  129. (swap! state update path f)))
  130. (defn get-current-repo
  131. []
  132. (or (:git/current-repo @state) "local"))
  133. (defn get-config
  134. ([]
  135. (get-config (get-current-repo)))
  136. ([repo-url]
  137. (get-in @state [:config repo-url])))
  138. (defonce built-in-macros
  139. {"img" "[:img.$4 {:src \"$1\" :style {:width $2 :height $3}}]"})
  140. (defn get-macros
  141. []
  142. (merge
  143. built-in-macros
  144. (:macros (get-config))))
  145. (defn sub-config
  146. []
  147. (sub :config))
  148. (defn get-custom-css-link
  149. []
  150. (:custom-css-url (get-config)))
  151. (defn all-pages-public?
  152. []
  153. (true? (:all-pages-public? (get-config))))
  154. (defn enable-grammarly?
  155. []
  156. (true? (:feature/enable-grammarly?
  157. (get (sub-config) (get-current-repo)))))
  158. (defn enable-timetracking?
  159. []
  160. (not (false? (:feature/enable-timetracking?
  161. (get (sub-config) (get-current-repo))))))
  162. (defn enable-journals?
  163. [repo]
  164. (not (false? (:feature/enable-journals?
  165. (get (sub-config) repo)))))
  166. (defn enable-git-auto-push?
  167. [repo]
  168. (not (false? (:git-auto-push
  169. (get (sub-config) repo)))))
  170. (defn enable-block-time?
  171. []
  172. ;; (true? (:feature/enable-block-time?
  173. ;; (get (sub-config) (get-current-repo))))
  174. ;; Disable block timestamps for now, because it doesn't work with undo/redo
  175. false)
  176. ;; Enable by default
  177. (defn show-brackets?
  178. []
  179. (not (false? (:ui/show-brackets?
  180. (get (sub-config) (get-current-repo))))))
  181. (defn get-default-home
  182. []
  183. (:default-home (get-config)))
  184. (defn sub-default-home-page
  185. []
  186. (get-in (sub-config) [(get-current-repo) :default-home :page] ""))
  187. (defn custom-home-page?
  188. []
  189. (some? (:page (get-default-home))))
  190. (defn get-preferred-format
  191. ([]
  192. (get-preferred-format (get-current-repo)))
  193. ([repo-url]
  194. (keyword
  195. (or
  196. (when-let [fmt (:preferred-format (get-config repo-url))]
  197. (string/lower-case (name fmt)))
  198. (get-in @state [:me :preferred_format] "markdown")))))
  199. (defn get-pages-directory
  200. []
  201. (or
  202. (when-let [repo (get-current-repo)]
  203. (:pages-directory (get-config repo)))
  204. "pages"))
  205. (defn org-mode-file-link?
  206. [repo]
  207. (:org-mode/insert-file-link? (get-config repo)))
  208. (defn get-journal-file-name-format
  209. []
  210. (when-let [repo (get-current-repo)]
  211. (:journal/file-name-format (get-config repo))))
  212. (defn get-preferred-workflow
  213. []
  214. (keyword
  215. (or
  216. (when-let [workflow (:preferred-workflow (get-config))]
  217. (let [workflow (name workflow)]
  218. (if (re-find #"now|NOW" workflow)
  219. :now
  220. :todo)))
  221. (get-in @state [:me :preferred_workflow] :now))))
  222. (defn get-preferred-todo
  223. []
  224. (if (= (get-preferred-workflow) :now)
  225. "LATER"
  226. "TODO"))
  227. (defn hide-file?
  228. []
  229. (:hide-file-in-page? (get-config)))
  230. (defn page-name-order
  231. "Decide whether to use file name or :title as page name. If it returns \"file\", use the file
  232. name unless it is missing."
  233. []
  234. (:page-name-order (get-config)))
  235. (defn get-repos
  236. []
  237. (get-in @state [:me :repos]))
  238. (defn set-repos!
  239. [repos]
  240. (set-state! [:me :repos] repos))
  241. (defn add-repo!
  242. [repo]
  243. (when repo
  244. (update-state! [:me :repos]
  245. (fn [repos]
  246. (->> (conj repos repo)
  247. (distinct))))))
  248. (defn set-current-repo!
  249. [repo]
  250. (swap! state assoc :git/current-repo repo)
  251. (if repo
  252. (storage/set :git/current-repo repo)
  253. (storage/remove :git/current-repo)))
  254. (defn set-preferred-format!
  255. [format]
  256. (swap! state assoc-in [:me :preferred_format] (name format)))
  257. (defn set-preferred-workflow!
  258. [workflow]
  259. (swap! state assoc-in [:me :preferred_workflow] (name workflow)))
  260. (defn set-preferred-language!
  261. [language]
  262. (set-state! :preferred-language (name language))
  263. (storage/set :preferred-language (name language)))
  264. (defn delete-repo!
  265. [repo]
  266. (swap! state update-in [:me :repos]
  267. (fn [repos]
  268. (->> (remove #(= (:url repo)
  269. (:url %))
  270. repos)
  271. (util/distinct-by :url))))
  272. (when (= (get-current-repo) (:url repo))
  273. (set-current-repo! (:url (first (get-repos))))))
  274. (defn next-collapse-mode
  275. []
  276. (case (:ui/cycle-collapse @state)
  277. :show-all
  278. :hide-block-body
  279. :hide-block-body
  280. :hide-block-children
  281. :hide-block-children
  282. :show-all))
  283. (defn cycle-collapse!
  284. []
  285. (set-state! :ui/cycle-collapse (next-collapse-mode)))
  286. (defn set-timestamp-block!
  287. [value]
  288. (set-state! :editor/set-timestamp-block value))
  289. (defn get-timestamp-block
  290. []
  291. (:editor/set-timestamp-block @state))
  292. (defn set-edit-content!
  293. ([input-id value] (set-edit-content! input-id value true))
  294. ([input-id value set-input-value?]
  295. (when input-id
  296. (when set-input-value?
  297. (when-let [input (gdom/getElement input-id)]
  298. (util/set-change-value input value)))
  299. (update-state! :editor/content (fn [m]
  300. (assoc m input-id value)))
  301. ;; followers
  302. ;; (when-let [s (util/extract-uuid input-id)]
  303. ;; (let [input (gdom/getElement input-id)
  304. ;; leader-parent (util/rec-get-block-node input)
  305. ;; followers (->> (array-seq (js/document.getElementsByClassName s))
  306. ;; (remove #(= leader-parent %)))]
  307. ;; (prn "followers: " (count followers))
  308. ;; ))
  309. )))
  310. (defn get-edit-input-id
  311. []
  312. (ffirst (:editor/editing? @state)))
  313. (defn get-last-edit-input-id
  314. []
  315. (:editor/last-edit-block-id @state))
  316. (defn editing?
  317. []
  318. (some? (get-edit-input-id)))
  319. (defn get-edit-content
  320. []
  321. (get (:editor/content @state) (get-edit-input-id)))
  322. (defn append-current-edit-content!
  323. [append-text]
  324. (when-not (string/blank? append-text)
  325. (when-let [input-id (get-edit-input-id)]
  326. (when-let [input (gdom/getElement input-id)]
  327. (let [value (gobj/get input "value")
  328. new-value (str value append-text)
  329. new-value (if (or (= (last value) " ")
  330. (= (last value) "\n"))
  331. new-value
  332. (str "\n" new-value))]
  333. (js/document.execCommand "insertText" false append-text)
  334. (update-state! :editor/content (fn [m]
  335. (assoc m input-id new-value))))))))
  336. (defn get-cursor-range
  337. []
  338. (:cursor-range @state))
  339. (defn set-cursor-range!
  340. [range]
  341. (set-state! :cursor-range range))
  342. ; FIXME: unused function
  343. (defn get-cloning?
  344. []
  345. (:repo/cloning? @state))
  346. (defn set-cloning!
  347. [value]
  348. (set-state! :repo/cloning? value))
  349. (defn get-block-collapsed-state
  350. [block-id]
  351. (get-in @state [:ui/collapsed-blocks block-id]))
  352. (defn set-collapsed-state!
  353. [block-id value]
  354. (set-state! [:ui/collapsed-blocks block-id] value))
  355. (defn collapse-block!
  356. [block-id]
  357. (set-collapsed-state! block-id true))
  358. (defn expand-block!
  359. [block-id]
  360. (set-collapsed-state! block-id false))
  361. (defn collapsed?
  362. [block-id]
  363. (get-in @state [:ui/collapsed-blocks block-id]))
  364. (defn clear-collapsed-blocks!
  365. []
  366. (set-state! :ui/collapsed-blocks {}))
  367. (defn set-q!
  368. [value]
  369. (set-state! :search/q value))
  370. (defn set-editor-show-page-search!
  371. [value]
  372. (set-state! :editor/show-page-search? value)
  373. (set-state! :editor/show-page-search-hashtag? false))
  374. (defn set-editor-show-page-search-hashtag!
  375. [value]
  376. (set-state! :editor/show-page-search? value)
  377. (set-state! :editor/show-page-search-hashtag? value))
  378. (defn get-editor-show-page-search?
  379. []
  380. (get @state :editor/show-page-search?))
  381. (defn get-editor-show-page-search-hashtag?
  382. []
  383. (get @state :editor/show-page-search-hashtag?))
  384. (defn set-editor-show-block-search!
  385. [value]
  386. (set-state! :editor/show-block-search? value))
  387. (defn get-editor-show-block-search?
  388. []
  389. (get @state :editor/show-block-search?))
  390. (defn set-editor-show-template-search!
  391. [value]
  392. (set-state! :editor/show-template-search? value))
  393. (defn get-editor-show-template-search?
  394. []
  395. (get @state :editor/show-template-search?))
  396. (defn set-editor-show-date-picker!
  397. [value]
  398. (set-state! :editor/show-date-picker? value))
  399. (defn get-editor-show-date-picker?
  400. []
  401. (get @state :editor/show-date-picker?))
  402. (defn set-editor-show-input!
  403. [value]
  404. (set-state! :editor/show-input value))
  405. (defn get-editor-show-input
  406. []
  407. (get @state :editor/show-input))
  408. (defn set-edit-input-id!
  409. [input-id]
  410. (swap! state update :editor/editing?
  411. (fn [m]
  412. (and input-id {input-id true}))))
  413. (defn set-edit-pos!
  414. [pos]
  415. (set-state! :editor/pos pos))
  416. (defn get-edit-pos
  417. []
  418. (:editor/pos @state))
  419. (defn set-selection-start-block!
  420. [start-block]
  421. (swap! state assoc :selection/start-block start-block))
  422. (defn get-selection-start-block
  423. []
  424. (get @state :selection/start-block))
  425. (defn set-selection-blocks!
  426. [blocks]
  427. (when (seq blocks)
  428. (swap! state assoc
  429. :selection/mode true
  430. :selection/blocks blocks)))
  431. (defn into-selection-mode!
  432. []
  433. (swap! state assoc :selection/mode true))
  434. (defn clear-selection!
  435. []
  436. (swap! state assoc
  437. :selection/mode false
  438. :selection/blocks nil
  439. :selection/up? nil))
  440. (defn clear-selection-blocks!
  441. []
  442. (swap! state assoc :selection/blocks nil))
  443. (defn get-selection-blocks
  444. []
  445. (:selection/blocks @state))
  446. (defn in-selection-mode?
  447. []
  448. (:selection/mode @state))
  449. (defn conj-selection-block!
  450. [block up?]
  451. (dom/add-class! block "selected noselect")
  452. (swap! state assoc
  453. :selection/mode true
  454. :selection/blocks (conj (:selection/blocks @state) block)
  455. :selection/up? up?))
  456. (defn pop-selection-block!
  457. []
  458. (let [[first-block & others] (:selection/blocks @state)]
  459. (swap! state assoc
  460. :selection/mode true
  461. :selection/blocks others)
  462. first-block))
  463. (defn selection-up?
  464. []
  465. (:selection/up? @state))
  466. (defn set-selection-up!
  467. [value]
  468. (swap! state assoc :selection/up? value))
  469. (defn show-custom-context-menu!
  470. [links]
  471. (swap! state assoc
  472. :custom-context-menu/show? true
  473. :custom-context-menu/links links))
  474. (defn hide-custom-context-menu!
  475. []
  476. (swap! state assoc
  477. :custom-context-menu/show? false
  478. :custom-context-menu/links nil))
  479. (defn set-github-token!
  480. [repo token-result]
  481. (when token-result
  482. (let [{:keys [token expires_at]} token-result]
  483. (swap! state update-in [:me :repos]
  484. (fn [repos]
  485. (map (fn [r]
  486. (if (= repo (:url r))
  487. (merge r {:token token :expires_at expires_at})
  488. repo)) repos))))))
  489. (defn set-github-installation-tokens!
  490. [tokens]
  491. (when (seq tokens)
  492. (let [tokens (medley/index-by :installation_id tokens)
  493. repos (get-repos)]
  494. (when (seq repos)
  495. (let [set-token-f
  496. (fn [{:keys [installation_id] :as repo}]
  497. (let [{:keys [token] :as m} (get tokens installation_id)]
  498. (if (string? token)
  499. ;; Github API returns a expires_at key which is a timestamp (expires after 60 minutes at present),
  500. ;; however, user's system time may be inaccurate. Here, based on the client system time, we use
  501. ;; 40-minutes interval to deal with some critical conditions, for e.g. http request time consume.
  502. (let [formatter (tf/formatters :date-time-no-ms)
  503. expires-at (->> (t/plus (t/now) (t/minutes 40))
  504. (tf/unparse formatter))]
  505. (merge repo {:token token :expires_at expires-at}))
  506. (do
  507. (when (and
  508. (:url repo)
  509. (string/starts-with? (:url repo) "https://"))
  510. (log/error :token/cannot-set-token {:repo-m repo :token-m m}))
  511. repo))))
  512. repos (mapv set-token-f repos)]
  513. (swap! state assoc-in [:me :repos] repos))))))
  514. (defn get-github-token
  515. [repo]
  516. (when repo
  517. (let [repos (get-repos)]
  518. (some #(when (= repo (:url %)) %) repos))))
  519. (defn toggle-sidebar-open?!
  520. []
  521. (swap! state update :ui/sidebar-open? not))
  522. (defn open-right-sidebar!
  523. []
  524. (swap! state assoc :ui/sidebar-open? true))
  525. (defn hide-right-sidebar!
  526. []
  527. (swap! state assoc :ui/sidebar-open? false))
  528. (defn sidebar-add-block!
  529. [repo db-id block-type block-data]
  530. (when db-id
  531. (update-state! :sidebar/blocks (fn [blocks]
  532. (->> (remove #(= (second %) db-id) blocks)
  533. (cons [repo db-id block-type block-data])
  534. ; FIXME: No need to call `distinct`?
  535. (distinct))))
  536. (open-right-sidebar!)
  537. (when-let [elem (gdom/getElement "right-sidebar")]
  538. (util/scroll-to elem 0))))
  539. (defn sidebar-remove-block!
  540. [idx]
  541. (update-state! :sidebar/blocks #(util/drop-nth idx %))
  542. (when (empty? (:sidebar/blocks @state))
  543. (hide-right-sidebar!)))
  544. (defn get-sidebar-blocks
  545. []
  546. (:sidebar/blocks @state))
  547. (defn sidebar-block-toggle-collapse!
  548. [db-id]
  549. (when db-id
  550. (update-state! [:ui/sidebar-collapsed-blocks db-id] not)))
  551. (defn set-editing!
  552. [edit-input-id content block cursor-range]
  553. (when edit-input-id
  554. (let [block-element (gdom/getElement (string/replace edit-input-id "edit-block" "ls-block"))
  555. {:keys [idx container]} (util/get-block-idx-inside-container block-element)
  556. block (if (and idx container)
  557. (assoc block
  558. :block/idx idx
  559. :block/container (gobj/get container "id"))
  560. block)
  561. content (or content "")]
  562. (swap! state
  563. (fn [state]
  564. (-> state
  565. (assoc-in [:editor/content edit-input-id] (string/trim content))
  566. (assoc
  567. :editor/block block
  568. :editor/editing? {edit-input-id true}
  569. :editor/last-edit-block-id edit-input-id
  570. :cursor-range cursor-range)))))))
  571. (defn clear-edit!
  572. []
  573. (swap! state merge {:editor/editing? nil
  574. :editor/block nil
  575. :cursor-range nil}))
  576. (defn get-edit-block
  577. []
  578. (get @state :editor/block))
  579. (defn set-last-pos!
  580. [new-pos]
  581. (set-state! :editor/last-saved-cursor new-pos))
  582. (defn set-block-content-and-last-pos!
  583. [edit-input-id content new-pos]
  584. (when edit-input-id
  585. (set-edit-content! edit-input-id content)
  586. (set-state! :editor/last-saved-cursor new-pos)))
  587. (defn set-theme!
  588. [theme]
  589. (set-state! :ui/theme theme)
  590. (storage/set :ui/theme theme))
  591. (defn set-editing-block-dom-id!
  592. [block-dom-id]
  593. (set-state! :editor/block-dom-id block-dom-id))
  594. (defn get-editing-block-dom-id
  595. []
  596. (:editor/block-dom-id @state))
  597. (defn toggle-theme!
  598. []
  599. (let [theme (:ui/theme @state)
  600. theme' (if (= theme "dark") "white" "dark")]
  601. (set-theme! theme')))
  602. (defn- file-content-key
  603. [repo path]
  604. (str "ls_file_content_" repo path))
  605. (defn update-sync-status!
  606. [status]
  607. (when (seq status)
  608. (when-let [current-repo (get-current-repo)]
  609. (set-state! [:repo/sync-status current-repo] status))))
  610. (defn set-root-component!
  611. [component]
  612. (set-state! :ui/root-component component))
  613. (defn get-root-component
  614. []
  615. (get @state :ui/root-component))
  616. (defn setup-electron-updater!
  617. []
  618. (when (util/electron?)
  619. (js/window.apis.setUpdatesCallback
  620. (fn [_ args]
  621. (let [data (bean/->clj args)
  622. pending? (not= (:type data) "completed")]
  623. (set-state! :electron/updater-pending? pending?)
  624. (when pending? (set-state! :electron/updater data))
  625. nil)))))
  626. (defn set-file-component!
  627. [component]
  628. (set-state! :ui/file-component component))
  629. (defn clear-file-component!
  630. []
  631. (set-state! :ui/file-component nil))
  632. (defn get-file-component
  633. []
  634. (get @state :ui/file-component))
  635. (defn set-journals-length!
  636. [value]
  637. (when value
  638. (set-state! :journals-length value)))
  639. (defn add-custom-query-component!
  640. [query-string component]
  641. (update-state! :ui/custom-query-components
  642. (fn [m]
  643. (assoc m query-string component))))
  644. (defn remove-custom-query-component!
  645. [query-string]
  646. (update-state! :ui/custom-query-components
  647. (fn [m]
  648. (dissoc m query-string))))
  649. (defn get-custom-query-components
  650. []
  651. (vals (get @state :ui/custom-query-components)))
  652. (defn get-journal-template
  653. []
  654. (when-let [repo (get-current-repo)]
  655. (get-in @state [:config repo :default-templates :journals])))
  656. (defn set-today!
  657. [value]
  658. (set-state! :today value))
  659. (defn toggle-document-mode!
  660. []
  661. (let [mode (get @state :document/mode?)]
  662. (set-state! :document/mode? (not mode))
  663. (storage/set :document/mode? (not mode))))
  664. (defn get-date-formatter
  665. []
  666. (or
  667. (when-let [repo (get-current-repo)]
  668. (get-in @state [:config repo :date-formatter]))
  669. ;; TODO:
  670. (get-in @state [:me :settings :date-formatter])
  671. "MMM do, yyyy"))
  672. (defn set-git-status!
  673. [repo-url value]
  674. (swap! state assoc-in [:git/status repo-url] value))
  675. (defn get-shortcut
  676. ([key]
  677. (get-shortcut (get-current-repo) key))
  678. ([repo key]
  679. (or
  680. (get (storage/get (str repo "-shortcuts")) key)
  681. (get-in @state [:config repo :shortcuts key]))))
  682. (defn get-me
  683. []
  684. (:me @state))
  685. (defn get-name
  686. []
  687. (:name (get-me)))
  688. (defn logged?
  689. "Whether the user has logged in."
  690. []
  691. (some? (get-name)))
  692. (defn set-draw!
  693. [value]
  694. (set-state! :draw? value))
  695. (defn in-draw-mode?
  696. []
  697. (:draw? @state))
  698. (defn set-db-restoring!
  699. [value]
  700. (set-state! :db/restoring? value))
  701. (defn get-default-branch
  702. [repo-url]
  703. (or
  704. (some->> (get-repos)
  705. (filter (fn [m]
  706. (= (:url m) repo-url)))
  707. (first)
  708. :branch)
  709. "master"))
  710. (defn get-current-project
  711. []
  712. (when-let [project (get-in (get-config) [:project :name])]
  713. (when-not (string/blank? project)
  714. project)))
  715. (defn update-current-project
  716. [& kv]
  717. {:pre [(even? (count kv))]}
  718. (when-let [current-repo (get-current-repo)]
  719. (let [new-kvs (apply array-map (vec kv))
  720. projects (:projects (get-me))
  721. new-projects (reduce (fn [acc project]
  722. (if (= (:repo project) current-repo)
  723. (conj acc (merge project new-kvs))
  724. (conj acc project)))
  725. []
  726. projects)]
  727. (set-state! [:me :projects] new-projects))))
  728. (defn remove-current-project
  729. []
  730. (when-let [current-repo (get-current-repo)]
  731. (update-state! [:me :projects]
  732. (fn [projects]
  733. (remove #(= (:repo %) current-repo) projects)))))
  734. (defn set-indexedb-support!
  735. [value]
  736. (set-state! :indexeddb/support? value))
  737. (defn set-modal!
  738. [modal-panel-content]
  739. (swap! state assoc
  740. :modal/show? true
  741. :modal/panel-content modal-panel-content))
  742. (defn close-modal!
  743. []
  744. (swap! state assoc
  745. :modal/show? false
  746. :modal/panel-content nil))
  747. (defn get-db-batch-txs-chan
  748. []
  749. (:db/batch-txs @state))
  750. (defn get-file-write-chan
  751. []
  752. (:file/writes @state))
  753. (defn get-write-chan-length
  754. []
  755. (let [c (get-file-write-chan)]
  756. (count (gobj/get c "buf"))))
  757. (defn add-tx!
  758. ;; TODO: replace f with data for batch transactions
  759. [f]
  760. (when f
  761. (when-let [chan (get-db-batch-txs-chan)]
  762. (async/put! chan f))))
  763. (defn get-left-sidebar-open?
  764. []
  765. (get-in @state [:ui/left-sidebar-open?]))
  766. (defn set-left-sidebar-open!
  767. [value]
  768. (set-state! :ui/left-sidebar-open? value))
  769. (defn set-developer-mode!
  770. [value]
  771. (set-state! :ui/developer-mode? value)
  772. (storage/set "developer-mode" (str value)))
  773. (defn get-notification-contents
  774. []
  775. (get @state :notification/contents))
  776. (defn get-new-block-shortcut
  777. []
  778. (let [shortcut (get-in @state [:shortcuts :editor/new-block])]
  779. (if (and shortcut (contains? #{"enter" "alt+enter"} (string/lower-case shortcut)))
  780. shortcut
  781. "enter")))
  782. (defn set-new-block-shortcut!
  783. [value]
  784. (set-state! [:shortcuts :editor/new-block] value))
  785. (defn toggle-new-block-shortcut!
  786. []
  787. (if-let [enter? (= "enter" (get-new-block-shortcut))]
  788. (set-new-block-shortcut! "alt+enter")
  789. (set-new-block-shortcut! "enter")))
  790. (defn set-config!
  791. [repo-url value]
  792. (let [old-shortcuts (get-in @state [:config repo-url :shortcuts])]
  793. (set-state! [:config repo-url] value)
  794. ;; TODO: refactor. This seems useless as the default value has already been handled in
  795. ;; `get-new-block-shortcut`.
  796. (set-new-block-shortcut!
  797. (or (get-shortcut repo-url :editor/new-block)
  798. "enter"))
  799. (let [shortcuts (or (:shortcuts value) {})]
  800. (storage/set (str repo-url "-shortcuts") shortcuts))))
  801. (defn get-git-auto-push?
  802. ([]
  803. (get-git-auto-push? (get-current-repo)))
  804. ([repo]
  805. (true? (:git-auto-push (get-config repo)))))
  806. (defn set-changed-files!
  807. [repo changed-files]
  808. (set-state! [:repo/changed-files repo] changed-files))
  809. (defn get-changed-files
  810. []
  811. (get-in @state [:repo/changed-files (get-current-repo)]))
  812. (defn set-online!
  813. [value]
  814. (set-state! :network/online? value))
  815. (defn online?
  816. []
  817. (:network/online? @state))
  818. (defn get-commands
  819. []
  820. (:commands (get-config)))
  821. (defn set-graph-syncing?
  822. [value]
  823. (set-state! :graph/syncing? value))
  824. (defn set-editor-in-composition!
  825. [value]
  826. (set-state! :editor/in-composition? value))
  827. (defn editor-in-composition?
  828. []
  829. (:editor/in-composition? @state))
  830. (defn set-loading-files!
  831. [value]
  832. (set-state! :repo/loading-files? value))
  833. (defn set-importing-to-db!
  834. [value]
  835. (set-state! :repo/importing-to-db? value))
  836. (defn set-editor-last-input-time!
  837. [repo time]
  838. (swap! state assoc-in [:editor/last-input-time repo] time))
  839. (defn set-last-transact-time!
  840. [repo time]
  841. (swap! state assoc-in [:db/last-transact-time repo] time)
  842. ;; THINK: new block, indent/outdent, drag && drop, etc.
  843. (set-editor-last-input-time! repo time))
  844. (defn set-published-pages
  845. [pages]
  846. (when-let [repo (get-current-repo)]
  847. (set-state! [:me :published-pages repo] pages)))
  848. (defn reset-published-pages
  849. []
  850. (set-published-pages []))
  851. (defn set-db-persisted!
  852. [repo value]
  853. (swap! state assoc-in [:db/persisted? repo] value))
  854. (defn db-idle?
  855. [repo]
  856. (when repo
  857. (when-let [last-time (get-in @state [:db/last-transact-time repo])]
  858. (let [now (util/time-ms)]
  859. (>= (- now last-time) 3000)))))
  860. (defn input-idle?
  861. [repo]
  862. (when repo
  863. (or
  864. (when-let [last-time (get-in @state [:editor/last-input-time repo])]
  865. (let [now (util/time-ms)]
  866. (>= (- now last-time) 1000)))
  867. ;; not in editing mode
  868. (not (get-edit-input-id)))))
  869. (defn set-last-persist-transact-id!
  870. [repo files? id]
  871. (swap! state assoc-in [:db/last-persist-transact-ids :repo files?] id))
  872. (defn get-last-persist-transact-id
  873. [repo files?]
  874. (get-in @state [:db/last-persist-transact-ids :repo files?]))
  875. (defn persist-transaction!
  876. [repo files? tx-id tx-data]
  877. (when (seq tx-data)
  878. (let [latest-txs (:db/latest-txs @state)
  879. last-persist-tx-id (get-last-persist-transact-id repo files?)
  880. latest-txs (if last-persist-tx-id
  881. (update-in latest-txs [repo files?]
  882. (fn [result]
  883. (remove (fn [tx] (<= (:tx-id tx) last-persist-tx-id)) result)))
  884. latest-txs)
  885. new-txs (update-in latest-txs [repo files?] (fn [result]
  886. (vec (conj result {:tx-id tx-id
  887. :tx-data tx-data}))))]
  888. (storage/set-transit! :db/latest-txs new-txs)
  889. (set-state! :db/latest-txs new-txs))))
  890. (defn get-repo-latest-txs
  891. [repo file?]
  892. (get-in (:db/latest-txs @state) [repo file?]))
  893. (defn set-nfs-refreshing!
  894. [value]
  895. (set-state! :nfs/refreshing? value))
  896. (defn nfs-refreshing?
  897. []
  898. (:nfs/refreshing? @state))
  899. (defn set-search-result!
  900. [value]
  901. (set-state! :search/result value))
  902. (defn clear-search-result!
  903. []
  904. (set-search-result! nil))
  905. ;; TODO: Move those to the uni `state`
  906. (defonce editor-op (atom nil))
  907. (defn set-editor-op!
  908. [value]
  909. (reset! editor-op value))
  910. (defn get-editor-op
  911. []
  912. @editor-op)
  913. (defn get-start-of-week
  914. []
  915. (or
  916. (when-let [repo (get-current-repo)]
  917. (get-in @state [:config repo :start-of-week]))
  918. (get-in @state [:me :settings :start-of-week])
  919. 6))
  920. (defonce diffs (atom nil))