CustomWebSocket.class.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import ListenerHandler from "@/classes/ListenerHandler.class";
  2. import { useUserAuthStore } from "@/stores/userAuth";
  3. export default class CustomWebSocket extends WebSocket {
  4. dispatcher: ListenerHandler;
  5. onConnectCbs: any[];
  6. ready: boolean;
  7. firstInit: boolean;
  8. pendingDispatches: any[];
  9. onDisconnectCbs: {
  10. temp: any[];
  11. persist: any[];
  12. };
  13. CB_REF: number;
  14. CB_REFS: object;
  15. PROGRESS_CB_REFS: object;
  16. data: {
  17. dispatch?: {
  18. [key: string]: (...args: any[]) => any;
  19. };
  20. progress?: {
  21. [key: string]: (...args: any[]) => any;
  22. };
  23. on?: {
  24. [key: string]: any;
  25. };
  26. }; // Mock only
  27. executeDispatch: boolean; // Mock only
  28. trigger: (type: string, target: string, data?: any) => void; // Mock only
  29. constructor(url) {
  30. super(url);
  31. this.dispatcher = new ListenerHandler();
  32. this.onConnectCbs = [];
  33. this.ready = false;
  34. this.firstInit = true;
  35. this.pendingDispatches = [];
  36. this.onDisconnectCbs = {
  37. temp: [],
  38. persist: []
  39. };
  40. // references for when a dispatch event is ready to callback from server to client
  41. this.CB_REF = 0;
  42. this.CB_REFS = {};
  43. this.PROGRESS_CB_REFS = {};
  44. this.init();
  45. }
  46. init() {
  47. const userAuthStore = useUserAuthStore();
  48. this.onopen = () => {
  49. console.log("WS: SOCKET OPENED");
  50. };
  51. this.onmessage = message => {
  52. const data = JSON.parse(message.data);
  53. const name = data.shift(0);
  54. if (name === "CB_REF") {
  55. const CB_REF = data.shift(0);
  56. this.CB_REFS[CB_REF](...data);
  57. return delete this.CB_REFS[CB_REF];
  58. }
  59. if (name === "PROGRESS_CB_REF") {
  60. const PROGRESS_CB_REF = data.shift(0);
  61. this.PROGRESS_CB_REFS[PROGRESS_CB_REF](...data);
  62. }
  63. if (name === "ERROR") console.log("WS: SOCKET ERROR:", data[0]);
  64. return this.dispatcher.dispatchEvent(
  65. new CustomEvent(name, {
  66. detail: data
  67. })
  68. );
  69. };
  70. this.onclose = () => {
  71. console.log("WS: SOCKET CLOSED");
  72. this.ready = false;
  73. this.onDisconnectCbs.temp.forEach(cb => cb());
  74. this.onDisconnectCbs.persist.forEach(cb => cb());
  75. // try to reconnect every 1000ms, if the user isn't banned
  76. if (!userAuthStore.banned) setTimeout(() => this.init(), 1000);
  77. };
  78. this.onerror = err => {
  79. console.log("WS: SOCKET ERROR", err);
  80. };
  81. if (this.firstInit) {
  82. this.firstInit = false;
  83. this.on("ready", () => {
  84. console.log("WS: SOCKET READY");
  85. this.onConnectCbs.forEach(cb => cb());
  86. this.ready = true;
  87. setTimeout(() => {
  88. // dispatches that were attempted while the server was offline
  89. this.pendingDispatches.forEach(cb => cb());
  90. this.pendingDispatches = [];
  91. }, 150); // small delay between readyState being 1 and the server actually receiving dispatches
  92. userAuthStore.updatePermissions();
  93. });
  94. }
  95. }
  96. on(target, cb, options?) {
  97. this.dispatcher.addEventListener(
  98. target,
  99. event => cb(...event.detail),
  100. options
  101. );
  102. }
  103. dispatch(...args) {
  104. if (this.readyState !== 1)
  105. return this.pendingDispatches.push(() => this.dispatch(...args));
  106. const lastArg = args[args.length - 1];
  107. if (typeof lastArg === "function") {
  108. this.CB_REF += 1;
  109. this.CB_REFS[this.CB_REF] = lastArg;
  110. return this.send(
  111. JSON.stringify([...args.slice(0, -1), { CB_REF: this.CB_REF }])
  112. );
  113. }
  114. if (typeof lastArg === "object") {
  115. this.CB_REF += 1;
  116. this.CB_REFS[this.CB_REF] = lastArg.cb;
  117. this.PROGRESS_CB_REFS[this.CB_REF] = lastArg.onProgress;
  118. return this.send(
  119. JSON.stringify([
  120. ...args.slice(0, -1),
  121. { CB_REF: this.CB_REF, onProgress: true }
  122. ])
  123. );
  124. }
  125. return this.send(JSON.stringify([...args]));
  126. }
  127. onConnect(cb) {
  128. if (this.readyState === 1 && this.ready) cb();
  129. return this.onConnectCbs.push(cb);
  130. }
  131. onDisconnect(...args) {
  132. if (args[0] === true) this.onDisconnectCbs.persist.push(args[1]);
  133. else this.onDisconnectCbs.temp.push(args[0]);
  134. }
  135. clearCallbacks() {
  136. this.onDisconnectCbs.temp = [];
  137. }
  138. destroyListeners() {
  139. Object.keys(this.CB_REFS).forEach(id => {
  140. if (
  141. id.indexOf("$event:") !== -1 &&
  142. id.indexOf("$event:keep.") === -1
  143. )
  144. delete this.CB_REFS[id];
  145. });
  146. Object.keys(this.PROGRESS_CB_REFS).forEach(id => {
  147. if (
  148. id.indexOf("$event:") !== -1 &&
  149. id.indexOf("$event:keep.") === -1
  150. )
  151. delete this.PROGRESS_CB_REFS[id];
  152. });
  153. // destroy all listeners that aren't site-wide
  154. Object.keys(this.dispatcher.listeners).forEach(type => {
  155. if (type.indexOf("keep.") === -1 && type !== "ready")
  156. delete this.dispatcher.listeners[type];
  157. });
  158. }
  159. destroyModalListeners(modalUuid) {
  160. // destroy all listeners for a specific modal
  161. Object.keys(this.dispatcher.listeners).forEach(type =>
  162. this.dispatcher.listeners[type].forEach((element, index) => {
  163. if (element.options && element.options.modalUuid === modalUuid)
  164. this.dispatcher.listeners[type].splice(index, 1);
  165. })
  166. );
  167. }
  168. }