settings.cljs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. (ns frontend.components.settings
  2. (:require [clojure.string :as string]
  3. [frontend.components.svg :as svg]
  4. [frontend.components.plugins :as plugins]
  5. [frontend.components.assets :as assets]
  6. [frontend.config :as config]
  7. [frontend.context.i18n :refer [t]]
  8. [frontend.storage :as storage]
  9. [frontend.date :as date]
  10. [frontend.dicts :as dicts]
  11. [frontend.handler :as handler]
  12. [frontend.handler.config :as config-handler]
  13. [frontend.handler.notification :as notification]
  14. [frontend.handler.route :as route-handler]
  15. [frontend.handler.ui :as ui-handler]
  16. [frontend.handler.user :as user-handler]
  17. [frontend.handler.plugin :as plugin-handler]
  18. [frontend.handler.file-sync :as file-sync-handler]
  19. [frontend.handler.global-config :as global-config-handler]
  20. [frontend.modules.instrumentation.core :as instrument]
  21. [frontend.modules.shortcut.data-helper :as shortcut-helper]
  22. [frontend.state :as state]
  23. [frontend.ui :as ui]
  24. [electron.ipc :as ipc]
  25. [promesa.core :as p]
  26. [frontend.util :refer [classnames web-platform?] :as util]
  27. [frontend.version :refer [version]]
  28. [goog.object :as gobj]
  29. [reitit.frontend.easy :as rfe]
  30. [rum.core :as rum]
  31. [frontend.mobile.util :as mobile-util]
  32. [frontend.db :as db]
  33. [frontend.components.conversion :as conversion-component]))
  34. (defn toggle
  35. [label-for name state on-toggle & [detail-text]]
  36. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  37. [:label.block.text-sm.font-medium.leading-5.opacity-70
  38. {:for label-for}
  39. name]
  40. [:div.rounded-md.sm:max-w-tss.sm:col-span-2
  41. [:div.rounded-md {:style {:display "flex" :gap "1rem" :align-items "center"}}
  42. (ui/toggle state on-toggle true)
  43. detail-text]]])
  44. (rum/defcs app-updater < rum/reactive
  45. [state version]
  46. (let [update-pending? (state/sub :electron/updater-pending?)
  47. {:keys [type payload]} (state/sub :electron/updater)]
  48. [:span.cp__settings-app-updater
  49. [:div.ctls.flex.items-center
  50. [:div.mt-1.sm:mt-0.sm:col-span-2
  51. {:style {:display "flex" :gap "0.5rem" :align-items "center"}}
  52. [:div (cond
  53. (mobile-util/native-android?)
  54. (ui/button
  55. "Check for updates"
  56. :class "text-sm p-1 mr-1"
  57. :href "https://github.com/logseq/logseq/releases" )
  58. (mobile-util/native-ios?)
  59. (ui/button
  60. "Check for updates"
  61. :class "text-sm p-1 mr-1"
  62. :href "https://apps.apple.com/app/logseq/id1601013908" )
  63. (util/electron?)
  64. (ui/button
  65. (if update-pending? "Checking ..." "Check for updates")
  66. :class "text-sm p-1 mr-1"
  67. :disabled update-pending?
  68. :on-click #(js/window.apis.checkForUpdates false))
  69. :else
  70. nil)]
  71. [:div.text-sm.opacity-50 (str "Version " version)]]]
  72. (when-not (or update-pending?
  73. (string/blank? type))
  74. [:div.update-state.text-sm
  75. (case type
  76. "update-not-available"
  77. [:p "Your app is up-to-date 🎉"]
  78. "update-available"
  79. (let [{:keys [name url]} payload]
  80. [:p (str "Found new release ")
  81. [:a.link
  82. {:on-click
  83. (fn [e]
  84. (js/window.apis.openExternal url)
  85. (util/stop e))}
  86. svg/external-link name " 🎉"]])
  87. "error"
  88. [:p "⚠️ Oops, Something Went Wrong!" [:br] " Please check out the "
  89. [:a.link
  90. {:on-click
  91. (fn [e]
  92. (js/window.apis.openExternal "https://github.com/logseq/logseq/releases")
  93. (util/stop e))}
  94. svg/external-link " release channel"]])])]))
  95. (rum/defc outdenting-hint
  96. []
  97. [:div.ui__modal-panel
  98. {:style {:box-shadow "0 4px 20px 4px rgba(0, 20, 60, .1), 0 4px 80px -8px rgba(0, 20, 60, .2)"}}
  99. [:div {:style {:margin "12px" :max-width "500px"}}
  100. [:p.text-sm
  101. "The left side shows outdenting with the default setting, and the right shows outdenting with logical outdenting enabled. "
  102. [:a.text-sm
  103. {:target "_blank" :href "https://discuss.logseq.com/t/whats-your-preferred-outdent-behavior-the-direct-one-or-the-logical-one/978"}
  104. "→ Learn more"]]
  105. [:img {:src "https://discuss.logseq.com/uploads/default/original/1X/e8ea82f63a5e01f6d21b5da827927f538f3277b9.gif"
  106. :width 500
  107. :height 500}]]])
  108. (defn row-with-button-action
  109. [{:keys [left-label action button-label href on-click desc -for]}]
  110. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  111. ;; left column
  112. [:label.block.text-sm.font-medium.leading-5.opacity-70
  113. {:for -for}
  114. left-label]
  115. ;; right column
  116. [:div.mt-1.sm:mt-0.sm:col-span-2
  117. {:style {:display "flex" :gap "0.5rem" :align-items "center"}}
  118. [:div (if action action (ui/button
  119. button-label
  120. :class "text-sm p-1"
  121. :href href
  122. :on-click on-click))]
  123. (when-not (or (util/mobile?)
  124. (mobile-util/native-platform?))
  125. [:div.text-sm desc])]])
  126. (defn edit-config-edn []
  127. (row-with-button-action
  128. {:left-label (t :settings-page/custom-configuration)
  129. :button-label (t :settings-page/edit-config-edn)
  130. :href (rfe/href :file {:path (config/get-repo-config-path)})
  131. :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))
  132. :-for "config_edn"}))
  133. (defn edit-global-config-edn []
  134. (row-with-button-action
  135. {:left-label (t :settings-page/custom-global-configuration)
  136. :button-label (t :settings-page/edit-global-config-edn)
  137. :href (rfe/href :file {:path (global-config-handler/global-config-path)})
  138. :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))
  139. :-for "global_config_edn"}))
  140. (defn edit-custom-css []
  141. (row-with-button-action
  142. {:left-label (t :settings-page/custom-theme)
  143. :button-label (t :settings-page/edit-custom-css)
  144. :href (rfe/href :file {:path (config/get-custom-css-path)})
  145. :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))
  146. :-for "customize_css"}))
  147. (defn edit-export-css []
  148. (row-with-button-action
  149. {:left-label (t :settings-page/export-theme)
  150. :button-label (t :settings-page/edit-export-css)
  151. :href (rfe/href :file {:path (config/get-export-css-path)})
  152. :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))
  153. :-for "customize_css"}))
  154. (defn show-brackets-row [t show-brackets?]
  155. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  156. [:label.block.text-sm.font-medium.leading-5.opacity-70
  157. {:for "show_brackets"}
  158. (t :settings-page/show-brackets)]
  159. [:div
  160. [:div.rounded-md.sm:max-w-xs
  161. (ui/toggle show-brackets?
  162. config-handler/toggle-ui-show-brackets!
  163. true)]]
  164. (when (not (or (util/mobile?) (mobile-util/native-platform?)))
  165. [:div {:style {:text-align "right"}}
  166. (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-brackets))])])
  167. (rum/defcs switch-spell-check-row < rum/reactive
  168. [state t]
  169. (let [enabled? (state/sub [:electron/user-cfgs :spell-check])]
  170. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  171. [:label.block.text-sm.font-medium.leading-5.opacity-70
  172. (t :settings-page/spell-checker)]
  173. [:div
  174. [:div.rounded-md.sm:max-w-xs
  175. (ui/toggle
  176. enabled?
  177. (fn []
  178. (state/set-state! [:electron/user-cfgs :spell-check] (not enabled?))
  179. (p/then (ipc/ipc :userAppCfgs :spell-check (not enabled?))
  180. #(when (js/confirm (t :relaunch-confirm-to-work))
  181. (js/logseq.api.relaunch))))
  182. true)]]]))
  183. (rum/defcs switch-git-auto-commit-row < rum/reactive
  184. [state t]
  185. (let [enabled? (state/get-git-auto-commit-enabled?)]
  186. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  187. [:label.block.text-sm.font-medium.leading-5.opacity-70
  188. (t :settings-page/git-switcher-label)]
  189. [:div
  190. [:div.rounded-md.sm:max-w-xs
  191. (ui/toggle
  192. enabled?
  193. (fn []
  194. (state/set-state! [:electron/user-cfgs :git/disable-auto-commit?] enabled?)
  195. (ipc/ipc :userAppCfgs :git/disable-auto-commit? enabled?))
  196. true)]]]))
  197. (rum/defcs git-auto-commit-seconds < rum/reactive
  198. [state t]
  199. (let [secs (or (state/sub [:electron/user-cfgs :git/auto-commit-seconds]) 60)]
  200. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  201. [:label.block.text-sm.font-medium.leading-5.opacity-70
  202. (t :settings-page/git-commit-delay)]
  203. [:div.mt-1.sm:mt-0.sm:col-span-2
  204. [:div.max-w-lg.rounded-md.sm:max-w-xs
  205. [:input#home-default-page.form-input.is-small.transition.duration-150.ease-in-out
  206. {:default-value secs
  207. :on-blur (fn [event]
  208. (let [value (-> (util/evalue event)
  209. util/safe-parse-int)]
  210. (if (and (number? value)
  211. (< 0 value (inc 600)))
  212. (do
  213. (state/set-state! [:electron/user-cfgs :git/auto-commit-seconds] value)
  214. (ipc/ipc :userAppCfgs :git/auto-commit-seconds value))
  215. (when-let [elem (gobj/get event "target")]
  216. (notification/show!
  217. [:div "Invalid value! Must be a number between 1 and 600."]
  218. :warning true)
  219. (gobj/set elem "value" secs)))))}]]]]))
  220. (rum/defc app-auto-update-row < rum/reactive [t]
  221. (let [enabled? (state/sub [:electron/user-cfgs :auto-update])
  222. enabled? (if (nil? enabled?) true enabled?)]
  223. (toggle "usage-diagnostics"
  224. (t :settings-page/auto-updater)
  225. enabled?
  226. #((state/set-state! [:electron/user-cfgs :auto-update] (not enabled?))
  227. (ipc/ipc :userAppCfgs :auto-update (not enabled?))))))
  228. (defn language-row [t preferred-language]
  229. (let [on-change (fn [e]
  230. (let [lang-code (util/evalue e)]
  231. (state/set-preferred-language! lang-code)
  232. (ui-handler/re-render-root!)))
  233. action [:select.form-select.is-small {:value preferred-language
  234. :on-change on-change}
  235. (for [language dicts/languages]
  236. (let [lang-code (name (:value language))
  237. lang-label (:label language)]
  238. [:option {:key lang-code :value lang-code} lang-label]))]]
  239. (row-with-button-action {:left-label (t :language)
  240. :-for "preferred_language"
  241. :action action})))
  242. (defn theme-modes-row [t switch-theme system-theme? dark?]
  243. (let [pick-theme [:ul.theme-modes-options
  244. [:li {:on-click (partial state/use-theme-mode! "light")
  245. :class (classnames [{:active (and (not system-theme?) (not dark?))}])} [:i.mode-light] [:strong "light"]]
  246. [:li {:on-click (partial state/use-theme-mode! "dark")
  247. :class (classnames [{:active (and (not system-theme?) dark?)}])} [:i.mode-dark] [:strong "dark"]]
  248. [:li {:on-click (partial state/use-theme-mode! "system")
  249. :class (classnames [{:active system-theme?}])} [:i.mode-system] [:strong "system"]]]]
  250. (row-with-button-action {:left-label (t :right-side-bar/switch-theme (string/capitalize switch-theme))
  251. :-for "toggle_theme"
  252. :action pick-theme
  253. :desc (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-theme))})))
  254. (defn file-format-row [t preferred-format]
  255. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  256. [:label.block.text-sm.font-medium.leading-5.opacity-70
  257. {:for "preferred_format"}
  258. (t :settings-page/preferred-file-format)]
  259. [:div.mt-1.sm:mt-0.sm:col-span-2
  260. [:div.max-w-lg.rounded-md
  261. [:select.form-select.is-small
  262. {:value (name preferred-format)
  263. :on-change (fn [e]
  264. (let [format (-> (util/evalue e)
  265. (string/lower-case)
  266. keyword)]
  267. (user-handler/set-preferred-format! format)))}
  268. (for [format (map name [:org :markdown])]
  269. [:option {:key format :value format} (string/capitalize format)])]]]])
  270. (defn date-format-row [t preferred-date-format]
  271. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  272. [:label.block.text-sm.font-medium.leading-5.opacity-70
  273. {:for "custom_date_format"}
  274. (t :settings-page/custom-date-format)]
  275. [:div.mt-1.sm:mt-0.sm:col-span-2
  276. [:div.max-w-lg.rounded-md
  277. [:select.form-select.is-small
  278. {:value preferred-date-format
  279. :on-change (fn [e]
  280. (let [format (util/evalue e)]
  281. (when-not (string/blank? format)
  282. (config-handler/set-config! :journal/page-title-format format)
  283. (notification/show!
  284. [:div "You must re-index your graph for this change to take effect"]
  285. :warning false)
  286. (state/close-modal!)
  287. (route-handler/redirect! {:to :repos}))))}
  288. (for [format (sort (date/journal-title-formatters))]
  289. [:option {:key format} format])]]]])
  290. (defn workflow-row [t preferred-workflow]
  291. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  292. [:label.block.text-sm.font-medium.leading-5.opacity-70
  293. {:for "preferred_workflow"}
  294. (t :settings-page/preferred-workflow)]
  295. [:div.mt-1.sm:mt-0.sm:col-span-2
  296. [:div.max-w-lg.rounded-md
  297. [:select.form-select.is-small
  298. {:value (name preferred-workflow)
  299. :on-change (fn [e]
  300. (-> (util/evalue e)
  301. string/lower-case
  302. keyword
  303. (#(if (= % :now) :now :todo))
  304. user-handler/set-preferred-workflow!))}
  305. (for [workflow [:now :todo]]
  306. [:option {:key (name workflow) :value (name workflow)}
  307. (if (= workflow :now) "NOW/LATER" "TODO/DOING")])]]]])
  308. (defn outdenting-row [t logical-outdenting?]
  309. (toggle "preferred_outdenting"
  310. [(t :settings-page/preferred-outdenting)
  311. (ui/tippy {:html (outdenting-hint)
  312. :class "tippy-hover ml-2"
  313. :interactive true
  314. :disabled false}
  315. (svg/info))]
  316. logical-outdenting?
  317. config-handler/toggle-logical-outdenting!))
  318. (defn tooltip-row [t enable-tooltip?]
  319. (toggle "enable_tooltip"
  320. (t :settings-page/enable-tooltip)
  321. enable-tooltip?
  322. (fn []
  323. (config-handler/toggle-ui-enable-tooltip!))))
  324. (defn shortcut-tooltip-row [t enable-shortcut-tooltip?]
  325. (toggle "enable_tooltip"
  326. (t :settings-page/enable-shortcut-tooltip)
  327. enable-shortcut-tooltip?
  328. (fn []
  329. (state/toggle-shortcut-tooltip!))))
  330. (defn timetracking-row [t enable-timetracking?]
  331. (toggle "enable_timetracking"
  332. (t :settings-page/enable-timetracking)
  333. enable-timetracking?
  334. #(let [value (not enable-timetracking?)]
  335. (config-handler/set-config! :feature/enable-timetracking? value))))
  336. (defn update-home-page
  337. [event]
  338. (let [value (util/evalue event)]
  339. (cond
  340. (string/blank? value)
  341. (let [home (get (state/get-config) :default-home {})
  342. new-home (dissoc home :page)]
  343. (config-handler/set-config! :default-home new-home)
  344. (notification/show! "Home default page updated successfully!" :success))
  345. (db/page-exists? value)
  346. (let [home (get (state/get-config) :default-home {})
  347. new-home (assoc home :page value)]
  348. (config-handler/set-config! :default-home new-home)
  349. (notification/show! "Home default page updated successfully!" :success))
  350. :else
  351. (notification/show! (str "The page \"" value "\" doesn't exist yet. Please create that page first, and then try again.") :warning))))
  352. (defn journal-row [enable-journals?]
  353. (toggle "enable_journals"
  354. (t :settings-page/enable-journals)
  355. enable-journals?
  356. (fn []
  357. (let [value (not enable-journals?)]
  358. (config-handler/set-config! :feature/enable-journals? value)))))
  359. (defn enable-all-pages-public-row [t enable-all-pages-public?]
  360. (toggle "all pages public"
  361. (t :settings-page/enable-all-pages-public)
  362. enable-all-pages-public?
  363. (fn []
  364. (let [value (not enable-all-pages-public?)]
  365. (config-handler/set-config! :publishing/all-pages-public? value)))))
  366. ;; (defn enable-block-timestamps-row [t enable-block-timestamps?]
  367. ;; (toggle "block timestamps"
  368. ;; (t :settings-page/enable-block-time)
  369. ;; enable-block-timestamps?
  370. ;; (fn []
  371. ;; (let [value (not enable-block-timestamps?)]
  372. ;; (config-handler/set-config! :feature/enable-block-timestamps? value)))))
  373. (defn encryption-row [enable-encryption?]
  374. (toggle "enable_encryption"
  375. (t :settings-page/enable-encryption)
  376. enable-encryption?
  377. #(let [value (not enable-encryption?)]
  378. (config-handler/set-config! :feature/enable-encryption? value)
  379. (when value
  380. (state/close-modal!)
  381. ;; FIXME: Don't send the `(atom false)` ! Should check multi-window! or internal status error happens
  382. (js/setTimeout (fn [] (state/pub-event! [:graph/ask-for-re-index (atom false) nil]))
  383. 100)))
  384. [:p.text-sm.opacity-50 "⚠️ This feature is experimental! "
  385. [:span "You can use "]
  386. [:a {:href "https://github.com/kanru/logseq-encrypt-ui"
  387. :target "_blank"}
  388. "logseq-encrypt-ui"]
  389. [:span " to decrypt your graph."]]))
  390. (rum/defc keyboard-shortcuts-row [t]
  391. (row-with-button-action
  392. {:left-label (t :settings-page/customize-shortcuts)
  393. :button-label (t :settings-page/shortcut-settings)
  394. :on-click #((state/close-settings!)
  395. (route-handler/redirect! {:to :shortcut-setting}))
  396. :-for "customize_shortcuts"}))
  397. (defn zotero-settings-row []
  398. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  399. [:label.block.text-sm.font-medium.leading-5.opacity-70
  400. {:for "zotero_settings"}
  401. "Zotero"]
  402. [:div.mt-1.sm:mt-0.sm:col-span-2
  403. [:div
  404. (ui/button
  405. "Settings"
  406. :class "text-sm p-1"
  407. :style {:margin-top "0px"}
  408. :on-click
  409. (fn []
  410. (state/close-settings!)
  411. (route-handler/redirect! {:to :zotero-setting})))]]])
  412. (defn auto-push-row [_t current-repo enable-git-auto-push?]
  413. (when (and current-repo (string/starts-with? current-repo "https://"))
  414. (toggle "enable_git_auto_push"
  415. "Enable Git auto push"
  416. enable-git-auto-push?
  417. (fn []
  418. (let [value (not enable-git-auto-push?)]
  419. (config-handler/set-config! :git-auto-push value))))))
  420. (defn usage-diagnostics-row [t instrument-disabled?]
  421. (toggle "usage-diagnostics"
  422. (t :settings-page/disable-sentry)
  423. (not instrument-disabled?)
  424. (fn [] (instrument/disable-instrument
  425. (not instrument-disabled?)))
  426. [:span.text-sm.opacity-50 "Logseq will never collect your local graph database or sell your data."]))
  427. (defn clear-cache-row [t]
  428. (row-with-button-action {:left-label (t :settings-page/clear-cache)
  429. :button-label (t :settings-page/clear)
  430. :on-click handler/clear-cache!
  431. :-for "clear_cache"}))
  432. (defn version-row [t version]
  433. (row-with-button-action {:left-label (t :settings-page/current-version)
  434. :action (app-updater version)
  435. :-for "current-version"}))
  436. (defn developer-mode-row [t developer-mode?]
  437. (toggle "developer_mode"
  438. (t :settings-page/developer-mode)
  439. developer-mode?
  440. (fn []
  441. (let [mode (not developer-mode?)]
  442. (state/set-developer-mode! mode)))
  443. [:div.text-sm.opacity-50 (t :settings-page/developer-mode-desc)]))
  444. (rum/defc plugin-enabled-switcher
  445. [t]
  446. (let [value (state/lsp-enabled?-or-theme)
  447. [on? set-on?] (rum/use-state value)
  448. on-toggle #(let [v (not on?)]
  449. (set-on? v)
  450. (storage/set :lsp-core-enabled v))]
  451. [:div.flex.items-center
  452. (ui/toggle on? on-toggle true)
  453. (when (not= (boolean value) on?)
  454. [:div.relative.opacity-70
  455. [:span.absolute.whitespace-nowrap
  456. {:style {:top -18 :left 10}}
  457. (ui/button (t :plugin/restart)
  458. :on-click #(js/logseq.api.relaunch)
  459. :small? true :intent "logseq")]])]))
  460. (rum/defc flashcards-enabled-switcher
  461. [enable-flashcards?]
  462. (ui/toggle enable-flashcards?
  463. (fn []
  464. (let [value (not enable-flashcards?)]
  465. (config-handler/set-config! :feature/enable-flashcards? value)))
  466. true))
  467. (rum/defc user-proxy-settings
  468. [{:keys [protocol host port] :as agent-opts}]
  469. (ui/button [:span
  470. (when-let [e (and protocol host port (str protocol "://" host ":" port))]
  471. [:strong.pr-1 e])
  472. (ui/icon "edit")]
  473. :small? true
  474. :on-click #(state/set-sub-modal!
  475. (fn [_] (plugins/user-proxy-settings-panel agent-opts))
  476. {:id :https-proxy-panel :center? true})))
  477. (defn plugin-system-switcher-row []
  478. (row-with-button-action
  479. {:left-label (t :settings-page/plugin-system)
  480. :action (plugin-enabled-switcher t)}))
  481. (defn flashcards-switcher-row [enable-flashcards?]
  482. (row-with-button-action
  483. {:left-label (t :settings-page/enable-flashcards)
  484. :action (flashcards-enabled-switcher enable-flashcards?)}))
  485. (defn https-user-agent-row [agent-opts]
  486. (row-with-button-action
  487. {:left-label (t :settings-page/network-proxy)
  488. :action (user-proxy-settings agent-opts)}))
  489. (defn filename-format-row []
  490. (row-with-button-action
  491. {:left-label (t :settings-page/filename-format)
  492. :button-label (t :settings-page/edit-setting)
  493. :on-click #(state/set-sub-modal!
  494. (fn [_] (conversion-component/files-breaking-changed))
  495. {:id :filename-format-panel :center? true})}))
  496. (rum/defcs settings-general < rum/reactive
  497. [_state current-repo]
  498. (let [preferred-language (state/sub [:preferred-language])
  499. theme (state/sub :ui/theme)
  500. dark? (= "dark" theme)
  501. system-theme? (state/sub :ui/system-theme?)
  502. switch-theme (if dark? "light" "dark")]
  503. [:div.panel-wrap.is-general
  504. (version-row t version)
  505. (language-row t preferred-language)
  506. (theme-modes-row t switch-theme system-theme? dark?)
  507. (when (config/global-config-enabled?) (edit-global-config-edn))
  508. (when current-repo (edit-config-edn))
  509. (when current-repo (edit-custom-css))
  510. (when current-repo (edit-export-css))
  511. (keyboard-shortcuts-row t)]))
  512. (rum/defcs settings-editor < rum/reactive
  513. [_state current-repo]
  514. (let [preferred-format (state/get-preferred-format)
  515. preferred-date-format (state/get-date-formatter)
  516. preferred-workflow (state/get-preferred-workflow)
  517. enable-timetracking? (state/enable-timetracking?)
  518. enable-all-pages-public? (state/all-pages-public?)
  519. logical-outdenting? (state/logical-outdenting?)
  520. enable-tooltip? (state/enable-tooltip?)
  521. enable-shortcut-tooltip? (state/sub :ui/shortcut-tooltip?)
  522. show-brackets? (state/show-brackets?)
  523. enable-git-auto-push? (state/enable-git-auto-push? current-repo)]
  524. [:div.panel-wrap.is-editor
  525. (file-format-row t preferred-format)
  526. (date-format-row t preferred-date-format)
  527. (workflow-row t preferred-workflow)
  528. ;; (enable-block-timestamps-row t enable-block-timestamps?)
  529. (show-brackets-row t show-brackets?)
  530. (when (util/electron?) (switch-spell-check-row t))
  531. (outdenting-row t logical-outdenting?)
  532. (when-not (or (util/mobile?) (mobile-util/native-platform?))
  533. (shortcut-tooltip-row t enable-shortcut-tooltip?))
  534. (when-not (or (util/mobile?) (mobile-util/native-platform?))
  535. (tooltip-row t enable-tooltip?))
  536. (timetracking-row t enable-timetracking?)
  537. (enable-all-pages-public-row t enable-all-pages-public?)
  538. (auto-push-row t current-repo enable-git-auto-push?)]))
  539. (rum/defc settings-git
  540. []
  541. [:div.panel-wrap
  542. [:div.text-sm.my-4
  543. [:span.text-sm.opacity-50.my-4
  544. "You can view a page's edit history by clicking the three vertical dots "
  545. "in the top-right corner and selecting \"Check page's history\". "
  546. "Logseq uses "]
  547. [:a {:href "https://git-scm.com/" :target "_blank"}
  548. "Git"]
  549. [:span.text-sm.opacity-50.my-4
  550. " for version control."]]
  551. [:br]
  552. (switch-git-auto-commit-row t)
  553. (git-auto-commit-seconds t)
  554. (ui/admonition
  555. :warning
  556. [:p (t :settings-page/git-confirm)])])
  557. (rum/defc settings-advanced < rum/reactive
  558. [current-repo]
  559. (let [instrument-disabled? (state/sub :instrument/disabled?)
  560. developer-mode? (state/sub [:ui/developer-mode?])
  561. https-agent-opts (state/sub [:electron/user-cfgs :settings/agent])]
  562. [:div.panel-wrap.is-advanced
  563. (when (and (or util/mac? util/win32?) (util/electron?)) (app-auto-update-row t))
  564. (usage-diagnostics-row t instrument-disabled?)
  565. (when-not (mobile-util/native-platform?) (developer-mode-row t developer-mode?))
  566. (when (util/electron?) (https-user-agent-row https-agent-opts))
  567. (when (and (util/electron?) (not (config/demo-graph? current-repo))) (filename-format-row))
  568. (clear-cache-row t)
  569. (ui/admonition
  570. :warning
  571. [:p "Clearing the cache will discard open graphs. You will lose unsaved changes."])]))
  572. (rum/defc sync-enabled-switcher
  573. [enabled?]
  574. (ui/toggle enabled?
  575. (fn []
  576. (let [value (not enabled?)]
  577. (storage/set :logseq-sync-enabled value)
  578. (state/set-state! :feature/enable-sync? value)))
  579. true))
  580. (defn sync-switcher-row [enabled?]
  581. (row-with-button-action
  582. {:left-label (t :settings-page/sync)
  583. :action (sync-enabled-switcher enabled?)}))
  584. (rum/defc whiteboards-enabled-switcher
  585. [enabled?]
  586. (ui/toggle enabled?
  587. (fn []
  588. (let [value (not enabled?)]
  589. (config-handler/set-config! :feature/enable-whiteboards? value)))
  590. true))
  591. (defn whiteboards-switcher-row [enabled?]
  592. (row-with-button-action
  593. {:left-label (t :settings-page/enable-whiteboards)
  594. :action (whiteboards-enabled-switcher enabled?)}))
  595. (rum/defc settings-features < rum/reactive
  596. []
  597. (let [current-repo (state/get-current-repo)
  598. enable-journals? (state/enable-journals? current-repo)
  599. enable-encryption? (state/enable-encryption? current-repo)
  600. enable-flashcards? (state/enable-flashcards? current-repo)
  601. enable-sync? (state/enable-sync?)
  602. enable-whiteboards? (state/enable-whiteboards? current-repo)
  603. logged-in? (user-handler/logged-in?)]
  604. [:div.panel-wrap.is-features.mb-8
  605. (journal-row enable-journals?)
  606. (when (not enable-journals?)
  607. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  608. [:label.block.text-sm.font-medium.leading-5.opacity-70
  609. {:for "default page"}
  610. (t :settings-page/home-default-page)]
  611. [:div.mt-1.sm:mt-0.sm:col-span-2
  612. [:div.max-w-lg.rounded-md.sm:max-w-xs
  613. [:input#home-default-page.form-input.is-small.transition.duration-150.ease-in-out
  614. {:default-value (state/sub-default-home-page)
  615. :on-blur update-home-page
  616. :on-key-press (fn [e]
  617. (when (= "Enter" (util/ekey e))
  618. (update-home-page e)))}]]]])
  619. (when (and (util/electron?) config/enable-plugins?) (plugin-system-switcher-row))
  620. (flashcards-switcher-row enable-flashcards?)
  621. (zotero-settings-row)
  622. (encryption-row enable-encryption?)
  623. (when-not web-platform?
  624. [:div.mt-1.sm:mt-0.sm:col-span-2
  625. [:hr]
  626. (if logged-in?
  627. [:div
  628. (user-handler/email)
  629. [:p (ui/button (t :logout) {:class "p-1"
  630. :icon "logout"
  631. :on-click user-handler/logout})]]
  632. [:div
  633. (ui/button (t :login) {:class "p-1"
  634. :icon "login"
  635. :on-click (fn []
  636. (state/close-settings!)
  637. (js/window.open config/LOGIN-URL))})
  638. [:p.text-sm.opacity-50 (t :settings-page/login-prompt)]])])
  639. (when-not web-platform?
  640. [:<>
  641. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  642. [:label.flex.font-medium.leading-5.self-start.mt-1 (ui/icon (if logged-in? "lock-open" "lock") {:class "mr-1"}) (t :settings-page/beta-features)]]
  643. [:div.flex.flex-col.gap-4
  644. {:class (when-not user-handler/alpha-or-beta-user? "opacity-50 pointer-events-none cursor-not-allowed")}
  645. (sync-switcher-row enable-sync?)
  646. [:div.text-sm
  647. "Click"
  648. [:a.mx-1 {:href "https://blog.logseq.com/how-to-setup-and-use-logseq-sync/"
  649. :target "_blank"}
  650. "here"]
  651. "for instructions on how to set up and use Sync."]]])
  652. (when-not web-platform?
  653. [:<>
  654. [:hr]
  655. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  656. [:label.flex.font-medium.leading-5.self-start.mt-1 (ui/icon (if logged-in? "lock-open" "lock") {:class "mr-1"}) (t :settings-page/alpha-features)]]
  657. [:div.flex.flex-col.gap-4
  658. {:class (when-not user-handler/alpha-user? "opacity-50 pointer-events-none cursor-not-allowed")}
  659. (whiteboards-switcher-row enable-whiteboards?)]])]))
  660. (rum/defcs settings
  661. < (rum/local [:general :general] ::active)
  662. {:will-mount
  663. (fn [state]
  664. (state/load-app-user-cfgs)
  665. state)
  666. :will-unmount
  667. (fn [state]
  668. (state/close-settings!)
  669. state)}
  670. rum/reactive
  671. [state]
  672. (let [current-repo (state/sub :git/current-repo)
  673. ;; enable-block-timestamps? (state/enable-block-timestamps?)
  674. _installed-plugins (state/sub :plugin/installed-plugins)
  675. plugins-of-settings (and config/lsp-enabled? (seq (plugin-handler/get-enabled-plugins-if-setting-schema)))
  676. *active (::active state)]
  677. [:div#settings.cp__settings-main
  678. [:header
  679. [:h1.title (t :settings)]]
  680. [:div.cp__settings-inner
  681. [:aside.md:w-64 {:style {:min-width "10rem"}}
  682. [:ul.settings-menu
  683. (for [[label id text icon]
  684. [[:general "general" (t :settings-page/tab-general) (ui/icon "adjustments")]
  685. [:editor "editor" (t :settings-page/tab-editor) (ui/icon "writing")]
  686. (when (and
  687. (util/electron?)
  688. (not (file-sync-handler/synced-file-graph? current-repo)))
  689. [:git "git" (t :settings-page/tab-version-control) (ui/icon "history")])
  690. ;; (when (util/electron?)
  691. ;; [:assets "assets" (t :settings-page/tab-assets) (ui/icon "box")])
  692. [:advanced "advanced" (t :settings-page/tab-advanced) (ui/icon "bulb")]
  693. [:features "features" (t :settings-page/tab-features) (ui/icon "app-feature" {:extension? true
  694. :style {:margin-left 2}})]
  695. (when plugins-of-settings
  696. [:plugins-setting "plugins" (t :settings-of-plugins) (ui/icon "puzzle")])]]
  697. (when label
  698. [:li.settings-menu-item
  699. {:key text
  700. :class (util/classnames [{:active (= label (first @*active))}])
  701. :on-click #(reset! *active [label (first @*active)])}
  702. [:a.flex.items-center.settings-menu-link
  703. {:data-id id}
  704. icon
  705. [:strong text]]]))]]
  706. [:article
  707. (case (first @*active)
  708. :plugins-setting
  709. (let [label (second @*active)]
  710. (state/pub-event! [:go/plugins-settings (:id (first plugins-of-settings))])
  711. (reset! *active [label label])
  712. nil)
  713. :general
  714. (settings-general current-repo)
  715. :editor
  716. (settings-editor current-repo)
  717. :git
  718. (settings-git)
  719. :assets
  720. (assets/settings-content)
  721. :advanced
  722. (settings-advanced current-repo)
  723. :features
  724. (settings-features)
  725. nil)]]]))