Sse.vue 6.6 KB

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