Request.vue (hoppscotch-2.2.1) | : | Request.vue (hoppscotch-3.0.0) | ||
---|---|---|---|---|
<template> | <template> | |||
<div | <div | |||
class="sticky top-0 z-10 flex p-4 space-x-2 overflow-x-auto bg-primary hide- scrollbar" | class="sticky top-0 z-20 flex-none p-4 sm:flex sm:flex-shrink-0 sm:space-x-2 bg-primary" | |||
> | > | |||
<div class="flex flex-1"> | <div | |||
class="flex flex-1 border rounded min-w-52 border-divider whitespace-nowra | ||||
p" | ||||
> | ||||
<div class="relative flex"> | <div class="relative flex"> | |||
<label for="method"> | <label for="method"> | |||
<tippy | <tippy | |||
ref="methodOptions" | ref="methodOptions" | |||
interactive | interactive | |||
trigger="click" | trigger="click" | |||
theme="popover" | theme="popover" | |||
arrow | arrow | |||
:on-shown="() => methodTippyActions.focus()" | ||||
> | > | |||
<template #trigger> | <span class="select-wrapper"> | |||
<span class="select-wrapper"> | <input | |||
<input | id="method" | |||
id="method" | class="flex px-4 py-2 font-semibold rounded-l cursor-pointer tra | |||
class="flex px-4 py-2 font-semibold border rounded-l cursor-po | nsition text-secondaryDark w-26 bg-primaryLight" | |||
inter bg-primaryLight border-divider text-secondaryDark w-26 hover:border-divide | :value="newMethod" | |||
rDark focus-visible:bg-transparent focus-visible:border-dividerDark" | :readonly="!isCustomMethod" | |||
:value="newMethod" | :placeholder="`${t('request.method')}`" | |||
:readonly="!isCustomMethod" | @input="onSelectMethod($event.target.value)" | |||
:placeholder="`${t('request.method')}`" | /> | |||
@input="onSelectMethod($event.target.value)" | </span> | |||
<template #content="{ hide }"> | ||||
<div | ||||
ref="methodTippyActions" | ||||
class="flex flex-col focus:outline-none" | ||||
tabindex="0" | ||||
role="menu" | ||||
@keyup.escape="hide()" | ||||
> | ||||
<SmartItem | ||||
v-for="(method, index) in methods" | ||||
:key="`method-${index}`" | ||||
:label="method" | ||||
@click=" | ||||
() => { | ||||
onSelectMethod(method) | ||||
hide() | ||||
} | ||||
" | ||||
/> | /> | |||
</span> | </div> | |||
</template> | </template> | |||
<SmartItem | ||||
v-for="(method, index) in methods" | ||||
:key="`method-${index}`" | ||||
:label="method" | ||||
@click.native="onSelectMethod(method)" | ||||
/> | ||||
</tippy> | </tippy> | |||
</label> | </label> | |||
</div> | </div> | |||
<div class="flex flex-1"> | <div | |||
class="flex flex-1 overflow-auto border-l rounded-r transition border-di | ||||
vider bg-primaryLight whitespace-nowrap" | ||||
> | ||||
<SmartEnvInput | <SmartEnvInput | |||
v-model="newEndpoint" | v-model="newEndpoint" | |||
:placeholder="`${t('request.url')}`" | :placeholder="`${t('request.url')}`" | |||
styles=" | ||||
bg-primaryLight | ||||
border border-divider | ||||
flex | ||||
flex-1 | ||||
rounded-r | ||||
text-secondaryDark | ||||
min-w-32 | ||||
py-1 | ||||
px-4 | ||||
hover:border-dividerDark | ||||
focus-visible:border-dividerDark | ||||
focus-visible:bg-transparent | ||||
" | ||||
@enter="newSendRequest()" | @enter="newSendRequest()" | |||
@paste="onPasteUrl($event)" | @paste="onPasteUrl($event)" | |||
/> | /> | |||
</div> | </div> | |||
</div> | </div> | |||
<div class="flex"> | <div class="flex mt-2 sm:mt-0"> | |||
<ButtonPrimary | <ButtonPrimary | |||
id="send" | id="send" | |||
class="flex-1 rounded-r-none min-w-20" | class="flex-1 rounded-r-none min-w-20" | |||
:label="`${!loading ? t('action.send') : t('action.cancel')}`" | :label="`${!loading ? t('action.send') : t('action.cancel')}`" | |||
@click.native="!loading ? newSendRequest() : cancelRequest()" | @click="!loading ? newSendRequest() : cancelRequest()" | |||
/> | /> | |||
<span class="flex"> | <span class="flex"> | |||
<tippy | <tippy | |||
ref="sendOptions" | ref="sendOptions" | |||
interactive | interactive | |||
trigger="click" | trigger="click" | |||
theme="popover" | theme="popover" | |||
arrow | arrow | |||
:on-shown="() => sendTippyActions.focus()" | :on-shown="() => sendTippyActions.focus()" | |||
> | > | |||
<template #trigger> | <ButtonPrimary | |||
<ButtonPrimary class="rounded-l-none" filled svg="chevron-down" /> | class="rounded-l-none" | |||
filled | ||||
:icon="IconChevronDown" | ||||
/> | ||||
<template #content="{ hide }"> | ||||
<div | ||||
ref="sendTippyActions" | ||||
class="flex flex-col focus:outline-none" | ||||
tabindex="0" | ||||
role="menu" | ||||
@keyup.c="curl.$el.click()" | ||||
@keyup.s="show.$el.click()" | ||||
@keyup.delete="clearAll.$el.click()" | ||||
@keyup.escape="hide()" | ||||
> | ||||
<SmartItem | ||||
ref="curl" | ||||
:label="`${t('import.curl')}`" | ||||
:icon="IconFileCode" | ||||
:shortcut="['C']" | ||||
@click=" | ||||
() => { | ||||
showCurlImportModal = !showCurlImportModal | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="show" | ||||
:label="`${t('show.code')}`" | ||||
:icon="IconCode2" | ||||
:shortcut="['S']" | ||||
@click=" | ||||
() => { | ||||
showCodegenModal = !showCodegenModal | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="clearAll" | ||||
:label="`${t('action.clear_all')}`" | ||||
:icon="IconRotateCCW" | ||||
:shortcut="['⌫']" | ||||
@click=" | ||||
() => { | ||||
clearContent() | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
</div> | ||||
</template> | </template> | |||
<div | ||||
ref="sendTippyActions" | ||||
class="flex flex-col focus:outline-none" | ||||
tabindex="0" | ||||
@keyup.c="curl.$el.click()" | ||||
@keyup.s="show.$el.click()" | ||||
@keyup.delete="clearAll.$el.click()" | ||||
@keyup.escape="sendOptions.tippy().hide()" | ||||
> | ||||
<SmartItem | ||||
ref="curl" | ||||
:label="`${t('import.curl')}`" | ||||
svg="file-code" | ||||
:shortcut="['C']" | ||||
@click.native=" | ||||
() => { | ||||
showCurlImportModal = !showCurlImportModal | ||||
sendOptions.tippy().hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="show" | ||||
:label="`${t('show.code')}`" | ||||
svg="code-2" | ||||
:shortcut="['S']" | ||||
@click.native=" | ||||
() => { | ||||
showCodegenModal = !showCodegenModal | ||||
sendOptions.tippy().hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="clearAll" | ||||
:label="`${t('action.clear_all')}`" | ||||
svg="rotate-ccw" | ||||
:shortcut="['⌫']" | ||||
@click.native=" | ||||
() => { | ||||
clearContent() | ||||
sendOptions.tippy().hide() | ||||
} | ||||
" | ||||
/> | ||||
</div> | ||||
</tippy> | </tippy> | |||
</span> | </span> | |||
<ButtonSecondary | <ButtonSecondary | |||
class="ml-2 rounded rounded-r-none" | class="flex-1 ml-2 rounded rounded-r-none" | |||
:label=" | :label="COLUMN_LAYOUT ? `${t('request.save')}` : ''" | |||
windowInnerWidth.x.value >= 768 && COLUMN_LAYOUT | ||||
? `${t('request.save')}` | ||||
: '' | ||||
" | ||||
filled | filled | |||
svg="save" | :icon="IconSave" | |||
@click.native="saveRequest()" | @click="saveRequest()" | |||
/> | /> | |||
<span class="flex"> | <span class="flex"> | |||
<tippy | <tippy | |||
ref="saveOptions" | ref="saveOptions" | |||
interactive | interactive | |||
trigger="click" | trigger="click" | |||
theme="popover" | theme="popover" | |||
arrow | arrow | |||
:on-shown="() => saveTippyActions.focus()" | :on-shown="() => saveTippyActions.focus()" | |||
> | > | |||
<template #trigger> | <ButtonSecondary | |||
<ButtonSecondary | :icon="IconChevronDown" | |||
svg="chevron-down" | filled | |||
filled | class="rounded rounded-l-none" | |||
class="rounded rounded-l-none" | ||||
/> | ||||
</template> | ||||
<input | ||||
id="request-name" | ||||
v-model="requestName" | ||||
:placeholder="`${t('request.name')}`" | ||||
name="request-name" | ||||
type="text" | ||||
autocomplete="off" | ||||
class="mb-2 input" | ||||
@keyup.enter="saveOptions.tippy().hide()" | ||||
/> | /> | |||
<div | <template #content="{ hide }"> | |||
ref="saveTippyActions" | <input | |||
class="flex flex-col focus:outline-none" | id="request-name" | |||
tabindex="0" | v-model="requestName" | |||
@keyup.c="copyRequestAction.$el.click()" | :placeholder="`${t('request.name')}`" | |||
@keyup.s="saveRequestAction.$el.click()" | name="request-name" | |||
@keyup.escape="saveOptions.tippy().hide()" | type="text" | |||
> | autocomplete="off" | |||
<SmartItem | class="mb-2 input" | |||
ref="copyRequestAction" | @keyup.enter="hide()" | |||
:label="shareButtonText" | ||||
:svg="copyLinkIcon" | ||||
:loading="fetchingShareLink" | ||||
:shortcut="['C']" | ||||
@click.native=" | ||||
() => { | ||||
copyRequest() | ||||
} | ||||
" | ||||
/> | /> | |||
<SmartItem | <div | |||
ref="saveRequestAction" | ref="saveTippyActions" | |||
:label="`${t('request.save_as')}`" | class="flex flex-col focus:outline-none" | |||
svg="folder-plus" | tabindex="0" | |||
:shortcut="['S']" | role="menu" | |||
@click.native=" | @keyup.c="copyRequestAction.$el.click()" | |||
() => { | @keyup.s="saveRequestAction.$el.click()" | |||
showSaveRequestModal = true | @keyup.escape="hide()" | |||
saveOptions.tippy().hide() | > | |||
} | <SmartItem | |||
" | ref="copyRequestAction" | |||
/> | :label="shareButtonText" | |||
</div> | :icon="copyLinkIcon" | |||
:loading="fetchingShareLink" | ||||
:shortcut="['C']" | ||||
@click=" | ||||
() => { | ||||
copyRequest() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
:icon="IconLink2" | ||||
:label="`${t('request.view_my_links')}`" | ||||
to="/profile" | ||||
/> | ||||
<hr /> | ||||
<SmartItem | ||||
ref="saveRequestAction" | ||||
:label="`${t('request.save_as')}`" | ||||
:icon="IconFolderPlus" | ||||
:shortcut="['S']" | ||||
@click=" | ||||
() => { | ||||
showSaveRequestModal = true | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
</div> | ||||
</template> | ||||
</tippy> | </tippy> | |||
</span> | </span> | |||
</div> | </div> | |||
<HttpImportCurl | <HttpImportCurl | |||
:text="curlText" | :text="curlText" | |||
:show="showCurlImportModal" | :show="showCurlImportModal" | |||
@hide-modal="showCurlImportModal = false" | @hide-modal="showCurlImportModal = false" | |||
/> | /> | |||
<HttpCodegenModal | <HttpCodegenModal | |||
:show="showCodegenModal" | :show="showCodegenModal" | |||
skipping to change at line 217 | skipping to change at line 229 | |||
/> | /> | |||
<CollectionsSaveRequest | <CollectionsSaveRequest | |||
mode="rest" | mode="rest" | |||
:show="showSaveRequestModal" | :show="showSaveRequestModal" | |||
@hide-modal="showSaveRequestModal = false" | @hide-modal="showSaveRequestModal = false" | |||
/> | /> | |||
</div> | </div> | |||
</template> | </template> | |||
<script setup lang="ts"> | <script setup lang="ts"> | |||
import { computed, ref, watch } from "@nuxtjs/composition-api" | import IconShare2 from "~icons/lucide/share-2" | |||
import IconCopy from "~icons/lucide/copy" | ||||
import IconCheck from "~icons/lucide/check" | ||||
import IconFileCode from "~icons/lucide/file-code" | ||||
import IconCode2 from "~icons/lucide/code-2" | ||||
import IconRotateCCW from "~icons/lucide/rotate-ccw" | ||||
import IconSave from "~icons/lucide/save" | ||||
import IconChevronDown from "~icons/lucide/chevron-down" | ||||
import IconLink2 from "~icons/lucide/link-2" | ||||
import IconFolderPlus from "~icons/lucide/folder-plus" | ||||
import { computed, ref, watch } from "vue" | ||||
import { isLeft, isRight } from "fp-ts/lib/Either" | import { isLeft, isRight } from "fp-ts/lib/Either" | |||
import * as E from "fp-ts/Either" | import * as E from "fp-ts/Either" | |||
import { cloneDeep } from "lodash-es" | ||||
import { refAutoReset } from "@vueuse/core" | ||||
import { | import { | |||
updateRESTResponse, | updateRESTResponse, | |||
restEndpoint$, | restEndpoint$, | |||
setRESTEndpoint, | setRESTEndpoint, | |||
restMethod$, | restMethod$, | |||
updateRESTMethod, | updateRESTMethod, | |||
resetRESTRequest, | resetRESTRequest, | |||
useRESTRequestName, | useRESTRequestName, | |||
getRESTSaveContext, | getRESTSaveContext, | |||
getRESTRequest, | getRESTRequest, | |||
restRequest$, | restRequest$, | |||
setRESTSaveContext, | setRESTSaveContext, | |||
} from "~/newstore/RESTSession" | } from "~/newstore/RESTSession" | |||
import { editRESTRequest } from "~/newstore/collections" | import { editRESTRequest } from "~/newstore/collections" | |||
import { runRESTRequest$ } from "~/helpers/RequestRunner" | import { runRESTRequest$ } from "~/helpers/RequestRunner" | |||
import { | import { | |||
useStreamSubscriber, | ||||
useStream, | useStream, | |||
useNuxt, | useStreamSubscriber, | |||
useI18n, | ||||
useToast, | ||||
useReadonlyStream, | useReadonlyStream, | |||
} from "~/helpers/utils/composables" | } from "@composables/stream" | |||
import { useI18n } from "@composables/i18n" | ||||
import { useToast } from "@composables/toast" | ||||
import { useSetting } from "@composables/settings" | ||||
import { startPageProgress, completePageProgress } from "@modules/loadingbar" | ||||
import { defineActionHandler } from "~/helpers/actions" | import { defineActionHandler } from "~/helpers/actions" | |||
import { copyToClipboard } from "~/helpers/utils/clipboard" | import { copyToClipboard } from "~/helpers/utils/clipboard" | |||
import { useSetting } from "~/newstore/settings" | ||||
import { overwriteRequestTeams } from "~/helpers/teams/utils" | ||||
import { apolloClient } from "~/helpers/apollo" | ||||
import useWindowSize from "~/helpers/utils/useWindowSize" | ||||
import { createShortcode } from "~/helpers/backend/mutations/Shortcode" | import { createShortcode } from "~/helpers/backend/mutations/Shortcode" | |||
import { runMutation } from "~/helpers/backend/GQLClient" | ||||
import { UpdateRequestDocument } from "~/helpers/backend/graphql" | ||||
const t = useI18n() | const t = useI18n() | |||
const methods = [ | const methods = [ | |||
"GET", | "GET", | |||
"POST", | "POST", | |||
"PUT", | "PUT", | |||
"PATCH", | "PATCH", | |||
"DELETE", | "DELETE", | |||
"HEAD", | "HEAD", | |||
"CONNECT", | "CONNECT", | |||
"OPTIONS", | "OPTIONS", | |||
"TRACE", | "TRACE", | |||
"CUSTOM", | "CUSTOM", | |||
] | ] | |||
const toast = useToast() | const toast = useToast() | |||
const nuxt = useNuxt() | ||||
const { subscribeToStream } = useStreamSubscriber() | const { subscribeToStream } = useStreamSubscriber() | |||
const newEndpoint = useStream(restEndpoint$, "", setRESTEndpoint) | const newEndpoint = useStream(restEndpoint$, "", setRESTEndpoint) | |||
const curlText = ref("") | const curlText = ref("") | |||
const newMethod = useStream(restMethod$, "", updateRESTMethod) | const newMethod = useStream(restMethod$, "", updateRESTMethod) | |||
const loading = ref(false) | const loading = ref(false) | |||
const showCurlImportModal = ref(false) | const showCurlImportModal = ref(false) | |||
const showCodegenModal = ref(false) | const showCodegenModal = ref(false) | |||
const showSaveRequestModal = ref(false) | const showSaveRequestModal = ref(false) | |||
const hasNavigatorShare = !!navigator.share | const hasNavigatorShare = !!navigator.share | |||
// Template refs | // Template refs | |||
const methodOptions = ref<any | null>(null) | const methodOptions = ref<any | null>(null) | |||
const saveOptions = ref<any | null>(null) | const saveOptions = ref<any | null>(null) | |||
const sendOptions = ref<any | null>(null) | const sendOptions = ref<any | null>(null) | |||
const methodTippyActions = ref<any | null>(null) | ||||
const sendTippyActions = ref<any | null>(null) | const sendTippyActions = ref<any | null>(null) | |||
const saveTippyActions = ref<any | null>(null) | const saveTippyActions = ref<any | null>(null) | |||
const curl = ref<any | null>(null) | const curl = ref<any | null>(null) | |||
const show = ref<any | null>(null) | const show = ref<any | null>(null) | |||
const clearAll = ref<any | null>(null) | const clearAll = ref<any | null>(null) | |||
const copyRequestAction = ref<any | null>(null) | const copyRequestAction = ref<any | null>(null) | |||
const saveRequestAction = ref<any | null>(null) | const saveRequestAction = ref<any | null>(null) | |||
// Update Nuxt Loading bar | // Update Nuxt Loading bar | |||
watch(loading, () => { | watch(loading, () => { | |||
if (loading.value) { | if (loading.value) { | |||
nuxt.value.$loading.start() | startPageProgress() | |||
} else { | } else { | |||
nuxt.value.$loading.finish() | completePageProgress() | |||
} | } | |||
}) | }) | |||
const newSendRequest = async () => { | const newSendRequest = async () => { | |||
if (newEndpoint.value === "" || /^\s+$/.test(newEndpoint.value)) { | if (newEndpoint.value === "" || /^\s+$/.test(newEndpoint.value)) { | |||
toast.error(`${t("empty.endpoint")}`) | toast.error(`${t("empty.endpoint")}`) | |||
return | return | |||
} | } | |||
ensureMethodInEndpoint() | ||||
loading.value = true | loading.value = true | |||
// Double calling is because the function returns a TaskEither than should be executed | // Double calling is because the function returns a TaskEither than should be executed | |||
const streamResult = await runRESTRequest$()() | const streamResult = await runRESTRequest$()() | |||
if (isRight(streamResult)) { | if (isRight(streamResult)) { | |||
subscribeToStream( | subscribeToStream( | |||
streamResult.right, | streamResult.right, | |||
(responseState) => { | (responseState) => { | |||
if (loading.value) { | if (loading.value) { | |||
skipping to change at line 348 | skipping to change at line 373 | |||
} else { | } else { | |||
error = streamResult.left | error = streamResult.left | |||
} | } | |||
updateRESTResponse({ | updateRESTResponse({ | |||
type: "script_fail", | type: "script_fail", | |||
error, | error, | |||
}) | }) | |||
} | } | |||
} | } | |||
const onPasteUrl = (e: { event: ClipboardEvent; previousValue: string }) => { | const ensureMethodInEndpoint = () => { | |||
if (!e) return | if ( | |||
!/^http[s]?:\/\//.test(newEndpoint.value) && | ||||
const clipboardData = e.event.clipboardData | !newEndpoint.value.startsWith("<<") | |||
) { | ||||
const domain = newEndpoint.value.split(/[/:#?]+/)[0] | ||||
if (domain === "localhost" || /([0-9]+\.)*[0-9]/.test(domain)) { | ||||
setRESTEndpoint("http://" + newEndpoint.value) | ||||
} else { | ||||
setRESTEndpoint("https://" + newEndpoint.value) | ||||
} | ||||
} | ||||
} | ||||
const pastedData = clipboardData?.getData("Text") | const onPasteUrl = (e: { pastedValue: string; prevValue: string }) => { | |||
if (!e) return | ||||
if (!pastedData) return | const pastedData = e.pastedValue | |||
if (isCURL(pastedData)) { | if (isCURL(pastedData)) { | |||
e.event.preventDefault() | ||||
showCurlImportModal.value = true | showCurlImportModal.value = true | |||
curlText.value = pastedData | curlText.value = pastedData | |||
newEndpoint.value = e.previousValue | newEndpoint.value = e.prevValue | |||
} | } | |||
} | } | |||
function isCURL(curl: string) { | function isCURL(curl: string) { | |||
return curl.includes("curl ") | return curl.includes("curl ") | |||
} | } | |||
const cancelRequest = () => { | const cancelRequest = () => { | |||
loading.value = false | loading.value = false | |||
updateRESTResponse(null) | updateRESTResponse(null) | |||
} | } | |||
const updateMethod = (method: string) => { | const updateMethod = (method: string) => { | |||
updateRESTMethod(method) | updateRESTMethod(method) | |||
} | } | |||
const onSelectMethod = (method: string) => { | const onSelectMethod = (method: string) => { | |||
updateMethod(method) | updateMethod(method) | |||
// Vue-tippy has no typescript support yet | ||||
methodOptions.value.tippy().hide() | ||||
} | } | |||
const clearContent = () => { | const clearContent = () => { | |||
resetRESTRequest() | resetRESTRequest() | |||
} | } | |||
const copyLinkIcon = hasNavigatorShare ? ref("share-2") : ref("copy") | const copyLinkIcon = refAutoReset< | |||
typeof IconShare2 | typeof IconCopy | typeof IconCheck | ||||
>(hasNavigatorShare ? IconShare2 : IconCopy, 1000) | ||||
const shareLink = ref<string | null>("") | const shareLink = ref<string | null>("") | |||
const fetchingShareLink = ref(false) | const fetchingShareLink = ref(false) | |||
const shareButtonText = computed(() => { | const shareButtonText = computed(() => { | |||
if (shareLink.value) { | if (shareLink.value) { | |||
return shareLink.value | return shareLink.value | |||
} else if (fetchingShareLink.value) { | } else if (fetchingShareLink.value) { | |||
return t("state.loading") | return t("state.loading") | |||
} else { | } else { | |||
return t("request.copy_link") | return t("request.copy_link") | |||
skipping to change at line 431 | skipping to change at line 466 | |||
copyShareLink(shareLink.value) | copyShareLink(shareLink.value) | |||
} | } | |||
fetchingShareLink.value = false | fetchingShareLink.value = false | |||
} | } | |||
} | } | |||
const copyShareLink = (shareLink: string) => { | const copyShareLink = (shareLink: string) => { | |||
if (navigator.share) { | if (navigator.share) { | |||
const time = new Date().toLocaleTimeString() | const time = new Date().toLocaleTimeString() | |||
const date = new Date().toLocaleDateString() | const date = new Date().toLocaleDateString() | |||
navigator | navigator.share({ | |||
.share({ | title: "Hoppscotch", | |||
title: "Hoppscotch", | text: `Hoppscotch • Open source API development ecosystem at ${time} on ${ | |||
text: `Hoppscotch • Open source API development ecosystem at ${time} on | date}`, | |||
${date}`, | url: `https://hopp.sh/r${shareLink}`, | |||
url: `https://hopp.sh/r${shareLink}`, | }) | |||
}) | ||||
.then(() => {}) | ||||
.catch(() => {}) | ||||
} else { | } else { | |||
copyLinkIcon.value = "check" | copyLinkIcon.value = IconCheck | |||
copyToClipboard(`https://hopp.sh/r${shareLink}`) | copyToClipboard(`https://hopp.sh/r${shareLink}`) | |||
toast.success(`${t("state.copied_to_clipboard")}`) | toast.success(`${t("state.copied_to_clipboard")}`) | |||
setTimeout(() => (copyLinkIcon.value = "copy"), 2000) | ||||
} | } | |||
} | } | |||
const cycleUpMethod = () => { | const cycleUpMethod = () => { | |||
const currentIndex = methods.indexOf(newMethod.value) | const currentIndex = methods.indexOf(newMethod.value) | |||
if (currentIndex === -1) { | if (currentIndex === -1) { | |||
// Most probs we are in CUSTOM mode | // Most probs we are in CUSTOM mode | |||
// Cycle up from CUSTOM is PATCH | // Cycle up from CUSTOM is PATCH | |||
updateMethod("PATCH") | updateMethod("PATCH") | |||
} else if (currentIndex === 0) { | } else if (currentIndex === 0) { | |||
skipping to change at line 479 | skipping to change at line 510 | |||
updateMethod(methods[currentIndex + 1]) | updateMethod(methods[currentIndex + 1]) | |||
} | } | |||
} | } | |||
const saveRequest = () => { | const saveRequest = () => { | |||
const saveCtx = getRESTSaveContext() | const saveCtx = getRESTSaveContext() | |||
if (!saveCtx) { | if (!saveCtx) { | |||
showSaveRequestModal.value = true | showSaveRequestModal.value = true | |||
return | return | |||
} | } | |||
if (saveCtx.originLocation === "user-collection") { | if (saveCtx.originLocation === "user-collection") { | |||
const req = getRESTRequest() | ||||
try { | try { | |||
editRESTRequest( | editRESTRequest( | |||
saveCtx.folderPath, | saveCtx.folderPath, | |||
saveCtx.requestIndex, | saveCtx.requestIndex, | |||
getRESTRequest() | getRESTRequest() | |||
) | ) | |||
setRESTSaveContext({ | ||||
originLocation: "user-collection", | ||||
folderPath: saveCtx.folderPath, | ||||
requestIndex: saveCtx.requestIndex, | ||||
req: cloneDeep(req), | ||||
}) | ||||
toast.success(`${t("request.saved")}`) | toast.success(`${t("request.saved")}`) | |||
} catch (e) { | } catch (e) { | |||
setRESTSaveContext(null) | setRESTSaveContext(null) | |||
saveRequest() | saveRequest() | |||
} | } | |||
} else if (saveCtx.originLocation === "team-collection") { | } else if (saveCtx.originLocation === "team-collection") { | |||
const req = getRESTRequest() | const req = getRESTRequest() | |||
// TODO: handle error case (NOTE: overwriteRequestTeams is async) | // TODO: handle error case (NOTE: overwriteRequestTeams is async) | |||
try { | try { | |||
overwriteRequestTeams( | runMutation(UpdateRequestDocument, { | |||
apolloClient, | requestID: saveCtx.requestID, | |||
JSON.stringify(req), | data: { | |||
req.name, | title: req.name, | |||
saveCtx.requestID | request: JSON.stringify(req), | |||
) | }, | |||
.then(() => { | })().then((result) => { | |||
toast.success(`${t("request.saved")}`) | if (E.isLeft(result)) { | |||
}) | ||||
.catch(() => { | ||||
toast.error(`${t("profile.no_permission")}`) | toast.error(`${t("profile.no_permission")}`) | |||
}) | } else { | |||
setRESTSaveContext({ | ||||
originLocation: "team-collection", | ||||
requestID: saveCtx.requestID, | ||||
req: cloneDeep(req), | ||||
}) | ||||
toast.success(`${t("request.saved")}`) | ||||
} | ||||
}) | ||||
} catch (error) { | } catch (error) { | |||
showSaveRequestModal.value = true | showSaveRequestModal.value = true | |||
toast.error(`${t("error.something_went_wrong")}`) | toast.error(`${t("error.something_went_wrong")}`) | |||
console.error(error) | console.error(error) | |||
} | } | |||
} | } | |||
} | } | |||
defineActionHandler("request.send-cancel", () => { | defineActionHandler("request.send-cancel", () => { | |||
if (!loading.value) newSendRequest() | if (!loading.value) newSendRequest() | |||
skipping to change at line 542 | skipping to change at line 586 | |||
defineActionHandler("request.method.put", () => updateMethod("PUT")) | defineActionHandler("request.method.put", () => updateMethod("PUT")) | |||
defineActionHandler("request.method.delete", () => updateMethod("DELETE")) | defineActionHandler("request.method.delete", () => updateMethod("DELETE")) | |||
defineActionHandler("request.method.head", () => updateMethod("HEAD")) | defineActionHandler("request.method.head", () => updateMethod("HEAD")) | |||
const isCustomMethod = computed(() => { | const isCustomMethod = computed(() => { | |||
return newMethod.value === "CUSTOM" || !methods.includes(newMethod.value) | return newMethod.value === "CUSTOM" || !methods.includes(newMethod.value) | |||
}) | }) | |||
const requestName = useRESTRequestName() | const requestName = useRESTRequestName() | |||
const windowInnerWidth = useWindowSize() | ||||
const COLUMN_LAYOUT = useSetting("COLUMN_LAYOUT") | const COLUMN_LAYOUT = useSetting("COLUMN_LAYOUT") | |||
</script> | </script> | |||
End of changes. 45 change blocks. | ||||
189 lines changed or deleted | 233 lines changed or added |