refactor: monorepo+pnpm (removed husky)
This commit is contained in:
6
packages/hoppscotch-app/pages/README.md
Normal file
6
packages/hoppscotch-app/pages/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# PAGES
|
||||
|
||||
This directory contains your Application Views and Routes.
|
||||
The framework reads all the `*.vue` files inside this directory and creates the router of your application.
|
||||
|
||||
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
|
||||
359
packages/hoppscotch-app/pages/documentation.vue
Normal file
359
packages/hoppscotch-app/pages/documentation.vue
Normal file
@@ -0,0 +1,359 @@
|
||||
<template>
|
||||
<Splitpanes
|
||||
class="smart-splitter"
|
||||
:dbl-click-splitter="false"
|
||||
:horizontal="!(windowInnerWidth.x.value >= 768)"
|
||||
>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<Splitpanes class="smart-splitter" :dbl-click-splitter="false" horizontal>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="import">
|
||||
<div class="flex p-4 items-start justify-between">
|
||||
<label>
|
||||
{{ $t("documentation.generate_message") }}
|
||||
</label>
|
||||
<span
|
||||
class="
|
||||
bg-accentDark
|
||||
rounded
|
||||
text-accentContrast
|
||||
py-1
|
||||
px-2
|
||||
inline-flex
|
||||
"
|
||||
>
|
||||
BETA
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
top-0
|
||||
z-10
|
||||
items-start
|
||||
justify-between
|
||||
sticky
|
||||
"
|
||||
>
|
||||
<label for="collectionUpload">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
title="JSON"
|
||||
svg="folder"
|
||||
class="!rounded-none"
|
||||
:label="$t('import.collections')"
|
||||
@click.native="$refs.collectionUpload.click()"
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
ref="collectionUpload"
|
||||
class="input"
|
||||
name="collectionUpload"
|
||||
type="file"
|
||||
@change="uploadCollection"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.clear')"
|
||||
svg="trash-2"
|
||||
@click.native="collectionJSON = '[]'"
|
||||
/>
|
||||
</div>
|
||||
<SmartAceEditor
|
||||
v-model="collectionJSON"
|
||||
:lang="'json'"
|
||||
:lint="false"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-t border-b border-dividerLight
|
||||
flex
|
||||
p-4
|
||||
bottom-0
|
||||
z-10
|
||||
justify-between
|
||||
items-start
|
||||
sticky
|
||||
"
|
||||
>
|
||||
<ButtonPrimary
|
||||
:label="$t('documentation.generate')"
|
||||
@click.native="getDoc"
|
||||
/>
|
||||
</div>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<AppSection label="documentation">
|
||||
<div class="flex flex-col">
|
||||
<div
|
||||
v-if="items.length === 0"
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">topic</i>
|
||||
<span class="text-center">
|
||||
{{ $t("helpers.generate_documentation_first") }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
p-4
|
||||
top-0
|
||||
z-10
|
||||
sticky
|
||||
"
|
||||
>
|
||||
<span
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
!currentUser
|
||||
? $t('export.require_github')
|
||||
: currentUser.provider !== 'github.com'
|
||||
? $t('export.require_github')
|
||||
: 'Beta'
|
||||
"
|
||||
>
|
||||
<ButtonPrimary
|
||||
:disabled="
|
||||
!currentUser
|
||||
? true
|
||||
: currentUser.provider !== 'github.com'
|
||||
? true
|
||||
: false
|
||||
"
|
||||
:label="$t('export.create_secret_gist')"
|
||||
@click.native="createDocsGist"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(collection, index) in items"
|
||||
:key="`collection-${index}`"
|
||||
>
|
||||
<DocsCollection :collection="collection" />
|
||||
</div>
|
||||
</div>
|
||||
</AppSection>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</Pane>
|
||||
<Pane
|
||||
v-if="RIGHT_SIDEBAR"
|
||||
max-size="35"
|
||||
size="25"
|
||||
min-size="20"
|
||||
class="hide-scrollbar !overflow-auto"
|
||||
>
|
||||
<aside>
|
||||
<Collections
|
||||
:selected="selected"
|
||||
:doc="true"
|
||||
@use-collection="useSelectedCollection($event)"
|
||||
@remove-collection="removeSelectedCollection($event)"
|
||||
/>
|
||||
</aside>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { Splitpanes, Pane } from "splitpanes"
|
||||
import "splitpanes/dist/splitpanes.css"
|
||||
import Mustache from "mustache"
|
||||
import { currentUser$ } from "~/helpers/fb/auth"
|
||||
import DocsTemplate from "~/assets/md/docs.md"
|
||||
import folderContents from "~/assets/md/folderContents.md"
|
||||
import folderBody from "~/assets/md/folderBody.md"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import useWindowSize from "~/helpers/utils/useWindowSize"
|
||||
|
||||
export default defineComponent({
|
||||
components: { Splitpanes, Pane },
|
||||
setup() {
|
||||
return {
|
||||
windowInnerWidth: useWindowSize(),
|
||||
RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
|
||||
currentUser: useReadonlyStream(currentUser$, null),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
collectionJSON: "[]",
|
||||
items: [],
|
||||
docsMarkdown: "",
|
||||
selected: [],
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `${this.$t("navigation.doc")} • Hoppscotch`,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async createDocsGist() {
|
||||
await this.$axios
|
||||
.$post(
|
||||
"https://api.github.com/gists",
|
||||
{
|
||||
files: {
|
||||
"api-docs.md": {
|
||||
content: this.docsMarkdown,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `token ${this.currentUser.accessToken}`,
|
||||
Accept: "application/vnd.github.v3+json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((res) => {
|
||||
this.$toast.success(this.$t("export.gist_created"), {
|
||||
icon: "done",
|
||||
})
|
||||
window.open(res.html_url)
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$toast.error(this.$t("error.something_went_wrong"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
})
|
||||
},
|
||||
|
||||
uploadCollection() {
|
||||
const file = this.$refs.collectionUpload.files[0]
|
||||
if (file !== undefined && file !== null) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
this.collectionJSON = target.result
|
||||
}
|
||||
reader.readAsText(file)
|
||||
this.$toast.success(this.$t("state.file_imported"), {
|
||||
icon: "attach_file",
|
||||
})
|
||||
} else {
|
||||
this.$toast.error(this.$t("action.choose_file"), {
|
||||
icon: "attach_file",
|
||||
})
|
||||
}
|
||||
this.$refs.collectionUpload.value = ""
|
||||
},
|
||||
|
||||
assignIDs(items, pref, nestingLevel) {
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
items[i].id = ` ${pref}${i + 1}.`
|
||||
items[i].ref = `${items[i].name.split(" ").join("-")}`
|
||||
items[i].nestingLevel = nestingLevel
|
||||
items[i].folders = this.assignIDs(
|
||||
items[i].folders,
|
||||
items[i].id,
|
||||
nestingLevel + "#"
|
||||
)
|
||||
for (let j = 0; j < items[i].requests.length; ++j) {
|
||||
items[i].requests[j].id = ` ${items[i].id}${i + 1}`
|
||||
items[i].requests[j].ref = `${items[i].requests[j].name
|
||||
.split(" ")
|
||||
.join("-")}`
|
||||
items[i].requests[j].nestingLevel = nestingLevel + "#"
|
||||
}
|
||||
}
|
||||
return items
|
||||
},
|
||||
|
||||
getDoc() {
|
||||
try {
|
||||
this.items = JSON.parse(this.collectionJSON)
|
||||
this.assignIDs(this.items, "", "#")
|
||||
this.$toast.clear()
|
||||
this.$toast.success(this.$t("state.docs_generated"), {
|
||||
icon: "book",
|
||||
})
|
||||
const docsMarkdown = Mustache.render(
|
||||
DocsTemplate,
|
||||
{
|
||||
collections: this.items,
|
||||
isHeaders() {
|
||||
return this.headers.length
|
||||
},
|
||||
isParams() {
|
||||
return this.params.length
|
||||
},
|
||||
isAuth() {
|
||||
return this.auth !== "None"
|
||||
},
|
||||
isAuthBasic() {
|
||||
return this.httpUser && this.httpPassword
|
||||
},
|
||||
isRawParams() {
|
||||
return this.rawParams && this.rawParams !== ""
|
||||
},
|
||||
isPreRequestScript() {
|
||||
return this.preRequestScript && this.preRequestScript !== ""
|
||||
},
|
||||
isTestScript() {
|
||||
return this.testScript && this.testScript !== ""
|
||||
},
|
||||
},
|
||||
{
|
||||
folderContents,
|
||||
folderBody,
|
||||
}
|
||||
)
|
||||
this.docsMarkdown = docsMarkdown.replace(/^\s*[\r\n]/gm, "\n\n")
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.$toast.error(this.$t("error.something_went_wrong"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
useSelectedCollection(collection) {
|
||||
if (this.selected.find((coll) => coll === collection)) {
|
||||
return
|
||||
}
|
||||
this.selected.push(collection)
|
||||
const importCollection = JSON.stringify(this.selected, null, 2)
|
||||
this.collectionJSON = JSON.stringify(
|
||||
JSON.parse(importCollection),
|
||||
null,
|
||||
2
|
||||
)
|
||||
},
|
||||
|
||||
removeSelectedCollection(collection) {
|
||||
this.selected = this.selected.filter((coll) => coll !== collection)
|
||||
const importCollection = JSON.stringify(this.selected, null, 2)
|
||||
this.collectionJSON = JSON.stringify(
|
||||
JSON.parse(importCollection),
|
||||
null,
|
||||
2
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
53
packages/hoppscotch-app/pages/enter.vue
Normal file
53
packages/hoppscotch-app/pages/enter.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="flex flex-col min-h-screen items-center justify-center">
|
||||
<SmartSpinner v-if="signingInWithEmail" />
|
||||
<SmartLoadingIndicator v-else />
|
||||
<pre v-if="error">{{ error }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { initializeFirebase } from "~/helpers/fb"
|
||||
import { isSignInWithEmailLink, signInWithEmailLink } from "~/helpers/fb/auth"
|
||||
import { getLocalConfig, removeLocalConfig } from "~/newstore/localpersistence"
|
||||
|
||||
export default defineComponent({
|
||||
layout: "empty",
|
||||
data() {
|
||||
return {
|
||||
signingInWithEmail: false,
|
||||
error: null,
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
initializeFirebase()
|
||||
},
|
||||
async mounted() {
|
||||
if (isSignInWithEmailLink(window.location.href)) {
|
||||
this.signingInWithEmail = true
|
||||
|
||||
let email = getLocalConfig("emailForSignIn")
|
||||
|
||||
if (!email) {
|
||||
email = window.prompt(
|
||||
"Please provide your email for confirmation"
|
||||
) as string
|
||||
}
|
||||
|
||||
await signInWithEmailLink(email, window.location.href)
|
||||
.then(() => {
|
||||
removeLocalConfig("emailForSignIn")
|
||||
this.$router.push({ path: "/" })
|
||||
})
|
||||
.catch((e) => {
|
||||
this.signingInWithEmail = false
|
||||
this.error = e.message
|
||||
})
|
||||
.finally(() => {
|
||||
this.signingInWithEmail = false
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
77
packages/hoppscotch-app/pages/graphql.vue
Normal file
77
packages/hoppscotch-app/pages/graphql.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div>
|
||||
<Splitpanes
|
||||
class="smart-splitter"
|
||||
:dbl-click-splitter="false"
|
||||
:horizontal="!(windowInnerWidth.x.value >= 768)"
|
||||
>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<Splitpanes
|
||||
class="smart-splitter"
|
||||
:dbl-click-splitter="false"
|
||||
horizontal
|
||||
>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<GraphqlRequest :conn="gqlConn" />
|
||||
<GraphqlRequestOptions :conn="gqlConn" />
|
||||
</Pane>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<GraphqlResponse :conn="gqlConn" />
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</Pane>
|
||||
<Pane
|
||||
v-if="RIGHT_SIDEBAR"
|
||||
max-size="35"
|
||||
size="25"
|
||||
min-size="20"
|
||||
class="hide-scrollbar !overflow-auto"
|
||||
>
|
||||
<GraphqlSidebar :conn="gqlConn" />
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, watch } from "@nuxtjs/composition-api"
|
||||
import { Splitpanes, Pane } from "splitpanes"
|
||||
import "splitpanes/dist/splitpanes.css"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
import { GQLConnection } from "~/helpers/GQLConnection"
|
||||
import { useNuxt, useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import useWindowSize from "~/helpers/utils/useWindowSize"
|
||||
|
||||
export default defineComponent({
|
||||
components: { Splitpanes, Pane },
|
||||
beforeRouteLeave(_to, _from, next) {
|
||||
if (this.gqlConn.connected$.value) {
|
||||
this.gqlConn.disconnect()
|
||||
}
|
||||
next()
|
||||
},
|
||||
setup() {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
const gqlConn = new GQLConnection()
|
||||
|
||||
const isLoading = useReadonlyStream(gqlConn.isLoading$, false)
|
||||
|
||||
watch(isLoading, () => {
|
||||
if (isLoading) nuxt.value.$loading.start()
|
||||
else nuxt.value.$loading.finish()
|
||||
})
|
||||
|
||||
return {
|
||||
windowInnerWidth: useWindowSize(),
|
||||
RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
|
||||
gqlConn,
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `${this.$t("navigation.graphql")} • Hoppscotch`,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
9
packages/hoppscotch-app/pages/home.vue
Normal file
9
packages/hoppscotch-app/pages/home.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div class="flex flex-col space-y-16">
|
||||
<LandingHero />
|
||||
<LandingUsers />
|
||||
<LandingFeatures />
|
||||
<LandingCTA />
|
||||
<LandingFooter />
|
||||
</div>
|
||||
</template>
|
||||
275
packages/hoppscotch-app/pages/index.vue
Normal file
275
packages/hoppscotch-app/pages/index.vue
Normal file
@@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<Splitpanes
|
||||
class="smart-splitter"
|
||||
:dbl-click-splitter="false"
|
||||
:horizontal="!(windowInnerWidth.x.value >= 768)"
|
||||
>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<Splitpanes class="smart-splitter" :dbl-click-splitter="false" horizontal>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<HttpRequest />
|
||||
<SmartTabs styles="sticky bg-primary top-upperPrimaryStickyFold z-10">
|
||||
<SmartTab
|
||||
:id="'params'"
|
||||
:label="$t('tab.parameters')"
|
||||
:selected="true"
|
||||
:info="newActiveParamsCount$"
|
||||
>
|
||||
<HttpParameters />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'bodyParams'" :label="$t('tab.body')">
|
||||
<HttpBody />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab
|
||||
:id="'headers'"
|
||||
:label="$t('tab.headers')"
|
||||
:info="newActiveHeadersCount$"
|
||||
>
|
||||
<HttpHeaders />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'authorization'" :label="$t('tab.authorization')">
|
||||
<HttpAuthorization />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab
|
||||
:id="'preRequestScript'"
|
||||
:label="$t('tab.pre_request_script')"
|
||||
>
|
||||
<HttpPreRequestScript />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'tests'" :label="$t('tab.tests')">
|
||||
<HttpTests />
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
</Pane>
|
||||
<Pane class="hide-scrollbar !overflow-auto">
|
||||
<HttpResponse ref="response" />
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</Pane>
|
||||
<Pane
|
||||
v-if="RIGHT_SIDEBAR"
|
||||
max-size="35"
|
||||
size="25"
|
||||
min-size="20"
|
||||
class="hide-scrollbar !overflow-auto"
|
||||
>
|
||||
<aside>
|
||||
<SmartTabs styles="sticky bg-primary z-10 top-0">
|
||||
<SmartTab :id="'history'" :label="$t('tab.history')" :selected="true">
|
||||
<History ref="historyComponent" :page="'rest'" />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'collections'" :label="$t('tab.collections')">
|
||||
<Collections />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'env'" :label="$t('environment.title')">
|
||||
<Environments />
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
</aside>
|
||||
</Pane>
|
||||
<SmartConfirmModal
|
||||
:show="confirmSync"
|
||||
:title="$t('confirm.sync')"
|
||||
@hide-modal="confirmSync = false"
|
||||
@resolve="syncRequest"
|
||||
/>
|
||||
</Splitpanes>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
Ref,
|
||||
ref,
|
||||
useContext,
|
||||
watch,
|
||||
} from "@nuxtjs/composition-api"
|
||||
import { Splitpanes, Pane } from "splitpanes"
|
||||
import "splitpanes/dist/splitpanes.css"
|
||||
import { map } from "rxjs/operators"
|
||||
import { Subscription } from "rxjs"
|
||||
import isEqual from "lodash/isEqual"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
import {
|
||||
restRequest$,
|
||||
restActiveParamsCount$,
|
||||
restActiveHeadersCount$,
|
||||
getRESTRequest,
|
||||
setRESTRequest,
|
||||
setRESTAuth,
|
||||
restAuth$,
|
||||
} from "~/newstore/RESTSession"
|
||||
import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
|
||||
import {
|
||||
pluckRef,
|
||||
useReadonlyStream,
|
||||
useStream,
|
||||
} from "~/helpers/utils/composables"
|
||||
import { loadRequestFromSync, startRequestSync } from "~/helpers/fb/request"
|
||||
import { onLoggedIn } from "~/helpers/fb/auth"
|
||||
import { HoppRESTRequest } from "~/helpers/types/HoppRESTRequest"
|
||||
import { oauthRedirect } from "~/helpers/oauth"
|
||||
import { HoppRESTAuthOAuth2 } from "~/helpers/types/HoppRESTAuth"
|
||||
import useWindowSize from "~/helpers/utils/useWindowSize"
|
||||
|
||||
function bindRequestToURLParams() {
|
||||
const {
|
||||
route,
|
||||
app: { router },
|
||||
} = useContext()
|
||||
|
||||
const request = useStream(restRequest$, getRESTRequest(), setRESTRequest)
|
||||
|
||||
// Process headers and params to proper values
|
||||
const headers = computed(() => {
|
||||
const filtered = request.value.headers.filter((x) => x.key !== "")
|
||||
|
||||
return filtered.length > 0 ? JSON.stringify(filtered) : null
|
||||
})
|
||||
|
||||
const params = computed(() => {
|
||||
const filtered = request.value.params.filter((x) => x.key !== "")
|
||||
return filtered.length > 0 ? JSON.stringify(filtered) : null
|
||||
})
|
||||
|
||||
// Combine them together to a cleaner value
|
||||
const urlParams = computed(() => ({
|
||||
v: request.value.v,
|
||||
method: request.value.method,
|
||||
endpoint: request.value.endpoint,
|
||||
headers: headers.value,
|
||||
params: params.value,
|
||||
}))
|
||||
|
||||
// Watch and update accordingly
|
||||
watch(urlParams, () => {
|
||||
history.replaceState(
|
||||
window.location.href,
|
||||
"",
|
||||
`${router!.options.base}?${encodeURI(
|
||||
Object.entries(urlParams.value)
|
||||
.filter((x) => x[1] !== null)
|
||||
.map((x) => `${x[0]}=${x[1]!}`)
|
||||
.join("&")
|
||||
)}`
|
||||
)
|
||||
})
|
||||
|
||||
// Now, we have to see the initial URL param and set that as the request
|
||||
onMounted(() => {
|
||||
const query = route.value.query
|
||||
|
||||
// If query params are empty, or contains code or error param (these are from Oauth Redirect)
|
||||
// We skip URL params parsing
|
||||
if (Object.keys(query).length === 0 || query.code || query.error) return
|
||||
setRESTRequest(translateExtURLParams(query))
|
||||
})
|
||||
}
|
||||
|
||||
function oAuthURL() {
|
||||
const auth = useStream(
|
||||
restAuth$,
|
||||
{ authType: "none", authActive: true },
|
||||
setRESTAuth
|
||||
)
|
||||
|
||||
const oauth2Token = pluckRef(auth as Ref<HoppRESTAuthOAuth2>, "token")
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const tokenInfo = await oauthRedirect()
|
||||
if (Object.prototype.hasOwnProperty.call(tokenInfo, "access_token")) {
|
||||
if (typeof tokenInfo === "object") {
|
||||
oauth2Token.value = tokenInfo.access_token
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setupRequestSync(
|
||||
confirmSync: Ref<boolean>,
|
||||
requestForSync: Ref<HoppRESTRequest | null>
|
||||
) {
|
||||
const { route } = useContext()
|
||||
|
||||
// Subscription to request sync
|
||||
let sub: Subscription | null = null
|
||||
|
||||
// Load request on login resolve and start sync
|
||||
onLoggedIn(async () => {
|
||||
if (
|
||||
Object.keys(route.value.query).length === 0 &&
|
||||
!(route.value.query.code || route.value.query.error)
|
||||
) {
|
||||
const request = await loadRequestFromSync()
|
||||
if (request) {
|
||||
// setRESTRequest(request)
|
||||
if (!isEqual(request, getRESTRequest())) {
|
||||
requestForSync.value = request
|
||||
confirmSync.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub = startRequestSync()
|
||||
})
|
||||
|
||||
// Stop subscripton to stop syncing
|
||||
onBeforeUnmount(() => {
|
||||
sub?.unsubscribe()
|
||||
})
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { Splitpanes, Pane },
|
||||
setup() {
|
||||
const requestForSync = ref<HoppRESTRequest | null>(null)
|
||||
|
||||
const confirmSync = ref(false)
|
||||
|
||||
const syncRequest = () => {
|
||||
setRESTRequest(requestForSync.value!)
|
||||
}
|
||||
|
||||
setupRequestSync(confirmSync, requestForSync)
|
||||
bindRequestToURLParams()
|
||||
|
||||
return {
|
||||
windowInnerWidth: useWindowSize(),
|
||||
newActiveParamsCount$: useReadonlyStream(
|
||||
restActiveParamsCount$.pipe(
|
||||
map((e) => {
|
||||
if (e === 0) return null
|
||||
return e.toString()
|
||||
})
|
||||
),
|
||||
null
|
||||
),
|
||||
newActiveHeadersCount$: useReadonlyStream(
|
||||
restActiveHeadersCount$.pipe(
|
||||
map((e) => {
|
||||
if (e === 0) return null
|
||||
return e.toString()
|
||||
})
|
||||
),
|
||||
null
|
||||
),
|
||||
RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
|
||||
confirmSync,
|
||||
syncRequest,
|
||||
oAuthURL,
|
||||
requestForSync,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
48
packages/hoppscotch-app/pages/realtime.vue
Normal file
48
packages/hoppscotch-app/pages/realtime.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<SmartTabs
|
||||
class="h-full overflow-hidden"
|
||||
styles="sticky bg-primary top-0 z-10 border-b border-dividerLight !overflow-visible"
|
||||
>
|
||||
<SmartTab
|
||||
id="websocket"
|
||||
:label="$t('tab.websocket')"
|
||||
:selected="true"
|
||||
style="height: calc(100% - var(--sidebar-primary-sticky-fold))"
|
||||
>
|
||||
<RealtimeWebsocket />
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
id="sse"
|
||||
:label="$t('tab.sse')"
|
||||
style="height: calc(100% - var(--sidebar-primary-sticky-fold))"
|
||||
>
|
||||
<RealtimeSse />
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
id="socketio"
|
||||
:label="$t('tab.socketio')"
|
||||
style="height: calc(100% - var(--sidebar-primary-sticky-fold))"
|
||||
>
|
||||
<RealtimeSocketio />
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
id="mqtt"
|
||||
:label="$t('tab.mqtt')"
|
||||
style="height: calc(100% - var(--sidebar-primary-sticky-fold))"
|
||||
>
|
||||
<RealtimeMqtt />
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
head() {
|
||||
return {
|
||||
title: `${this.$t("navigation.realtime")} • Hoppscotch`,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
475
packages/hoppscotch-app/pages/settings.vue
Normal file
475
packages/hoppscotch-app/pages/settings.vue
Normal file
@@ -0,0 +1,475 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="divide-y divide-dividerLight space-y-8">
|
||||
<div class="md:grid md:gap-4 md:grid-cols-3">
|
||||
<div class="p-8 md:col-span-1">
|
||||
<h3 class="heading">
|
||||
{{ $t("settings.account") }}
|
||||
</h3>
|
||||
<p class="mt-1 text-secondaryLight">
|
||||
{{ $t("settings.account_description") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-8 md:col-span-2">
|
||||
<div v-if="currentUser === null">
|
||||
<ButtonPrimary
|
||||
:label="$t('auth.login')"
|
||||
@click.native="showLogin = true"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="space-y-8">
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.user") }}
|
||||
</h4>
|
||||
<div class="space-y-4 py-4">
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
v-if="currentUser.photoURL"
|
||||
:src="currentUser.photoURL"
|
||||
class="rounded-full h-5 w-5"
|
||||
/>
|
||||
<SmartIcon v-else name="user" class="svg-icons" />
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<label>
|
||||
{{ currentUser.displayName || $t("state.nothing_found") }}
|
||||
</label>
|
||||
<p class="mt-1 text-secondaryLight">
|
||||
{{ $t("settings.account_name_description") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start">
|
||||
<div class="flex items-center">
|
||||
<SmartIcon name="at-sign" class="svg-icons" />
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<label>
|
||||
{{ currentUser.email || $t("state.nothing_found") }}
|
||||
</label>
|
||||
<p class="mt-1 text-secondaryLight">
|
||||
{{ $t("settings.account_email_description") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<Teams v-if="currentBackendUser && currentBackendUser.eaInvited" />
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.sync") }}
|
||||
</h4>
|
||||
<div class="mt-1 text-secondaryLight">
|
||||
{{ $t("settings.sync_description") }}
|
||||
</div>
|
||||
<div class="space-y-4 py-4">
|
||||
<div class="flex items-center">
|
||||
<SmartToggle
|
||||
:on="SYNC_COLLECTIONS"
|
||||
@change="
|
||||
toggleSettings('syncCollections', !SYNC_COLLECTIONS)
|
||||
"
|
||||
>
|
||||
{{ $t("settings.sync_collections") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<SmartToggle
|
||||
:on="SYNC_ENVIRONMENTS"
|
||||
@change="
|
||||
toggleSettings('syncEnvironments', !SYNC_ENVIRONMENTS)
|
||||
"
|
||||
>
|
||||
{{ $t("settings.sync_environments") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<SmartToggle
|
||||
:on="SYNC_HISTORY"
|
||||
@change="toggleSettings('syncHistory', !SYNC_HISTORY)"
|
||||
>
|
||||
{{ $t("settings.sync_history") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:grid md:gap-4 md:grid-cols-3">
|
||||
<div class="p-8 md:col-span-1">
|
||||
<h3 class="heading">
|
||||
{{ $t("settings.theme") }}
|
||||
</h3>
|
||||
<p class="mt-1 text-secondaryLight">
|
||||
{{ $t("settings.theme_description") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-8 p-8 md:col-span-2">
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.background") }}
|
||||
</h4>
|
||||
<div class="mt-1 text-secondaryLight">
|
||||
<ColorScheme placeholder="..." tag="span">
|
||||
{{ $t(getColorModeName($colorMode.preference)) }}
|
||||
<span v-if="$colorMode.preference === 'system'">
|
||||
({{ $t(getColorModeName($colorMode.value)) }})
|
||||
</span>
|
||||
</ColorScheme>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<SmartColorModePicker />
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.accent_color") }}
|
||||
</h4>
|
||||
<div class="mt-1 text-secondaryLight">
|
||||
{{ active.charAt(0).toUpperCase() + active.slice(1) }}
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<SmartAccentModePicker />
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.font_size") }}
|
||||
</h4>
|
||||
<div class="mt-4">
|
||||
<SmartFontSizePicker />
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.language") }}
|
||||
</h4>
|
||||
<div class="mt-4">
|
||||
<SmartChangeLanguage />
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.experiments") }}
|
||||
</h4>
|
||||
<div class="mt-1 text-secondaryLight">
|
||||
{{ $t("settings.experiments_notice") }}
|
||||
<SmartLink
|
||||
class="link"
|
||||
to="https://github.com/hoppscotch/hoppscotch/issues/new/choose"
|
||||
blank
|
||||
>
|
||||
{{ $t("app.contact_us") }} </SmartLink
|
||||
>.
|
||||
</div>
|
||||
<div class="space-y-4 py-4">
|
||||
<div class="flex items-center">
|
||||
<SmartToggle
|
||||
:on="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
@change="toggleSetting('EXPERIMENTAL_URL_BAR_ENABLED')"
|
||||
>
|
||||
{{ $t("settings.use_experimental_url_bar") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<SmartToggle :on="TELEMETRY_ENABLED" @change="showConfirmModal">
|
||||
{{ $t("settings.telemetry") }}
|
||||
{{
|
||||
TELEMETRY_ENABLED
|
||||
? $t("state.enabled")
|
||||
: $t("state.disabled")
|
||||
}}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
<!-- <div class="flex items-center">
|
||||
<SmartToggle
|
||||
:on="LEFT_SIDEBAR"
|
||||
@change="toggleSetting('LEFT_SIDEBAR')"
|
||||
>
|
||||
{{ $t("settings.navigation_sidebar") }}
|
||||
{{
|
||||
LEFT_SIDEBAR ? $t("state.enabled") : $t("state.disabled")
|
||||
}}
|
||||
</SmartToggle>
|
||||
</div> -->
|
||||
<div class="flex items-center">
|
||||
<SmartToggle :on="ZEN_MODE" @change="toggleSetting('ZEN_MODE')">
|
||||
{{ $t("layout.zen_mode") }}
|
||||
{{ ZEN_MODE ? $t("state.enabled") : $t("state.disabled") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:grid md:gap-4 md:grid-cols-3">
|
||||
<div class="p-8 md:col-span-1">
|
||||
<h3 class="heading">
|
||||
{{ $t("settings.interceptor") }}
|
||||
</h3>
|
||||
<p class="mt-1 text-secondaryLight">
|
||||
{{ $t("settings.interceptor_description") }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-8 p-8 md:col-span-2">
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.extensions") }}
|
||||
</h4>
|
||||
<div class="mt-1 text-secondaryLight">
|
||||
<span v-if="extensionVersion != null">
|
||||
{{
|
||||
`${$t("settings.extension_version")}: v${
|
||||
extensionVersion.major
|
||||
}.${extensionVersion.minor}`
|
||||
}}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $t("settings.extension_version") }}:
|
||||
{{ $t("settings.extension_ver_not_reported") }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2 py-4">
|
||||
<span>
|
||||
<SmartItem
|
||||
to="https://addons.mozilla.org/en-US/firefox/addon/hoppscotch"
|
||||
blank
|
||||
svg="brands/firefox"
|
||||
label="Firefox"
|
||||
:info-icon="hasFirefoxExtInstalled ? 'check_circle' : ''"
|
||||
:active-info-icon="hasFirefoxExtInstalled"
|
||||
outline
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<SmartItem
|
||||
to="https://chrome.google.com/webstore/detail/hoppscotch-browser-extens/amknoiejhlmhancpahfcfcfhllgkpbld"
|
||||
blank
|
||||
svg="brands/chrome"
|
||||
label="Chrome"
|
||||
:info-icon="hasChromeExtInstalled ? 'check_circle' : ''"
|
||||
:active-info-icon="hasChromeExtInstalled"
|
||||
outline
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="space-y-4 py-4">
|
||||
<div class="flex items-center">
|
||||
<SmartToggle
|
||||
:on="EXTENSIONS_ENABLED"
|
||||
@change="toggleSetting('EXTENSIONS_ENABLED')"
|
||||
>
|
||||
{{ $t("settings.extensions_use_toggle") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h4 class="font-semibold text-secondaryDark">
|
||||
{{ $t("settings.proxy") }}
|
||||
</h4>
|
||||
<div class="mt-1 text-secondaryLight">
|
||||
{{
|
||||
`${$t("settings.official_proxy_hosting")} ${$t(
|
||||
"settings.read_the"
|
||||
)}`
|
||||
}}
|
||||
<SmartLink
|
||||
class="link"
|
||||
to="https://docs.hoppscotch.io/privacy"
|
||||
blank
|
||||
>
|
||||
{{ $t("app.proxy_privacy_policy") }} </SmartLink
|
||||
>.
|
||||
</div>
|
||||
<div class="space-y-4 py-4">
|
||||
<div class="flex items-center">
|
||||
<SmartToggle
|
||||
:on="PROXY_ENABLED"
|
||||
@change="toggleSetting('PROXY_ENABLED')"
|
||||
>
|
||||
{{ $t("settings.proxy_use_toggle") }}
|
||||
</SmartToggle>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-2 py-4 items-center">
|
||||
<div class="flex flex-1 items-center relative">
|
||||
<input
|
||||
id="url"
|
||||
v-model="PROXY_URL"
|
||||
class="input floating-input"
|
||||
placeholder=" "
|
||||
type="url"
|
||||
autocomplete="off"
|
||||
:disabled="!PROXY_ENABLED"
|
||||
/>
|
||||
<label for="url">
|
||||
{{ $t("settings.proxy_url") }}
|
||||
</label>
|
||||
</div>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('settings.reset_default')"
|
||||
:svg="clearIcon"
|
||||
outline
|
||||
class="rounded"
|
||||
@click.native="resetProxy"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
||||
<SmartConfirmModal
|
||||
:show="confirmRemove"
|
||||
:title="`${$t('confirm.remove_telemetry')} ${$t(
|
||||
'settings.telemetry_helps_us'
|
||||
)}`"
|
||||
@hide-modal="confirmRemove = false"
|
||||
@resolve="
|
||||
toggleSetting('TELEMETRY_ENABLED')
|
||||
confirmRemove = false
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { currentUserInfo$ } from "~/helpers/teams/BackendUserInfo"
|
||||
import {
|
||||
hasExtensionInstalled,
|
||||
hasChromeExtensionInstalled,
|
||||
hasFirefoxExtensionInstalled,
|
||||
} from "~/helpers/strategies/ExtensionStrategy"
|
||||
import {
|
||||
applySetting,
|
||||
toggleSetting,
|
||||
defaultSettings,
|
||||
useSetting,
|
||||
} from "~/newstore/settings"
|
||||
import type { KeysMatching } from "~/types/ts-utils"
|
||||
import { currentUser$ } from "~/helpers/fb/auth"
|
||||
import { getLocalConfig } from "~/newstore/localpersistence"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
|
||||
type SettingsType = typeof defaultSettings
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
PROXY_ENABLED: useSetting("PROXY_ENABLED"),
|
||||
PROXY_URL: useSetting("PROXY_URL"),
|
||||
PROXY_KEY: useSetting("PROXY_KEY"),
|
||||
EXTENSIONS_ENABLED: useSetting("EXTENSIONS_ENABLED"),
|
||||
EXPERIMENTAL_URL_BAR_ENABLED: useSetting("EXPERIMENTAL_URL_BAR_ENABLED"),
|
||||
SYNC_COLLECTIONS: useSetting("syncCollections"),
|
||||
SYNC_ENVIRONMENTS: useSetting("syncEnvironments"),
|
||||
SYNC_HISTORY: useSetting("syncHistory"),
|
||||
TELEMETRY_ENABLED: useSetting("TELEMETRY_ENABLED"),
|
||||
LEFT_SIDEBAR: useSetting("LEFT_SIDEBAR"),
|
||||
ZEN_MODE: useSetting("ZEN_MODE"),
|
||||
currentUser: useReadonlyStream(currentUser$, currentUser$.value),
|
||||
currentBackendUser: useReadonlyStream(
|
||||
currentUserInfo$,
|
||||
currentUserInfo$.value
|
||||
),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
extensionVersion: hasExtensionInstalled()
|
||||
? window.__POSTWOMAN_EXTENSION_HOOK__.getVersion()
|
||||
: null,
|
||||
|
||||
hasChromeExtInstalled: hasChromeExtensionInstalled(),
|
||||
hasFirefoxExtInstalled: hasFirefoxExtensionInstalled(),
|
||||
|
||||
clearIcon: "rotate-ccw",
|
||||
|
||||
showLogin: false,
|
||||
|
||||
active: getLocalConfig("THEME_COLOR") || "blue",
|
||||
confirmRemove: false,
|
||||
}
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: `${this.$t("navigation.settings")} • Hoppscotch`,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
proxySettings(): { url: string; key: string } {
|
||||
return {
|
||||
url: this.PROXY_URL,
|
||||
key: this.PROXY_KEY,
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
ZEN_MODE(ZEN_MODE) {
|
||||
this.applySetting("LEFT_SIDEBAR", !ZEN_MODE)
|
||||
// this.applySetting("RIGHT_SIDEBAR", !ZEN_MODE)
|
||||
},
|
||||
proxySettings: {
|
||||
deep: true,
|
||||
handler({ url, key }) {
|
||||
this.applySetting("PROXY_URL", url)
|
||||
this.applySetting("PROXY_KEY", key)
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showConfirmModal() {
|
||||
if (this.TELEMETRY_ENABLED) this.confirmRemove = true
|
||||
else toggleSetting("TELEMETRY_ENABLED")
|
||||
},
|
||||
applySetting<K extends keyof SettingsType>(key: K, value: SettingsType[K]) {
|
||||
applySetting(key, value)
|
||||
},
|
||||
toggleSetting<K extends KeysMatching<SettingsType, boolean>>(key: K) {
|
||||
if (key === "EXTENSIONS_ENABLED" && this.PROXY_ENABLED) {
|
||||
toggleSetting("PROXY_ENABLED")
|
||||
}
|
||||
if (key === "PROXY_ENABLED" && this.EXTENSIONS_ENABLED) {
|
||||
toggleSetting("EXTENSIONS_ENABLED")
|
||||
}
|
||||
toggleSetting(key)
|
||||
},
|
||||
toggleSettings<K extends KeysMatching<SettingsType, boolean>>(
|
||||
name: K,
|
||||
value: SettingsType[K]
|
||||
) {
|
||||
this.applySetting(name, value)
|
||||
},
|
||||
resetProxy() {
|
||||
applySetting("PROXY_URL", `https://proxy.hoppscotch.io/`)
|
||||
this.clearIcon = "check"
|
||||
this.$toast.success(this.$t("state.cleared").toString(), {
|
||||
icon: "clear_all",
|
||||
})
|
||||
setTimeout(() => (this.clearIcon = "rotate-ccw"), 1000)
|
||||
},
|
||||
getColorModeName(colorMode: string) {
|
||||
switch (colorMode) {
|
||||
case "system":
|
||||
return "settings.system_mode"
|
||||
case "light":
|
||||
return "settings.light_mode"
|
||||
case "dark":
|
||||
return "settings.dark_mode"
|
||||
case "black":
|
||||
return "settings.black_mode"
|
||||
default:
|
||||
return "settings.system_mode"
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user