settings.cljs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. (ns frontend.components.settings
  2. (:require [clojure.string :as string]
  3. [frontend.components.svg :as svg]
  4. [frontend.config :as config]
  5. [frontend.context.i18n :as i18n]
  6. [frontend.date :as date]
  7. [frontend.dicts :as dicts]
  8. [frontend.handler :as handler]
  9. [frontend.handler.config :as config-handler]
  10. [frontend.handler.notification :as notification]
  11. [frontend.handler.page :as page-handler]
  12. [frontend.handler.route :as route-handler]
  13. [frontend.handler.ui :as ui-handler]
  14. [frontend.handler.user :as user-handler]
  15. [frontend.modules.instrumentation.core :as instrument]
  16. [frontend.modules.shortcut.data-helper :as shortcut-helper]
  17. [frontend.state :as state]
  18. [frontend.ui :as ui]
  19. [electron.ipc :as ipc]
  20. [promesa.core :as p]
  21. [frontend.util :refer [classnames] :as util]
  22. [frontend.version :refer [version]]
  23. [goog.object :as gobj]
  24. [reitit.frontend.easy :as rfe]
  25. [rum.core :as rum]))
  26. (rum/defcs set-email < (rum/local "" ::email)
  27. [state]
  28. (let [email (get state ::email)]
  29. [:div.p-8.flex.items-center.justify-center
  30. [:div.w-full.mx-auto
  31. [:div
  32. [:div
  33. [:h1.title.mb-1
  34. "Your email address:"]
  35. [:div.mt-2.mb-4.relative.rounded-md.max-w-xs
  36. [:input#.form-input.is-small
  37. {:autoFocus true
  38. :on-change (fn [e]
  39. (reset! email (util/evalue e)))}]]]]
  40. (ui/button
  41. "Submit"
  42. :on-click
  43. (fn []
  44. (user-handler/set-email! @email)))
  45. [:hr]
  46. [:span.pl-1.opacity-70 "Git commit requires the email address."]]]))
  47. (rum/defcs set-cors < (rum/local "" ::cors)
  48. [state]
  49. (let [cors (get state ::cors)]
  50. [:div.p-8.flex.items-center.justify-center
  51. [:div.w-full.mx-auto
  52. [:div
  53. [:div
  54. [:h1.title.mb-1
  55. "Your cors address:"]
  56. [:div.mt-2.mb-4.relative.rounded-md.max-w-xs
  57. [:input#.form-input.is-small
  58. {:autoFocus true
  59. :on-change (fn [e]
  60. (reset! cors (util/evalue e)))}]]]]
  61. (ui/button
  62. "Submit"
  63. :on-click
  64. (fn []
  65. (user-handler/set-cors! @cors)))
  66. [:hr]
  67. [:span.pl-1.opacity-70 "Git commit requires the cors address."]]]))
  68. (defn toggle
  69. [label-for name state on-toggle & [detail-text]]
  70. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  71. [:label.block.text-sm.font-medium.leading-5.opacity-70
  72. {:for label-for}
  73. name]
  74. [:div.rounded-md.sm:max-w-tss.sm:col-span-2
  75. [:div.rounded-md {:style {:display "flex" :gap "1rem" :align-items "center"}}
  76. (ui/toggle state on-toggle true)
  77. detail-text]]])
  78. (rum/defcs app-updater < rum/reactive
  79. [state version]
  80. (let [update-pending? (state/sub :electron/updater-pending?)
  81. {:keys [type payload]} (state/sub :electron/updater)]
  82. [:span.cp__settings-app-updater
  83. [:div.ctls.flex.items-center
  84. [:div.mt-1.sm:mt-0.sm:col-span-2
  85. {:style {:display "flex" :gap "0.5rem" :align-items "center"}}
  86. [:div (ui/button
  87. (if update-pending? "Checking ..." "Check for updates")
  88. :class "text-sm p-1 mr-1"
  89. :disabled update-pending?
  90. :on-click #(js/window.apis.checkForUpdates false))]
  91. [:div.text-sm.opacity-50 (str "Version " version)]]]
  92. (when-not (or update-pending?
  93. (string/blank? type))
  94. [:div.update-state.text-sm
  95. (case type
  96. "update-not-available"
  97. [:p "Your app is up-to-date 🎉"]
  98. "update-available"
  99. (let [{:keys [name url]} payload]
  100. [:p (str "Found new release ")
  101. [:a.link
  102. {:on-click
  103. (fn [e]
  104. (js/window.apis.openExternal url)
  105. (util/stop e))}
  106. svg/external-link name " 🎉"]])
  107. "error"
  108. [:p "⚠️ Oops, Something Went Wrong!" [:br] " Please check out the "
  109. [:a.link
  110. {:on-click
  111. (fn [e]
  112. (js/window.apis.openExternal "https://github.com/logseq/logseq/releases")
  113. (util/stop e))}
  114. svg/external-link " release channel"]])])]))
  115. (rum/defc delete-account-confirm
  116. [close-fn]
  117. (rum/with-context [[t] i18n/*tongue-context*]
  118. [:div
  119. (ui/admonition
  120. :important
  121. [:p.text-gray-700 (t :user/delete-account-notice)])
  122. [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
  123. [:span.flex.w-full.rounded-md.sm:ml-3.sm:w-auto
  124. [:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
  125. {:type "button"
  126. :on-click user-handler/delete-account!}
  127. (t :user/delete-account)]]
  128. [:span.mt-3.flex.w-full.rounded-md.sm:mt-0.sm:w-auto
  129. [:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
  130. {:type "button"
  131. :on-click close-fn}
  132. "Cancel"]]]]))
  133. (rum/defc outdenting-hint
  134. []
  135. [:div.ui__modal-panel
  136. {:style {:box-shadow "0 4px 20px 4px rgba(0, 20, 60, .1), 0 4px 80px -8px rgba(0, 20, 60, .2)"}}
  137. [:div {:style {:margin "12px" :max-width "500px"}}
  138. [:p.text-sm
  139. "The left side shows outdenting with the default setting, and the right shows outdenting with logical outdenting enabled. "
  140. [:a.text-sm
  141. {:target "_blank" :href "https://discuss.logseq.com/t/whats-your-preferred-outdent-behavior-the-direct-one-or-the-logical-one/978"}
  142. "→ Learn more"]]
  143. [:img {:src "https://discuss.logseq.com/uploads/default/original/1X/e8ea82f63a5e01f6d21b5da827927f538f3277b9.gif"
  144. :width 500
  145. :height 500}]]])
  146. (defn row-with-button-action
  147. [{:keys [left-label action button-label href on-click desc -for]}]
  148. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  149. ;; left column
  150. [:label.block.text-sm.font-medium.leading-5.opacity-70
  151. {:for -for}
  152. left-label]
  153. ;; right column
  154. [:div.mt-1.sm:mt-0.sm:col-span-2
  155. {:style {:display "flex" :gap "0.5rem" :align-items "center"}}
  156. [:div (if action action (ui/button
  157. button-label
  158. :class "text-sm p-1"
  159. :href href
  160. :on-click on-click))]
  161. [:div.text-sm desc]]])
  162. (defn edit-config-edn []
  163. (rum/with-context [[t] i18n/*tongue-context*]
  164. (row-with-button-action
  165. {:left-label "Custom configuration"
  166. :button-label (t :settings-page/edit-config-edn)
  167. :href (rfe/href :file {:path (config/get-config-path)})
  168. :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))
  169. :-for "config_edn"})))
  170. (defn edit-custom-css []
  171. (rum/with-context [[t] i18n/*tongue-context*]
  172. (row-with-button-action
  173. {:left-label "Custom theme"
  174. :button-label (t :settings-page/edit-custom-css)
  175. :href (rfe/href :file {:path (config/get-custom-css-path)})
  176. :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))
  177. :-for "customize_css"})))
  178. (defn show-brackets-row [t show-brackets?]
  179. (toggle "show_brackets"
  180. (t :settings-page/show-brackets)
  181. show-brackets?
  182. config-handler/toggle-ui-show-brackets!
  183. [:span {:text-align "right"}
  184. (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-brackets))]))
  185. (rum/defcs switch-spell-check-row < rum/reactive
  186. [state t]
  187. (let [enabled? (state/sub [:electron/user-cfgs :spell-check])
  188. enabled? (if (nil? enabled?) true enabled?)]
  189. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  190. [:label.block.text-sm.font-medium.leading-5.opacity-70
  191. (t :settings-page/spell-checker)]
  192. [:div
  193. [:div.rounded-md.sm:max-w-xs
  194. (ui/toggle
  195. enabled?
  196. (fn []
  197. (state/set-state! [:electron/user-cfgs :spell-check] (not enabled?))
  198. (p/then (ipc/ipc "userAppCfgs" :spell-check (not enabled?))
  199. #(when (js/confirm (t :relaunch-confirm-to-work))
  200. (js/logseq.api.relaunch))))
  201. true)]]]))
  202. (rum/defcs switch-git-auto-commit-row < rum/reactive
  203. [state t]
  204. (let [enabled? (state/get-git-auto-commit-enabled?)]
  205. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  206. [:label.block.text-sm.font-medium.leading-5.opacity-70
  207. (t :settings-page/git-switcher-label)]
  208. [:div
  209. [:div.rounded-md.sm:max-w-xs
  210. (ui/toggle
  211. enabled?
  212. (fn []
  213. (state/set-state! [:electron/user-cfgs :git/disable-auto-commit?] enabled?)
  214. (ipc/ipc "userAppCfgs" :git/disable-auto-commit? enabled?))
  215. true)]]]))
  216. (rum/defcs git-auto-commit-seconds < rum/reactive
  217. [state t]
  218. (let [secs (or (state/sub [:electron/user-cfgs :git/auto-commit-seconds]) 60)]
  219. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  220. [:label.block.text-sm.font-medium.leading-5.opacity-70
  221. (t :settings-page/git-commit-delay)]
  222. [:div.mt-1.sm:mt-0.sm:col-span-2
  223. [:div.max-w-lg.rounded-md.sm:max-w-xs
  224. [:input#home-default-page.form-input.is-small.transition.duration-150.ease-in-out
  225. {:default-value secs
  226. :on-blur (fn [event]
  227. (when-let [value (-> (util/evalue event)
  228. util/safe-parse-int)]
  229. (when (< 0 value (inc 600))
  230. (state/set-state! [:electron/user-cfgs :git/auto-commit-seconds] value)
  231. (ipc/ipc "userAppCfgs" :git/auto-commit-seconds value))))}]]]]))
  232. (rum/defc app-auto-update-row < rum/reactive [t]
  233. (let [enabled? (state/sub [:electron/user-cfgs :auto-update])
  234. enabled? (if (nil? enabled?) true enabled?)]
  235. (toggle "usage-diagnostics"
  236. (t :settings-page/auto-updater)
  237. enabled?
  238. #((state/set-state! [:electron/user-cfgs :auto-update] (not enabled?))
  239. (ipc/ipc "userAppCfgs" :auto-update (not enabled?))))))
  240. (defn language-row [t preferred-language]
  241. (let [on-change (fn [e]
  242. (let [lang-code (util/evalue e)]
  243. (state/set-preferred-language! lang-code)
  244. (ui-handler/re-render-root!)))
  245. action [:select.form-select.is-small {:value preferred-language
  246. :on-change on-change}
  247. (for [language dicts/languages]
  248. (let [lang-code (name (:value language))
  249. lang-label (:label language)]
  250. [:option {:key lang-code :value lang-code} lang-label]))]]
  251. (row-with-button-action {:left-label (t :language)
  252. :-for "preferred_language"
  253. :action action})))
  254. (defn theme-modes-row [t switch-theme system-theme? dark?]
  255. (let [pick-theme [:ul.theme-modes-options
  256. [:li {:on-click (partial state/use-theme-mode! "light")
  257. :class (classnames [{:active (and (not system-theme?) (not dark?))}])} [:i.mode-light] [:strong "light"]]
  258. [:li {:on-click (partial state/use-theme-mode! "dark")
  259. :class (classnames [{:active (and (not system-theme?) dark?)}])} [:i.mode-dark] [:strong "dark"]]
  260. [:li {:on-click (partial state/use-theme-mode! "system")
  261. :class (classnames [{:active system-theme?}])} [:i.mode-system] [:strong "system"]]]]
  262. (row-with-button-action {:left-label (t :right-side-bar/switch-theme (string/capitalize switch-theme))
  263. :-for "toggle_theme"
  264. :action pick-theme
  265. :desc (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-theme))})))
  266. (defn file-format-row [t preferred-format]
  267. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  268. [:label.block.text-sm.font-medium.leading-5.opacity-70
  269. {:for "preferred_format"}
  270. (t :settings-page/preferred-file-format)]
  271. [:div.mt-1.sm:mt-0.sm:col-span-2
  272. [:div.max-w-lg.rounded-md
  273. [:select.form-select.is-small
  274. {:value (name preferred-format)
  275. :on-change (fn [e]
  276. (let [format (-> (util/evalue e)
  277. (string/lower-case)
  278. keyword)]
  279. (user-handler/set-preferred-format! format)))}
  280. (for [format (map name [:org :markdown])]
  281. [:option {:key format :value format} (string/capitalize format)])]]]])
  282. (defn date-format-row [t preferred-date-format]
  283. [: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
  284. [:label.block.text-sm.font-medium.leading-5.opacity-70
  285. {:for "custom_date_format"}
  286. (t :settings-page/custom-date-format)]
  287. [:div.mt-1.sm:mt-0.sm:col-span-2
  288. [:div.max-w-lg.rounded-md
  289. [:select.form-select.is-small
  290. {:value preferred-date-format
  291. :on-change (fn [e]
  292. (let [format (util/evalue e)]
  293. (when-not (string/blank? format)
  294. (config-handler/set-config! :journal/page-title-format format)
  295. (notification/show!
  296. [:div "You need to re-index your graph to make the change works"]
  297. :success)
  298. (state/close-modal!)
  299. (route-handler/redirect! {:to :repos}))))}
  300. (for [format (sort (date/journal-title-formatters))]
  301. [:option {:key format} format])]]]])
  302. (defn workflow-row [t preferred-workflow]
  303. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  304. [:label.block.text-sm.font-medium.leading-5.opacity-70
  305. {:for "preferred_workflow"}
  306. (t :settings-page/preferred-workflow)]
  307. [:div.mt-1.sm:mt-0.sm:col-span-2
  308. [:div.max-w-lg.rounded-md
  309. [:select.form-select.is-small
  310. {:value (name preferred-workflow)
  311. :on-change (fn [e]
  312. (-> (util/evalue e)
  313. string/lower-case
  314. keyword
  315. (#(if (= % :now) :now :todo))
  316. user-handler/set-preferred-workflow!))}
  317. (for [workflow [:now :todo]]
  318. [:option {:key (name workflow) :value (name workflow)}
  319. (if (= workflow :now) "NOW/LATER" "TODO/DOING")])]]]])
  320. (defn outdenting-row [t logical-outdenting?]
  321. (toggle "preferred_outdenting"
  322. [(t :settings-page/preferred-outdenting)
  323. (ui/tippy {:html (outdenting-hint)
  324. :class "tippy-hover ml-2"
  325. :interactive true
  326. :disabled false}
  327. (svg/info))]
  328. logical-outdenting?
  329. config-handler/toggle-logical-outdenting!))
  330. (defn tooltip-row [t enable-tooltip?]
  331. (toggle "enable_tooltip"
  332. (t :settings-page/enable-tooltip)
  333. enable-tooltip?
  334. (fn []
  335. (config-handler/toggle-ui-enable-tooltip!))))
  336. (defn shortcut-tooltip-row [t enable-shortcut-tooltip?]
  337. (toggle "enable_tooltip"
  338. (t :settings-page/enable-shortcut-tooltip)
  339. enable-shortcut-tooltip?
  340. (fn []
  341. (state/toggle-shortcut-tooltip!))))
  342. (defn timetracking-row [t enable-timetracking?]
  343. (toggle "enable_timetracking"
  344. (t :settings-page/enable-timetracking)
  345. enable-timetracking?
  346. #((let [value (not enable-timetracking?)]
  347. (config-handler/set-config! :feature/enable-timetracking? value)))))
  348. (defn update-home-page
  349. [event]
  350. (let [value (util/evalue event)]
  351. (cond
  352. (string/blank? value)
  353. (let [home (get (state/get-config) :default-home {})
  354. new-home (dissoc home :page)]
  355. (config-handler/set-config! :default-home new-home)
  356. (notification/show! "Home default page updated successfully!" :success))
  357. (page-handler/page-exists? (string/lower-case value))
  358. (let [home (get (state/get-config) :default-home {})
  359. new-home (assoc home :page value)]
  360. (config-handler/set-config! :default-home new-home)
  361. (notification/show! "Home default page updated successfully!" :success))
  362. :else
  363. (notification/show! (str "The page \"" value "\" doesn't exist yet. Please create that page first, and then try again.") :warning))))
  364. (defn journal-row [t enable-journals?]
  365. [(toggle "enable_journals"
  366. (t :settings-page/enable-journals)
  367. enable-journals?
  368. (fn []
  369. (let [value (not enable-journals?)]
  370. (config-handler/set-config! :feature/enable-journals? value))))
  371. (when (not enable-journals?)
  372. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  373. [:label.block.text-sm.font-medium.leading-5.opacity-70
  374. {:for "default page"}
  375. (t :settings-page/home-default-page)]
  376. [:div.mt-1.sm:mt-0.sm:col-span-2
  377. [:div.max-w-lg.rounded-md.sm:max-w-xs
  378. [:input#home-default-page.form-input.is-small.transition.duration-150.ease-in-out
  379. {:default-value (state/sub-default-home-page)
  380. :on-blur update-home-page
  381. :on-key-press (fn [e]
  382. (when (= "Enter" (util/ekey e))
  383. (update-home-page e)))}]]]])])
  384. (defn enable-all-pages-public-row [t enable-all-pages-public?]
  385. (toggle "all pages public"
  386. (t :settings-page/enable-all-pages-public)
  387. enable-all-pages-public?
  388. (fn []
  389. (let [value (not enable-all-pages-public?)]
  390. (config-handler/set-config! :publishing/all-pages-public? value)))))
  391. ;; (defn enable-block-timestamps-row [t enable-block-timestamps?]
  392. ;; (toggle "block timestamps"
  393. ;; (t :settings-page/enable-block-time)
  394. ;; enable-block-timestamps?
  395. ;; (fn []
  396. ;; (let [value (not enable-block-timestamps?)]
  397. ;; (config-handler/set-config! :feature/enable-block-timestamps? value)))))
  398. (defn encryption-row [t enable-encryption?]
  399. (toggle "enable_encryption"
  400. (t :settings-page/enable-encryption)
  401. enable-encryption?
  402. #((let [value (not enable-encryption?)]
  403. (config-handler/set-config! :feature/enable-encryption? value)))
  404. [:div.text-sm.opacity-50 "⚠️ This feature is experimental"]))
  405. (rum/defc keyboard-shortcuts-row [t]
  406. (row-with-button-action
  407. {:left-label (t :settings-page/customize-shortcuts)
  408. :button-label (t :settings-page/shortcut-settings)
  409. :on-click #((state/close-settings!)
  410. (route-handler/redirect! {:to :shortcut-setting}))
  411. :-for "customize_shortcuts"}))
  412. (defn zotero-settings-row [t]
  413. [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
  414. [:label.block.text-sm.font-medium.leading-5.opacity-70
  415. {:for "zotero_settings"}
  416. "Zotero settings"]
  417. [:div.mt-1.sm:mt-0.sm:col-span-2
  418. [:div
  419. (ui/button
  420. "Zotero settings"
  421. :class "text-sm p-1"
  422. :style {:margin-top "0px"}
  423. :on-click
  424. (fn []
  425. (state/close-settings!)
  426. (route-handler/redirect! {:to :zotero-setting})))]]])
  427. (defn auto-push-row [t current-repo enable-git-auto-push?]
  428. (when (string/starts-with? current-repo "https://")
  429. (toggle "enable_git_auto_push"
  430. "Enable Git auto push"
  431. enable-git-auto-push?
  432. (fn []
  433. (let [value (not enable-git-auto-push?)]
  434. (config-handler/set-config! :git-auto-push value))))))
  435. (defn usage-diagnostics-row [t instrument-disabled?]
  436. (toggle "usage-diagnostics"
  437. (t :settings-page/disable-sentry)
  438. (not instrument-disabled?)
  439. (fn [] (instrument/disable-instrument
  440. (not instrument-disabled?)))
  441. [:span.text-sm.opacity-50 "Logseq will never collect your local graph database or sell your data."]))
  442. (defn clear-cache-row [t]
  443. (row-with-button-action {:left-label (t :settings-page/clear-cache)
  444. :button-label (t :settings-page/clear)
  445. :on-click handler/clear-cache!
  446. :-for "clear_cache"}))
  447. (defn version-row [t version]
  448. (row-with-button-action {:left-label (t :settings-page/current-version)
  449. :action (app-updater version)
  450. :-for "current-version"}))
  451. (defn developer-mode-row [t developer-mode?]
  452. (toggle "developer_mode"
  453. (t :settings-page/developer-mode)
  454. developer-mode?
  455. (fn []
  456. (let [mode (not developer-mode?)]
  457. (state/set-developer-mode! mode)
  458. (and mode (util/electron?)
  459. (when (js/confirm (t :developer-mode-alert))
  460. (js/logseq.api.relaunch)))))
  461. [:div.text-sm.opacity-50 (t :settings-page/developer-mode-desc)]))
  462. (rum/defcs settings
  463. < (rum/local :general ::active)
  464. {:will-mount
  465. (fn [state]
  466. (state/load-app-user-cfgs)
  467. state)
  468. :will-unmount
  469. (fn [state]
  470. (state/close-settings!)
  471. state)}
  472. rum/reactive
  473. [state]
  474. (let [preferred-format (state/get-preferred-format)
  475. preferred-date-format (state/get-date-formatter)
  476. preferred-workflow (state/get-preferred-workflow)
  477. preferred-language (state/sub [:preferred-language])
  478. enable-timetracking? (state/enable-timetracking?)
  479. current-repo (state/get-current-repo)
  480. enable-journals? (state/enable-journals? current-repo)
  481. enable-encryption? (state/enable-encryption? current-repo)
  482. enable-all-pages-public? (state/all-pages-public?)
  483. instrument-disabled? (state/sub :instrument/disabled?)
  484. logical-outdenting? (state/logical-outdenting?)
  485. enable-tooltip? (state/enable-tooltip?)
  486. enable-shortcut-tooltip? (state/sub :ui/shortcut-tooltip?)
  487. enable-git-auto-push? (state/enable-git-auto-push? current-repo)
  488. ;; enable-block-timestamps? (state/enable-block-timestamps?)
  489. show-brackets? (state/show-brackets?)
  490. github-token (state/sub [:me :access-token])
  491. cors-proxy (state/sub [:me :cors_proxy])
  492. logged? (state/logged?)
  493. developer-mode? (state/sub [:ui/developer-mode?])
  494. theme (state/sub :ui/theme)
  495. dark? (= "dark" theme)
  496. system-theme? (state/sub :ui/system-theme?)
  497. switch-theme (if dark? "white" "dark")
  498. *active (::active state)]
  499. (rum/with-context
  500. [[t] i18n/*tongue-context*]
  501. [:div#settings.cp__settings-main
  502. [:header
  503. [:h1.title (t :settings)]]
  504. [:div.cp__settings-inner.md:flex
  505. [:aside.md:w-64
  506. [:ul
  507. (for [[label text icon]
  508. [[:general (t :settings-page/tab-general) (ui/icon "adjustments" {:style {:font-size 20}})]
  509. [:editor (t :settings-page/tab-editor) (ui/icon "writing" {:style {:font-size 20}})]
  510. [:git (t :settings-page/tab-version-control) (ui/icon "history" {:style {:font-size 20}})]
  511. [:advanced (t :settings-page/tab-advanced) (ui/icon "bulb" {:style {:font-size 20}})]]]
  512. [:li
  513. {:class (util/classnames [{:active (= label @*active)}])
  514. :on-click #(reset! *active label)}
  515. [:a.flex.items-center
  516. icon
  517. [:strong text]]])]]
  518. [:article
  519. (case @*active
  520. :general
  521. [:div.panel-wrap.is-general
  522. (version-row t version)
  523. (language-row t preferred-language)
  524. (theme-modes-row t switch-theme system-theme? dark?)
  525. (when-let [current-repo (state/sub :git/current-repo)]
  526. [(edit-config-edn)
  527. (edit-custom-css)])
  528. (keyboard-shortcuts-row t)]
  529. :editor
  530. [:div.panel-wrap.is-editor
  531. (file-format-row t preferred-format)
  532. (date-format-row t preferred-date-format)
  533. (workflow-row t preferred-workflow)
  534. ;; (enable-block-timestamps-row t enable-block-timestamps?)
  535. (show-brackets-row t show-brackets?)
  536. (when (util/electron?) (switch-spell-check-row t))
  537. (outdenting-row t logical-outdenting?)
  538. (shortcut-tooltip-row t enable-shortcut-tooltip?)
  539. (tooltip-row t enable-tooltip?)
  540. (timetracking-row t enable-timetracking?)
  541. (journal-row t enable-journals?)
  542. (encryption-row t enable-encryption?)
  543. (enable-all-pages-public-row t enable-all-pages-public?)
  544. (zotero-settings-row t)
  545. (auto-push-row t current-repo enable-git-auto-push?)]
  546. :git
  547. [:div.panel-wrap
  548. [:div.text-sm.my-4
  549. [:span.text-sm.opacity-50.my-4
  550. "You can view a page's edit history by clicking the three vertical dots "
  551. "in the top-right corner and selecting \"Check page's history\". "
  552. "Logseq uses "]
  553. [:a {:href "https://git-scm.com/" :target "_blank"}
  554. "Git"]
  555. [:span.text-sm.opacity-50.my-4
  556. " for version control."]]
  557. [:br]
  558. (switch-git-auto-commit-row t)
  559. (git-auto-commit-seconds t)
  560. (ui/admonition
  561. :warning
  562. [:p (t :settings-page/git-confirm)])]
  563. :advanced
  564. [:div.panel-wrap.is-advanced
  565. (when (and util/mac? (util/electron?)) (app-auto-update-row t))
  566. (usage-diagnostics-row t instrument-disabled?)
  567. (developer-mode-row t developer-mode?)
  568. (clear-cache-row t)
  569. (when logged?
  570. [:div
  571. [:div.mt-6.sm:mt-5.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center.sm:pt-5
  572. [:label.block.text-sm.font-medium.leading-5.sm:mt-px..opacity-70
  573. {:for "cors"}
  574. (t :settings-page/custom-cors-proxy-server)]
  575. [:div.mt-1.sm:mt-0.sm:col-span-2
  576. [:div.max-w-lg.rounded-md.sm:max-w-xs
  577. [:input#pat.form-input.is-small.transition.duration-150.ease-in-out
  578. {:default-value cors-proxy
  579. :on-blur (fn [event]
  580. (when-let [server (util/evalue event)]
  581. (user-handler/set-cors! server)
  582. (notification/show! "Custom CORS proxy updated successfully!" :success)))
  583. :on-key-press (fn [event]
  584. (let [k (gobj/get event "key")]
  585. (when (= "Enter" k)
  586. (when-let [server (util/evalue event)]
  587. (user-handler/set-cors! server)
  588. (notification/show! "Custom CORS proxy updated successfully!" :success)))))}]]]]
  589. (ui/admonition
  590. :important
  591. [:p (t :settings-page/dont-use-other-peoples-proxy-servers)
  592. [:a {:href "https://github.com/isomorphic-git/cors-proxy"
  593. :target "_blank"}
  594. "https://github.com/isomorphic-git/cors-proxy"]])])
  595. (when logged?
  596. [:div
  597. [:hr]
  598. [:div.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center.sm:pt-5
  599. [:label.block.text-sm.font-medium.leading-5.opacity-70.text-red-600.dark:text-red-400
  600. {:for "delete account"}
  601. (t :user/delete-account)]
  602. [:div.mt-1.sm:mt-0.sm:col-span-2
  603. [:div.max-w-lg.rounded-md.sm:max-w-xs
  604. (ui/button (t :user/delete-your-account)
  605. :on-click (fn []
  606. (ui-handler/toggle-settings-modal!)
  607. (js/setTimeout #(state/set-modal! delete-account-confirm))))]]]])]
  608. nil)]]])))