ws.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import Toast from "toasters";
  2. // eslint-disable-next-line import/no-cycle
  3. import store from "./store";
  4. const onConnect = {
  5. temp: [],
  6. persist: []
  7. };
  8. const onDisconnect = {
  9. temp: [],
  10. persist: []
  11. };
  12. // references for when a dispatch event is ready to callback from server to client
  13. const CB_REFS = {};
  14. let CB_REF = 0;
  15. export default {
  16. socket: null,
  17. dispatcher: null,
  18. onConnect(...args) {
  19. if (args[0] === true) onConnect.persist.push(args[1]);
  20. else onConnect.temp.push(args[0]);
  21. },
  22. onDisconnect(...args) {
  23. if (args[0] === true) onDisconnect.persist.push(args[1]);
  24. else onDisconnect.temp.push(args[0]);
  25. },
  26. clear: () => {
  27. onConnect.temp = [];
  28. onDisconnect.temp = [];
  29. },
  30. removeAllListeners: () =>
  31. Object.keys(CB_REFS).forEach(id => {
  32. if (
  33. id.indexOf("$event:") !== -1 &&
  34. id.indexOf("$event:keep.") === -1
  35. )
  36. delete CB_REFS[id];
  37. }),
  38. init() {
  39. class ListenerHandler extends EventTarget {
  40. constructor() {
  41. super();
  42. this.listeners = {};
  43. }
  44. addEventListener(type, cb) {
  45. if (!(type in this.listeners)) this.listeners[type] = []; // add the listener type to listeners object
  46. this.listeners[type].push(cb); // push the callback
  47. }
  48. // eslint-disable-next-line consistent-return
  49. removeEventListener(type, cb) {
  50. if (!(type in this.listeners)) return true; // event type doesn't exist
  51. const stack = this.listeners[type];
  52. stack.forEach((element, index) => {
  53. if (element === cb) stack.splice(index, 1);
  54. });
  55. }
  56. dispatchEvent(event) {
  57. if (!(event.type in this.listeners)) return true; // event type doesn't exist
  58. const stack = this.listeners[event.type].slice();
  59. stack.forEach(element => element.call(this, event));
  60. return !event.defaultPrevented;
  61. }
  62. }
  63. class CustomWebSocket extends WebSocket {
  64. constructor(url) {
  65. super(url);
  66. this.dispatcher = new ListenerHandler();
  67. }
  68. on(target, cb) {
  69. this.dispatcher.addEventListener(target, event =>
  70. cb(...event.detail)
  71. );
  72. }
  73. dispatch(...args) {
  74. CB_REF += 1;
  75. if (this.readyState !== 1)
  76. return onConnect.temp.push(() => this.dispatch(...args));
  77. const cb = args[args.length - 1];
  78. if (typeof cb === "function") CB_REFS[CB_REF] = cb;
  79. return this.send(
  80. JSON.stringify([...args.slice(0, -1), { CB_REF }])
  81. );
  82. }
  83. }
  84. this.socket = new CustomWebSocket("ws://localhost:8080/ws");
  85. store.dispatch("websockets/createSocket", this.socket);
  86. this.socket.onopen = () => {
  87. console.log("IO: SOCKET CONNECTED");
  88. onConnect.temp.forEach(cb => cb());
  89. onConnect.persist.forEach(cb => cb());
  90. };
  91. this.socket.onmessage = message => {
  92. const data = JSON.parse(message.data);
  93. const name = data.shift(0);
  94. if (name === "CB_REF") {
  95. const CB_REF = data.shift(0);
  96. CB_REFS[CB_REF](...data);
  97. return delete CB_REFS[CB_REF];
  98. }
  99. if (name === "ERROR") console.log("WS: SOCKET ERROR:", data[0]);
  100. return this.socket.dispatcher.dispatchEvent(
  101. new CustomEvent(name, {
  102. detail: data
  103. })
  104. );
  105. };
  106. this.socket.onclose = () => {
  107. console.log("WS: SOCKET DISCONNECTED");
  108. onDisconnect.temp.forEach(cb => cb());
  109. onDisconnect.persist.forEach(cb => cb());
  110. // try to reconnect every 1000ms
  111. setTimeout(() => this.init(), 1000);
  112. };
  113. this.socket.onerror = err => {
  114. console.log("WS: SOCKET ERROR", err);
  115. new Toast({
  116. content: "Cannot perform this action at this time.",
  117. timeout: 8000
  118. });
  119. };
  120. }
  121. };