settings.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <template>
  2. <div>
  3. <div class="container space-y-8 divide-y divide-dividerLight">
  4. <div class="md:grid md:gap-4 md:grid-cols-3">
  5. <div class="p-8 md:col-span-1">
  6. <h3 class="heading">
  7. {{ t("settings.theme") }}
  8. </h3>
  9. <p class="my-1 text-secondaryLight">
  10. {{ t("settings.theme_description") }}
  11. </p>
  12. </div>
  13. <div class="p-8 space-y-8 md:col-span-2">
  14. <section>
  15. <h4 class="font-semibold text-secondaryDark">
  16. {{ t("settings.background") }}
  17. </h4>
  18. <div class="my-1 text-secondaryLight">
  19. <ColorScheme placeholder="..." tag="span">
  20. {{ t(getColorModeName(colorMode.preference)) }}
  21. <span v-if="colorMode.preference === 'system'">
  22. ({{ t(getColorModeName(colorMode.value)) }})
  23. </span>
  24. </ColorScheme>
  25. </div>
  26. <div class="mt-4">
  27. <SmartColorModePicker />
  28. </div>
  29. </section>
  30. <section>
  31. <h4 class="font-semibold text-secondaryDark">
  32. {{ t("settings.accent_color") }}
  33. </h4>
  34. <div class="my-1 text-secondaryLight">
  35. {{ ACCENT_COLOR.charAt(0).toUpperCase() + ACCENT_COLOR.slice(1) }}
  36. </div>
  37. <div class="mt-4">
  38. <SmartAccentModePicker />
  39. </div>
  40. </section>
  41. <section>
  42. <h4 class="font-semibold text-secondaryDark">
  43. {{ t("settings.font_size") }}
  44. </h4>
  45. <div class="mt-4">
  46. <SmartFontSizePicker />
  47. </div>
  48. </section>
  49. <section>
  50. <h4 class="font-semibold text-secondaryDark">
  51. {{ t("settings.language") }}
  52. </h4>
  53. <div class="mt-4">
  54. <SmartChangeLanguage />
  55. </div>
  56. </section>
  57. <section>
  58. <h4 class="font-semibold text-secondaryDark">
  59. {{ t("settings.experiments") }}
  60. </h4>
  61. <div class="my-1 text-secondaryLight">
  62. {{ t("settings.experiments_notice") }}
  63. <SmartLink
  64. class="link"
  65. to="https://github.com/hoppscotch/hoppscotch/issues/new/choose"
  66. blank
  67. >
  68. {{ t("app.contact_us") }} </SmartLink
  69. >.
  70. </div>
  71. <div class="py-4 space-y-4">
  72. <div class="flex items-center">
  73. <SmartToggle :on="TELEMETRY_ENABLED" @change="showConfirmModal">
  74. {{ t("settings.telemetry") }}
  75. </SmartToggle>
  76. </div>
  77. <div class="flex items-center">
  78. <SmartToggle
  79. :on="EXPAND_NAVIGATION"
  80. @change="toggleSetting('EXPAND_NAVIGATION')"
  81. >
  82. {{ t("settings.expand_navigation") }}
  83. </SmartToggle>
  84. </div>
  85. <div class="flex items-center">
  86. <SmartToggle
  87. :on="SIDEBAR_ON_LEFT"
  88. @change="toggleSetting('SIDEBAR_ON_LEFT')"
  89. >
  90. {{ t("settings.sidebar_on_left") }}
  91. </SmartToggle>
  92. </div>
  93. <div class="flex items-center">
  94. <SmartToggle :on="ZEN_MODE" @change="toggleSetting('ZEN_MODE')">
  95. {{ t("layout.zen_mode") }}
  96. </SmartToggle>
  97. </div>
  98. </div>
  99. </section>
  100. </div>
  101. </div>
  102. <div class="md:grid md:gap-4 md:grid-cols-3">
  103. <div class="p-8 md:col-span-1">
  104. <h3 class="heading">
  105. {{ t("settings.interceptor") }}
  106. </h3>
  107. <p class="my-1 text-secondaryLight">
  108. {{ t("settings.interceptor_description") }}
  109. </p>
  110. </div>
  111. <div class="p-8 space-y-8 md:col-span-2">
  112. <section>
  113. <h4 class="font-semibold text-secondaryDark">
  114. {{ t("settings.extensions") }}
  115. </h4>
  116. <div class="my-1 text-secondaryLight">
  117. <span v-if="extensionVersion != null">
  118. {{
  119. `${t("settings.extension_version")}: v${
  120. extensionVersion.major
  121. }.${extensionVersion.minor}`
  122. }}
  123. </span>
  124. <span v-else>
  125. {{ t("settings.extension_version") }}:
  126. {{ t("settings.extension_ver_not_reported") }}
  127. </span>
  128. </div>
  129. <div class="flex flex-col py-4 space-y-2">
  130. <span>
  131. <SmartItem
  132. to="https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
  133. blank
  134. svg="brands/chrome"
  135. label="Chrome"
  136. :info-icon="hasChromeExtInstalled ? 'check_circle' : ''"
  137. :active-info-icon="hasChromeExtInstalled"
  138. outline
  139. />
  140. </span>
  141. <span>
  142. <SmartItem
  143. to="https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
  144. blank
  145. svg="brands/firefox"
  146. label="Firefox"
  147. :info-icon="hasFirefoxExtInstalled ? 'check_circle' : ''"
  148. :active-info-icon="hasFirefoxExtInstalled"
  149. outline
  150. />
  151. </span>
  152. </div>
  153. <div class="py-4 space-y-4">
  154. <div class="flex items-center">
  155. <SmartToggle
  156. :on="EXTENSIONS_ENABLED"
  157. @change="toggleInterceptor('extension')"
  158. >
  159. {{ t("settings.extensions_use_toggle") }}
  160. </SmartToggle>
  161. </div>
  162. </div>
  163. </section>
  164. <section>
  165. <h4 class="font-semibold text-secondaryDark">
  166. {{ t("settings.proxy") }}
  167. </h4>
  168. <div class="my-1 text-secondaryLight">
  169. {{
  170. `${t("settings.official_proxy_hosting")} ${t(
  171. "settings.read_the"
  172. )}`
  173. }}
  174. <SmartLink
  175. class="link"
  176. to="https://docs.hoppscotch.io/privacy"
  177. blank
  178. >
  179. {{ t("app.proxy_privacy_policy") }} </SmartLink
  180. >.
  181. </div>
  182. <div class="py-4 space-y-4">
  183. <div class="flex items-center">
  184. <SmartToggle
  185. :on="PROXY_ENABLED"
  186. @change="toggleInterceptor('proxy')"
  187. >
  188. {{ t("settings.proxy_use_toggle") }}
  189. </SmartToggle>
  190. </div>
  191. </div>
  192. <div class="flex items-center py-4 space-x-2">
  193. <div class="relative flex flex-col flex-1">
  194. <input
  195. id="url"
  196. v-model="PROXY_URL"
  197. class="input floating-input"
  198. placeholder=" "
  199. type="url"
  200. autocomplete="off"
  201. :disabled="!PROXY_ENABLED"
  202. />
  203. <label for="url">
  204. {{ t("settings.proxy_url") }}
  205. </label>
  206. </div>
  207. <ButtonSecondary
  208. v-tippy="{ theme: 'tooltip' }"
  209. :title="t('settings.reset_default')"
  210. :svg="clearIcon"
  211. outline
  212. class="rounded"
  213. @click.native="resetProxy"
  214. />
  215. </div>
  216. </section>
  217. </div>
  218. </div>
  219. </div>
  220. <SmartConfirmModal
  221. :show="confirmRemove"
  222. :title="`${t('confirm.remove_telemetry')} ${t(
  223. 'settings.telemetry_helps_us'
  224. )}`"
  225. @hide-modal="confirmRemove = false"
  226. @resolve="
  227. () => {
  228. toggleSetting('TELEMETRY_ENABLED')
  229. confirmRemove = false
  230. }
  231. "
  232. />
  233. </div>
  234. </template>
  235. <script setup lang="ts">
  236. import { ref, computed, watch, defineComponent } from "@nuxtjs/composition-api"
  237. import { applySetting, toggleSetting, useSetting } from "~/newstore/settings"
  238. import {
  239. useToast,
  240. useI18n,
  241. useColorMode,
  242. useReadonlyStream,
  243. } from "~/helpers/utils/composables"
  244. import { browserIsChrome, browserIsFirefox } from "~/helpers/utils/userAgent"
  245. import { extensionStatus$ } from "~/newstore/HoppExtension"
  246. const t = useI18n()
  247. const toast = useToast()
  248. const colorMode = useColorMode()
  249. const ACCENT_COLOR = useSetting("THEME_COLOR")
  250. const PROXY_ENABLED = useSetting("PROXY_ENABLED")
  251. const PROXY_URL = useSetting("PROXY_URL")
  252. const EXTENSIONS_ENABLED = useSetting("EXTENSIONS_ENABLED")
  253. const TELEMETRY_ENABLED = useSetting("TELEMETRY_ENABLED")
  254. const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
  255. const SIDEBAR_ON_LEFT = useSetting("SIDEBAR_ON_LEFT")
  256. const ZEN_MODE = useSetting("ZEN_MODE")
  257. const currentExtensionStatus = useReadonlyStream(extensionStatus$, null)
  258. const extensionVersion = computed(() => {
  259. return currentExtensionStatus.value === "available"
  260. ? window.__POSTWOMAN_EXTENSION_HOOK__?.getVersion() ?? null
  261. : null
  262. })
  263. const hasChromeExtInstalled = computed(
  264. () => browserIsChrome() && currentExtensionStatus.value === "available"
  265. )
  266. const hasFirefoxExtInstalled = computed(
  267. () => browserIsFirefox() && currentExtensionStatus.value === "available"
  268. )
  269. const clearIcon = ref("rotate-ccw")
  270. const confirmRemove = ref(false)
  271. const proxySettings = computed(() => ({
  272. url: PROXY_URL.value,
  273. }))
  274. watch(ZEN_MODE, (mode) => {
  275. applySetting("EXPAND_NAVIGATION", !mode)
  276. })
  277. watch(
  278. proxySettings,
  279. ({ url }) => {
  280. applySetting("PROXY_URL", url)
  281. },
  282. { deep: true }
  283. )
  284. // Extensions and proxy should not be enabled at the same time
  285. const toggleInterceptor = (interceptor: "extension" | "proxy") => {
  286. if (interceptor === "extension") {
  287. EXTENSIONS_ENABLED.value = !EXTENSIONS_ENABLED.value
  288. if (EXTENSIONS_ENABLED.value) {
  289. PROXY_ENABLED.value = false
  290. }
  291. } else {
  292. PROXY_ENABLED.value = !PROXY_ENABLED.value
  293. if (PROXY_ENABLED.value) {
  294. EXTENSIONS_ENABLED.value = false
  295. }
  296. }
  297. }
  298. const showConfirmModal = () => {
  299. if (TELEMETRY_ENABLED.value) confirmRemove.value = true
  300. else toggleSetting("TELEMETRY_ENABLED")
  301. }
  302. const resetProxy = () => {
  303. applySetting("PROXY_URL", `https://proxy.hoppscotch.io/`)
  304. clearIcon.value = "check"
  305. toast.success(`${t("state.cleared")}`)
  306. setTimeout(() => (clearIcon.value = "rotate-ccw"), 1000)
  307. }
  308. const getColorModeName = (colorMode: string) => {
  309. switch (colorMode) {
  310. case "system":
  311. return "settings.system_mode"
  312. case "light":
  313. return "settings.light_mode"
  314. case "dark":
  315. return "settings.dark_mode"
  316. case "black":
  317. return "settings.black_mode"
  318. default:
  319. return "settings.system_mode"
  320. }
  321. }
  322. </script>
  323. <script lang="ts">
  324. export default defineComponent({
  325. head() {
  326. return {
  327. title: `${this.$t("navigation.settings")} • Hoppscotch`,
  328. }
  329. },
  330. })
  331. </script>