settings.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <template>
  2. <div>
  3. <div class="divide-y divide-dividerLight space-y-8">
  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.account") }}
  8. </h3>
  9. <p class="mt-1 text-secondaryLight">
  10. {{ $t("settings.account_description") }}
  11. </p>
  12. </div>
  13. <div class="p-8 md:col-span-2">
  14. <div v-if="currentUser === null">
  15. <ButtonPrimary
  16. :label="$t('auth.login')"
  17. @click.native="showLogin = true"
  18. />
  19. </div>
  20. <div v-else class="space-y-8">
  21. <section>
  22. <h4 class="font-semibold text-secondaryDark">
  23. {{ $t("settings.user") }}
  24. </h4>
  25. <div class="space-y-4 py-4">
  26. <div class="flex items-start">
  27. <div class="flex items-center">
  28. <img
  29. v-if="currentUser.photoURL"
  30. :src="currentUser.photoURL"
  31. class="rounded-full h-5 w-5"
  32. />
  33. <SmartIcon v-else name="user" class="svg-icons" />
  34. </div>
  35. <div class="ml-4">
  36. <label>
  37. {{ currentUser.displayName || $t("state.nothing_found") }}
  38. </label>
  39. <p class="mt-1 text-secondaryLight">
  40. {{ $t("settings.account_name_description") }}
  41. </p>
  42. </div>
  43. </div>
  44. <div class="flex items-start">
  45. <div class="flex items-center">
  46. <SmartIcon name="at-sign" class="svg-icons" />
  47. </div>
  48. <div class="ml-4">
  49. <label>
  50. {{ currentUser.email || $t("state.nothing_found") }}
  51. </label>
  52. <p class="mt-1 text-secondaryLight">
  53. {{ $t("settings.account_email_description") }}
  54. </p>
  55. </div>
  56. </div>
  57. </div>
  58. </section>
  59. <Teams v-if="currentBackendUser && currentBackendUser.eaInvited" />
  60. <section>
  61. <h4 class="font-semibold text-secondaryDark">
  62. {{ $t("settings.sync") }}
  63. </h4>
  64. <div class="mt-1 text-secondaryLight">
  65. {{ $t("settings.sync_description") }}
  66. </div>
  67. <div class="space-y-4 py-4">
  68. <div class="flex items-center">
  69. <SmartToggle
  70. :on="SYNC_COLLECTIONS"
  71. @change="
  72. toggleSettings('syncCollections', !SYNC_COLLECTIONS)
  73. "
  74. >
  75. {{ $t("settings.sync_collections") }}
  76. </SmartToggle>
  77. </div>
  78. <div class="flex items-center">
  79. <SmartToggle
  80. :on="SYNC_ENVIRONMENTS"
  81. @change="
  82. toggleSettings('syncEnvironments', !SYNC_ENVIRONMENTS)
  83. "
  84. >
  85. {{ $t("settings.sync_environments") }}
  86. </SmartToggle>
  87. </div>
  88. <div class="flex items-center">
  89. <SmartToggle
  90. :on="SYNC_HISTORY"
  91. @change="toggleSettings('syncHistory', !SYNC_HISTORY)"
  92. >
  93. {{ $t("settings.sync_history") }}
  94. </SmartToggle>
  95. </div>
  96. </div>
  97. </section>
  98. </div>
  99. </div>
  100. </div>
  101. <div class="md:grid md:gap-4 md:grid-cols-3">
  102. <div class="p-8 md:col-span-1">
  103. <h3 class="heading">
  104. {{ $t("settings.theme") }}
  105. </h3>
  106. <p class="mt-1 text-secondaryLight">
  107. {{ $t("settings.theme_description") }}
  108. </p>
  109. </div>
  110. <div class="space-y-8 p-8 md:col-span-2">
  111. <section>
  112. <h4 class="font-semibold text-secondaryDark">
  113. {{ $t("settings.background") }}
  114. </h4>
  115. <div class="mt-1 text-secondaryLight">
  116. <ColorScheme placeholder="..." tag="span">
  117. {{ $t(getColorModeName($colorMode.preference)) }}
  118. <span v-if="$colorMode.preference === 'system'">
  119. ({{ $t(getColorModeName($colorMode.value)) }})
  120. </span>
  121. </ColorScheme>
  122. </div>
  123. <div class="mt-4">
  124. <SmartColorModePicker />
  125. </div>
  126. </section>
  127. <section>
  128. <h4 class="font-semibold text-secondaryDark">
  129. {{ $t("settings.accent_color") }}
  130. </h4>
  131. <div class="mt-1 text-secondaryLight">
  132. {{ active.charAt(0).toUpperCase() + active.slice(1) }}
  133. </div>
  134. <div class="mt-4">
  135. <SmartAccentModePicker />
  136. </div>
  137. </section>
  138. <section>
  139. <h4 class="font-semibold text-secondaryDark">
  140. {{ $t("settings.font_size") }}
  141. </h4>
  142. <div class="mt-4">
  143. <SmartFontSizePicker />
  144. </div>
  145. </section>
  146. <section>
  147. <h4 class="font-semibold text-secondaryDark">
  148. {{ $t("settings.language") }}
  149. </h4>
  150. <div class="mt-4">
  151. <SmartChangeLanguage />
  152. </div>
  153. </section>
  154. <section>
  155. <h4 class="font-semibold text-secondaryDark">
  156. {{ $t("settings.experiments") }}
  157. </h4>
  158. <div class="mt-1 text-secondaryLight">
  159. {{ $t("settings.experiments_notice") }}
  160. <SmartLink
  161. class="link"
  162. to="https://github.com/hoppscotch/hoppscotch/issues/new/choose"
  163. blank
  164. >
  165. {{ $t("app.contact_us") }} </SmartLink
  166. >.
  167. </div>
  168. <div class="space-y-4 py-4">
  169. <div class="flex items-center">
  170. <SmartToggle
  171. :on="EXPERIMENTAL_URL_BAR_ENABLED"
  172. @change="toggleSetting('EXPERIMENTAL_URL_BAR_ENABLED')"
  173. >
  174. {{ $t("settings.use_experimental_url_bar") }}
  175. </SmartToggle>
  176. </div>
  177. <div class="flex items-center">
  178. <SmartToggle :on="TELEMETRY_ENABLED" @change="showConfirmModal">
  179. {{ $t("settings.telemetry") }}
  180. {{
  181. TELEMETRY_ENABLED
  182. ? $t("state.enabled")
  183. : $t("state.disabled")
  184. }}
  185. </SmartToggle>
  186. </div>
  187. <!-- <div class="flex items-center">
  188. <SmartToggle
  189. :on="LEFT_SIDEBAR"
  190. @change="toggleSetting('LEFT_SIDEBAR')"
  191. >
  192. {{ $t("settings.navigation_sidebar") }}
  193. {{
  194. LEFT_SIDEBAR ? $t("state.enabled") : $t("state.disabled")
  195. }}
  196. </SmartToggle>
  197. </div> -->
  198. <div class="flex items-center">
  199. <SmartToggle :on="ZEN_MODE" @change="toggleSetting('ZEN_MODE')">
  200. {{ $t("layout.zen_mode") }}
  201. {{ ZEN_MODE ? $t("state.enabled") : $t("state.disabled") }}
  202. </SmartToggle>
  203. </div>
  204. </div>
  205. </section>
  206. </div>
  207. </div>
  208. <div class="md:grid md:gap-4 md:grid-cols-3">
  209. <div class="p-8 md:col-span-1">
  210. <h3 class="heading">
  211. {{ $t("settings.interceptor") }}
  212. </h3>
  213. <p class="mt-1 text-secondaryLight">
  214. {{ $t("settings.interceptor_description") }}
  215. </p>
  216. </div>
  217. <div class="space-y-8 p-8 md:col-span-2">
  218. <section>
  219. <h4 class="font-semibold text-secondaryDark">
  220. {{ $t("settings.extensions") }}
  221. </h4>
  222. <div class="mt-1 text-secondaryLight">
  223. <span v-if="extensionVersion != null">
  224. {{
  225. `${$t("settings.extension_version")}: v${
  226. extensionVersion.major
  227. }.${extensionVersion.minor}`
  228. }}
  229. </span>
  230. <span v-else>
  231. {{ $t("settings.extension_version") }}:
  232. {{ $t("settings.extension_ver_not_reported") }}
  233. </span>
  234. </div>
  235. <div class="flex flex-col space-y-2 py-4">
  236. <span>
  237. <SmartItem
  238. to="https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
  239. blank
  240. svg="brands/firefox"
  241. label="Firefox"
  242. :info-icon="hasFirefoxExtInstalled ? 'check_circle' : ''"
  243. :active-info-icon="hasFirefoxExtInstalled"
  244. outline
  245. />
  246. </span>
  247. <span>
  248. <SmartItem
  249. to="https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
  250. blank
  251. svg="brands/chrome"
  252. label="Chrome"
  253. :info-icon="hasChromeExtInstalled ? 'check_circle' : ''"
  254. :active-info-icon="hasChromeExtInstalled"
  255. outline
  256. />
  257. </span>
  258. </div>
  259. <div class="space-y-4 py-4">
  260. <div class="flex items-center">
  261. <SmartToggle
  262. :on="EXTENSIONS_ENABLED"
  263. @change="toggleSetting('EXTENSIONS_ENABLED')"
  264. >
  265. {{ $t("settings.extensions_use_toggle") }}
  266. </SmartToggle>
  267. </div>
  268. </div>
  269. </section>
  270. <section>
  271. <h4 class="font-semibold text-secondaryDark">
  272. {{ $t("settings.proxy") }}
  273. </h4>
  274. <div class="mt-1 text-secondaryLight">
  275. {{
  276. `${$t("settings.official_proxy_hosting")} ${$t(
  277. "settings.read_the"
  278. )}`
  279. }}
  280. <SmartLink
  281. class="link"
  282. to="https://docs.hoppscotch.io/privacy"
  283. blank
  284. >
  285. {{ $t("app.proxy_privacy_policy") }} </SmartLink
  286. >.
  287. </div>
  288. <div class="space-y-4 py-4">
  289. <div class="flex items-center">
  290. <SmartToggle
  291. :on="PROXY_ENABLED"
  292. @change="toggleSetting('PROXY_ENABLED')"
  293. >
  294. {{ $t("settings.proxy_use_toggle") }}
  295. </SmartToggle>
  296. </div>
  297. </div>
  298. <div class="flex space-x-2 py-4 items-center">
  299. <div class="flex flex-1 items-center relative">
  300. <input
  301. id="url"
  302. v-model="PROXY_URL"
  303. class="input floating-input"
  304. placeholder=" "
  305. type="url"
  306. autocomplete="off"
  307. :disabled="!PROXY_ENABLED"
  308. />
  309. <label for="url">
  310. {{ $t("settings.proxy_url") }}
  311. </label>
  312. </div>
  313. <ButtonSecondary
  314. v-tippy="{ theme: 'tooltip' }"
  315. :title="$t('settings.reset_default')"
  316. :svg="clearIcon"
  317. outline
  318. class="rounded"
  319. @click.native="resetProxy"
  320. />
  321. </div>
  322. </section>
  323. </div>
  324. </div>
  325. </div>
  326. <FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
  327. <SmartConfirmModal
  328. :show="confirmRemove"
  329. :title="`${$t('confirm.remove_telemetry')} ${$t(
  330. 'settings.telemetry_helps_us'
  331. )}`"
  332. @hide-modal="confirmRemove = false"
  333. @resolve="
  334. toggleSetting('TELEMETRY_ENABLED')
  335. confirmRemove = false
  336. "
  337. />
  338. </div>
  339. </template>
  340. <script lang="ts">
  341. import { defineComponent } from "@nuxtjs/composition-api"
  342. import { currentUserInfo$ } from "~/helpers/teams/BackendUserInfo"
  343. import {
  344. hasExtensionInstalled,
  345. hasChromeExtensionInstalled,
  346. hasFirefoxExtensionInstalled,
  347. } from "~/helpers/strategies/ExtensionStrategy"
  348. import {
  349. applySetting,
  350. toggleSetting,
  351. defaultSettings,
  352. useSetting,
  353. } from "~/newstore/settings"
  354. import type { KeysMatching } from "~/types/ts-utils"
  355. import { currentUser$ } from "~/helpers/fb/auth"
  356. import { getLocalConfig } from "~/newstore/localpersistence"
  357. import { useReadonlyStream } from "~/helpers/utils/composables"
  358. type SettingsType = typeof defaultSettings
  359. export default defineComponent({
  360. setup() {
  361. return {
  362. PROXY_ENABLED: useSetting("PROXY_ENABLED"),
  363. PROXY_URL: useSetting("PROXY_URL"),
  364. PROXY_KEY: useSetting("PROXY_KEY"),
  365. EXTENSIONS_ENABLED: useSetting("EXTENSIONS_ENABLED"),
  366. EXPERIMENTAL_URL_BAR_ENABLED: useSetting("EXPERIMENTAL_URL_BAR_ENABLED"),
  367. SYNC_COLLECTIONS: useSetting("syncCollections"),
  368. SYNC_ENVIRONMENTS: useSetting("syncEnvironments"),
  369. SYNC_HISTORY: useSetting("syncHistory"),
  370. TELEMETRY_ENABLED: useSetting("TELEMETRY_ENABLED"),
  371. LEFT_SIDEBAR: useSetting("LEFT_SIDEBAR"),
  372. ZEN_MODE: useSetting("ZEN_MODE"),
  373. currentUser: useReadonlyStream(currentUser$, currentUser$.value),
  374. currentBackendUser: useReadonlyStream(
  375. currentUserInfo$,
  376. currentUserInfo$.value
  377. ),
  378. }
  379. },
  380. data() {
  381. return {
  382. extensionVersion: hasExtensionInstalled()
  383. ? window.__POSTWOMAN_EXTENSION_HOOK__.getVersion()
  384. : null,
  385. hasChromeExtInstalled: hasChromeExtensionInstalled(),
  386. hasFirefoxExtInstalled: hasFirefoxExtensionInstalled(),
  387. clearIcon: "rotate-ccw",
  388. showLogin: false,
  389. active: getLocalConfig("THEME_COLOR") || "blue",
  390. confirmRemove: false,
  391. }
  392. },
  393. head() {
  394. return {
  395. title: `${this.$t("navigation.settings")} • Hoppscotch`,
  396. }
  397. },
  398. computed: {
  399. proxySettings(): { url: string; key: string } {
  400. return {
  401. url: this.PROXY_URL,
  402. key: this.PROXY_KEY,
  403. }
  404. },
  405. },
  406. watch: {
  407. ZEN_MODE(ZEN_MODE) {
  408. this.applySetting("LEFT_SIDEBAR", !ZEN_MODE)
  409. // this.applySetting("RIGHT_SIDEBAR", !ZEN_MODE)
  410. },
  411. proxySettings: {
  412. deep: true,
  413. handler({ url, key }) {
  414. this.applySetting("PROXY_URL", url)
  415. this.applySetting("PROXY_KEY", key)
  416. },
  417. },
  418. },
  419. methods: {
  420. showConfirmModal() {
  421. if (this.TELEMETRY_ENABLED) this.confirmRemove = true
  422. else toggleSetting("TELEMETRY_ENABLED")
  423. },
  424. applySetting<K extends keyof SettingsType>(key: K, value: SettingsType[K]) {
  425. applySetting(key, value)
  426. },
  427. toggleSetting<K extends KeysMatching<SettingsType, boolean>>(key: K) {
  428. if (key === "EXTENSIONS_ENABLED" && this.PROXY_ENABLED) {
  429. toggleSetting("PROXY_ENABLED")
  430. }
  431. if (key === "PROXY_ENABLED" && this.EXTENSIONS_ENABLED) {
  432. toggleSetting("EXTENSIONS_ENABLED")
  433. }
  434. toggleSetting(key)
  435. },
  436. toggleSettings<K extends KeysMatching<SettingsType, boolean>>(
  437. name: K,
  438. value: SettingsType[K]
  439. ) {
  440. this.applySetting(name, value)
  441. },
  442. resetProxy() {
  443. applySetting("PROXY_URL", `https://proxy.hoppscotch.io/`)
  444. this.clearIcon = "check"
  445. this.$toast.success(this.$t("state.cleared").toString(), {
  446. icon: "clear_all",
  447. })
  448. setTimeout(() => (this.clearIcon = "rotate-ccw"), 1000)
  449. },
  450. getColorModeName(colorMode: string) {
  451. switch (colorMode) {
  452. case "system":
  453. return "settings.system_mode"
  454. case "light":
  455. return "settings.light_mode"
  456. case "dark":
  457. return "settings.dark_mode"
  458. case "black":
  459. return "settings.black_mode"
  460. default:
  461. return "settings.system_mode"
  462. }
  463. },
  464. },
  465. })
  466. </script>