123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- <template>
- <Splitpanes
- class="smart-splitter"
- :dbl-click-splitter="false"
- :horizontal="!(windowInnerWidth.x.value >= 768)"
- >
- <Pane class="hide-scrollbar !overflow-auto">
- <Splitpanes class="smart-splitter" :dbl-click-splitter="false" horizontal>
- <Pane class="hide-scrollbar !overflow-auto">
- <AppSection label="request">
- <div class="bg-primary flex p-4 top-0 z-10 sticky">
- <div class="space-x-2 flex-1 inline-flex">
- <input
- id="websocket-url"
- v-model="url"
- v-focus
- class="
- bg-primaryLight
- border border-divider
- rounded
- text-secondaryDark
- w-full
- py-2
- px-4
- hover:border-dividerDark
- focus-visible:bg-transparent
- focus-visible:border-dividerDark
- "
- type="url"
- autocomplete="off"
- spellcheck="false"
- :class="{ error: !urlValid }"
- :placeholder="$t('websocket.url')"
- @keyup.enter="urlValid ? toggleConnection() : null"
- />
- <ButtonPrimary
- id="connect"
- :disabled="!urlValid"
- class="w-32"
- name="connect"
- :label="
- !connectionState
- ? $t('action.connect')
- : $t('action.disconnect')
- "
- :loading="connectingState"
- @click.native="toggleConnection"
- />
- </div>
- </div>
- <div
- class="
- bg-primary
- border-b border-dividerLight
- flex flex-1
- top-upperPrimaryStickyFold
- pl-4
- z-10
- sticky
- items-center
- justify-between
- "
- >
- <label class="font-semibold text-secondaryLight">
- {{ $t("websocket.protocols") }}
- </label>
- <div class="flex">
- <ButtonSecondary
- v-tippy="{ theme: 'tooltip' }"
- :title="$t('action.clear_all')"
- svg="trash-2"
- @click.native="clearContent"
- />
- <ButtonSecondary
- v-tippy="{ theme: 'tooltip' }"
- :title="$t('add.new')"
- svg="plus"
- @click.native="addProtocol"
- />
- </div>
- </div>
- <div
- v-for="(protocol, index) of protocols"
- :key="`protocol-${index}`"
- class="
- divide-x divide-dividerLight
- border-b border-dividerLight
- flex
- "
- >
- <input
- v-model="protocol.value"
- class="bg-transparent flex flex-1 py-2 px-4"
- :placeholder="$t('count.protocol', { count: index + 1 })"
- name="message"
- type="text"
- autocomplete="off"
- />
- <span>
- <ButtonSecondary
- v-tippy="{ theme: 'tooltip' }"
- :title="
- protocol.hasOwnProperty('active')
- ? protocol.active
- ? $t('action.turn_off')
- : $t('action.turn_on')
- : $t('action.turn_off')
- "
- :svg="
- protocol.hasOwnProperty('active')
- ? protocol.active
- ? 'check-circle'
- : 'circle'
- : 'check-circle'
- "
- color="green"
- @click.native="
- protocol.active = protocol.hasOwnProperty('active')
- ? !protocol.active
- : false
- "
- />
- </span>
- <span>
- <ButtonSecondary
- v-tippy="{ theme: 'tooltip' }"
- :title="$t('action.remove')"
- svg="trash"
- color="red"
- @click.native="deleteProtocol({ index })"
- />
- </span>
- </div>
- <div
- v-if="protocols.length === 0"
- class="
- flex flex-col
- text-secondaryLight
- p-4
- items-center
- justify-center
- "
- >
- <i class="opacity-75 pb-2 material-icons">topic</i>
- <span class="text-center">
- {{ $t("empty.protocols") }}
- </span>
- </div>
- </AppSection>
- </Pane>
- <Pane class="hide-scrollbar !overflow-auto">
- <AppSection label="response">
- <RealtimeLog
- :title="$t('websocket.log')"
- :log="communication.log"
- />
- </AppSection>
- </Pane>
- </Splitpanes>
- </Pane>
- <Pane
- v-if="RIGHT_SIDEBAR"
- max-size="35"
- size="25"
- min-size="20"
- class="hide-scrollbar !overflow-auto"
- >
- <AppSection label="messages">
- <div class="flex flex-col flex-1 p-4 inline-flex">
- <label
- for="websocket-message"
- class="font-semibold text-secondaryLight"
- >
- {{ $t("websocket.communication") }}
- </label>
- </div>
- <div class="flex space-x-2 px-4">
- <input
- id="websocket-message"
- v-model="communication.input"
- name="message"
- type="text"
- autocomplete="off"
- :disabled="!connectionState"
- :placeholder="$t('websocket.message')"
- class="input"
- @keyup.enter="connectionState ? sendMessage() : null"
- @keyup.up="connectionState ? walkHistory('up') : null"
- @keyup.down="connectionState ? walkHistory('down') : null"
- />
- <ButtonPrimary
- id="send"
- name="send"
- :disabled="!connectionState"
- :label="$t('action.send')"
- @click.native="sendMessage"
- />
- </div>
- </AppSection>
- </Pane>
- </Splitpanes>
- </template>
- <script>
- import { defineComponent } from "@nuxtjs/composition-api"
- import { Splitpanes, Pane } from "splitpanes"
- import "splitpanes/dist/splitpanes.css"
- import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
- import debounce from "~/helpers/utils/debounce"
- import useWindowSize from "~/helpers/utils/useWindowSize"
- import { useSetting } from "~/newstore/settings"
- export default defineComponent({
- components: { Splitpanes, Pane },
- setup() {
- return {
- windowInnerWidth: useWindowSize(),
- RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
- }
- },
- data() {
- return {
- connectionState: false,
- connectingState: false,
- url: "wss://echo.websocket.org",
- isUrlValid: true,
- socket: null,
- communication: {
- log: null,
- input: "",
- },
- currentIndex: -1, // index of the message log array to put in input box
- protocols: [],
- activeProtocols: [],
- }
- },
- computed: {
- urlValid() {
- return this.isUrlValid
- },
- },
- watch: {
- url() {
- this.debouncer()
- },
- protocols: {
- handler(newVal) {
- this.activeProtocols = newVal
- .filter((item) =>
- Object.prototype.hasOwnProperty.call(item, "active")
- ? item.active === true
- : true
- )
- .map(({ value }) => value)
- },
- deep: true,
- },
- },
- mounted() {
- if (process.browser) {
- this.worker = this.$worker.createRejexWorker()
- this.worker.addEventListener("message", this.workerResponseHandler)
- }
- },
- destroyed() {
- this.worker.terminate()
- },
- methods: {
- clearContent() {
- this.protocols = []
- },
- debouncer: debounce(function () {
- this.worker.postMessage({ type: "ws", url: this.url })
- }, 1000),
- workerResponseHandler({ data }) {
- if (data.url === this.url) this.isUrlValid = data.result
- },
- toggleConnection() {
- // If it is connecting:
- if (!this.connectionState) return this.connect()
- // Otherwise, it's disconnecting.
- else return this.disconnect()
- },
- connect() {
- this.communication.log = [
- {
- payload: this.$t("state.connecting_to", { name: this.url }),
- source: "info",
- color: "var(--accent-color)",
- },
- ]
- try {
- this.connectingState = true
- this.socket = new WebSocket(this.url, this.activeProtocols)
- this.socket.onopen = () => {
- this.connectingState = false
- this.connectionState = true
- this.communication.log = [
- {
- payload: this.$t("state.connected_to", { name: this.url }),
- source: "info",
- color: "var(--accent-color)",
- ts: new Date().toLocaleTimeString(),
- },
- ]
- this.$toast.success(this.$t("state.connected"), {
- icon: "sync",
- })
- }
- this.socket.onerror = () => {
- this.handleError()
- }
- this.socket.onclose = () => {
- this.connectionState = false
- this.communication.log.push({
- payload: this.$t("state.disconnected_from", { name: this.url }),
- source: "info",
- color: "#ff5555",
- ts: new Date().toLocaleTimeString(),
- })
- this.$toast.error(this.$t("state.disconnected"), {
- icon: "sync_disabled",
- })
- }
- this.socket.onmessage = ({ data }) => {
- this.communication.log.push({
- payload: data,
- source: "server",
- ts: new Date().toLocaleTimeString(),
- })
- }
- } catch (e) {
- this.handleError(e)
- this.$toast.error(this.$t("error.something_went_wrong"), {
- icon: "error_outline",
- })
- }
- logHoppRequestRunToAnalytics({
- platform: "wss",
- })
- },
- disconnect() {
- if (this.socket) {
- this.socket.close()
- this.connectionState = false
- this.connectingState = false
- }
- },
- handleError(error) {
- this.disconnect()
- this.connectionState = false
- this.communication.log.push({
- payload: this.$t("error.something_went_wrong"),
- source: "info",
- color: "#ff5555",
- ts: new Date().toLocaleTimeString(),
- })
- if (error !== null)
- this.communication.log.push({
- payload: error,
- source: "info",
- color: "#ff5555",
- ts: new Date().toLocaleTimeString(),
- })
- },
- sendMessage() {
- const message = this.communication.input
- this.socket.send(message)
- this.communication.log.push({
- payload: message,
- source: "client",
- ts: new Date().toLocaleTimeString(),
- })
- this.communication.input = ""
- },
- walkHistory(direction) {
- const clientMessages = this.communication.log.filter(
- ({ source }) => source === "client"
- )
- const length = clientMessages.length
- switch (direction) {
- case "up":
- if (length > 0 && this.currentIndex !== 0) {
- // does nothing if message log is empty or the currentIndex is 0 when up arrow is pressed
- if (this.currentIndex === -1) {
- this.currentIndex = length - 1
- this.communication.input =
- clientMessages[this.currentIndex].payload
- } else if (this.currentIndex === 0) {
- this.communication.input = clientMessages[0].payload
- } else if (this.currentIndex > 0) {
- this.currentIndex = this.currentIndex - 1
- this.communication.input =
- clientMessages[this.currentIndex].payload
- }
- }
- break
- case "down":
- if (length > 0 && this.currentIndex > -1) {
- if (this.currentIndex === length - 1) {
- this.currentIndex = -1
- this.communication.input = ""
- } else if (this.currentIndex < length - 1) {
- this.currentIndex = this.currentIndex + 1
- this.communication.input =
- clientMessages[this.currentIndex].payload
- }
- }
- break
- }
- },
- addProtocol() {
- this.protocols.push({ value: "", active: true })
- },
- deleteProtocol({ index }) {
- const oldProtocols = this.protocols.slice()
- this.$delete(this.protocols, index)
- this.$toast.success(this.$t("state.deleted"), {
- icon: "delete",
- action: {
- text: this.$t("action.undo"),
- duration: 4000,
- onClick: (_, toastObject) => {
- this.protocols = oldProtocols
- toastObject.remove()
- },
- },
- })
- },
- },
- })
- </script>
|