state.cljs 31 KB

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