state.cljs 61 KB

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