fix: embeds response and request option section getting hidden bug (#4181)

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Nivedin
2024-07-18 15:13:43 +05:30
committed by GitHub
parent 7ebc9a6fd6
commit 8c6b80dc42
13 changed files with 251 additions and 196 deletions

View File

@@ -69,6 +69,8 @@ declare module 'vue' {
CookiesAllModal: typeof import('./components/cookies/AllModal.vue')['default']
CookiesEditCookie: typeof import('./components/cookies/EditCookie.vue')['default']
Embeds: typeof import('./components/embeds/index.vue')['default']
EmbedsHeader: typeof import('./components/embeds/Header.vue')['default']
EmbedsRequest: typeof import('./components/embeds/Request.vue')['default']
Environments: typeof import('./components/environments/index.vue')['default']
EnvironmentsAdd: typeof import('./components/environments/Add.vue')['default']
EnvironmentsImportExport: typeof import('./components/environments/ImportExport.vue')['default']

View File

@@ -22,7 +22,7 @@
<Pane
:size="PANE_MAIN_TOP_SIZE"
class="flex flex-col !overflow-auto"
min-size="25"
:min-size="isEmbed ? 12 : 25"
>
<slot name="primary" />
</Pane>
@@ -78,6 +78,10 @@ const props = defineProps({
type: String,
default: null,
},
isEmbed: {
type: Boolean,
default: false,
},
})
type PaneEvent = {

View File

@@ -0,0 +1,33 @@
<template>
<div class="flex flex-col">
<header
class="flex items-center justify-between flex-1 flex-shrink-0 px-2 py-2 space-x-2 overflow-x-auto overflow-y-hidden"
>
<div class="flex items-center justify-between flex-1 space-x-2">
<HoppButtonSecondary
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
:label="t('app.name')"
to="https://hoppscotch.io"
blank
/>
<div class="flex">
<HoppButtonSecondary
:label="t('app.open_in_hoppscotch')"
:to="sharedRequestURL"
blank
/>
</div>
</div>
</header>
</div>
</template>
<script lang="ts" setup>
import { useI18n } from "~/composables/i18n"
const t = useI18n()
defineProps<{
sharedRequestURL: string
}>()
</script>

View File

@@ -0,0 +1,185 @@
<template>
<div class="sticky top-0 z-10">
<div
class="flex-none flex-shrink-0 p-4 bg-primary sm:flex sm:flex-shrink-0 sm:space-x-2"
>
<div
class="flex flex-1 overflow-hidden border divide-x rounded text-secondaryDark divide-divider min-w-[12rem] overflow-x-auto border-divider"
>
<span
class="flex items-center justify-center px-4 py-2 font-semibold transition rounded-l"
>
{{ tab.document.request.method }}
</span>
<div
class="flex items-center flex-1 flex-shrink-0 min-w-0 truncate rounded-r"
>
<SmartEnvInput
v-model="tab.document.request.endpoint"
:readonly="true"
:envs="tabRequestVariables"
/>
</div>
</div>
<div class="flex mt-2 space-x-2 sm:mt-0">
<HoppButtonPrimary
id="send"
:title="`${t(
'action.send'
)} <kbd>${getSpecialKey()}</kbd><kbd>↩</kbd>`"
:label="`${!loading ? t('action.send') : t('action.cancel')}`"
class="flex-1 min-w-20"
outline
@click="!loading ? newSendRequest() : cancelRequest()"
/>
<div class="flex">
<HoppButtonSecondary
:title="`${t(
'request.save'
)} <kbd>${getSpecialKey()}</kbd><kbd>S</kbd>`"
:label="t('request.save')"
filled
:icon="IconSave"
class="flex-1 rounded"
blank
outline
:to="sharedRequestURL"
/>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
import IconSave from "~icons/lucide/save"
import { Ref } from "vue"
import { computed, useModel } from "vue"
import { ref } from "vue"
import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast"
import * as E from "fp-ts/Either"
import { useStreamSubscriber } from "~/composables/stream"
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import { runRESTRequest$ } from "~/helpers/RequestRunner"
import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document"
const toast = useToast()
const t = useI18n()
const props = defineProps<{
modelTab: HoppTab<HoppRESTDocument>
sharedRequestURL: string
}>()
const tab = useModel(props, "modelTab")
const requestCancelFunc: Ref<(() => void) | null> = ref(null)
const loading = ref(false)
const tabRequestVariables = computed(() => {
return tab.value.document.request.requestVariables.map(({ key, value }) => ({
key,
value,
secret: false,
sourceEnv: "RequestVariable",
}))
})
const { subscribeToStream } = useStreamSubscriber()
const newSendRequest = async () => {
if (newEndpoint.value === "" || /^\s+$/.test(newEndpoint.value)) {
toast.error(`${t("empty.endpoint")}`)
return
}
ensureMethodInEndpoint()
loading.value = true
const [cancel, streamPromise] = runRESTRequest$(tab)
const streamResult = await streamPromise
requestCancelFunc.value = cancel
if (E.isRight(streamResult)) {
subscribeToStream(
streamResult.right,
(responseState) => {
if (loading.value) {
// Check exists because, loading can be set to false
// when cancelled
updateRESTResponse(responseState)
}
},
() => {
loading.value = false
},
() => {
// TODO: Change this any to a proper type
const result = (streamResult.right as any).value
if (
result.type === "network_fail" &&
result.error?.error === "NO_PW_EXT_HOOK"
) {
const errorResponse: HoppRESTResponse = {
type: "extension_error",
error: result.error.humanMessage.heading,
component: result.error.component,
req: result.req,
}
updateRESTResponse(errorResponse)
}
loading.value = false
}
)
} else {
loading.value = false
toast.error(`${t("error.script_fail")}`)
let error: Error
if (typeof streamResult.left === "string") {
error = { name: "RequestFailure", message: streamResult.left }
} else {
error = streamResult.left
}
updateRESTResponse({
type: "script_fail",
error,
})
}
}
const updateRESTResponse = (response: HoppRESTResponse | null) => {
tab.value.document.response = response
}
const newEndpoint = computed(() => {
return tab.value.document.request.endpoint
})
const ensureMethodInEndpoint = () => {
if (
!/^http[s]?:\/\//.test(newEndpoint.value) &&
!newEndpoint.value.startsWith("<<")
) {
const domain = newEndpoint.value.split(/[/:#?]+/)[0]
if (domain === "localhost" || /([0-9]+\.)*[0-9]/.test(domain)) {
tab.value.document.request.endpoint =
"http://" + tab.value.document.request.endpoint
} else {
tab.value.document.request.endpoint =
"https://" + tab.value.document.request.endpoint
}
}
}
const cancelRequest = () => {
loading.value = false
requestCancelFunc.value?.()
updateRESTResponse(null)
}
</script>

View File

@@ -1,105 +1,34 @@
<template>
<div class="flex flex-col flex-1">
<header
class="flex items-center justify-between flex-1 flex-shrink-0 px-2 py-2 space-x-2 overflow-x-auto overflow-y-hidden"
>
<div class="flex items-center justify-between flex-1 space-x-2">
<HoppButtonSecondary
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
:label="t('app.name')"
to="https://hoppscotch.io"
blank
<AppPaneLayout layout-id="embed-primary" :is-embed="true">
<template #primary>
<EmbedsHeader :shared-request-u-r-l="sharedRequestURL" />
<div class="flex flex-col flex-1">
<EmbedsRequest
:model-tab="modelTab"
:shared-request-u-r-l="sharedRequestURL"
/>
<div class="flex">
<HoppButtonSecondary
:label="t('app.open_in_hoppscotch')"
:to="sharedRequestURL"
blank
<div class="flex flex-col flex-1">
<HttpRequestOptions
v-model="tab.document.request"
v-model:option-tab="selectedOptionTab"
:properties="properties"
:envs="tabRequestVariables"
/>
</div>
</div>
</header>
<div class="sticky top-0 z-10 flex-1">
<div
class="flex-none flex-shrink-0 p-4 bg-primary sm:flex sm:flex-shrink-0 sm:space-x-2"
>
<div
class="flex flex-1 overflow-hidden border divide-x rounded text-secondaryDark divide-divider min-w-[12rem] overflow-x-auto border-divider"
>
<span
class="flex items-center justify-center px-4 py-2 font-semibold transition rounded-l"
>
{{ tab.document.request.method }}
</span>
<div
class="flex items-center flex-1 flex-shrink-0 min-w-0 truncate rounded-r"
>
<SmartEnvInput
v-model="tab.document.request.endpoint"
:readonly="true"
:envs="tabRequestVariables"
/>
</div>
</div>
<div class="flex mt-2 space-x-2 sm:mt-0">
<HoppButtonPrimary
id="send"
:title="`${t(
'action.send'
)} <kbd>${getSpecialKey()}</kbd><kbd>↩</kbd>`"
:label="`${!loading ? t('action.send') : t('action.cancel')}`"
class="flex-1 min-w-20"
outline
@click="!loading ? newSendRequest() : cancelRequest()"
/>
<div class="flex">
<HoppButtonSecondary
:title="`${t(
'request.save'
)} <kbd>${getSpecialKey()}</kbd><kbd>S</kbd>`"
:label="t('request.save')"
filled
:icon="IconSave"
class="flex-1 rounded"
blank
outline
:to="sharedRequestURL"
/>
</div>
</div>
</div>
</div>
<HttpRequestOptions
v-model="tab.document.request"
v-model:option-tab="selectedOptionTab"
:properties="properties"
:envs="tabRequestVariables"
/>
<HttpResponse
v-if="tab.document.response"
:document="tab.document"
:is-embed="true"
/>
</div>
</template>
<template #secondary>
<HttpResponse :document="tab.document" :is-embed="true" />
</template>
</AppPaneLayout>
</template>
<script lang="ts" setup>
import { Ref } from "vue"
import { computed, useModel } from "vue"
import { ref } from "vue"
import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
import * as E from "fp-ts/Either"
import { useStreamSubscriber } from "~/composables/stream"
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import { runRESTRequest$ } from "~/helpers/RequestRunner"
import { HoppTab } from "~/services/tab"
import { HoppRESTDocument } from "~/helpers/rest/document"
import IconSave from "~icons/lucide/save"
import { RESTOptionTabs } from "../http/RequestOptions.vue"
const t = useI18n()
const toast = useToast()
const props = defineProps<{
modelTab: HoppTab<HoppRESTDocument>
@@ -111,10 +40,6 @@ const tab = useModel(props, "modelTab")
const selectedOptionTab = ref<RESTOptionTabs>(props.properties[0])
const requestCancelFunc: Ref<(() => void) | null> = ref(null)
const loading = ref(false)
const shortcodeBaseURL =
import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
@@ -130,98 +55,4 @@ const tabRequestVariables = computed(() => {
sourceEnv: "RequestVariable",
}))
})
const { subscribeToStream } = useStreamSubscriber()
const newSendRequest = async () => {
if (newEndpoint.value === "" || /^\s+$/.test(newEndpoint.value)) {
toast.error(`${t("empty.endpoint")}`)
return
}
ensureMethodInEndpoint()
loading.value = true
const [cancel, streamPromise] = runRESTRequest$(tab)
const streamResult = await streamPromise
requestCancelFunc.value = cancel
if (E.isRight(streamResult)) {
subscribeToStream(
streamResult.right,
(responseState) => {
if (loading.value) {
// Check exists because, loading can be set to false
// when cancelled
updateRESTResponse(responseState)
}
},
() => {
loading.value = false
},
() => {
// TODO: Change this any to a proper type
const result = (streamResult.right as any).value
if (
result.type === "network_fail" &&
result.error?.error === "NO_PW_EXT_HOOK"
) {
const errorResponse: HoppRESTResponse = {
type: "extension_error",
error: result.error.humanMessage.heading,
component: result.error.component,
req: result.req,
}
updateRESTResponse(errorResponse)
}
loading.value = false
}
)
} else {
loading.value = false
toast.error(`${t("error.script_fail")}`)
let error: Error
if (typeof streamResult.left === "string") {
error = { name: "RequestFailure", message: streamResult.left }
} else {
error = streamResult.left
}
updateRESTResponse({
type: "script_fail",
error,
})
}
}
const updateRESTResponse = (response: HoppRESTResponse | null) => {
tab.value.document.response = response
}
const newEndpoint = computed(() => {
return tab.value.document.request.endpoint
})
const ensureMethodInEndpoint = () => {
if (
!/^http[s]?:\/\//.test(newEndpoint.value) &&
!newEndpoint.value.startsWith("<<")
) {
const domain = newEndpoint.value.split(/[/:#?]+/)[0]
if (domain === "localhost" || /([0-9]+\.)*[0-9]/.test(domain)) {
tab.value.document.request.endpoint =
"http://" + tab.value.document.request.endpoint
} else {
tab.value.document.request.endpoint =
"https://" + tab.value.document.request.endpoint
}
}
}
const cancelRequest = () => {
loading.value = false
requestCancelFunc.value?.()
updateRESTResponse(null)
}
</script>

View File

@@ -49,7 +49,7 @@
/>
</div>
</div>
<div v-if="bulkMode" class="h-full relative w-full">
<div v-if="bulkMode" class="h-full relative w-full flex flex-col flex-1">
<div ref="bulkEditor" class="absolute inset-0"></div>
</div>
<div v-else>

View File

@@ -44,7 +44,7 @@
/>
</div>
</div>
<div v-if="bulkMode" class="h-full relative">
<div v-if="bulkMode" class="h-full relative flex flex-col flex-1">
<div ref="bulkEditor" class="absolute inset-0"></div>
</div>
<div v-else>

View File

@@ -59,7 +59,7 @@
/>
</div>
</div>
<div class="h-full relative">
<div class="h-full relative flex flex-col flex-1">
<div ref="rawBodyParameters" class="absolute inset-0"></div>
</div>
</div>

View File

@@ -46,7 +46,7 @@
/>
</div>
</div>
<div v-if="bulkMode" class="h-full relative">
<div v-if="bulkMode" class="h-full relative flex flex-col flex-1">
<div ref="bulkEditor" class="absolute inset-0"></div>
</div>
<div v-else>

View File

@@ -44,7 +44,7 @@
/>
</div>
</div>
<div v-if="bulkMode" class="h-full relative">
<div v-if="bulkMode" class="h-full relative w-full flex flex-col flex-1">
<div ref="bulkEditor" class="absolute inset-0"></div>
</div>
<div v-else>

View File

@@ -119,7 +119,7 @@
/>
</div>
</div>
<div class="h-full relative overflow-auto">
<div class="h-full relative overflow-auto flex flex-col flex-1">
<div
ref="jsonResponse"
:class="toggleFilter ? 'responseToggleOn' : 'responseToggleOff'"

View File

@@ -35,7 +35,7 @@
/>
</div>
</div>
<div class="h-full relative overflow-auto">
<div class="h-full relative overflow-auto flex flex-col flex-1">
<div ref="rawResponse" class="absolute inset-0"></div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex flex-col justify-center">
<div class="flex flex-col justify-center h-screen">
<div
v-if="sharedRequestDetails.loading"
class="flex justify-center items-center py-5"