state.cljs 27 KB

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