Merge remote-tracking branch 'upstream/main' into newstate/firebase

This commit is contained in:
Andrew Bastin
2021-06-19 20:41:25 -04:00
41 changed files with 1568 additions and 3022 deletions

View File

@@ -31,15 +31,6 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
@@ -47,7 +38,7 @@ jobs:
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file. # By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file. # Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main # queries: ./path/to/local/query, your-org/your-repo/queries@main

View File

@@ -22,5 +22,5 @@ jobs:
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- run: npm ci - run: npm ci
- run: npm run build --if-present - run: npm run generate --if-present
- run: npm test - run: npm test

View File

@@ -477,7 +477,7 @@ docker pull hoppscotch/hoppscotch
docker build -t hoppscotch/hoppscotch:latest . docker build -t hoppscotch/hoppscotch:latest .
#run #run
docker run -p 3000:3000 hoppscotch/hoppscotch:latest docker run --rm --name hoppscotch -p 3000:3000 hoppscotch/hoppscotch:latest
``` ```
**Legacy container**   [![liyasthomas/postwoman](https://img.shields.io/docker/pulls/liyasthomas/postwoman?style=social)](https://hub.docker.com/r/liyasthomas/postwoman) **Legacy container**   [![liyasthomas/postwoman](https://img.shields.io/docker/pulls/liyasthomas/postwoman?style=social)](https://hub.docker.com/r/liyasthomas/postwoman)

View File

@@ -727,11 +727,13 @@ section {
} }
.inner-right { .inner-right {
--width: 33%;
--ml: 1rem;
@apply flex; @apply flex;
@apply order-2; @apply order-2;
@apply ml-4;
width: 33%; margin-left: var(--ml);
width: var(--width);
} }
@media (max-width: $responsiveWidth) { @media (max-width: $responsiveWidth) {
@@ -770,7 +772,7 @@ section {
} }
.inner-right { .inner-right {
@apply ml-0; --ml: 0;
} }
.toasted-container { .toasted-container {

View File

@@ -189,14 +189,6 @@ export default {
} }
}, },
async mounted() { async mounted() {
this._keyListener = function (e) {
if (e.key === "Escape") {
e.preventDefault()
this.showExtensions = this.showShortcuts = this.showSupport = false
}
}
document.addEventListener("keydown", this._keyListener.bind(this))
// Initializes the PWA code - checks if the app is installed, // Initializes the PWA code - checks if the app is installed,
// etc. // etc.
this.showInstallPrompt = await intializePwa() this.showInstallPrompt = await intializePwa()
@@ -281,9 +273,6 @@ export default {
// }, 5000) // }, 5000)
// } // }
}, },
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
methods: { methods: {
nativeShare() { nativeShare() {
if (navigator.share) { if (navigator.share) {

View File

@@ -26,7 +26,7 @@ export default Vue.extend({
}, },
noLegend: { noLegend: {
type: Boolean, type: Boolean,
default: false, default: true,
}, },
}, },
computed: { computed: {

View File

@@ -232,6 +232,7 @@ export default {
this.hideModal() this.hideModal()
}, },
hideModal() { hideModal() {
this.picked = null
this.$emit("hide-modal") this.$emit("hide-modal")
}, },
}, },

View File

@@ -36,7 +36,7 @@
> >
<i class="material-icons">topic</i> <i class="material-icons">topic</i>
</button> </button>
<v-popover v-if="!savingMode"> <v-popover>
<button v-tooltip.left="$t('more')" class="tooltip-target icon"> <button v-tooltip.left="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
@@ -197,6 +197,14 @@ export default Vue.extend({
this.showChildren = !this.showChildren this.showChildren = !this.showChildren
}, },
removeCollection() { removeCollection() {
// Cancel pick if picked collection is deleted
if (
this.picked &&
this.picked.pickedType === "gql-my-collection" &&
this.picked.collectionIndex === this.collectionIndex
) {
this.$emit("select", { picked: null })
}
removeGraphqlCollection(this.collectionIndex) removeGraphqlCollection(this.collectionIndex)
this.$toast.error(this.$t("deleted").toString(), { this.$toast.error(this.$t("deleted").toString(), {

View File

@@ -28,7 +28,7 @@
<span>{{ folder.name }}</span> <span>{{ folder.name }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!savingMode"> <v-popover>
<button v-tooltip.left="$t('more')" class="tooltip-target icon"> <button v-tooltip.left="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
@@ -181,6 +181,15 @@ export default Vue.extend({
this.showChildren = !this.showChildren this.showChildren = !this.showChildren
}, },
removeFolder() { removeFolder() {
// Cancel pick if the picked folder is deleted
if (
this.picked &&
this.picked.pickedType === "gql-my-folder" &&
this.picked.folderPath === this.folderPath
) {
this.$emit("select", { picked: null })
}
removeGraphqlFolder(this.folderPath) removeGraphqlFolder(this.folderPath)
this.$toast.error(this.$t("deleted").toString(), { this.$toast.error(this.$t("deleted").toString(), {
icon: "delete", icon: "delete",

View File

@@ -25,7 +25,7 @@
<span>{{ request.name }}</span> <span>{{ request.name }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!savingMode"> <v-popover>
<button v-tooltip="$t('more')" class="tooltip-target icon"> <button v-tooltip="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
@@ -122,6 +122,16 @@ export default Vue.extend({
dataTransfer.setData("requestIndex", this.requestIndex) dataTransfer.setData("requestIndex", this.requestIndex)
}, },
removeRequest() { removeRequest() {
// Cancel pick if the picked request is deleted
if (
this.picked &&
this.picked.pickedType === "gql-my-request" &&
this.picked.folderPath === this.folderPath &&
this.picked.requestIndex === this.requestIndex
) {
this.$emit("select", { picked: null })
}
removeGraphqlRequest(this.folderPath, this.requestIndex) removeGraphqlRequest(this.folderPath, this.requestIndex)
this.$toast.error(this.$t("deleted").toString(), { this.$toast.error(this.$t("deleted").toString(), {
icon: "delete", icon: "delete",

View File

@@ -1,12 +1,8 @@
<template> <template>
<AppSection <AppSection ref="collections" :label="$t('collections')">
ref="collections"
class="yellow"
:label="$t('collections')"
no-legend
>
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<input <input
v-if="showCollActions"
v-model="filterText" v-model="filterText"
aria-label="Search" aria-label="Search"
type="search" type="search"
@@ -50,11 +46,7 @@
@hide-modal="displayModalImportExport(false)" @hide-modal="displayModalImportExport(false)"
/> />
<div class="border-b row-wrapper border-divider"> <div class="border-b row-wrapper border-divider">
<button <button class="icon" @click="displayModalAdd(true)">
v-if="showCollActions"
class="icon"
@click="displayModalAdd(true)"
>
<i class="material-icons">add</i> <i class="material-icons">add</i>
<span>{{ $t("new") }}</span> <span>{{ $t("new") }}</span>
</button> </button>
@@ -178,24 +170,6 @@ export default {
return filteredCollections return filteredCollections
}, },
}, },
mounted() {
this._keyListener = function (e) {
if (e.key === "Escape") {
e.preventDefault()
this.showModalAdd =
this.showModalEdit =
this.showModalImportExport =
this.showModalAddFolder =
this.showModalEditFolder =
this.showModalEditRequest =
false
}
}
document.addEventListener("keydown", this._keyListener.bind(this))
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
methods: { methods: {
displayModalAdd(shouldDisplay) { displayModalAdd(shouldDisplay) {
this.showModalAdd = shouldDisplay this.showModalAdd = shouldDisplay
@@ -250,7 +224,9 @@ export default {
folderName, folderName,
request, request,
requestIndex, requestIndex,
folderPath,
} = payload } = payload
this.$data.editingFolderPath = folderPath
this.$data.editingCollectionIndex = collectionIndex this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolderIndex = folderIndex this.$data.editingFolderIndex = folderIndex
this.$data.editingFolderName = folderName this.$data.editingFolderName = folderName

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection ref="collections" :label="$t('collections')" no-legend> <AppSection ref="collections" :label="$t('collections')">
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<input <input
v-if="!saveRequest" v-if="!saveRequest"
@@ -57,8 +57,7 @@
v-if=" v-if="
collectionsType.type == 'team-collections' && collectionsType.type == 'team-collections' &&
(collectionsType.selectedTeam == undefined || (collectionsType.selectedTeam == undefined ||
collectionsType.selectedTeam.myRole == 'VIEWER') && collectionsType.selectedTeam.myRole == 'VIEWER')
!saveRequest
" "
class="icon" class="icon"
disabled disabled
@@ -69,11 +68,7 @@
<span>{{ $t("new") }}</span> <span>{{ $t("new") }}</span>
</div> </div>
</button> </button>
<button <button v-else class="icon" @click="displayModalAdd(true)">
v-else-if="!saveRequest"
class="icon"
@click="displayModalAdd(true)"
>
<i class="material-icons">add</i> <i class="material-icons">add</i>
<span>{{ $t("new") }}</span> <span>{{ $t("new") }}</span>
</button> </button>
@@ -261,27 +256,10 @@ export default {
}, },
}, },
mounted() { mounted() {
this._keyListener = function (e) {
if (e.key === "Escape") {
e.preventDefault()
this.showModalAdd =
this.showModalEdit =
this.showModalImportExport =
this.showModalAddFolder =
this.showModalEditFolder =
this.showModalEditRequest =
false
}
}
document.addEventListener("keydown", this._keyListener.bind(this))
this.$subscribeTo(this.teamCollectionAdapter.collections$, (colls) => { this.$subscribeTo(this.teamCollectionAdapter.collections$, (colls) => {
this.teamCollectionsNew = cloneDeep(colls) this.teamCollectionsNew = cloneDeep(colls)
}) })
}, },
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
methods: { methods: {
updateTeamCollections() { updateTeamCollections() {
// TODO: Remove this at some point // TODO: Remove this at some point
@@ -554,12 +532,30 @@ export default {
}, },
removeCollection({ collectionsType, collectionIndex, collectionID }) { removeCollection({ collectionsType, collectionIndex, collectionID }) {
if (collectionsType.type === "my-collections") { if (collectionsType.type === "my-collections") {
// Cancel pick if picked collection is deleted
if (
this.picked &&
this.picked.pickedType === "my-collection" &&
this.picked.collectionIndex === collectionIndex
) {
this.$emit("select", { picked: null })
}
removeRESTCollection(collectionIndex) removeRESTCollection(collectionIndex)
this.$toast.error(this.$t("deleted"), { this.$toast.error(this.$t("deleted"), {
icon: "delete", icon: "delete",
}) })
} else if (collectionsType.type === "team-collections") { } else if (collectionsType.type === "team-collections") {
// Cancel pick if picked collection is deleted
if (
this.picked &&
this.picked.pickedType === "teams-collection" &&
this.picked.collectionID === collectionID
) {
this.$emit("select", { picked: null })
}
if (collectionsType.selectedTeam.myRole !== "VIEWER") { if (collectionsType.selectedTeam.myRole !== "VIEWER") {
this.$apollo this.$apollo
.mutate({ .mutate({
@@ -592,12 +588,30 @@ export default {
}, },
removeRequest({ requestIndex, folderPath }) { removeRequest({ requestIndex, folderPath }) {
if (this.collectionsType.type === "my-collections") { if (this.collectionsType.type === "my-collections") {
// Cancel pick if the picked item is being deleted
if (
this.picked &&
this.picked.pickedType === "my-request" &&
this.picked.folderPath === folderPath &&
this.picked.requestIndex === requestIndex
) {
this.$emit("select", { picked: null })
}
removeRESTRequest(folderPath, requestIndex) removeRESTRequest(folderPath, requestIndex)
this.$toast.error(this.$t("deleted"), { this.$toast.error(this.$t("deleted"), {
icon: "delete", icon: "delete",
}) })
} else if (this.collectionsType.type === "team-collections") { } else if (this.collectionsType.type === "team-collections") {
// Cancel pick if the picked item is being deleted
if (
this.picked &&
this.picked.pickedType === "teams-request" &&
this.picked.requestID === requestIndex
) {
this.$emit("select", { picked: null })
}
teamUtils teamUtils
.deleteRequest(this.$apollo, requestIndex) .deleteRequest(this.$apollo, requestIndex)
.then(() => { .then(() => {

View File

@@ -44,7 +44,7 @@
> >
<i class="material-icons">check_box</i> <i class="material-icons">check_box</i>
</button> </button>
<v-popover v-if="!saveRequest"> <v-popover>
<button v-tooltip.left="$t('more')" class="tooltip-target icon"> <button v-tooltip.left="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>

View File

@@ -27,7 +27,7 @@
<span>{{ folder.name ? folder.name : folder.title }}</span> <span>{{ folder.name ? folder.name : folder.title }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!saveRequest"> <v-popover>
<button v-tooltip.left="$t('more')" class="tooltip-target icon"> <button v-tooltip.left="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>
@@ -193,6 +193,15 @@ export default {
this.showChildren = !this.showChildren this.showChildren = !this.showChildren
}, },
removeFolder() { removeFolder() {
// TODO: Bubble it up ?
// Cancel pick if picked folder was deleted
if (
this.picked &&
this.picked.pickedType === "my-folder" &&
this.picked.folderPath === this.folderPath
) {
this.$emit("select", { picked: null })
}
removeRESTFolder(this.folderPath) removeRESTFolder(this.folderPath)
this.$toast.error(this.$t("deleted"), { this.$toast.error(this.$t("deleted"), {
@@ -206,6 +215,16 @@ export default {
moveRESTRequest(folderPath, requestIndex, this.folderPath) moveRESTRequest(folderPath, requestIndex, this.folderPath)
}, },
removeRequest({ requestIndex }) { removeRequest({ requestIndex }) {
// TODO: Bubble it up to root ?
// Cancel pick if the picked item is being deleted
if (
this.picked &&
this.picked.pickedType === "my-request" &&
this.picked.folderPath === this.folderPath &&
this.picked.requestIndex === requestIndex
) {
this.$emit("select", { picked: null })
}
removeRESTRequest(this.folderPath, requestIndex) removeRESTRequest(this.folderPath, requestIndex)
}, },
}, },

View File

@@ -27,7 +27,7 @@
<span>{{ request.name }}</span> <span>{{ request.name }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!saveRequest"> <v-popover>
<button v-tooltip="$t('more')" class="tooltip-target icon"> <button v-tooltip="$t('more')" class="tooltip-target icon">
<i class="material-icons">more_vert</i> <i class="material-icons">more_vert</i>
</button> </button>

View File

@@ -33,7 +33,7 @@
> >
<i class="material-icons">check_box</i> <i class="material-icons">check_box</i>
</button> </button>
<v-popover v-if="!saveRequest"> <v-popover>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
v-tooltip.left="$t('more')" v-tooltip.left="$t('more')"

View File

@@ -18,7 +18,7 @@
<span>{{ folder.name ? folder.name : folder.title }}</span> <span>{{ folder.name ? folder.name : folder.title }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!saveRequest"> <v-popover>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
v-tooltip.left="$t('more')" v-tooltip.left="$t('more')"
@@ -189,6 +189,15 @@ export default {
}, },
removeFolder() { removeFolder() {
if (this.collectionsType.selectedTeam.myRole !== "VIEWER") { if (this.collectionsType.selectedTeam.myRole !== "VIEWER") {
// Cancel pick if picked collection folder was deleted
if (
this.picked &&
this.picked.pickedType === "teams-folder" &&
this.picked.folderID === this.folder.id
) {
this.$emit("select", { picked: null })
}
teamUtils teamUtils
.deleteCollection(this.$apollo, this.folder.id) .deleteCollection(this.$apollo, this.folder.id)
.then(() => { .then(() => {

View File

@@ -17,7 +17,7 @@
<span>{{ request.name }}</span> <span>{{ request.name }}</span>
</button> </button>
</div> </div>
<v-popover v-if="!saveRequest"> <v-popover>
<button <button
v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'" v-if="collectionsType.selectedTeam.myRole !== 'VIEWER'"
v-tooltip="$t('more')" v-tooltip="$t('more')"

View File

@@ -1,10 +1,5 @@
<template> <template>
<AppSection <AppSection ref="environments" :label="$t('environments')">
ref="environments"
icon="history"
:label="$t('environments')"
no-legend
>
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<span class="select-wrapper"> <span class="select-wrapper">
<select <select
@@ -103,21 +98,6 @@ export default {
setCurrentEnvironment(val) setCurrentEnvironment(val)
}, },
}, },
mounted() {
this._keyListener = function (e) {
if (e.key === "Escape") {
e.preventDefault()
this.showModalImportExport =
this.showModalAdd =
this.showModalEdit =
false
}
}
document.addEventListener("keydown", this._keyListener.bind(this))
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
methods: { methods: {
displayModalAdd(shouldDisplay) { displayModalAdd(shouldDisplay) {
this.showModalAdd = shouldDisplay this.showModalAdd = shouldDisplay

View File

@@ -90,17 +90,6 @@ export default {
this.$subscribeTo(currentUser$, (user) => { this.$subscribeTo(currentUser$, (user) => {
if (user) this.hideModal() if (user) this.hideModal()
}) })
this._keyListener = function (e) {
if (e.key === "Escape") {
e.preventDefault()
this.hideModal()
}
}
document.addEventListener("keydown", this._keyListener.bind(this))
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
}, },
methods: { methods: {
async signInWithEmail() { async signInWithEmail() {

View File

@@ -9,7 +9,8 @@
readonly readonly
:value="entry.url" :value="entry.url"
:placeholder="$t('empty_req_name')" :placeholder="$t('empty_req_name')"
class="bg-transparent" class="cursor-pointer text-sm bg-transparent"
@click="$emit('use-entry')"
/> />
</li> </li>
<button <button

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection ref="history" icon="history" :label="$t('history')" no-legend> <AppSection ref="history" :label="$t('history')">
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<input <input
v-model="filterText" v-model="filterText"

View File

@@ -2,9 +2,10 @@
<div> <div>
<div class="show-on-large-screen"> <div class="show-on-large-screen">
<span <span
class="p-2 m-2 truncate" class="p-2 m-2 truncate inline-flex cursor-pointer items-center text-sm"
:class="entryStatus.className" :class="entryStatus.className"
:style="{ '--status-code': entry.status }" :style="{ '--status-code': entry.status }"
@click="$emit('use-entry')"
> >
{{ `${entry.method} \xA0 • \xA0 ${entry.status}` }} {{ `${entry.method} \xA0 • \xA0 ${entry.status}` }}
</span> </span>
@@ -15,7 +16,8 @@
readonly readonly
:value="entry.name" :value="entry.name"
:placeholder="$t('empty_req_name')" :placeholder="$t('empty_req_name')"
class="bg-transparent" class="cursor-pointer text-sm bg-transparent"
@click="$emit('use-entry')"
/> />
</li> </li>
<span> <span>

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection ref="headers" label="Headers" no-legend> <AppSection ref="headers" label="Headers">
<ul v-if="headers.length !== 0"> <ul v-if="headers.length !== 0">
<li> <li>
<div class="row-wrapper"> <div class="row-wrapper">

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection ref="parameters" label="Parameters" no-legend> <AppSection ref="parameters" label="Parameters">
<ul v-if="params.length !== 0"> <ul v-if="params.length !== 0">
<li> <li>
<div class="row-wrapper"> <div class="row-wrapper">

View File

@@ -1,5 +1,5 @@
<template> <template>
<AppSection id="response" ref="response" :label="$t('response')" no-legend> <AppSection id="response" ref="response" :label="$t('response')">
<HttpResponseMeta :response="response" :active="active" /> <HttpResponseMeta :response="response" :active="active" />
<div v-if="response.body && response.body !== $t('loading')"> <div v-if="response.body && response.body !== $t('loading')">
<LensesResponseBodyRenderer :response="response" /> <LensesResponseBodyRenderer :response="response" />

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<AppSection :label="$t('request')" no-legend> <AppSection :label="$t('request')">
<ul> <ul>
<li> <li>
<label for="mqtt-url">{{ $t("url") }}</label> <label for="mqtt-url">{{ $t("url") }}</label>
@@ -34,7 +34,7 @@
</ul> </ul>
</AppSection> </AppSection>
<AppSection :label="$t('communication')" no-legend> <AppSection :label="$t('communication')">
<ul> <ul>
<li> <li>
<RealtimeLog :title="$t('log')" :log="log" /> <RealtimeLog :title="$t('log')" :log="log" />

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<AppSection ref="request" :label="$t('request')" no-legend> <AppSection ref="request" :label="$t('request')">
<ul> <ul>
<li> <li>
<label for="socketio-url">{{ $t("url") }}</label> <label for="socketio-url">{{ $t("url") }}</label>
@@ -43,12 +43,7 @@
</ul> </ul>
</AppSection> </AppSection>
<AppSection <AppSection id="response" ref="response" :label="$t('communication')">
id="response"
ref="response"
:label="$t('communication')"
no-legend
>
<ul> <ul>
<li> <li>
<RealtimeLog :title="$t('log')" :log="communication.log" /> <RealtimeLog :title="$t('log')" :log="communication.log" />

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<AppSection ref="request" :label="$t('request')" no-legend> <AppSection ref="request" :label="$t('request')">
<ul> <ul>
<li> <li>
<label for="server">{{ $t("server") }}</label> <label for="server">{{ $t("server") }}</label>
@@ -36,12 +36,7 @@
</ul> </ul>
</AppSection> </AppSection>
<AppSection <AppSection id="response" ref="response" :label="$t('communication')">
id="response"
ref="response"
:label="$t('communication')"
no-legend
>
<ul> <ul>
<li> <li>
<RealtimeLog :title="$t('events')" :log="events.log" /> <RealtimeLog :title="$t('events')" :log="events.log" />

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<AppSection ref="request" :label="$t('request')" no-legend> <AppSection ref="request" :label="$t('request')">
<ul> <ul>
<li> <li>
<label for="websocket-url">{{ $t("url") }}</label> <label for="websocket-url">{{ $t("url") }}</label>
@@ -10,7 +10,6 @@
type="url" type="url"
spellcheck="false" spellcheck="false"
:class="{ error: !urlValid }" :class="{ error: !urlValid }"
class="md:rounded-bl-lg"
:placeholder="$t('url')" :placeholder="$t('url')"
@keyup.enter="urlValid ? toggleConnection() : null" @keyup.enter="urlValid ? toggleConnection() : null"
/> />
@@ -22,7 +21,6 @@
id="connect" id="connect"
:disabled="!urlValid" :disabled="!urlValid"
name="connect" name="connect"
class="rounded-b-lg md:rounded-bl-none md:rounded-br-lg"
@click="toggleConnection" @click="toggleConnection"
> >
{{ !connectionState ? $t("connect") : $t("disconnect") }} {{ !connectionState ? $t("connect") : $t("disconnect") }}
@@ -35,14 +33,86 @@
</li> </li>
</div> </div>
</ul> </ul>
<ul>
<li>
<div class="row-wrapper">
<label>{{ $t("protocols") }}</label>
</div>
</li>
</ul>
<ul
v-for="(protocol, index) of protocols"
:key="`protocol-${index}`"
:class="{ 'border-t': index == 0 }"
class="
border-b border-dashed
divide-y
md:divide-x
border-divider
divide-dashed divide-divider
md:divide-y-0
"
>
<li>
<input
v-model="protocol.value"
:placeholder="$t('protocol_count', { count: index + 1 })"
name="message"
type="text"
/>
</li>
<div>
<li>
<button
v-tooltip.bottom="{
content: protocol.hasOwnProperty('active')
? protocol.active
? $t('turn_off')
: $t('turn_on')
: $t('turn_off'),
}"
class="icon"
@click="
protocol.active = protocol.hasOwnProperty('active')
? !protocol.active
: false
"
>
<i class="material-icons">
{{
protocol.hasOwnProperty("active")
? protocol.active
? "check_box"
: "check_box_outline_blank"
: "check_box"
}}
</i>
</button>
</li>
</div>
<div>
<li>
<button
v-tooltip.bottom="$t('delete')"
class="icon"
@click="deleteProtocol({ index })"
>
<i class="material-icons">delete</i>
</button>
</li>
</div>
</ul>
<ul>
<li>
<button class="icon" @click="addProtocol">
<i class="material-icons">add</i>
<span>{{ $t("add_new") }}</span>
</button>
</li>
</ul>
</AppSection> </AppSection>
<AppSection <AppSection id="response" ref="response" :label="$t('communication')">
id="response"
ref="response"
:label="$t('communication')"
no-legend
>
<ul> <ul>
<li> <li>
<RealtimeLog :title="$t('log')" :log="communication.log" /> <RealtimeLog :title="$t('log')" :log="communication.log" />
@@ -100,6 +170,8 @@ export default {
input: "", input: "",
}, },
currentIndex: -1, // index of the message log array to put in input box currentIndex: -1, // index of the message log array to put in input box
protocols: [],
activeProtocols: [],
} }
}, },
computed: { computed: {
@@ -111,6 +183,18 @@ export default {
url() { url() {
this.debouncer() this.debouncer()
}, },
protocols: {
handler(newVal) {
this.activeProtocols = newVal
.filter((item) =>
Object.prototype.hasOwnProperty.call(item, "active")
? item.active === true
: true
)
.map(({ value }) => value)
},
deep: true,
},
}, },
mounted() { mounted() {
if (process.browser) { if (process.browser) {
@@ -143,7 +227,7 @@ export default {
}, },
] ]
try { try {
this.socket = new WebSocket(this.url) this.socket = new WebSocket(this.url, this.activeProtocols)
this.socket.onopen = () => { this.socket.onopen = () => {
this.connectionState = true this.connectionState = true
this.communication.log = [ this.communication.log = [
@@ -255,6 +339,24 @@ export default {
break break
} }
}, },
addProtocol() {
this.protocols.push({ value: "", active: true })
},
deleteProtocol({ index }) {
const oldProtocols = this.protocols.slice()
this.$delete(this.protocols, index)
this.$toast.error(this.$t("deleted"), {
icon: "delete",
action: {
text: this.$t("undo"),
duration: 4000,
onClick: (_, toastObject) => {
this.protocols = oldProtocols
toastObject.remove()
},
},
})
},
}, },
} }
</script> </script>

View File

@@ -47,18 +47,6 @@ export default {
}, },
}, },
}, },
mounted() {
this._keyListener = function (e) {
if (e.key === "Escape") {
e.preventDefault()
this.hideModal()
}
}
document.addEventListener("keydown", this._keyListener.bind(this))
},
beforeDestroy() {
document.removeEventListener("keydown", this._keyListener)
},
methods: { methods: {
hideModal() { hideModal() {
this.$emit("hide-modal") this.$emit("hide-modal")

View File

@@ -1,6 +1,13 @@
<template> <template>
<transition name="modal" appear> <transition name="modal" appear @leave="onTransitionLeaveStart">
<div class="modal-backdrop"> <div
ref="modal"
class="modal-backdrop"
@touchstart="onBackdropMouseDown"
@touchend="onBackdropMouseUp"
@mouseup="onBackdropMouseUp"
@mousedown="onBackdropMouseDown"
>
<div class="modal-wrapper"> <div class="modal-wrapper">
<div class="modal-container"> <div class="modal-container">
<div class="modal-header"> <div class="modal-header">
@@ -21,12 +28,86 @@
</template> </template>
<script> <script>
const PORTAL_DOM_ID = "hoppscotch-modal-portal"
const stack = (() => {
const stack = []
return {
push: stack.push.bind(stack),
pop: stack.pop.bind(stack),
peek: () => (stack.length === 0 ? undefined : stack[stack.length - 1]),
}
})()
export default { export default {
data() {
return {
stackId: Math.random(),
shouldCloseOnBackdropClick: true,
// when transition doesn't fire on unmount, we should manually remove the modal from DOM
// (for example, when the parent component of this modal gets destroyed)
shouldCleanupDomOnUnmount: true,
}
},
computed: { computed: {
hasFooterSlot() { hasFooterSlot() {
return !!this.$slots.footer return !!this.$slots.footer
}, },
}, },
mounted() {
const $portal = this.$getPortal()
$portal.appendChild(this.$refs.modal)
stack.push(this.stackId)
document.addEventListener("keydown", this.onKeyDown)
},
beforeDestroy() {
const $modal = this.$refs.modal
if (this.shouldCleanupDomOnUnmount && $modal) {
this.$getPortal().removeChild($modal)
}
stack.pop()
document.removeEventListener("keydown", this.onKeyDown)
},
methods: {
close() {
this.$emit("close")
},
onBackdropMouseDown({ target }) {
this.shouldCloseOnBackdropClick =
!this.checkIfTargetInsideModalContent(target)
},
onBackdropMouseUp({ target }) {
if (
this.shouldCloseOnBackdropClick &&
!this.checkIfTargetInsideModalContent(target)
) {
this.close()
}
this.shouldCloseOnBackdropClick = true
},
checkIfTargetInsideModalContent($target) {
return $target?.closest(".modal-container")
},
onKeyDown(e) {
if (e.key === "Escape" && this.stackId === stack.peek()) {
e.preventDefault()
this.close()
}
},
onTransitionLeaveStart() {
this.shouldCleanupDomOnUnmount = false
},
$getPortal() {
let $el = document.querySelector("#" + PORTAL_DOM_ID)
if ($el) {
return $el
}
$el = document.createElement("DIV")
$el.id = PORTAL_DOM_ID
document.body.appendChild($el)
return $el
},
},
} }
</script> </script>

View File

@@ -1,11 +1,5 @@
<template> <template>
<AppSection <AppSection ref="teams" :label="$t('teams')">
ref="teams"
class="green"
icon="history"
:label="$t('teams')"
no-legend
>
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("teams") }}</label> <label>{{ $t("teams") }}</label>
<div v-if="currentUser"></div> <div v-if="currentUser"></div>

View File

@@ -1,18 +1,8 @@
<template> <template>
<transition <transition :appear="appear" name="translate-slide-left">
:appear="appear"
name="translate-slide-left"
enter-active-class="transition duration-500 transform"
enter-class="translate-x-full"
enter-to-class="translate-x-0"
leave-active-class="transition duration-500 transform"
leave-class="translate-x-0"
leave-to-class="translate-x-full"
>
<slot></slot> <slot></slot>
</transition> </transition>
</template> </template>
<script> <script>
export default { export default {
props: { props: {
@@ -23,3 +13,26 @@ export default {
}, },
} }
</script> </script>
<style scoped lang="scss">
.translate-slide-left {
&-enter,
&-leave-to {
width: 0%;
opacity: 0;
margin-left: 0;
}
&-enter-to,
&-leave {
width: var(--width, 33%);
margin-left: var(--ml, 0);
opacity: 1;
}
&-enter-active,
&-leave-active {
overflow-x: hidden;
white-space: nowrap;
transition: width 0.5s ease, opacity 0.3s ease, margin-left 0.5s ease;
}
}
</style>

View File

@@ -336,5 +336,7 @@
"we_sent_magic_link": "We sent you a magic link!", "we_sent_magic_link": "We sent you a magic link!",
"we_sent_magic_link_description": "We sent an email to {email}. It contains a magic link thatll log you in.", "we_sent_magic_link_description": "We sent an email to {email}. It contains a magic link thatll log you in.",
"hide_sidebar": "Hide sidebar", "hide_sidebar": "Hide sidebar",
"show_sidebar": "Show sidebar" "show_sidebar": "Show sidebar",
"protocols": "Protocols",
"protocol_count": "protocol {count}"
} }

3986
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"node-interval-tree": "^1.3.3", "node-interval-tree": "^1.3.3",
"nuxt": "^2.15.6", "nuxt": "^2.15.7",
"nuxt-i18n": "^6.27.0", "nuxt-i18n": "^6.27.0",
"paho-mqtt": "^1.1.0", "paho-mqtt": "^1.1.0",
"rxjs": "^7.1.0", "rxjs": "^7.1.0",
@@ -57,7 +57,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.5", "@babel/core": "^7.14.5",
"@babel/preset-env": "^7.14.5", "@babel/preset-env": "^7.14.5",
"@nuxt/types": "^2.15.6", "@nuxt/types": "^2.15.7",
"@nuxt/typescript-build": "^2.1.0", "@nuxt/typescript-build": "^2.1.0",
"@nuxtjs/color-mode": "^2.0.10", "@nuxtjs/color-mode": "^2.0.10",
"@nuxtjs/dotenv": "^1.4.1", "@nuxtjs/dotenv": "^1.4.1",
@@ -70,7 +70,7 @@
"@nuxtjs/tailwindcss": "^4.1.3", "@nuxtjs/tailwindcss": "^4.1.3",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@types/lodash": "^4.14.170", "@types/lodash": "^4.14.170",
"@vue/test-utils": "^1.2.0", "@vue/test-utils": "^1.2.1",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-jest": "^27.0.2", "babel-jest": "^27.0.2",
"eslint": "^7.28.0", "eslint": "^7.28.0",
@@ -82,7 +82,7 @@
"jest": "^27.0.4", "jest": "^27.0.4",
"jest-serializer-vue": "^2.0.2", "jest-serializer-vue": "^2.0.2",
"lint-staged": "^11.0.0", "lint-staged": "^11.0.0",
"postcss": "^8.3.2", "postcss": "^8.3.4",
"prettier": "^2.3.1", "prettier": "^2.3.1",
"pretty-quick": "^3.1.0", "pretty-quick": "^3.1.0",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",

View File

@@ -2,7 +2,7 @@
<div class="page"> <div class="page">
<div class="content"> <div class="content">
<div class="page-columns inner-left"> <div class="page-columns inner-left">
<AppSection ref="import" :label="$t('import')" no-legend> <AppSection ref="import" :label="$t('import')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("collection") }}</label> <label>{{ $t("collection") }}</label>
<p class="info"> <p class="info">
@@ -55,7 +55,7 @@
</div> </div>
</AppSection> </AppSection>
<AppSection ref="documentation" :label="$t('documentation')" no-legend> <AppSection ref="documentation" :label="$t('documentation')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("documentation") }}</label> <label>{{ $t("documentation") }}</label>
<p v-if="items.length === 0" class="info"> <p v-if="items.length === 0" class="info">

View File

@@ -2,7 +2,7 @@
<div class="page"> <div class="page">
<div class="content"> <div class="content">
<div class="page-columns inner-left"> <div class="page-columns inner-left">
<AppSection ref="endpoint" :label="$t('endpoint')" no-legend> <AppSection ref="endpoint" :label="$t('endpoint')">
<ul> <ul>
<li> <li>
<label for="url">{{ $t("url") }}</label> <label for="url">{{ $t("url") }}</label>
@@ -37,7 +37,7 @@
</ul> </ul>
</AppSection> </AppSection>
<AppSection ref="headers" :label="$t('headers')" no-legend> <AppSection ref="headers" :label="$t('headers')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("headers") }}</label> <label>{{ $t("headers") }}</label>
<ul v-if="headers.length !== 0"> <ul v-if="headers.length !== 0">
@@ -150,7 +150,7 @@
</div> </div>
</AppSection> </AppSection>
<AppSection ref="schema" :label="$t('schema')" no-legend> <AppSection ref="schema" :label="$t('schema')">
<div class="row-wrapper"> <div class="row-wrapper">
<label>{{ $t("schema") }}</label> <label>{{ $t("schema") }}</label>
<div v-if="schema"> <div v-if="schema">
@@ -212,7 +212,7 @@
/> />
</AppSection> </AppSection>
<AppSection ref="query" :label="$t('query')" no-legend> <AppSection ref="query" :label="$t('query')">
<div class="row-wrapper gqlRunQuery"> <div class="row-wrapper gqlRunQuery">
<label for="gqlQuery">{{ $t("query") }}</label> <label for="gqlQuery">{{ $t("query") }}</label>
<div> <div>
@@ -266,7 +266,7 @@
/> />
</AppSection> </AppSection>
<AppSection ref="variables" :label="$t('variables')" no-legend> <AppSection ref="variables" :label="$t('variables')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("variables") }}</label> <label>{{ $t("variables") }}</label>
<SmartAceEditor <SmartAceEditor
@@ -285,7 +285,7 @@
</div> </div>
</AppSection> </AppSection>
<AppSection ref="response" :label="$t('response')" no-legend> <AppSection ref="response" :label="$t('response')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("response") }}</label> <label>{{ $t("response") }}</label>
<div class="row-wrapper"> <div class="row-wrapper">
@@ -347,7 +347,7 @@
> >
<SmartTabs> <SmartTabs>
<SmartTab :id="'docs'" :label="`Docs`" :selected="true"> <SmartTab :id="'docs'" :label="`Docs`" :selected="true">
<AppSection ref="docs" :label="$t('docs')" no-legend> <AppSection ref="docs" :label="$t('docs')">
<section class="flex-col"> <section class="flex-col">
<input <input
v-model="graphqlFieldsFilterText" v-model="graphqlFieldsFilterText"

View File

@@ -3,7 +3,7 @@
<div class="page"> <div class="page">
<div class="content"> <div class="content">
<div class="page-columns inner-left"> <div class="page-columns inner-left">
<AppSection :label="$t('request')" ref="request" no-legend> <AppSection :label="$t('request')" ref="request">
<ul> <ul>
<li class="shrink"> <li class="shrink">
<label for="method">{{ $t("method") }}</label> <label for="method">{{ $t("method") }}</label>
@@ -253,11 +253,7 @@
</SmartTab> </SmartTab>
<SmartTab :id="'authentication'" :label="$t('authentication')"> <SmartTab :id="'authentication'" :label="$t('authentication')">
<AppSection <AppSection :label="$t('authentication')" ref="authentication">
:label="$t('authentication')"
ref="authentication"
no-legend
>
<ul> <ul>
<li> <li>
<div class="row-wrapper"> <div class="row-wrapper">
@@ -358,7 +354,6 @@
<AppSection <AppSection
v-if="showTokenRequest" v-if="showTokenRequest"
class="red"
label="Access Token Request" label="Access Token Request"
ref="accessTokenRequest" ref="accessTokenRequest"
> >
@@ -485,10 +480,8 @@
> >
<AppSection <AppSection
v-if="showPreRequestScript" v-if="showPreRequestScript"
class="orange"
:label="$t('pre_request_script')" :label="$t('pre_request_script')"
ref="preRequest" ref="preRequest"
no-legend
> >
<ul> <ul>
<li> <li>
@@ -527,10 +520,8 @@
<SmartTab :id="'tests'" :label="$t('tests')"> <SmartTab :id="'tests'" :label="$t('tests')">
<AppSection <AppSection
v-if="testsEnabled" v-if="testsEnabled"
class="orange"
:label="$t('tests')" :label="$t('tests')"
ref="postRequestTests" ref="postRequestTests"
no-legend
> >
<ul> <ul>
<li> <li>
@@ -2172,15 +2163,6 @@ export default {
e.preventDefault() e.preventDefault()
this.$refs.clearAll.click() this.$refs.clearAll.click()
} }
if (e.key === "Escape") {
e.preventDefault()
this.showCurlImportModal =
this.showTokenListModal =
this.showTokenRequestList =
this.showSaveRequestModal =
this.showCodegenModal =
false
}
if ((e.key === "g" || e.key === "G") && e.altKey) { if ((e.key === "g" || e.key === "G") && e.altKey) {
this.method = "GET" this.method = "GET"
} }

View File

@@ -4,7 +4,7 @@
<Teams /> <Teams />
</div> </div>
<AppSection ref="account" :label="$t('account')" no-legend> <AppSection ref="account" :label="$t('account')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("account") }}</label> <label>{{ $t("account") }}</label>
<div v-if="currentUser"> <div v-if="currentUser">
@@ -67,7 +67,7 @@
</div> </div>
</AppSection> </AppSection>
<AppSection ref="theme" :label="$t('theme')" no-legend> <AppSection ref="theme" :label="$t('theme')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("theme") }}</label> <label>{{ $t("theme") }}</label>
<SmartColorModePicker /> <SmartColorModePicker />
@@ -84,7 +84,7 @@
</div> </div>
</AppSection> </AppSection>
<AppSection ref="extensions" :label="$t('extensions')" no-legend> <AppSection ref="extensions" :label="$t('extensions')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("extensions") }}</label> <label>{{ $t("extensions") }}</label>
<div class="row-wrapper"> <div class="row-wrapper">
@@ -106,7 +106,7 @@
</div> </div>
</AppSection> </AppSection>
<AppSection ref="proxy" :label="$t('proxy')" no-legend> <AppSection ref="proxy" :label="$t('proxy')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("proxy") }}</label> <label>{{ $t("proxy") }}</label>
<div class="row-wrapper"> <div class="row-wrapper">
@@ -177,7 +177,7 @@
--> -->
</AppSection> </AppSection>
<AppSection ref="experiments" :label="$t('experiments')" no-legend> <AppSection ref="experiments" :label="$t('experiments')">
<div class="flex flex-col"> <div class="flex flex-col">
<label>{{ $t("experiments") }}</label> <label>{{ $t("experiments") }}</label>
<p class="info"> <p class="info">