Response.vue 4.2 KB

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