sockets.ts (2756B)
1 import { createNanoEvents, Emitter } from 'nanoevents' 2 import { remove } from 'next/dist/build/webpack/loaders/resolve-url-loader/lib/file-protocol' 3 4 interface SocketEvent { 5 payload: string 6 } 7 8 interface EventMap { 9 [event: string]: (e: SocketEvent) => void 10 } 11 12 export class SocketClient { 13 private eventEmitter: Emitter<EventMap> 14 private socket?: WebSocket 15 16 constructor () { 17 this.eventEmitter = createNanoEvents() 18 } 19 20 get isConnected () { 21 if (typeof WebSocket === 'undefined') { 22 return false 23 } 24 return this.socket?.readyState === WebSocket.OPEN 25 } 26 27 wait () { 28 return new Promise<void>((resolve) => { 29 const check = () => { 30 if (this.isConnected) { 31 setTimeout(() => resolve(), 500) 32 return 33 } 34 35 setTimeout(check, 100) 36 } 37 38 check() 39 }) 40 } 41 42 connect () { 43 return new Promise<void>((resolve, reject) => { 44 const self = this 45 46 this.socket = new WebSocket(process.env.NEXT_PUBLIC_WIKI_SOCKET_URL ?? 'ws://localhost:3001/ws') 47 48 function handleClose () { 49 removePromiseListeners() 50 reject() 51 } 52 53 function handleOpen () { 54 removePromiseListeners() 55 setTimeout(() => { 56 resolve() 57 }, 500) 58 } 59 60 function removePromiseListeners () { 61 self.socket?.removeEventListener('open', handleOpen) 62 self.socket?.removeEventListener('close', handleClose) 63 } 64 65 this.socket.addEventListener('open', handleOpen) 66 this.socket.addEventListener('close', handleClose) 67 68 this.socket.addEventListener('message', (event) => { 69 this.handleMessage(event.data) 70 }) 71 72 this.socket.addEventListener('open', () => { 73 console.log('SocketClient: connected') 74 }) 75 this.socket.addEventListener('close', () => { 76 console.log('SocketClient: disconnected') 77 }) 78 79 }) 80 } 81 82 disconnect () { 83 this.socket?.close() 84 } 85 86 public changePath (path: string) { 87 this.socket?.send(`PATH${path}`) 88 } 89 90 public on (event: string, callback: (e: SocketEvent) => void) { 91 return this.eventEmitter.on(event, callback) 92 } 93 94 private handleMessage (message: string) { 95 const [command, commandPayload] = this.parseMessage(message) 96 97 if (command === 'EMIT') { 98 const [eventName, eventArgs] = this.parseEmitArgs(commandPayload) 99 this.eventEmitter.emit(eventName, { payload: eventArgs }) 100 } 101 } 102 103 private parseMessage (message: string) { 104 message = message.trim() 105 return [message.slice(0, 4), message.slice(4)] 106 } 107 108 private parseEmitArgs (command: string) { 109 const index = command.indexOf(' ') 110 if (index === -1) { 111 return [command, ''] 112 } 113 return [command.slice(0, index), command.slice(index + 1)] 114 } 115 }