feat: response shortcuts (#2705)

This commit is contained in:
Liyas Thomas
2022-09-30 19:32:09 +05:30
committed by GitHub
parent 06a8d62dfe
commit 938f940f90
40 changed files with 307 additions and 107 deletions

View File

@@ -502,6 +502,11 @@
"send_request": "Stuur versoek",
"title": "Versoek"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "ارسل طلب",
"title": "طلب"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "انتقل الى الوضع الأسود",
"dark": "انتقل الى الوضع الليلي",

View File

@@ -502,6 +502,11 @@
"send_request": "Enviar sol.licitud",
"title": "Sol·licitud"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "发送请求",
"title": "请求"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "切换为黑色主题",
"dark": "切换为深色主题",

View File

@@ -502,6 +502,11 @@
"send_request": "Poslat žádost",
"title": "Žádost"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Send anmodning",
"title": "Anmodning"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Anfrage senden",
"title": "Anfrage"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Auf Schwarzes Design wechseln",
"dark": "Auf Dunkles Design wechseln",

View File

@@ -502,6 +502,11 @@
"send_request": "Στείλε αίτημα",
"title": "Αίτηση"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Send Request",
"title": "Request"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Enviar petición",
"title": "Petición"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Cambiar el tema al modo negro",
"dark": "Cambiar el tema al modo oscuro",

View File

@@ -502,6 +502,11 @@
"send_request": "Lähetä pyyntö",
"title": "Pyyntö"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Envoyer la requête",
"title": "Requête"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "שלח בקשה",
"title": "בַּקָשָׁה"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Kérés elküldése",
"title": "Kérés"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Téma átváltása fekete módra",
"dark": "Téma átváltása sötét módra",

View File

@@ -502,6 +502,11 @@
"send_request": "Invia richiesta",
"title": "Richiesta"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Send Request",
"title": "Request"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "요청 보내기",
"title": "요청"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "검은 테마로 전환",
"dark": "어두운 테마로 전환",

View File

@@ -502,6 +502,11 @@
"send_request": "Verstuur verzoek",
"title": "Verzoek"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Send forespørsel",
"title": "Be om"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Wyślij żądanie",
"title": "Żądania"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Enviar requisição",
"title": "Solicitar"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Trocar para o tema preto",
"dark": "Trocar para o tema escuro",

View File

@@ -502,6 +502,11 @@
"send_request": "Enviar pedido",
"title": "Solicitar"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Trimite cerere",
"title": "Cerere"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Послать запрос",
"title": "Запрос"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Пошаљите упит",
"title": "Захтев"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Skicka förfrågan",
"title": "Begäran"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "İstek gönder",
"title": "İstek"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Temayı siyah mod olarak ayarla",
"dark": "Temayı karanlık mod olarak ayarla",

View File

@@ -502,6 +502,11 @@
"send_request": "傳送請求",
"title": "請求"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "將主題切換至黑色模式",
"dark": "將主題切換至暗色模式",

View File

@@ -502,6 +502,11 @@
"send_request": "Відправляти запит",
"title": "Запит"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -502,6 +502,11 @@
"send_request": "Gửi yêu cầu",
"title": "Yêu cầu"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",

View File

@@ -3,7 +3,6 @@
v-if="response"
v-model="selectedLensTab"
styles="sticky z-10 bg-primary top-lowerPrimaryStickyFold"
render-inactive-tabs
>
<SmartTab
v-for="(lens, index) in validLenses"

View File

@@ -17,24 +17,28 @@
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${
previewEnabled ? t('hide.preview') : t('response.preview_html')
"
} <xmp>${getSpecialKey()}</xmp><xmp>Shift</xmp><xmp>P</xmp>`"
:icon="!previewEnabled ? IconEye : IconEyeOff"
@click.prevent="togglePreview"
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.download_file'
)} <xmp>${getSpecialKey()}</xmp><xmp>J</xmp>`"
:icon="downloadIcon"
@click="downloadResponse"
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.copy'
)} <xmp>${getSpecialKey()}</xmp><xmp>.</xmp>`"
:icon="copyIcon"
@click="copyResponse"
/>
@@ -70,6 +74,8 @@ import {
import { useCodemirror } from "@composables/codemirror"
import { useI18n } from "@composables/i18n"
import type { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import { defineActionHandler } from "~/helpers/actions"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
const t = useI18n()
@@ -105,6 +111,10 @@ useCodemirror(
environmentHighlights: true,
})
)
defineActionHandler("response.preview.toggle", () => togglePreview())
defineActionHandler("response.file.download", () => downloadResponse())
defineActionHandler("response.copy", () => copyResponse())
</script>
<style lang="scss" scoped>

View File

@@ -9,9 +9,11 @@
<div class="flex">
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
:icon="downloadIcon === 'download' ? IconDownload : IconCheck"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.download_file'
)} <xmp>${getSpecialKey()}</xmp><xmp>J</xmp>`"
:icon="downloadIcon"
@click="downloadResponse"
/>
</div>
@@ -25,92 +27,76 @@
</div>
</template>
<script lang="ts">
import IconDownload from "~icons/lucide/download"
import IconCheck from "~icons/lucide/check"
<script setup lang="ts">
import { useI18n } from "@composables/i18n"
import { useToast } from "@composables/toast"
import { defineComponent } from "vue"
import { defineActionHandler } from "~/helpers/actions"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
import { computed, onMounted, ref, watch } from "vue"
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import { useDownloadResponse } from "~/composables/lens-actions"
import { flow, pipe } from "fp-ts/function"
import * as S from "fp-ts/string"
import * as RNEA from "fp-ts/ReadonlyNonEmptyArray"
import * as A from "fp-ts/Array"
import * as O from "fp-ts/Option"
import { objFieldMatches } from "~/helpers/functional/object"
export default defineComponent({
props: {
response: { type: Object, default: () => ({}) },
},
setup() {
return {
t: useI18n(),
toast: useToast(),
IconDownload,
IconCheck,
}
},
data() {
return {
imageSource: "",
downloadIcon: "download",
}
},
computed: {
responseType() {
return (
this.response.headers.find(
(h) => h.key.toLowerCase() === "content-type"
).value || ""
const t = useI18n()
const props = defineProps<{
response: HoppRESTResponse & { type: "success" | "fail" }
}>()
const imageSource = ref("")
const responseType = computed(() =>
pipe(
props.response,
O.fromPredicate(objFieldMatches("type", ["fail", "success"] as const)),
O.chain(
// Try getting content-type
flow(
(res) => res.headers,
A.findFirst((h) => h.key.toLowerCase() === "content-type"),
O.map(flow((h) => h.value, S.split(";"), RNEA.head, S.toLowerCase))
)
.split(";")[0]
.toLowerCase()
},
},
watch: {
response: {
immediate: true,
handler() {
this.imageSource = ""
),
O.getOrElse(() => "text/plain")
)
)
const buf = this.response.body
const bytes = new Uint8Array(buf)
const blob = new Blob([bytes.buffer])
const { downloadIcon, downloadResponse } = useDownloadResponse(
responseType.value,
computed(() => props.response.body)
)
const reader = new FileReader()
reader.onload = ({ target }) => {
this.imageSource = target.result
}
reader.readAsDataURL(blob)
},
},
},
mounted() {
this.imageSource = ""
watch(props.response, () => {
imageSource.value = ""
const buf = props.response.body
const bytes = new Uint8Array(buf)
const blob = new Blob([bytes.buffer])
const buf = this.response.body
const bytes = new Uint8Array(buf)
const blob = new Blob([bytes.buffer])
const reader = new FileReader()
reader.onload = ({ target }) => {
this.imageSource = target.result
}
reader.readAsDataURL(blob)
},
methods: {
downloadResponse() {
const dataToWrite = this.response.body
const file = new Blob([dataToWrite], { type: this.responseType })
const a = document.createElement("a")
const url = URL.createObjectURL(file)
a.href = url
// TODO get uri from meta
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
document.body.appendChild(a)
a.click()
this.downloadIcon = "check"
this.toast.success(this.t("state.download_started"))
setTimeout(() => {
document.body.removeChild(a)
URL.revokeObjectURL(url)
this.downloadIcon = "download"
}, 1000)
},
},
const reader = new FileReader()
reader.onload = ({ target }) => {
// target.result will always be string because we're using FileReader.readAsDataURL
imageSource.value = target!.result as string
}
reader.readAsDataURL(blob)
})
onMounted(() => {
imageSource.value = ""
const buf = props.response.body
const bytes = new Uint8Array(buf)
const blob = new Blob([bytes.buffer])
const reader = new FileReader()
reader.onload = ({ target }) => {
// target.result will always be string because we're using FileReader.readAsDataURL
imageSource.value = target!.result as string
}
reader.readAsDataURL(blob)
})
defineActionHandler("response.file.download", () => downloadResponse())
</script>

View File

@@ -28,15 +28,19 @@
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.download_file'
)} <xmp>${getSpecialKey()}</xmp><xmp>J</xmp>`"
:icon="downloadIcon"
@click="downloadResponse"
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.copy'
)} <xmp>${getSpecialKey()}</xmp><xmp>.</xmp>`"
:icon="copyIcon"
@click="copyResponse"
/>
@@ -215,6 +219,8 @@ import {
useResponseBody,
useDownloadResponse,
} from "@composables/lens-actions"
import { defineActionHandler } from "~/helpers/actions"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
const t = useI18n()
@@ -358,6 +364,9 @@ const toggleFilterState = () => {
filterQueryText.value = ""
toggleFilter.value = !toggleFilter.value
}
defineActionHandler("response.file.download", () => downloadResponse())
defineActionHandler("response.copy", () => copyResponse())
</script>
<style lang="scss" scoped>

View File

@@ -9,8 +9,10 @@
<div class="flex">
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.download_file'
)} <xmp>${getSpecialKey()}</xmp><xmp>J</xmp>`"
:icon="downloadIcon"
@click="downloadResponse"
/>
@@ -30,6 +32,8 @@ import VuePdfEmbed from "vue-pdf-embed"
import { useI18n } from "@composables/i18n"
import { useDownloadResponse } from "@composables/lens-actions"
import { HoppRESTResponse } from "~/helpers/types/HoppRESTResponse"
import { defineActionHandler } from "~/helpers/actions"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
const t = useI18n()
@@ -51,4 +55,6 @@ const { downloadIcon, downloadResponse } = useDownloadResponse(
"application/pdf",
computed(() => props.response.body)
)
defineActionHandler("response.file.download", () => downloadResponse())
</script>

View File

@@ -17,15 +17,19 @@
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.download_file'
)} <xmp>${getSpecialKey()}</xmp><xmp>J</xmp>`"
:icon="downloadIcon"
@click="downloadResponse"
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.copy'
)} <xmp>${getSpecialKey()}</xmp><xmp>.</xmp>`"
:icon="copyIcon"
@click="copyResponse"
/>
@@ -52,6 +56,8 @@ import {
useCopyResponse,
} from "@composables/lens-actions"
import { objFieldMatches } from "~/helpers/functional/object"
import { defineActionHandler } from "~/helpers/actions"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
const t = useI18n()
@@ -107,4 +113,7 @@ useCodemirror(
environmentHighlights: true,
})
)
defineActionHandler("response.file.download", () => downloadResponse())
defineActionHandler("response.copy", () => copyResponse())
</script>

View File

@@ -17,15 +17,19 @@
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.download_file')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.download_file'
)} <xmp>${getSpecialKey()}</xmp><xmp>J</xmp>`"
:icon="downloadIcon"
@click="downloadResponse"
/>
<ButtonSecondary
v-if="response.body"
v-tippy="{ theme: 'tooltip' }"
:title="t('action.copy')"
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${t(
'action.copy'
)} <xmp>${getSpecialKey()}</xmp><xmp>.</xmp>`"
:icon="copyIcon"
@click="copyResponse"
/>
@@ -46,6 +50,8 @@ import {
useDownloadResponse,
useCopyResponse,
} from "@composables/lens-actions"
import { defineActionHandler } from "~/helpers/actions"
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
const t = useI18n()
@@ -88,4 +94,7 @@ useCodemirror(
environmentHighlights: true,
})
)
defineActionHandler("response.file.download", () => downloadResponse())
defineActionHandler("response.copy", () => copyResponse())
</script>

View File

@@ -32,6 +32,9 @@ export type HoppAction =
| "settings.theme.light" // Use light theme
| "settings.theme.dark" // Use dark theme
| "settings.theme.black" // Use black theme
| "response.preview.toggle" // Toggle response preview
| "response.file.download" // Download response as file
| "response.copy" // Copy response to clipboard
type BoundActionList = {
// eslint-disable-next-line no-unused-vars

View File

@@ -23,7 +23,7 @@ type Key =
| "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t"
| "u" | "v" | "w" | "x" | "y" | "z" | "0" | "1" | "2" | "3"
| "4" | "5" | "6" | "7" | "8" | "9" | "up" | "down" | "left"
| "right" | "/" | "?"
| "right" | "/" | "?" | "."
/* eslint-enable */
type ModifierBasedShortcutKey = `${ModifierKeys}-${Key}`
@@ -58,6 +58,9 @@ export const bindings: {
"alt-d": "navigation.jump.documentation",
"alt-m": "navigation.jump.profile",
"alt-s": "navigation.jump.settings",
"ctrl-shift-p": "response.preview.toggle",
"ctrl-j": "response.file.download",
"ctrl-.": "response.copy",
}
/**
@@ -130,6 +133,9 @@ function getPressedKey(ev: KeyboardEvent): Key | null {
// Check if question mark
if (val === "/") return "/"
// Check if period
if (val === ".") return "."
// If no other cases match, this is not a valid key
return null
}

View File

@@ -79,6 +79,19 @@ export default [
},
],
},
{
section: "shortcut.response.title",
shortcuts: [
{
keys: [getPlatformSpecialKey(), "J"],
label: "shortcut.response.download",
},
{
keys: [getPlatformSpecialKey(), "."],
label: "shortcut.response.copy",
},
],
},
{
section: "shortcut.navigation.title",
shortcuts: [