Sse.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <Splitpanes class="smart-splitter" :horizontal="COLUMN_LAYOUT">
  3. <Pane class="hide-scrollbar !overflow-auto">
  4. <div class="bg-primary flex p-4 top-0 z-10 sticky">
  5. <div class="space-x-2 flex-1 inline-flex">
  6. <div class="flex flex-1">
  7. <input
  8. id="server"
  9. v-model="server"
  10. type="url"
  11. autocomplete="off"
  12. :class="{ error: !serverValid }"
  13. class="
  14. bg-primaryLight
  15. border border-divider
  16. rounded-l
  17. flex flex-1
  18. text-secondaryDark
  19. w-full
  20. py-2
  21. px-4
  22. hover:border-dividerDark
  23. focus-visible:bg-transparent focus-visible:border-dividerDark
  24. "
  25. :placeholder="$t('sse.url')"
  26. :disabled="connectionSSEState"
  27. @keyup.enter="serverValid ? toggleSSEConnection() : null"
  28. />
  29. <label
  30. for="event-type"
  31. class="
  32. bg-primaryLight
  33. border-t border-b border-divider
  34. font-semibold
  35. text-secondaryLight
  36. py-2
  37. px-4
  38. truncate
  39. "
  40. >
  41. {{ $t("sse.event_type") }}
  42. </label>
  43. <input
  44. id="event-type"
  45. v-model="eventType"
  46. class="
  47. bg-primaryLight
  48. border border-divider
  49. rounded-r
  50. flex flex-1
  51. text-secondaryDark
  52. w-full
  53. py-2
  54. px-4
  55. hover:border-dividerDark
  56. focus-visible:bg-transparent focus-visible:border-dividerDark
  57. "
  58. spellcheck="false"
  59. :disabled="connectionSSEState"
  60. @keyup.enter="serverValid ? toggleSSEConnection() : null"
  61. />
  62. </div>
  63. <ButtonPrimary
  64. id="start"
  65. :disabled="!serverValid"
  66. name="start"
  67. class="w-32"
  68. :label="
  69. !connectionSSEState ? $t('action.start') : $t('action.stop')
  70. "
  71. :loading="connectingState"
  72. @click.native="toggleSSEConnection"
  73. />
  74. </div>
  75. </div>
  76. </Pane>
  77. <Pane class="hide-scrollbar !overflow-auto">
  78. <AppSection label="response">
  79. <ul>
  80. <li>
  81. <RealtimeLog :title="$t('sse.log')" :log="events.log" />
  82. <div id="result"></div>
  83. </li>
  84. </ul>
  85. </AppSection>
  86. </Pane>
  87. </Splitpanes>
  88. </template>
  89. <script>
  90. import { defineComponent } from "@nuxtjs/composition-api"
  91. import { Splitpanes, Pane } from "splitpanes"
  92. import "splitpanes/dist/splitpanes.css"
  93. import debounce from "lodash/debounce"
  94. import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
  95. import { useSetting } from "~/newstore/settings"
  96. export default defineComponent({
  97. components: { Splitpanes, Pane },
  98. setup() {
  99. return {
  100. COLUMN_LAYOUT: useSetting("COLUMN_LAYOUT"),
  101. }
  102. },
  103. data() {
  104. return {
  105. connectionSSEState: false,
  106. connectingState: false,
  107. server: "https://express-eventsource.herokuapp.com/events",
  108. isUrlValid: true,
  109. sse: null,
  110. events: {
  111. log: null,
  112. input: "",
  113. },
  114. eventType: "data",
  115. }
  116. },
  117. computed: {
  118. serverValid() {
  119. return this.isUrlValid
  120. },
  121. },
  122. watch: {
  123. server() {
  124. this.debouncer()
  125. },
  126. },
  127. created() {
  128. if (process.browser) {
  129. this.worker = this.$worker.createRejexWorker()
  130. this.worker.addEventListener("message", this.workerResponseHandler)
  131. }
  132. },
  133. destroyed() {
  134. this.worker.terminate()
  135. },
  136. methods: {
  137. debouncer: debounce(function () {
  138. this.worker.postMessage({ type: "sse", url: this.server })
  139. }, 1000),
  140. workerResponseHandler({ data }) {
  141. if (data.url === this.url) this.isUrlValid = data.result
  142. },
  143. toggleSSEConnection() {
  144. // If it is connecting:
  145. if (!this.connectionSSEState) return this.start()
  146. // Otherwise, it's disconnecting.
  147. else return this.stop()
  148. },
  149. start() {
  150. this.connectingState = true
  151. this.events.log = [
  152. {
  153. payload: this.$t("state.connecting_to", { name: this.server }),
  154. source: "info",
  155. color: "var(--accent-color)",
  156. },
  157. ]
  158. if (typeof EventSource !== "undefined") {
  159. try {
  160. this.sse = new EventSource(this.server)
  161. this.sse.onopen = () => {
  162. this.connectingState = false
  163. this.connectionSSEState = true
  164. this.events.log = [
  165. {
  166. payload: this.$t("state.connected_to", { name: this.server }),
  167. source: "info",
  168. color: "var(--accent-color)",
  169. ts: new Date().toLocaleTimeString(),
  170. },
  171. ]
  172. this.$toast.success(this.$t("state.connected"))
  173. }
  174. this.sse.onerror = () => {
  175. this.handleSSEError()
  176. }
  177. this.sse.onclose = () => {
  178. this.connectionSSEState = false
  179. this.events.log.push({
  180. payload: this.$t("state.disconnected_from", {
  181. name: this.server,
  182. }),
  183. source: "info",
  184. color: "#ff5555",
  185. ts: new Date().toLocaleTimeString(),
  186. })
  187. this.$toast.error(this.$t("state.disconnected"))
  188. }
  189. this.sse.addEventListener(this.eventType, ({ data }) => {
  190. this.events.log.push({
  191. payload: data,
  192. source: "server",
  193. ts: new Date().toLocaleTimeString(),
  194. })
  195. })
  196. } catch (e) {
  197. this.handleSSEError(e)
  198. this.$toast.error(this.$t("error.something_went_wrong"))
  199. }
  200. } else {
  201. this.events.log = [
  202. {
  203. payload: this.$t("error.browser_support_sse"),
  204. source: "info",
  205. color: "#ff5555",
  206. ts: new Date().toLocaleTimeString(),
  207. },
  208. ]
  209. }
  210. logHoppRequestRunToAnalytics({
  211. platform: "sse",
  212. })
  213. },
  214. handleSSEError(error) {
  215. this.stop()
  216. this.connectionSSEState = false
  217. this.events.log.push({
  218. payload: this.$t("error.something_went_wrong"),
  219. source: "info",
  220. color: "#ff5555",
  221. ts: new Date().toLocaleTimeString(),
  222. })
  223. if (error !== null)
  224. this.events.log.push({
  225. payload: error,
  226. source: "info",
  227. color: "#ff5555",
  228. ts: new Date().toLocaleTimeString(),
  229. })
  230. },
  231. stop() {
  232. this.sse.close()
  233. this.sse.onclose()
  234. },
  235. },
  236. })
  237. </script>