state.cljs 30 KB

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