Send received message through WebSocket

- send received message from through WebSocket
- remove SocketIO from client

GitLab: #96
Change-Id: I7a8eec04010f0773428f914792c13decef393ebf
diff --git a/server/src/handlers/webrtc-handler.ts b/server/src/handlers/webrtc-handler.ts
index 5280495..409a030 100644
--- a/server/src/handlers/webrtc-handler.ts
+++ b/server/src/handlers/webrtc-handler.ts
@@ -15,7 +15,7 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { AccountTextMessage, WebSocketMessage, WebSocketMessageType } from 'jami-web-common';
+import { AccountTextMessage, WebSocketMessageType } from 'jami-web-common';
 import log from 'loglevel';
 import { Container } from 'typedi';
 
@@ -25,13 +25,12 @@
 const jamid = Container.get(Jamid);
 const ws = Container.get(Ws);
 
-function sendWebRTCData(message: Partial<WebSocketMessage>) {
-  const data: AccountTextMessage = message.data;
+function sendWebRTCData<T>(data: Partial<AccountTextMessage<T>>) {
   if (!data.from || !data.to || !data.message) {
     log.warn('Incorrect format for AccountTextMessage (require from, to and message):', data);
     return;
   }
-  jamid.sendAccountTextMessage(data.from, data.to, JSON.stringify(message));
+  jamid.sendAccountTextMessage(data.from, data.to, JSON.stringify(data.message));
 }
 
 export function bindWebRTCCallbacks() {
diff --git a/server/src/jamid/jamid.ts b/server/src/jamid/jamid.ts
index a3db747..0c54adf 100644
--- a/server/src/jamid/jamid.ts
+++ b/server/src/jamid/jamid.ts
@@ -15,7 +15,15 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { AccountDetails, Message, VolatileDetails, WebSocketMessageType } from 'jami-web-common';
+import {
+  AccountDetails,
+  AccountTextMessage,
+  ConversationMessage,
+  Message,
+  VolatileDetails,
+  WebSocketMessage,
+  WebSocketMessageType,
+} from 'jami-web-common';
 import log from 'loglevel';
 import { filter, firstValueFrom, map, Subject } from 'rxjs';
 import { Service } from 'typedi';
@@ -369,7 +377,7 @@
 
     this.events.onIncomingAccountMessage.subscribe((signal) => {
       log.debug('Received IncomingAccountMessage:', JSON.stringify(signal));
-      const message = JSON.parse(signal.payload['application/json']);
+      const message: WebSocketMessage<any> = JSON.parse(signal.payload['application/json']);
 
       if (message === undefined) {
         log.debug('Undefined account message');
@@ -381,10 +389,15 @@
         return;
       }
 
-      message.data.from = signal.from;
-      message.data.to = signal.accountId;
+      const data: AccountTextMessage<unknown> = {
+        from: signal.from,
+        to: signal.accountId,
+        message: message.data.message,
+      };
+      message.data = data;
+
       log.info(`Sending ${JSON.stringify(message)} to ${signal.accountId}`);
-      this.ws.send(signal.accountId, message);
+      this.ws.send(signal.accountId, message.type, data);
     });
 
     this.events.onAccountMessageStatusChanged.subscribe((signal) => {
@@ -416,7 +429,11 @@
 
     this.events.onMessageReceived.subscribe((signal) => {
       log.debug('Received MessageReceived:', JSON.stringify(signal));
-      // TODO: Send message to client using WS service
+      const data: ConversationMessage = {
+        conversationId: signal.conversationId,
+        message: signal.message,
+      };
+      this.ws.send(signal.accountId, WebSocketMessageType.ConversationMessage, data);
     });
   }
 }
diff --git a/server/src/routers/account-router.ts b/server/src/routers/account-router.ts
index 4e1722d..9b72667 100644
--- a/server/src/routers/account-router.ts
+++ b/server/src/routers/account-router.ts
@@ -69,7 +69,7 @@
 });
 
 // TODO: Should this endpoint be removed?
