Response.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <template>
  2. <AppSection ref="response" label="response">
  3. <div
  4. v-if="responseString"
  5. class="
  6. bg-primary
  7. border-b border-dividerLight
  8. flex flex-1
  9. pl-4
  10. top-0
  11. z-10
  12. sticky
  13. items-center
  14. justify-between
  15. "
  16. >
  17. <label class="font-semibold text-secondaryLight">
  18. {{ $t("response.title") }}
  19. </label>
  20. <div class="flex">
  21. <ButtonSecondary
  22. ref="downloadResponse"
  23. v-tippy="{ theme: 'tooltip' }"
  24. :title="$t('action.download_file')"
  25. :svg="downloadResponseIcon"
  26. @click.native="downloadResponse"
  27. />
  28. <ButtonSecondary
  29. ref="copyResponseButton"
  30. v-tippy="{ theme: 'tooltip' }"
  31. :title="$t('action.copy')"
  32. :svg="copyResponseIcon"
  33. @click.native="copyResponse"
  34. />
  35. </div>
  36. </div>
  37. <SmartAceEditor
  38. v-if="responseString"
  39. :value="responseString"
  40. :lang="'json'"
  41. :lint="false"
  42. :options="{
  43. maxLines: Infinity,
  44. minLines: 16,
  45. autoScrollEditorIntoView: true,
  46. readOnly: true,
  47. showPrintMargin: false,
  48. useWorker: false,
  49. }"
  50. styles="border-b border-dividerLight"
  51. />
  52. <div
  53. v-else
  54. class="
  55. flex flex-col flex-1
  56. text-secondaryLight
  57. p-4
  58. items-center
  59. justify-center
  60. "
  61. >
  62. <div class="flex space-x-2 pb-4">
  63. <div class="flex flex-col space-y-4 items-end">
  64. <span class="flex flex-1 items-center">
  65. {{ $t("shortcut.request.send_request") }}
  66. </span>
  67. <span class="flex flex-1 items-center">
  68. {{ $t("shortcut.general.show_all") }}
  69. </span>
  70. <!-- <span class="flex flex-1 items-center">
  71. {{ $t("shortcut.general.command_menu") }}
  72. </span>
  73. <span class="flex flex-1 items-center">
  74. {{ $t("shortcut.general.help_menu") }}
  75. </span> -->
  76. </div>
  77. <div class="flex flex-col space-y-4">
  78. <div class="flex">
  79. <span class="shortcut-key">{{ getSpecialKey() }}</span>
  80. <span class="shortcut-key">G</span>
  81. </div>
  82. <div class="flex">
  83. <span class="shortcut-key">{{ getSpecialKey() }}</span>
  84. <span class="shortcut-key">K</span>
  85. </div>
  86. <!-- <div class="flex">
  87. <span class="shortcut-key">/</span>
  88. </div>
  89. <div class="flex">
  90. <span class="shortcut-key">?</span>
  91. </div> -->
  92. </div>
  93. </div>
  94. <ButtonSecondary
  95. :label="$t('app.documentation')"
  96. to="https://docs.hoppscotch.io"
  97. svg="external-link"
  98. blank
  99. outline
  100. reverse
  101. />
  102. </div>
  103. </AppSection>
  104. </template>
  105. <script lang="ts">
  106. import {
  107. defineComponent,
  108. PropType,
  109. ref,
  110. useContext,
  111. } from "@nuxtjs/composition-api"
  112. import { GQLConnection } from "~/helpers/GQLConnection"
  113. import { getPlatformSpecialKey } from "~/helpers/platformutils"
  114. import { copyToClipboard } from "~/helpers/utils/clipboard"
  115. import { useReadonlyStream } from "~/helpers/utils/composables"
  116. import { gqlResponse$ } from "~/newstore/GQLSession"
  117. export default defineComponent({
  118. props: {
  119. conn: {
  120. type: Object as PropType<GQLConnection>,
  121. required: true,
  122. },
  123. },
  124. setup() {
  125. const {
  126. $toast,
  127. app: { i18n },
  128. } = useContext()
  129. const t = i18n.t.bind(i18n)
  130. const responseString = useReadonlyStream(gqlResponse$, "")
  131. const downloadResponseIcon = ref("download")
  132. const copyResponseIcon = ref("copy")
  133. const copyResponse = () => {
  134. copyToClipboard(responseString.value!)
  135. copyResponseIcon.value = "check"
  136. setTimeout(() => (copyResponseIcon.value = "copy"), 1000)
  137. }
  138. const downloadResponse = () => {
  139. const dataToWrite = responseString.value
  140. const file = new Blob([dataToWrite!], { type: "application/json" })
  141. const a = document.createElement("a")
  142. const url = URL.createObjectURL(file)
  143. a.href = url
  144. a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}`
  145. document.body.appendChild(a)
  146. a.click()
  147. downloadResponseIcon.value = "check"
  148. $toast.success(t("state.download_started").toString(), {
  149. icon: "downloading",
  150. })
  151. setTimeout(() => {
  152. document.body.removeChild(a)
  153. URL.revokeObjectURL(url)
  154. downloadResponseIcon.value = "download"
  155. }, 1000)
  156. }
  157. return {
  158. responseString,
  159. downloadResponseIcon,
  160. copyResponseIcon,
  161. downloadResponse,
  162. copyResponse,
  163. getSpecialKey: getPlatformSpecialKey,
  164. }
  165. },
  166. })
  167. </script>
  168. <style lang="scss" scoped>
  169. .shortcut-key {
  170. @apply bg-dividerLight;
  171. @apply rounded;
  172. @apply ml-2;
  173. @apply py-1;
  174. @apply px-2;
  175. @apply inline-flex;
  176. }
  177. </style>