Response.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. <template>
  2. <div
  3. class="flex flex-col flex-1 overflow-auto hide-scrollbar whitespace-nowrap"
  4. >
  5. <div
  6. v-if="responseString === 'loading'"
  7. class="flex flex-col items-center justify-center p-4 text-secondaryLight"
  8. >
  9. <SmartSpinner class="my-4" />
  10. <span class="text-secondaryLight">{{ t("state.loading") }}</span>
  11. </div>
  12. <div v-else-if="responseString" class="flex flex-col flex-1">
  13. <div
  14. class="sticky top-0 z-10 flex items-center justify-between pl-4 border-b bg-primary border-dividerLight"
  15. >
  16. <label class="font-semibold text-secondaryLight">
  17. {{ t("response.title") }}
  18. </label>
  19. <div class="flex">
  20. <ButtonSecondary
  21. v-tippy="{ theme: 'tooltip' }"
  22. :title="t('state.linewrap')"
  23. :class="{ '!text-accent': linewrapEnabled }"
  24. svg="wrap-text"
  25. @click.native.prevent="linewrapEnabled = !linewrapEnabled"
  26. />
  27. <ButtonSecondary
  28. ref="downloadResponse"
  29. v-tippy="{ theme: 'tooltip' }"
  30. :title="t('action.download_file')"
  31. :svg="downloadResponseIcon"
  32. @click.native="downloadResponse"
  33. />
  34. <ButtonSecondary
  35. ref="copyResponseButton"
  36. v-tippy="{ theme: 'tooltip' }"
  37. :title="t('action.copy')"
  38. :svg="copyResponseIcon"
  39. @click.native="copyResponse"
  40. />
  41. </div>
  42. </div>
  43. <div ref="schemaEditor" class="flex flex-col flex-1"></div>
  44. </div>
  45. <div
  46. v-else
  47. class="flex flex-col items-center justify-center p-4 text-secondaryLight"
  48. >
  49. <div class="flex pb-4 my-4 space-x-2">
  50. <div class="flex flex-col items-end text-right space-y-4">
  51. <span class="flex items-center flex-1">
  52. {{ t("shortcut.general.command_menu") }}
  53. </span>
  54. <span class="flex items-center flex-1">
  55. {{ t("shortcut.general.help_menu") }}
  56. </span>
  57. </div>
  58. <div class="flex flex-col space-y-4">
  59. <div class="flex">
  60. <span class="shortcut-key">/</span>
  61. </div>
  62. <div class="flex">
  63. <span class="shortcut-key">?</span>
  64. </div>
  65. </div>
  66. </div>
  67. <ButtonSecondary
  68. :label="`${t('app.documentation')}`"
  69. to="https://docs.hoppscotch.io/features/response"
  70. svg="external-link"
  71. blank
  72. outline
  73. reverse
  74. />
  75. </div>
  76. </div>
  77. </template>
  78. <script setup lang="ts">
  79. import { reactive, ref } from "@nuxtjs/composition-api"
  80. import { refAutoReset } from "@vueuse/core"
  81. import { useCodemirror } from "~/helpers/editor/codemirror"
  82. import { copyToClipboard } from "~/helpers/utils/clipboard"
  83. import {
  84. useReadonlyStream,
  85. useI18n,
  86. useToast,
  87. } from "~/helpers/utils/composables"
  88. import { gqlResponse$ } from "~/newstore/GQLSession"
  89. const t = useI18n()
  90. const toast = useToast()
  91. const responseString = useReadonlyStream(gqlResponse$, "")
  92. const schemaEditor = ref<any | null>(null)
  93. const linewrapEnabled = ref(true)
  94. useCodemirror(
  95. schemaEditor,
  96. responseString,
  97. reactive({
  98. extendedEditorConfig: {
  99. mode: "application/ld+json",
  100. readOnly: true,
  101. lineWrapping: linewrapEnabled,
  102. },
  103. linter: null,
  104. completer: null,
  105. environmentHighlights: false,
  106. })
  107. )
  108. const downloadResponseIcon = refAutoReset<"download" | "check">(
  109. "download",
  110. 1000
  111. )
  112. const copyResponseIcon = refAutoReset<"copy" | "check">("copy", 1000)
  113. const copyResponse = () => {
  114. copyToClipboard(responseString.value!)
  115. copyResponseIcon.value = "check"
  116. toast.success(`${t("state.copied_to_clipboard")}`)
  117. }
  118. const downloadResponse = () => {
  119. const dataToWrite = responseString.value
  120. const file = new Blob([dataToWrite!], { type: "application/json" })
  121. const a = document.createElement("a")
  122. const url = URL.createObjectURL(file)
  123. a.href = url
  124. a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}`
  125. document.body.appendChild(a)
  126. a.click()
  127. downloadResponseIcon.value = "check"
  128. toast.success(`${t("state.download_started")}`)
  129. setTimeout(() => {
  130. document.body.removeChild(a)
  131. URL.revokeObjectURL(url)
  132. }, 1000)
  133. }
  134. </script>