settings.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. usePolled,
  243. } from "~/helpers/utils/composables"
  244. import {
  245. hasExtensionInstalled,
  246. hasChromeExtensionInstalled,
  247. hasFirefoxExtensionInstalled,
  248. } from "~/helpers/strategies/ExtensionStrategy"
  249. import { browserIsChrome, browserIsFirefox } from "~/helpers/utils/userAgent"
  250. const t = useI18n()
  251. const toast = useToast()
  252. const colorMode = useColorMode()
  253. const ACCENT_COLOR = useSetting("THEME_COLOR")
  254. const PROXY_ENABLED = useSetting("PROXY_ENABLED")
  255. const PROXY_URL = useSetting("PROXY_URL")
  256. const EXTENSIONS_ENABLED = useSetting("EXTENSIONS_ENABLED")
  257. const TELEMETRY_ENABLED = useSetting("TELEMETRY_ENABLED")
  258. const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
  259. const SIDEBAR_ON_LEFT = useSetting("SIDEBAR_ON_LEFT")
  260. const ZEN_MODE = useSetting("ZEN_MODE")
  261. const extensionVersion = usePolled(5000, (stopPolling) => {
  262. const result = hasExtensionInstalled()
  263. ? window.__POSTWOMAN_EXTENSION_HOOK__.getVersion()
  264. : null
  265. // We don't need to poll anymore after we get value
  266. if (result) stopPolling()
  267. return result
  268. })
  269. const hasChromeExtInstalled = usePolled(5000, (stopPolling) => {
  270. // If not Chrome, we don't need to worry about this value changing
  271. if (!browserIsChrome()) stopPolling()
  272. return hasChromeExtensionInstalled()
  273. })
  274. const hasFirefoxExtInstalled = usePolled(5000, (stopPolling) => {
  275. // If not Chrome, we don't need to worry about this value changing
  276. if (!browserIsFirefox()) stopPolling()
  277. return hasFirefoxExtensionInstalled()
  278. })
  279. const clearIcon = ref("rotate-ccw")
  280. const confirmRemove = ref(false)
  281. const proxySettings = computed(() => ({
  282. url: PROXY_URL.value,
  283. }))
  284. watch(ZEN_MODE, (mode) => {
  285. applySetting("EXPAND_NAVIGATION", !mode)
  286. })
  287. watch(
  288. proxySettings,
  289. ({ url }) => {
  290. applySetting("PROXY_URL", url)
  291. },
  292. { deep: true }
  293. )
  294. // Extensions and proxy should not be enabled at the same time
  295. const toggleInterceptor = (interceptor: "extension" | "proxy") => {
  296. if (interceptor === "extension") {
  297. EXTENSIONS_ENABLED.value = !EXTENSIONS_ENABLED.value
  298. if (EXTENSIONS_ENABLED.value) {
  299. PROXY_ENABLED.value = false
  300. }
  301. } else {
  302. PROXY_ENABLED.value = !PROXY_ENABLED.value
  303. if (PROXY_ENABLED.value) {
  304. EXTENSIONS_ENABLED.value = false
  305. }
  306. }
  307. }
  308. const showConfirmModal = () => {
  309. if (TELEMETRY_ENABLED.value) confirmRemove.value = true
  310. else toggleSetting("TELEMETRY_ENABLED")
  311. }
  312. const resetProxy = () => {
  313. applySetting("PROXY_URL", `https://proxy.hoppscotch.io/`)
  314. clearIcon.value = "check"
  315. toast.success(`${t("state.cleared")}`)
  316. setTimeout(() => (clearIcon.value = "rotate-ccw"), 1000)
  317. }
  318. const getColorModeName = (colorMode: string) => {
  319. switch (colorMode) {
  320. case "system":
  321. return "settings.system_mode"
  322. case "light":
  323. return "settings.light_mode"
  324. case "dark":
  325. return "settings.dark_mode"
  326. case "black":
  327. return "settings.black_mode"
  328. default:
  329. return "settings.system_mode"
  330. }
  331. }
  332. </script>
  333. <script lang="ts">
  334. export default defineComponent({
  335. head() {
  336. return {
  337. title: `${this.$t("navigation.settings")} • Hoppscotch`,
  338. }
  339. },
  340. })
  341. </script>