123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- <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
- />
- <div class="flex">
- <HoppButtonSecondary
- :label="t('app.open_in_hoppscotch')"
- :to="sharedRequestURL"
- blank
- />
- </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 px-4 py-2 truncate rounded-r"
- >
- {{ tab.document.request.endpoint }}
- </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"
- />
- <HttpResponse :document="tab.document" :is-embed="true" />
- </div>
- </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"
- const t = useI18n()
- const toast = useToast()
- const props = defineProps<{
- modelTab: HoppTab<HoppRESTDocument>
- properties: string[]
- sharedRequestID: string
- }>()
- const tab = useModel(props, "modelTab")
- const selectedOptionTab = ref(props.properties[0])
- const requestCancelFunc: Ref<(() => void) | null> = ref(null)
- const loading = ref(false)
- const baseURL = import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
- const sharedRequestURL = computed(() => {
- return `${baseURL}/r/${props.sharedRequestID}`
- })
- 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>
|