ws.js 3.6 KB

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