state.cljs 73 KB

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