state.cljs 32 KB

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