-accountRouter.post('/send-account-message', (req: Request<ParamsDictionary, any, AccountTextMessage>, res) => {
+accountRouter.post('/send-account-message', (req: Request<ParamsDictionary, any, AccountTextMessage<unknown>>, res) => {
   const { from, to, message } = req.body;
   if (from === undefined || to === undefined || message === undefined) {
     res.status(HttpStatusCode.BadRequest).send('Missing from, to, or message in body');
diff --git a/server/src/ws.ts b/server/src/ws.ts
index 46d563a..d33dd76 100644
--- a/server/src/ws.ts
+++ b/server/src/ws.ts
@@ -18,7 +18,7 @@
 import { IncomingMessage } from 'node:http';
 import { Duplex } from 'node:stream';
 
-import { WebSocketMessage, WebSocketMessageType } from 'jami-web-common';
+import { WebSocketCallbacks, WebSocketMessage, WebSocketMessageTable, WebSocketMessageType } from 'jami-web-common';
 import { jwtVerify } from 'jose';
 import log from 'loglevel';
 import { Service } from 'typedi';
@@ -30,11 +30,17 @@
 @Service()
 export class Ws {
   private sockets: Map<string, WebSocket[]>;
-  private callbacks: Map<WebSocketMessageType, ((message: WebSocketMessage) => void)[]>;
+  private callbacks: WebSocketCallbacks;
 
   constructor(private readonly vault: Vault) {
     this.sockets = new Map();
-    this.callbacks = new Map();
+    this.callbacks = {
+      [WebSocketMessageType.ConversationMessage]: [],
+      [WebSocketMessageType.ConversationView]: [],
+      [WebSocketMessageType.WebRTCOffer]: [],
+      [WebSocketMessageType.WebRTCAnswer]: [],
+      [WebSocketMessageType.IceCandidate]: [],
+    };
   }
 
   async build() {
@@ -49,17 +55,19 @@
         this.sockets.set(accountId, [ws]);
       }
 
-      ws.on('message', (messageString: string) => {
-        const message: WebSocketMessage = JSON.parse(messageString);
+      ws.on('message', <T extends WebSocketMessageType>(messageString: string) => {
+        const message: WebSocketMessage<T> = JSON.parse(messageString);
         if (!message.type || !message.data) {
           ws.send('Incorrect format (require type and data)');
           return;
         }
-        const callbacks = this.callbacks.get(message.type);
-        if (callbacks) {
-          for (const callback of callbacks) {
-            callback(message);
-          }
+        if (!Object.values(WebSocketMessageType).includes(message.type)) {
+          log.warn(`Unhandled account message type: ${message.type}`);
+          return;
+        }
+        const callbacks = this.callbacks[message.type];
+        for (const callback of callbacks) {
+          callback(message.data);
         }
       });
 
@@ -103,22 +111,17 @@
     };
   }
 
-  bind(messageType: WebSocketMessageType, callback: (message: WebSocketMessage) => void): void {
-    const messageTypeCallbacks = this.callbacks.get(messageType);
-    if (messageTypeCallbacks) {
-      messageTypeCallbacks.push(callback);
-    } else {
-      this.callbacks.set(messageType, [callback]);
-    }
+  bind<T extends WebSocketMessageType>(type: T, callback: (data: WebSocketMessageTable[T]) => void): void {
+    this.callbacks[type].push(callback);
   }
 
-  send(accountId: string, message: WebSocketMessage): boolean {
+  send<T extends WebSocketMessageType>(accountId: string, type: T, data: WebSocketMessageTable[T]): boolean {
     const accountSockets = this.sockets.get(accountId);
     if (!accountSockets) {
       return false;
     }
     for (const accountSocket of accountSockets) {
-      accountSocket.send(JSON.stringify(message));
+      accountSocket.send(JSON.stringify({ type, data }));
     }
     return true;
   }