state.cljs 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820
  1. (ns frontend.state
  2. (:require [cljs-bean.core :as bean]
  3. [cljs.core.async :as async]
  4. [clojure.string :as string]
  5. [cljs.spec.alpha :as s]
  6. [dommy.core :as dom]
  7. [medley.core :as medley]
  8. [electron.ipc :as ipc]
  9. [frontend.storage :as storage]
  10. [frontend.util :as util]
  11. [frontend.util.cursor :as cursor]
  12. [goog.dom :as gdom]
  13. [goog.object :as gobj]
  14. [promesa.core :as p]
  15. [rum.core :as rum]
  16. [logseq.graph-parser.config :as gp-config]
  17. [frontend.mobile.util :as mobile-util]))
  18. ;; Stores main application state
  19. (defonce ^:large-vars/data-var state
  20. (let [document-mode? (or (storage/get :document/mode?) false)
  21. current-graph (let [graph (storage/get :git/current-repo)]
  22. (when graph (ipc/ipc "setCurrentGraph" graph))
  23. graph)]
  24. (atom
  25. {:route-match nil
  26. :today nil
  27. :system/events (async/chan 1000)
  28. :db/batch-txs (async/chan 1000)
  29. :file/writes (async/chan 10000)
  30. :file/unlinked-dirs #{}
  31. :reactive/custom-queries (async/chan 1000)
  32. :notification/show? false
  33. :notification/content nil
  34. :repo/loading-files? {}
  35. :nfs/user-granted? {}
  36. :nfs/refreshing? nil
  37. :instrument/disabled? (storage/get "instrument-disabled")
  38. ;; TODO: how to detect the network reliably?
  39. :network/online? true
  40. :indexeddb/support? true
  41. :me nil
  42. :git/current-repo current-graph
  43. :format/loading {}
  44. :draw? false
  45. :db/restoring? nil
  46. :journals-length 3
  47. :search/q ""
  48. :search/mode :global
  49. :search/result nil
  50. :search/graph-filters []
  51. ;; modals
  52. :modal/dropdowns {}
  53. :modal/id nil
  54. :modal/label ""
  55. :modal/show? false
  56. :modal/panel-content nil
  57. :modal/fullscreen? false
  58. :modal/close-btn? nil
  59. :modal/close-backdrop? true
  60. :modal/subsets []
  61. ;; left sidebar
  62. :ui/navigation-item-collapsed? {}
  63. ;; right sidebar
  64. :ui/fullscreen? false
  65. :ui/settings-open? false
  66. :ui/sidebar-open? false
  67. :ui/left-sidebar-open? (boolean (storage/get "ls-left-sidebar-open?"))
  68. :ui/theme (or (storage/get :ui/theme) "light")
  69. :ui/system-theme? ((fnil identity (or util/mac? util/win32? false)) (storage/get :ui/system-theme?))
  70. :ui/custom-theme (or (storage/get :ui/custom-theme) {:light {:mode "light"} :dark {:mode "dark"}})
  71. :ui/wide-mode? (storage/get :ui/wide-mode)
  72. ;; ui/collapsed-blocks is to separate the collapse/expand state from db for:
  73. ;; 1. right sidebar
  74. ;; 2. zoom-in view
  75. ;; 3. queries
  76. ;; 4. references
  77. ;; graph => {:block-id bool}
  78. :ui/collapsed-blocks {}
  79. :ui/sidebar-collapsed-blocks {}
  80. :ui/root-component nil
  81. :ui/file-component nil
  82. :ui/custom-query-components {}
  83. :ui/show-recent? false
  84. :ui/command-palette-open? false
  85. :ui/developer-mode? (or (= (storage/get "developer-mode") "true")
  86. false)
  87. ;; remember scroll positions of visited paths
  88. :ui/paths-scroll-positions {}
  89. :ui/shortcut-tooltip? (if (false? (storage/get :ui/shortcut-tooltip?))
  90. false
  91. true)
  92. :ui/scrolling? false
  93. :document/mode? document-mode?
  94. :config {}
  95. :block/component-editing-mode? false
  96. :editor/hidden-editors #{} ;; page names
  97. :editor/draw-mode? false
  98. :editor/action nil
  99. :editor/action-data nil
  100. ;; With label or other data
  101. :editor/last-saved-cursor nil
  102. :editor/editing? nil
  103. :editor/in-composition? false
  104. :editor/content {}
  105. :editor/block nil
  106. :editor/block-dom-id nil
  107. :editor/set-timestamp-block nil
  108. :editor/last-input-time nil
  109. :editor/document-mode? document-mode?
  110. :editor/args nil
  111. :editor/on-paste? false
  112. :editor/last-key-code nil
  113. ;; for audio record
  114. :editor/record-status "NONE"
  115. :db/last-transact-time {}
  116. ;; whether database is persisted
  117. :db/persisted? {}
  118. :cursor-range nil
  119. :selection/mode false
  120. ;; Warning: blocks order is determined when setting this attribute
  121. :selection/blocks []
  122. :selection/start-block nil
  123. ;; either :up or :down, defaults to down
  124. ;; used to determine selection direction when two or more blocks are selected
  125. :selection/direction :down
  126. :custom-context-menu/show? false
  127. :custom-context-menu/links nil
  128. :custom-context-menu/position nil
  129. ;; pages or blocks in the right sidebar
  130. ;; It is a list of `[repo db-id block-type block-data]` 4-tuple
  131. :sidebar/blocks '()
  132. :preferred-language (storage/get :preferred-language)
  133. ;; electron
  134. :electron/auto-updater-downloaded false
  135. :electron/updater-pending? false
  136. :electron/updater {}
  137. :electron/user-cfgs nil
  138. ;; mobile
  139. :mobile/show-action-bar? false
  140. :mobile/actioned-block nil
  141. :mobile/show-toolbar? false
  142. :mobile/show-recording-bar? false
  143. :mobile/show-tabbar? false
  144. ;;; Used to monitor mobile app status,
  145. ;;; value spec:
  146. ;;; {:is-active? bool, :timestamp int}
  147. :mobile/app-state-change (atom nil)
  148. ;; plugin
  149. :plugin/enabled (and (util/electron?)
  150. ;; true false :theme-only
  151. ((fnil identity true) (storage/get :lsp-core-enabled)))
  152. :plugin/preferences nil
  153. :plugin/indicator-text nil
  154. :plugin/installed-plugins {}
  155. :plugin/installed-themes []
  156. :plugin/installed-slash-commands {}
  157. :plugin/installed-ui-items {}
  158. :plugin/installed-resources {}
  159. :plugin/installed-hooks {}
  160. :plugin/simple-commands {}
  161. :plugin/selected-theme nil
  162. :plugin/selected-unpacked-pkg nil
  163. :plugin/marketplace-pkgs nil
  164. :plugin/marketplace-stats nil
  165. :plugin/installing nil
  166. :plugin/active-readme nil
  167. :plugin/updates-pending {}
  168. :plugin/updates-coming {}
  169. :plugin/updates-downloading? false
  170. :plugin/updates-unchecked #{}
  171. :plugin/navs-settings? true
  172. :plugin/focused-settings nil ;; plugin id
  173. ;; pdf
  174. :pdf/current nil
  175. :pdf/ref-highlight nil
  176. ;; all notification contents as k-v pairs
  177. :notification/contents {}
  178. :graph/syncing? false
  179. ;; graph -> state
  180. :graph/parsing-state {}
  181. ;; copied blocks
  182. :copy/blocks {:copy/content nil
  183. :copy/graph nil
  184. :copy/blocks nil}
  185. :copy/export-block-text-indent-style (or (storage/get :copy/export-block-text-indent-style)
  186. "dashes")
  187. :copy/export-block-text-remove-options (or (storage/get :copy/export-block-text-remove-options)
  188. #{})
  189. :date-picker/date nil
  190. :youtube/players {}
  191. ;; command palette
  192. :command-palette/commands []
  193. :view/components {}
  194. :favorites/dragging nil
  195. :srs/mode? false
  196. :srs/cards-due-count nil
  197. :reactive/query-dbs {}
  198. ;; login, userinfo, token, ...
  199. :auth/refresh-token nil
  200. :auth/access-token nil
  201. :auth/id-token nil
  202. ;; file-sync
  203. :file-sync/jstour-inst nil
  204. :file-sync/remote-graphs {:loading false :graphs nil}
  205. :file-sync/sync-manager nil
  206. :file-sync/sync-state-manager nil
  207. :file-sync/sync-state nil
  208. :file-sync/sync-uploading-files nil
  209. :file-sync/sync-downloading-files nil
  210. :file-sync/onboarding-state (or (storage/get :file-sync/onboarding-state)
  211. {:welcome false})
  212. :encryption/graph-parsing? false
  213. :ui/loading? {}
  214. :file-sync/set-remote-graph-password-result {}
  215. :feature/enable-sync? (storage/get :logseq-sync-enabled)
  216. :file/rename-event-chan (async/chan 100)
  217. :ui/find-in-page nil
  218. :graph/importing nil
  219. :graph/importing-state {}
  220. })))
  221. ;; Block ast state
  222. ;; ===============
  223. ;; block uuid -> {content(String) -> ast}
  224. (def blocks-ast-cache (atom {}))
  225. (defn add-block-ast-cache!
  226. [block-uuid content ast]
  227. (when (and block-uuid content ast)
  228. (let [new-value (assoc-in @blocks-ast-cache [block-uuid content] ast)
  229. new-value (if (> (count new-value) 10000)
  230. (into {} (take 5000 new-value))
  231. new-value)]
  232. (reset! blocks-ast-cache new-value))))
  233. (defn get-block-ast
  234. [block-uuid content]
  235. (when (and block-uuid content)
  236. (get-in @blocks-ast-cache [block-uuid content])))
  237. ;; User configuration getters under :config (and sometimes :me)
  238. ;; ========================================
  239. ;; TODO: Refactor default config values to be data driven. Currently they are all
  240. ;; buried in getters
  241. ;; TODO: Refactor our access to be more data driven. Currently each getter
  242. ;; (re-)fetches get-current-repo needlessly
  243. ;; TODO: Add consistent validation. Only a few config options validate at get time
  244. (def default-config
  245. "Default config for a repo-specific, user config"
  246. {:feature/enable-search-remove-accents? true
  247. :default-arweave-gateway "https://arweave.net"})
  248. ;; State that most user config is dependent on
  249. (declare get-current-repo)
  250. (defn merge-configs
  251. "Merges user configs in given orders. All values are overriden except for maps
  252. which are merged."
  253. [& configs]
  254. (apply merge-with
  255. (fn merge-config [current new]
  256. (if (and (map? current) (map? new))
  257. (merge current new)
  258. new))
  259. configs))
  260. (defn get-config
  261. "User config for the given repo or current repo if none given. All config fetching
  262. should be done through this fn in order to get global config and config defaults"
  263. ([]
  264. (get-config (get-current-repo)))
  265. ([repo-url]
  266. (merge-configs
  267. default-config
  268. (get-in @state [:config ::global-config])
  269. (get-in @state [:config repo-url]))))
  270. (defonce publishing? (atom nil))
  271. (defn publishing-enable-editing?
  272. []
  273. (and @publishing? (:publishing/enable-editing? (get-config))))
  274. (defn enable-editing?
  275. []
  276. (or (not @publishing?) (:publishing/enable-editing? (get-config))))
  277. (defn get-arweave-gateway
  278. []
  279. (:arweave/gateway (get-config)))
  280. (defonce built-in-macros
  281. {"img" "[:img.$4 {:src \"$1\" :style {:width $2 :height $3}}]"})
  282. (defn get-macros
  283. []
  284. (merge
  285. built-in-macros
  286. (:macros (get-config))))
  287. (defn get-custom-css-link
  288. []
  289. (:custom-css-url (get-config)))
  290. (defn get-custom-js-link
  291. []
  292. (:custom-js-url (get-config)))
  293. (defn get-default-journal-template
  294. []
  295. (when-let [template (get-in (get-config) [:default-templates :journals])]
  296. (when-not (string/blank? template)
  297. (string/trim template))))
  298. (defn all-pages-public?
  299. []
  300. (let [value (:publishing/all-pages-public? (get-config))
  301. value (if (some? value) value (:all-pages-public? (get-config)))]
  302. (true? value)))
  303. (defn get-default-home
  304. []
  305. (:default-home (get-config)))
  306. (defn custom-home-page?
  307. []
  308. (some? (:page (get-default-home))))
  309. (defn get-preferred-format
  310. ([]
  311. (get-preferred-format (get-current-repo)))
  312. ([repo-url]
  313. (keyword
  314. (or
  315. (when-let [fmt (:preferred-format (get-config repo-url))]
  316. (string/lower-case (name fmt)))
  317. (get-in @state [:me :preferred_format] "markdown")))))
  318. ;; TODO: consider adding a pane in Settings to set this through the GUI (rather
  319. ;; than having to go through the config.edn file)
  320. (defn get-editor-command-trigger
  321. ([] (get-editor-command-trigger (get-current-repo)))
  322. ([repo-url]
  323. (or
  324. (:editor/command-trigger (get-config repo-url)) ;; Get from user config
  325. "/"))) ;; Set the default
  326. (defn markdown?
  327. []
  328. (= (keyword (get-preferred-format))
  329. :markdown))
  330. (defn get-pages-directory
  331. []
  332. (or
  333. (when-let [repo (get-current-repo)]
  334. (:pages-directory (get-config repo)))
  335. "pages"))
  336. (defn get-journals-directory
  337. []
  338. (or
  339. (when-let [repo (get-current-repo)]
  340. (:journals-directory (get-config repo)))
  341. "journals"))
  342. (defn get-whiteboards-directory
  343. []
  344. (or
  345. (when-let [repo (get-current-repo)]
  346. (:whiteboards-directory (get-config repo)))
  347. "whiteboards"))
  348. (defn org-mode-file-link?
  349. [repo]
  350. (:org-mode/insert-file-link? (get-config repo)))
  351. (defn get-journal-file-name-format
  352. []
  353. (when-let [repo (get-current-repo)]
  354. (:journal/file-name-format (get-config repo))))
  355. (defn get-preferred-workflow
  356. []
  357. (keyword
  358. (or
  359. (when-let [workflow (:preferred-workflow (get-config))]
  360. (let [workflow (name workflow)]
  361. (if (util/safe-re-find #"now|NOW" workflow)
  362. :now
  363. :todo)))
  364. (get-in @state [:me :preferred_workflow] :now))))
  365. (defn get-preferred-todo
  366. []
  367. (if (= (get-preferred-workflow) :now)
  368. "LATER"
  369. "TODO"))
  370. (defn page-name-order
  371. "Decide whether to use file name or :title as page name. If it returns \"file\", use the file
  372. name unless it is missing."
  373. []
  374. (:page-name-order (get-config)))
  375. (defn get-date-formatter
  376. []
  377. (gp-config/get-date-formatter (get-config)))
  378. (defn shortcuts []
  379. (:shortcuts (get-config)))
  380. (defn get-commands
  381. []
  382. (:commands (get-config)))
  383. (defn get-scheduled-future-days
  384. []
  385. (let [days (:scheduled/future-days (get-config))]
  386. (or (when (int? days) days) 0)))
  387. (defn get-start-of-week
  388. []
  389. (or (:start-of-week (get-config))
  390. (get-in @state [:me :settings :start-of-week])
  391. 6))
  392. (defn get-ref-open-blocks-level
  393. []
  394. (or
  395. (when-let [value (:ref/default-open-blocks-level (get-config))]
  396. (when (integer? value)
  397. value))
  398. 2))
  399. (defn get-linked-references-collapsed-threshold
  400. []
  401. (or
  402. (when-let [value (:ref/linked-references-collapsed-threshold (get-config))]
  403. (when (integer? value)
  404. value))
  405. 100))
  406. (defn get-export-bullet-indentation
  407. []
  408. (case (get (get-config) :export/bullet-indentation :tab)
  409. :eight-spaces
  410. " "
  411. :four-spaces
  412. " "
  413. :two-spaces
  414. " "
  415. :tab
  416. "\t"))
  417. (defn enable-search-remove-accents?
  418. []
  419. (:feature/enable-search-remove-accents? (get-config)))
  420. ;; State cursor fns for use with rum components
  421. ;; ============================================
  422. (declare document-mode?)
  423. (defn sub
  424. "Creates a rum cursor, https://github.com/tonsky/rum#cursors, for use in rum components.
  425. Similar to re-frame subscriptions"
  426. [ks]
  427. (if (coll? ks)
  428. (util/react (rum/cursor-in state ks))
  429. (util/react (rum/cursor state ks))))
  430. (defn sub-config
  431. "Sub equivalent to get-config which should handle all sub user-config access"
  432. ([] (sub-config (get-current-repo)))
  433. ([repo]
  434. (let [config (sub :config)]
  435. (merge-configs default-config
  436. (get config ::global-config)
  437. (get config repo)))))
  438. (defn enable-grammarly?
  439. []
  440. (true? (:feature/enable-grammarly? (sub-config))))
  441. (defn scheduled-deadlines-disabled?
  442. []
  443. (true? (:feature/disable-scheduled-and-deadline-query? (sub-config))))
  444. (defn enable-timetracking?
  445. []
  446. (not (false? (:feature/enable-timetracking? (sub-config)))))
  447. (defn enable-journals?
  448. ([]
  449. (enable-journals? (get-current-repo)))
  450. ([repo]
  451. (not (false? (:feature/enable-journals? (sub-config repo))))))
  452. (defn enable-flashcards?
  453. ([]
  454. (enable-flashcards? (get-current-repo)))
  455. ([repo]
  456. (not (false? (:feature/enable-flashcards? (sub-config repo))))))
  457. (defn enable-sync?
  458. []
  459. (sub :feature/enable-sync?))
  460. (defn export-heading-to-list?
  461. []
  462. (not (false? (:export/heading-to-list? (sub-config)))))
  463. (defn enable-git-auto-push?
  464. [repo]
  465. (not (false? (:git-auto-push (sub-config repo)))))
  466. (defn enable-block-timestamps?
  467. []
  468. (true? (:feature/enable-block-timestamps? (sub-config))))
  469. (defn graph-settings
  470. []
  471. (:graph/settings (sub-config)))
  472. ;; Enable by default
  473. (defn show-brackets?
  474. []
  475. (not (false? (:ui/show-brackets? (sub-config)))))
  476. (defn sub-default-home-page
  477. []
  478. (get-in (sub-config) [:default-home :page] ""))
  479. (defn sub-edit-content
  480. [id]
  481. (sub [:editor/content id]))
  482. (defn- get-selected-block-ids
  483. [blocks]
  484. (->> blocks
  485. (keep #(when-let [id (dom/attr % "blockid")]
  486. (uuid id)))
  487. (distinct)))
  488. (defn sub-block-selected?
  489. [block-uuid]
  490. (rum/react
  491. (rum/derived-atom [state] [::select-block block-uuid]
  492. (fn [state]
  493. (contains? (set (get-selected-block-ids (:selection/blocks state)))
  494. block-uuid)))))
  495. (defn block-content-max-length
  496. [repo]
  497. (or (:block/content-max-length (sub-config repo)) 5000))
  498. (defn mobile?
  499. []
  500. (or (util/mobile?) (mobile-util/native-platform?)))
  501. (defn enable-tooltip?
  502. []
  503. (if (mobile?)
  504. false
  505. (get (sub-config) :ui/enable-tooltip? true)))
  506. (defn show-command-doc?
  507. []
  508. (get (sub-config) :ui/show-command-doc? true))
  509. (defn logical-outdenting?
  510. []
  511. (:editor/logical-outdenting? (sub-config)))
  512. (defn enable-encryption?
  513. [repo]
  514. (:feature/enable-encryption? (sub-config repo)))
  515. (defn doc-mode-enter-for-new-line?
  516. []
  517. (and (document-mode?)
  518. (not (:shortcut/doc-mode-enter-for-new-block? (get-config)))))
  519. (defn user-groups
  520. []
  521. (set (sub [:user/info :UserGroups])))
  522. ;; State mutation helpers
  523. ;; ======================
  524. (defn set-state!
  525. [path value]
  526. (if (vector? path)
  527. (swap! state assoc-in path value)
  528. (swap! state assoc path value)))
  529. (defn update-state!
  530. [path f]
  531. (if (vector? path)
  532. (swap! state update-in path f)
  533. (swap! state update path f)))
  534. ;; State getters and setters
  535. ;; =========================
  536. ;; These fns handle any key except :config.
  537. ;; Some state is also stored in local storage and/or sent to electron's main process
  538. (defn get-route-match
  539. []
  540. (:route-match @state))
  541. (defn get-current-route
  542. []
  543. (get-in (get-route-match) [:data :name]))
  544. (defn home?
  545. []
  546. (= :home (get-current-route)))
  547. (defn setups-picker?
  548. []
  549. (= :repo-add (get-current-route)))
  550. (defn get-current-page
  551. []
  552. (when (= :page (get-current-route))
  553. (get-in (get-route-match)
  554. [:path-params :name])))
  555. (defn route-has-p?
  556. []
  557. (get-in (get-route-match) [:query-params :p]))
  558. (defn get-current-repo
  559. []
  560. (or (:git/current-repo @state)
  561. (when-not (mobile-util/native-platform?)
  562. "local")))
  563. (defn get-remote-repos
  564. []
  565. (get-in @state [:file-sync/remote-graphs :graphs]))
  566. (defn get-remote-graph-info-by-uuid
  567. [uuid]
  568. (when-let [graphs (seq (get-in @state [:file-sync/remote-graphs :graphs]))]
  569. (some #(when (= (:GraphUUID %) (str uuid)) %) graphs)))
  570. (defn get-repos
  571. []
  572. (get-in @state [:me :repos]))
  573. (defn set-repos!
  574. [repos]
  575. (set-state! [:me :repos] repos))
  576. (defn add-repo!
  577. [repo]
  578. (when (not (string/blank? repo))
  579. (update-state! [:me :repos]
  580. (fn [repos]
  581. (->> (conj repos repo)
  582. (distinct))))))
  583. (defn set-current-repo!
  584. [repo]
  585. (swap! state assoc :git/current-repo repo)
  586. (if repo
  587. (storage/set :git/current-repo repo)
  588. (storage/remove :git/current-repo))
  589. (ipc/ipc "setCurrentGraph" repo))
  590. (defn set-preferred-format!
  591. [format]
  592. (swap! state assoc-in [:me :preferred_format] (name format)))
  593. (defn set-preferred-workflow!
  594. [workflow]
  595. (swap! state assoc-in [:me :preferred_workflow] (name workflow)))
  596. (defn set-preferred-language!
  597. [language]
  598. (set-state! :preferred-language (name language))
  599. (storage/set :preferred-language (name language)))
  600. (defn delete-repo!
  601. [repo]
  602. (swap! state update-in [:me :repos]
  603. (fn [repos]
  604. (->> (remove #(= (:url repo)
  605. (:url %))
  606. repos)
  607. (util/distinct-by :url)))))
  608. (defn set-timestamp-block!
  609. [value]
  610. (set-state! :editor/set-timestamp-block value))
  611. (defn get-timestamp-block
  612. []
  613. (:editor/set-timestamp-block @state))
  614. (defn set-edit-content!
  615. ([input-id value] (set-edit-content! input-id value true))
  616. ([input-id value set-input-value?]
  617. (when input-id
  618. (when set-input-value?
  619. (when-let [input (gdom/getElement input-id)]
  620. (util/set-change-value input value)))
  621. (update-state! :editor/content (fn [m]
  622. (assoc m input-id value))))))
  623. (defn get-edit-input-id
  624. []
  625. (ffirst (:editor/editing? @state)))
  626. (defn get-input
  627. []
  628. (when-let [id (get-edit-input-id)]
  629. (gdom/getElement id)))
  630. (defn editing?
  631. []
  632. (let [input (get-input)]
  633. (and input (= input (.-activeElement js/document)))))
  634. (defn get-edit-content
  635. []
  636. (get (:editor/content @state) (get-edit-input-id)))
  637. (defn get-cursor-range
  638. []
  639. (:cursor-range @state))
  640. (defn set-cursor-range!
  641. [range]
  642. (set-state! :cursor-range range))
  643. (defn set-q!
  644. [value]
  645. (set-state! :search/q value))
  646. (defn set-search-mode!
  647. [value]
  648. (set-state! :search/mode value))
  649. (defn set-editor-action!
  650. [value]
  651. (set-state! :editor/action value))
  652. (defn set-editor-action-data!
  653. [value]
  654. (set-state! :editor/action-data value))
  655. (defn get-editor-action
  656. []
  657. (:editor/action @state))
  658. (defn get-editor-action-data
  659. []
  660. (:editor/action-data @state))
  661. (defn get-editor-show-page-search?
  662. []
  663. (= (get-editor-action) :page-search))
  664. (defn get-editor-show-page-search-hashtag?
  665. []
  666. (= (get-editor-action) :page-search-hashtag))
  667. (defn get-editor-show-block-search?
  668. []
  669. (= (get-editor-action) :block-search))
  670. (defn set-editor-show-input!
  671. [value]
  672. (if value
  673. (do
  674. (set-editor-action-data! (assoc (get-editor-action-data) :options value))
  675. (set-editor-action! :input))
  676. (do
  677. (set-editor-action! nil)
  678. (set-editor-action-data! nil))))
  679. (defn get-editor-show-input
  680. []
  681. (when (= (get-editor-action) :input)
  682. (get @state :editor/action-data)))
  683. (defn set-editor-show-commands!
  684. []
  685. (when-not (get-editor-action) (set-editor-action! :commands)))
  686. (defn set-editor-show-block-commands!
  687. []
  688. (when-not (get-editor-action) (set-editor-action! :block-commands)))
  689. (defn clear-editor-action!
  690. []
  691. (swap! state (fn [state]
  692. (assoc state :editor/action nil))))
  693. (defn set-edit-input-id!
  694. [input-id]
  695. (swap! state update :editor/editing?
  696. (fn [_m]
  697. (and input-id {input-id true}))))
  698. (defn get-edit-pos
  699. []
  700. (when-let [input (get-input)]
  701. (util/get-selection-start input)))
  702. (defn set-selection-start-block!
  703. [start-block]
  704. (swap! state assoc :selection/start-block start-block))
  705. (defn get-selection-start-block
  706. []
  707. (get @state :selection/start-block))
  708. (defn set-selection-blocks!
  709. ([blocks]
  710. (set-selection-blocks! blocks :down))
  711. ([blocks direction]
  712. (when (seq blocks)
  713. (let [blocks (util/sort-by-height blocks)]
  714. (swap! state assoc
  715. :selection/mode true
  716. :selection/blocks blocks
  717. :selection/direction direction)))))
  718. (defn into-selection-mode!
  719. []
  720. (swap! state assoc :selection/mode true))
  721. (defn clear-selection!
  722. []
  723. (swap! state assoc
  724. :selection/mode false
  725. :selection/blocks nil
  726. :selection/direction :down))
  727. (defn get-selection-blocks
  728. []
  729. (:selection/blocks @state))
  730. (defn get-selection-block-ids
  731. []
  732. (get-selected-block-ids (get-selection-blocks)))
  733. (defn get-selection-start-block-or-first
  734. []
  735. (or (get-selection-start-block)
  736. (some-> (first (get-selection-blocks))
  737. (gobj/get "id"))))
  738. (defn in-selection-mode?
  739. []
  740. (:selection/mode @state))
  741. (defn selection?
  742. "True sense of selection mode with valid selected block"
  743. []
  744. (and (in-selection-mode?) (seq (get-selection-blocks))))
  745. (defn conj-selection-block!
  746. [block direction]
  747. (swap! state assoc
  748. :selection/mode true
  749. :selection/blocks (-> (conj (vec (:selection/blocks @state)) block)
  750. (util/sort-by-height))
  751. :selection/direction direction))
  752. (defn drop-last-selection-block!
  753. []
  754. (let [direction (:selection/direction @state)
  755. up? (= direction :up)
  756. blocks (:selection/blocks @state)
  757. last-block (if up?
  758. (first blocks)
  759. (peek (vec blocks)))
  760. blocks' (if up?
  761. (rest blocks)
  762. (pop (vec blocks)))]
  763. (swap! state assoc
  764. :selection/mode true
  765. :selection/blocks blocks')
  766. last-block))
  767. (defn get-selection-direction
  768. []
  769. (:selection/direction @state))
  770. (defn show-custom-context-menu!
  771. [links position]
  772. (swap! state assoc
  773. :custom-context-menu/show? true
  774. :custom-context-menu/links links
  775. :custom-context-menu/position position))
  776. (defn hide-custom-context-menu!
  777. []
  778. (swap! state assoc
  779. :custom-context-menu/show? false
  780. :custom-context-menu/links nil
  781. :custom-context-menu/position nil))
  782. (defn toggle-navigation-item-collapsed!
  783. [item]
  784. (update-state! [:ui/navigation-item-collapsed? item] not))
  785. (defn toggle-sidebar-open?!
  786. []
  787. (swap! state update :ui/sidebar-open? not))
  788. (defn open-right-sidebar!
  789. []
  790. (swap! state assoc :ui/sidebar-open? true))
  791. (defn hide-right-sidebar!
  792. []
  793. (swap! state assoc :ui/sidebar-open? false))
  794. (defn sidebar-add-block!
  795. [repo db-id block-type]
  796. (when (not (util/sm-breakpoint?))
  797. (when db-id
  798. (update-state! :sidebar/blocks (fn [blocks]
  799. (->> (remove #(= (second %) db-id) blocks)
  800. (cons [repo db-id block-type])
  801. (distinct))))
  802. (open-right-sidebar!)
  803. (when-let [elem (gdom/getElementByClass "cp__right-sidebar-scrollable")]
  804. (util/scroll-to elem 0)))))
  805. (defn sidebar-remove-block!
  806. [idx]
  807. (update-state! :sidebar/blocks (fn [blocks]
  808. (if (string? idx)
  809. (remove #(= (second %) idx) blocks)
  810. (util/drop-nth idx blocks))))
  811. (when (empty? (:sidebar/blocks @state))
  812. (hide-right-sidebar!)))
  813. (defn sidebar-replace-block!
  814. [old-sidebar-key new-sidebar-key]
  815. (update-state! :sidebar/blocks (fn [blocks]
  816. (map #(if (= % old-sidebar-key)
  817. new-sidebar-key
  818. %) blocks))))
  819. (defn sidebar-block-exists?
  820. [idx]
  821. (some #(= (second %) idx) (:sidebar/blocks @state)))
  822. (defn clear-sidebar-blocks!
  823. []
  824. (set-state! :sidebar/blocks '()))
  825. (defn sidebar-block-toggle-collapse!
  826. [db-id]
  827. (when db-id
  828. (update-state! [:ui/sidebar-collapsed-blocks db-id] not)))
  829. (defn get-edit-block
  830. []
  831. (get @state :editor/block))
  832. (defn get-current-edit-block-and-position
  833. []
  834. (let [edit-input-id (get-edit-input-id)
  835. edit-block (get-edit-block)
  836. block-element (when edit-input-id (gdom/getElement (string/replace edit-input-id "edit-block" "ls-block")))
  837. container (when block-element
  838. (util/get-block-container block-element))]
  839. (when container
  840. {:last-edit-block edit-block
  841. :container (gobj/get container "id")
  842. :pos (cursor/pos (gdom/getElement edit-input-id))})))
  843. (defn clear-edit!
  844. []
  845. (swap! state merge {:editor/editing? nil
  846. :editor/block nil
  847. :cursor-range nil
  848. :editor/last-saved-cursor nil}))
  849. (defn into-code-editor-mode!
  850. []
  851. (swap! state merge {:editor/editing? nil
  852. :cursor-range nil
  853. :editor/code-mode? true}))
  854. (defn set-editor-last-pos!
  855. [new-pos]
  856. (set-state! [:editor/last-saved-cursor (:block/uuid (get-edit-block))] new-pos))
  857. (defn clear-editor-last-pos!
  858. []
  859. (set-state! :editor/last-saved-cursor nil))
  860. (defn get-editor-last-pos
  861. []
  862. (get-in @state [:editor/last-saved-cursor (:block/uuid (get-edit-block))]))
  863. (defn set-block-content-and-last-pos!
  864. [edit-input-id content new-pos]
  865. (when edit-input-id
  866. (set-edit-content! edit-input-id content)
  867. (set-state! [:editor/last-saved-cursor (:block/uuid (get-edit-block))] new-pos)))
  868. (defn set-theme-mode!
  869. [mode]
  870. (when (mobile-util/native-ios?)
  871. (if (= mode "light")
  872. (util/set-theme-light)
  873. (util/set-theme-dark)))
  874. (set-state! :ui/theme mode)
  875. (storage/set :ui/theme mode))
  876. (defn sync-system-theme!
  877. []
  878. (let [system-dark? (.-matches (js/window.matchMedia "(prefers-color-scheme: dark)"))]
  879. (set-theme-mode! (if system-dark? "dark" "light"))
  880. (set-state! :ui/system-theme? true)
  881. (storage/set :ui/system-theme? true)))
  882. (defn use-theme-mode!
  883. [theme-mode]
  884. (if (= theme-mode "system")
  885. (sync-system-theme!)
  886. (do
  887. (set-theme-mode! theme-mode)
  888. (set-state! :ui/system-theme? false)
  889. (storage/set :ui/system-theme? false))))
  890. (defn toggle-theme
  891. [theme]
  892. (if (= theme "dark") "light" "dark"))
  893. (defn toggle-theme!
  894. []
  895. (use-theme-mode! (toggle-theme (:ui/theme @state))))
  896. (defn set-custom-theme!
  897. ([custom-theme]
  898. (set-custom-theme! nil custom-theme))
  899. ([mode theme]
  900. (set-state! (if mode [:ui/custom-theme (keyword mode)] :ui/custom-theme) theme)
  901. (storage/set :ui/custom-theme (:ui/custom-theme @state))))
  902. (defn set-editing-block-dom-id!
  903. [block-dom-id]
  904. (set-state! :editor/block-dom-id block-dom-id))
  905. (defn get-editing-block-dom-id
  906. []
  907. (:editor/block-dom-id @state))
  908. (defn set-root-component!
  909. [component]
  910. (set-state! :ui/root-component component))
  911. (defn get-root-component
  912. []
  913. (get @state :ui/root-component))
  914. (defn load-app-user-cfgs
  915. ([] (load-app-user-cfgs false))
  916. ([refresh?]
  917. (p/let [cfgs (if (or refresh? (nil? (:electron/user-cfgs @state)))
  918. (ipc/ipc "userAppCfgs")
  919. (:electron/user-cfgs @state))
  920. cfgs (if (object? cfgs) (bean/->clj cfgs) cfgs)]
  921. (set-state! :electron/user-cfgs cfgs))))
  922. (defn setup-electron-updater!
  923. []
  924. (when (util/electron?)
  925. (js/window.apis.setUpdatesCallback
  926. (fn [_ args]
  927. (let [data (bean/->clj args)
  928. pending? (not= (:type data) "completed")]
  929. (set-state! :electron/updater-pending? pending?)
  930. (when pending? (set-state! :electron/updater data))
  931. nil)))))
  932. (defn set-file-component!
  933. [component]
  934. (set-state! :ui/file-component component))
  935. (defn clear-file-component!
  936. []
  937. (set-state! :ui/file-component nil))
  938. (defn set-journals-length!
  939. [value]
  940. (when value
  941. (set-state! :journals-length value)))
  942. (defn add-custom-query-component!
  943. [query-string component]
  944. (update-state! :ui/custom-query-components
  945. (fn [m]
  946. (assoc m query-string component))))
  947. (defn remove-custom-query-component!
  948. [query-string]
  949. (update-state! :ui/custom-query-components
  950. (fn [m]
  951. (dissoc m query-string))))
  952. (defn get-custom-query-components
  953. []
  954. (vals (get @state :ui/custom-query-components)))
  955. (defn save-scroll-position!
  956. ([value]
  957. (save-scroll-position! value js/window.location.hash))
  958. ([value path]
  959. (set-state! [:ui/paths-scroll-positions path] value)))
  960. (defn get-saved-scroll-position
  961. ([]
  962. (get-saved-scroll-position js/window.location.hash))
  963. ([path]
  964. (get-in @state [:ui/paths-scroll-positions path] 0)))
  965. (defn set-today!
  966. [value]
  967. (set-state! :today value))
  968. (defn get-me
  969. []
  970. (:me @state))
  971. (defn set-db-restoring!
  972. [value]
  973. (set-state! :db/restoring? value))
  974. (defn set-indexedb-support!
  975. [value]
  976. (set-state! :indexeddb/support? value))
  977. (defn modal-opened?
  978. []
  979. (:modal/show? @state))
  980. (declare set-modal!)
  981. (declare close-modal!)
  982. (defn get-sub-modals
  983. []
  984. (:modal/subsets @state))
  985. (defn set-sub-modal!
  986. ([panel-content]
  987. (set-sub-modal! panel-content
  988. {:close-btn? true}))
  989. ([panel-content {:keys [id label close-btn? show? center?] :as opts}]
  990. (if (not (modal-opened?))
  991. (set-modal! panel-content opts)
  992. (let [modals (:modal/subsets @state)
  993. idx (and id (first (keep-indexed #(when (= (:modal/id %2) id) %1)
  994. modals)))
  995. input (medley/filter-vals
  996. #(not (nil? %1))
  997. {:modal/id id
  998. :modal/label (or label (if center? "ls-modal-align-center" ""))
  999. :modal/show? (if (boolean? show?) show? true)
  1000. :modal/panel-content panel-content
  1001. :modal/close-btn? close-btn?})]
  1002. (swap! state update-in
  1003. [:modal/subsets (or idx (count modals))]
  1004. merge input)
  1005. (:modal/subsets @state)))))
  1006. (defn close-sub-modal!
  1007. ([] (close-sub-modal! nil))
  1008. ([all?-a-id]
  1009. (if (true? all?-a-id)
  1010. (swap! state assoc :modal/subsets [])
  1011. (let [id all?-a-id
  1012. mid (:modal/id @state)
  1013. modals (:modal/subsets @state)]
  1014. (if (and id (not (string/blank? mid)) (= id mid))
  1015. (close-modal!)
  1016. (when-let [idx (if id (first (keep-indexed #(when (= (:modal/id %2) id) %1) modals))
  1017. (dec (count modals)))]
  1018. (swap! state assoc :modal/subsets (into [] (medley/remove-nth idx modals)))))))
  1019. (:modal/subsets @state)))
  1020. (defn set-modal!
  1021. ([modal-panel-content]
  1022. (set-modal! modal-panel-content
  1023. {:fullscreen? false
  1024. :close-btn? true}))
  1025. ([modal-panel-content {:keys [id label fullscreen? close-btn? close-backdrop? center?]}]
  1026. (when (seq (get-sub-modals))
  1027. (close-sub-modal! true))
  1028. (swap! state assoc
  1029. :modal/id id
  1030. :modal/label (or label (if center? "ls-modal-align-center" ""))
  1031. :modal/show? (boolean modal-panel-content)
  1032. :modal/panel-content modal-panel-content
  1033. :modal/fullscreen? fullscreen?
  1034. :modal/close-btn? close-btn?
  1035. :modal/close-backdrop? (if (boolean? close-backdrop?) close-backdrop? true)) nil))
  1036. (defn close-modal!
  1037. []
  1038. (when-not (editing?)
  1039. (if (seq (get-sub-modals))
  1040. (close-sub-modal!)
  1041. (swap! state assoc
  1042. :modal/id nil
  1043. :modal/label ""
  1044. :modal/show? false
  1045. :modal/fullscreen? false
  1046. :modal/panel-content nil
  1047. :ui/open-select nil))))
  1048. (defn get-db-batch-txs-chan
  1049. []
  1050. (:db/batch-txs @state))
  1051. (defn get-file-write-chan
  1052. []
  1053. (:file/writes @state))
  1054. (defn get-reactive-custom-queries-chan
  1055. []
  1056. (:reactive/custom-queries @state))
  1057. (defn get-left-sidebar-open?
  1058. []
  1059. (get-in @state [:ui/left-sidebar-open?]))
  1060. (defn set-left-sidebar-open!
  1061. [value]
  1062. (storage/set "ls-left-sidebar-open?" (boolean value))
  1063. (set-state! :ui/left-sidebar-open? value))
  1064. (defn toggle-left-sidebar!
  1065. []
  1066. (set-left-sidebar-open!
  1067. (not (get-left-sidebar-open?))))
  1068. (defn set-developer-mode!
  1069. [value]
  1070. (set-state! :ui/developer-mode? value)
  1071. (storage/set "developer-mode" (str value)))
  1072. (defn developer-mode?
  1073. []
  1074. (:ui/developer-mode? @state))
  1075. (defn get-notification-contents
  1076. []
  1077. (get @state :notification/contents))
  1078. (defn document-mode?
  1079. []
  1080. (get @state :document/mode?))
  1081. (defn toggle-document-mode!
  1082. []
  1083. (let [mode (document-mode?)]
  1084. (set-state! :document/mode? (not mode))
  1085. (storage/set :document/mode? (not mode))))
  1086. (defn shortcut-tooltip-enabled?
  1087. []
  1088. (get @state :ui/shortcut-tooltip?))
  1089. (defn toggle-shortcut-tooltip!
  1090. []
  1091. (let [mode (shortcut-tooltip-enabled?)]
  1092. (set-state! :ui/shortcut-tooltip? (not mode))
  1093. (storage/set :ui/shortcut-tooltip? (not mode))))
  1094. (defn set-config!
  1095. [repo-url value]
  1096. (set-state! [:config repo-url] value))
  1097. (defn set-global-config!
  1098. [value]
  1099. ;; Placed under :config so cursors can work seamlessly
  1100. (set-config! ::global-config value))
  1101. (defn get-wide-mode?
  1102. []
  1103. (:ui/wide-mode? @state))
  1104. (defn toggle-wide-mode!
  1105. []
  1106. (update-state! :ui/wide-mode? not))
  1107. (defn set-online!
  1108. [value]
  1109. (set-state! :network/online? value))
  1110. (defn get-plugins-commands
  1111. []
  1112. (mapcat seq (flatten (vals (:plugin/installed-slash-commands @state)))))
  1113. (defn get-plugins-commands-with-type
  1114. [type]
  1115. (filterv #(= (keyword (first %)) (keyword type))
  1116. (apply concat (vals (:plugin/simple-commands @state)))))
  1117. (defn get-plugins-ui-items-with-type
  1118. [type]
  1119. (filterv #(= (keyword (first %)) (keyword type))
  1120. (apply concat (vals (:plugin/installed-ui-items @state)))))
  1121. (defn get-plugin-resources-with-type
  1122. [pid type]
  1123. (when-let [pid (and type (keyword pid))]
  1124. (get-in @state [:plugin/installed-resources pid (keyword type)])))
  1125. (defn get-plugin-resource
  1126. [pid type key]
  1127. (when-let [resources (get-plugin-resources-with-type pid type)]
  1128. (get resources key)))
  1129. (defn upt-plugin-resource
  1130. [pid type key attr val]
  1131. (when-let [resource (get-plugin-resource pid type key)]
  1132. (let [resource (assoc resource (keyword attr) val)]
  1133. (set-state!
  1134. [:plugin/installed-resources (keyword pid) (keyword type) key] resource)
  1135. resource)))
  1136. (defn install-plugin-hook
  1137. [pid hook]
  1138. (when-let [pid (keyword pid)]
  1139. (set-state!
  1140. [:plugin/installed-hooks hook]
  1141. (conj
  1142. ((fnil identity #{}) (get-in @state [:plugin/installed-hooks hook]))
  1143. pid)) true))
  1144. (defn uninstall-plugin-hook
  1145. [pid hook-or-all]
  1146. (when-let [pid (keyword pid)]
  1147. (if (nil? hook-or-all)
  1148. (swap! state update :plugin/installed-hooks #(update-vals % (fn [ids] (disj ids pid))))
  1149. (when-let [coll (get-in @state [:plugin/installed-hooks hook-or-all])]
  1150. (set-state! [:plugin/installed-hooks hook-or-all] (disj coll pid))))
  1151. true))
  1152. (defn set-graph-syncing?
  1153. [value]
  1154. (set-state! :graph/syncing? value))
  1155. (defn set-editor-in-composition!
  1156. [value]
  1157. (set-state! :editor/in-composition? value))
  1158. (defn editor-in-composition?
  1159. []
  1160. (:editor/in-composition? @state))
  1161. (defn set-loading-files!
  1162. [repo value]
  1163. (when repo
  1164. (set-state! [:repo/loading-files? repo] value)))
  1165. (defn loading-files?
  1166. [repo]
  1167. (get-in @state [:repo/loading-files? repo]))
  1168. (defn set-editor-last-input-time!
  1169. [repo time]
  1170. (swap! state assoc-in [:editor/last-input-time repo] time))
  1171. (defn set-last-transact-time!
  1172. [repo time]
  1173. (swap! state assoc-in [:db/last-transact-time repo] time)
  1174. ;; THINK: new block, indent/outdent, drag && drop, etc.
  1175. (set-editor-last-input-time! repo time))
  1176. (defn set-db-persisted!
  1177. [repo value]
  1178. (swap! state assoc-in [:db/persisted? repo] value))
  1179. (defn db-idle?
  1180. [repo]
  1181. (when repo
  1182. (when-let [last-time (get-in @state [:db/last-transact-time repo])]
  1183. (let [now (util/time-ms)]
  1184. (>= (- now last-time) 3000)))))
  1185. (defn input-idle?
  1186. [repo & {:keys [diff]
  1187. :or {diff 1000}}]
  1188. (when repo
  1189. (or
  1190. (when-let [last-time (get-in @state [:editor/last-input-time repo])]
  1191. (let [now (util/time-ms)]
  1192. (>= (- now last-time) diff)))
  1193. ;; not in editing mode
  1194. (not (get-edit-input-id)))))
  1195. (defn set-nfs-refreshing!
  1196. [value]
  1197. (set-state! :nfs/refreshing? value))
  1198. (defn nfs-refreshing?
  1199. []
  1200. (:nfs/refreshing? @state))
  1201. (defn set-search-result!
  1202. [value]
  1203. (set-state! :search/result value))
  1204. (defn clear-search-result!
  1205. []
  1206. (set-search-result! nil))
  1207. (defn add-graph-search-filter!
  1208. [q]
  1209. (when-not (string/blank? q)
  1210. (update-state! :search/graph-filters
  1211. (fn [value]
  1212. (vec (distinct (conj value q)))))))
  1213. (defn remove-search-filter!
  1214. [q]
  1215. (when-not (string/blank? q)
  1216. (update-state! :search/graph-filters
  1217. (fn [value]
  1218. (remove #{q} value)))))
  1219. (defn clear-search-filters!
  1220. []
  1221. (set-state! :search/graph-filters []))
  1222. (defn get-search-mode
  1223. []
  1224. (:search/mode @state))
  1225. (defn toggle!
  1226. [path]
  1227. (update-state! path not))
  1228. (defn toggle-settings!
  1229. []
  1230. (toggle! :ui/settings-open?))
  1231. (defn settings-open?
  1232. []
  1233. (:ui/settings-open? @state))
  1234. (defn close-settings!
  1235. []
  1236. (set-state! :ui/settings-open? false))
  1237. (defn open-settings!
  1238. []
  1239. (set-state! :ui/settings-open? true))
  1240. ;; TODO: Move those to the uni `state`
  1241. (defonce editor-op (atom nil))
  1242. (defn set-editor-op!
  1243. [value]
  1244. (reset! editor-op value))
  1245. (defn get-editor-op
  1246. []
  1247. @editor-op)
  1248. (defn get-events-chan
  1249. []
  1250. (:system/events @state))
  1251. (defn pub-event!
  1252. [payload]
  1253. (let [chan (get-events-chan)]
  1254. (async/put! chan payload)))
  1255. (defn get-copied-blocks
  1256. []
  1257. (:copy/blocks @state))
  1258. (defn set-copied-blocks!
  1259. [content blocks]
  1260. (set-state! :copy/blocks {:copy/graph (get-current-repo)
  1261. :copy/content (or content (get-in @state [:copy/blocks :copy/content]))
  1262. :copy/blocks blocks}))
  1263. (defn get-export-block-text-indent-style []
  1264. (:copy/export-block-text-indent-style @state))
  1265. (defn set-export-block-text-indent-style!
  1266. [v]
  1267. (set-state! :copy/export-block-text-indent-style v)
  1268. (storage/set :copy/export-block-text-indent-style v))
  1269. (defn get-export-block-text-remove-options []
  1270. (:copy/export-block-text-remove-options @state))
  1271. (defn update-export-block-text-remove-options!
  1272. [e k]
  1273. (let [f (if (util/echecked? e) conj disj)]
  1274. (update-state! :copy/export-block-text-remove-options
  1275. #(f % k))
  1276. (storage/set :copy/export-block-text-remove-options
  1277. (get-export-block-text-remove-options))))
  1278. (defn set-editor-args!
  1279. [args]
  1280. (set-state! :editor/args args))
  1281. (defn block-component-editing?
  1282. []
  1283. (:block/component-editing-mode? @state))
  1284. (defn set-block-component-editing-mode!
  1285. [value]
  1286. (set-state! :block/component-editing-mode? value))
  1287. (defn get-editor-args
  1288. []
  1289. (:editor/args @state))
  1290. (defn set-page-blocks-cp!
  1291. [value]
  1292. (set-state! [:view/components :page-blocks] value))
  1293. (defn get-page-blocks-cp
  1294. []
  1295. (get-in @state [:view/components :page-blocks]))
  1296. ;; To avoid circular dependencies
  1297. (defn set-component!
  1298. [k value]
  1299. (set-state! [:view/components k] value))
  1300. (defn get-component
  1301. [k]
  1302. (get-in @state [:view/components k]))
  1303. (defn exit-editing-and-set-selected-blocks!
  1304. ([blocks]
  1305. (exit-editing-and-set-selected-blocks! blocks :down))
  1306. ([blocks direction]
  1307. (clear-edit!)
  1308. (set-selection-blocks! blocks direction)))
  1309. (defn set-editing!
  1310. ([edit-input-id content block cursor-range]
  1311. (set-editing! edit-input-id content block cursor-range true))
  1312. ([edit-input-id content block cursor-range move-cursor?]
  1313. (if (> (count content)
  1314. (block-content-max-length (get-current-repo)))
  1315. (let [elements (array-seq (js/document.getElementsByClassName (:block/uuid block)))]
  1316. (when (first elements)
  1317. (util/scroll-to-element (gobj/get (first elements) "id")))
  1318. (exit-editing-and-set-selected-blocks! elements))
  1319. (when (and edit-input-id block
  1320. (or
  1321. (publishing-enable-editing?)
  1322. (not @publishing?)))
  1323. (let [block-element (gdom/getElement (string/replace edit-input-id "edit-block" "ls-block"))
  1324. container (util/get-block-container block-element)
  1325. block (if container
  1326. (assoc block
  1327. :block/container (gobj/get container "id"))
  1328. block)
  1329. content (string/trim (or content ""))]
  1330. (swap! state
  1331. (fn [state]
  1332. (-> state
  1333. (assoc-in [:editor/content edit-input-id] content)
  1334. (assoc
  1335. :editor/block block
  1336. :editor/editing? {edit-input-id true}
  1337. :editor/last-key-code nil
  1338. :cursor-range cursor-range))))
  1339. (when-let [input (gdom/getElement edit-input-id)]
  1340. (let [pos (count cursor-range)]
  1341. (when content
  1342. (util/set-change-value input content))
  1343. (when move-cursor?
  1344. (cursor/move-cursor-to input pos))
  1345. (when (or (util/mobile?) (mobile-util/native-platform?))
  1346. (set-state! :mobile/show-action-bar? false)))))))))
  1347. (defn remove-watch-state [key]
  1348. (remove-watch state key))
  1349. (defn get-git-auto-commit-enabled?
  1350. []
  1351. (false? (sub [:electron/user-cfgs :git/disable-auto-commit?])))
  1352. (defn set-last-key-code!
  1353. [key-code]
  1354. (set-state! :editor/last-key-code key-code))
  1355. (defn get-last-key-code
  1356. []
  1357. (:editor/last-key-code @state))
  1358. (defn get-plugin-by-id
  1359. [id]
  1360. (when-let [id (and id (keyword id))]
  1361. (get-in @state [:plugin/installed-plugins id])))
  1362. (defn get-enabled?-installed-plugins
  1363. ([theme?] (get-enabled?-installed-plugins theme? true false))
  1364. ([theme? enabled? include-unpacked?]
  1365. (filterv
  1366. #(and (if include-unpacked? true (:iir %))
  1367. (if-not (boolean? enabled?) true (= (not enabled?) (boolean (get-in % [:settings :disabled]))))
  1368. (= (boolean theme?) (:theme %)))
  1369. (vals (:plugin/installed-plugins @state)))))
  1370. (defn lsp-enabled?-or-theme
  1371. []
  1372. (:plugin/enabled @state))
  1373. (def lsp-enabled?
  1374. (lsp-enabled?-or-theme))
  1375. (defn consume-updates-coming-plugin
  1376. [payload updated?]
  1377. (when-let [id (keyword (:id payload))]
  1378. (let [pending? (boolean (seq (:plugin/updates-pending @state)))]
  1379. (swap! state update :plugin/updates-pending dissoc id)
  1380. (if updated?
  1381. (if-let [error (:error-code payload)]
  1382. (swap! state update-in [:plugin/updates-coming id] assoc :error-code error)
  1383. (swap! state update :plugin/updates-coming dissoc id))
  1384. (swap! state update :plugin/updates-coming assoc id payload))
  1385. (pub-event! [:plugin/consume-updates id pending? updated?]))))
  1386. (defn coming-update-new-version?
  1387. [pkg]
  1388. (and pkg (:latest-version pkg)))
  1389. (defn plugin-update-available?
  1390. [id]
  1391. (when-let [pkg (and id (get (:plugin/updates-coming @state) (keyword id)))]
  1392. (coming-update-new-version? pkg)))
  1393. (defn all-available-coming-updates
  1394. []
  1395. (when-let [updates (vals (:plugin/updates-coming @state))]
  1396. (filterv #(coming-update-new-version? %) updates)))
  1397. (defn get-next-selected-coming-update
  1398. []
  1399. (when-let [updates (all-available-coming-updates)]
  1400. (let [unchecked (:plugin/updates-unchecked @state)]
  1401. (first (filter #(and (not (and (seq unchecked) (contains? unchecked (:id %))))
  1402. (not (:error-code %))) updates)))))
  1403. (defn set-unchecked-update
  1404. [id unchecked?]
  1405. (swap! state update :plugin/updates-unchecked (if unchecked? conj disj) id))
  1406. (defn reset-unchecked-update
  1407. []
  1408. (swap! state assoc :plugin/updates-unchecked #{}))
  1409. (defn reset-all-updates-state
  1410. []
  1411. (swap! state assoc
  1412. :plugin/updates-pending {}
  1413. :plugin/updates-coming {}
  1414. :plugin/updates-downloading? false))
  1415. (defn sub-right-sidebar-blocks
  1416. []
  1417. (when-let [current-repo (get-current-repo)]
  1418. (->> (sub :sidebar/blocks)
  1419. (filter #(= (first %) current-repo)))))
  1420. (defn toggle-collapsed-block!
  1421. [block-id]
  1422. (let [current-repo (get-current-repo)]
  1423. (update-state! [:ui/collapsed-blocks current-repo block-id] not)))
  1424. (defn set-collapsed-block!
  1425. [block-id value]
  1426. (let [current-repo (get-current-repo)]
  1427. (set-state! [:ui/collapsed-blocks current-repo block-id] value)))
  1428. (defn sub-collapsed
  1429. [block-id]
  1430. (sub [:ui/collapsed-blocks (get-current-repo) block-id]))
  1431. (defn get-modal-id
  1432. []
  1433. (:modal/id @state))
  1434. (defn edit-in-query-or-refs-component
  1435. []
  1436. (let [config (last (get-editor-args))]
  1437. {:custom-query? (:custom-query? config)
  1438. :ref? (:ref? config)}))
  1439. (defn set-auth-id-token
  1440. [id-token]
  1441. (set-state! :auth/id-token id-token))
  1442. (defn set-auth-refresh-token
  1443. [refresh-token]
  1444. (set-state! :auth/refresh-token refresh-token))
  1445. (defn set-auth-access-token
  1446. [access-token]
  1447. (set-state! :auth/access-token access-token))
  1448. (defn get-auth-id-token []
  1449. (:auth/id-token @state))
  1450. (defn get-auth-refresh-token []
  1451. (:auth/refresh-token @state))
  1452. (defn set-file-sync-manager [v]
  1453. (set-state! :file-sync/sync-manager v))
  1454. (defn set-file-sync-state [graph v]
  1455. (when v (s/assert :frontend.fs.sync/sync-state v))
  1456. (set-state! [:file-sync/sync-state graph] v))
  1457. (defn get-file-sync-manager []
  1458. (:file-sync/sync-manager @state))
  1459. (defn get-file-sync-state [repo]
  1460. (get-in @state [:file-sync/sync-state repo]))
  1461. (defn reset-parsing-state!
  1462. []
  1463. (set-state! [:graph/parsing-state (get-current-repo)] {}))
  1464. (defn set-parsing-state!
  1465. [m]
  1466. (update-state! [:graph/parsing-state (get-current-repo)]
  1467. (if (fn? m) m
  1468. (fn [old-value] (merge old-value m)))))
  1469. (defn http-proxy-enabled-or-val? []
  1470. (when-let [agent-opts (sub [:electron/user-cfgs :settings/agent])]
  1471. (when (every? not-empty (vals agent-opts))
  1472. (str (:protocol agent-opts) "://" (:host agent-opts) ":" (:port agent-opts)))))
  1473. (defn set-mobile-app-state-change
  1474. [is-active?]
  1475. (set-state! :mobile/app-state-change
  1476. {:is-active? is-active?
  1477. :timestamp (inst-ms (js/Date.))}))
  1478. (defn get-sync-graph-by-uuid
  1479. [graph-uuid]
  1480. (when graph-uuid
  1481. (first (filter #(= graph-uuid (:GraphUUID %))(get-repos)))))
  1482. ;; (defn get-tldraw-api
  1483. ;; []
  1484. ;; (some-> (get-current-whiteboard)
  1485. ;; (gobj/get "api")))
  1486. (defn unlinked-dir?
  1487. [dir]
  1488. (contains? (:file/unlinked-dirs @state) dir))
  1489. (defn get-file-rename-event-chan
  1490. []
  1491. (:file/rename-event-chan @state))
  1492. (defn offer-file-rename-event-chan!
  1493. [v]
  1494. {:pre [(map? v)
  1495. (= #{:repo :old-path :new-path} (set (keys v)))]}
  1496. (async/offer! (get-file-rename-event-chan) v))