From 93e590509580cfaf1e92cd868da27b6c5b365c82 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Fri, 10 Apr 2020 15:59:42 +0200 Subject: [PATCH] Can join a call --- src/controllers/CallsController.ts | 88 ++++++++++++++++++++-- src/controllers/Routes.ts | 2 +- src/controllers/UserWebSocketController.ts | 8 ++ src/controllers/UserWebSocketRoutes.ts | 5 ++ src/helpers/EventsHelper.ts | 7 ++ 5 files changed, 102 insertions(+), 8 deletions(-) diff --git a/src/controllers/CallsController.ts b/src/controllers/CallsController.ts index 2876d6e..fc03de9 100644 --- a/src/controllers/CallsController.ts +++ b/src/controllers/CallsController.ts @@ -1,4 +1,9 @@ import { RequestHandler } from "../entities/RequestHandler"; +import { UserWebSocketRequestsHandler } from "../entities/WebSocketRequestHandler"; +import { ActiveClient, UserWebSocketController } from "./UserWebSocketController"; +import { EventsHelper } from "../helpers/EventsHelper"; +import * as ws from 'ws' +import { WsMessage } from "../entities/WsMessage"; /** * Legacy calls controller @@ -9,16 +14,85 @@ import { RequestHandler } from "../entities/RequestHandler"; export class CallsController { /** - * Get calls config - * - * This version of the API DOES NOT SUPPORT calls system for - * now. It is planned to reimagine it completely before attempting - * an implementation... + * Legacy calls config */ - public static GetConfig(h: RequestHandler) { + public static GetLegacyConfig(h: RequestHandler) { h.send({ enabled: false }); } -} \ No newline at end of file + /** + * Join a call + * + * @param h Request handler + */ + public static async JoinCall(h: UserWebSocketRequestsHandler) { + const convID = await h.postConversationId("convID"); + + // If the user was active in any other calls, remove him + for(const c of UserWebSocketController.active_clients.filter((f) => f.userID == convID && f.activeCalls.has(convID))) + await this.MakeUserLeaveCall(convID, c) + + + h.wsClient.activeCalls.add(convID); + + // Notify all other users + await UserWebSocketController.SendToSpecifcClients( + (c) => c.activeCalls.has(convID) && c.userID != h.getUserId(), + () => WsMessage.NoIDMessage("user_joined_call", h.getUserId()) + ) + + h.success(); + } + + /** + * Leave a call + * + * @param h Request handler + */ + public static async LeaveCall(h: UserWebSocketRequestsHandler) { + // Warning ! For some technical reasons, we do not check if the user + // really belongs to the conversation, so be careful when manipulating + // conversation ID here + const convID = h.postInt("convID"); + + // Check if user is already in the conversation + if(!h.wsClient.activeCalls.has(convID)) { + h.success(); + return; + } + + // Make the user leave the call + await this.MakeUserLeaveCall(convID, h.wsClient); + + h.success(); + } + + /** + * Make the client leave the call + * + * @param c Client information + */ + public static async MakeUserLeaveCall(convID: number, c: ActiveClient) { + c.activeCalls.delete(convID) + + // Notify user (if possible) + if(c.ws.readyState == ws.OPEN) + UserWebSocketController.SendToClient(c, WsMessage.NoIDMessage("call_closed",convID)); + + // Notify all other users + await UserWebSocketController.SendToSpecifcClients( + (c) => c.activeCalls.has(convID), + () => WsMessage.NoIDMessage("user_left_call", c.userID) + ) + } +} + +// Listen for websocket closed +EventsHelper.Listen("user_ws_closed", async w => { + for(const convID of w.client.activeCalls) + await CallsController.MakeUserLeaveCall(convID, w.client) +}); + +// TODO : close all call when RTC WS is closed \ No newline at end of file diff --git a/src/controllers/Routes.ts b/src/controllers/Routes.ts index 21876e5..f33b8ae 100644 --- a/src/controllers/Routes.ts +++ b/src/controllers/Routes.ts @@ -306,6 +306,6 @@ export const Routes : Route[] = [ {path: "/webApp/getMemberships", cb: (h) => WebAppControllers.GetMemberships(h)}, // Calls controller - {path: "/calls/config", cb: (h) => CallsController.GetConfig(h)}, + {path: "/calls/config", cb: (h) => CallsController.GetLegacyConfig(h)}, ] \ No newline at end of file diff --git a/src/controllers/UserWebSocketController.ts b/src/controllers/UserWebSocketController.ts index a5ec5be..94f3139 100644 --- a/src/controllers/UserWebSocketController.ts +++ b/src/controllers/UserWebSocketController.ts @@ -30,6 +30,7 @@ export interface ActiveClient { registeredConversations: Set, registeredPosts: Set, + activeCalls: Set, } // Tokens are valid only 10 seconds after they are generated @@ -122,6 +123,7 @@ export class UserWebSocketController { incognito: entry.incognito, registeredConversations: new Set(), registeredPosts: new Set(), + activeCalls: new Set(), } this.active_clients.push(client); @@ -129,6 +131,12 @@ export class UserWebSocketController { // Remove the client for the list as soon as the // socket is closed ws.addEventListener("close", () => { + + // Propagage event + EventsHelper.Emit("user_ws_closed", { + client: client + }); + this.active_clients.splice(this.active_clients.indexOf(client), 1); }) diff --git a/src/controllers/UserWebSocketRoutes.ts b/src/controllers/UserWebSocketRoutes.ts index 92f3089..f37f5cb 100644 --- a/src/controllers/UserWebSocketRoutes.ts +++ b/src/controllers/UserWebSocketRoutes.ts @@ -10,6 +10,7 @@ import { UserWebSocketRequestsHandler } from "../entities/WebSocketRequestHandler"; import { LikesController } from "./LikesController"; import { UserWebSocketActions } from "./UserWebSocketActions"; +import { CallsController } from "./CallsController"; export interface UserWebSocketRoute { title: string, @@ -31,4 +32,8 @@ export const UserWebSocketRoutes: UserWebSocketRoute[] = [ {title: "likes/update", handler: (h) => LikesController.Update(h)}, + // Calls controller + {title: "calls/join", handler: (h) => CallsController.JoinCall(h)}, + + {title: "calls/leave", handler: (h) => CallsController.LeaveCall(h)}, ] \ No newline at end of file diff --git a/src/helpers/EventsHelper.ts b/src/helpers/EventsHelper.ts index 1659c19..8e5a2c0 100644 --- a/src/helpers/EventsHelper.ts +++ b/src/helpers/EventsHelper.ts @@ -1,6 +1,7 @@ import { APIClient } from "../entities/APIClient"; import { ConversationMessage } from "../entities/ConversationMessage"; import { Comment } from "../entities/Comment"; +import { ActiveClient } from "../controllers/UserWebSocketController"; /** * Events manager @@ -14,6 +15,11 @@ export interface DestroyedLoginTokensEvent { client: APIClient } +// When a user WebSocket connection is closed +export interface UserWebSocketClosedEvent { + client: ActiveClient +} + // When some user might have an updated number of notifications export interface UpdatedNotificationsNumberEvent { usersID: number[] @@ -59,6 +65,7 @@ export interface CommentDeletedEvent { */ export interface EventsMap { "destroyed_login_tokens": DestroyedLoginTokensEvent, + "user_ws_closed": UserWebSocketClosedEvent, "updated_number_notifications": UpdatedNotificationsNumberEvent, "updated_number_unread_conversations": UpdateNumberUnreadConversationsEvent, "sent_conversation_message": SentNewConversationMessageEvent,