state.cljs 53 KB

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