Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Video Conference persistent chat #32793

Merged
merged 34 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3ea0a6e
feat: Video Conference persistent chat
pierre-lehnen-rc Jul 15, 2024
a2c4ac5
subscribe users to the discussion
pierre-lehnen-rc Jul 16, 2024
27a7905
pass new data to apps and validate provider capability
pierre-lehnen-rc Jul 16, 2024
79635dd
apps-engine bridge
pierre-lehnen-rc Jul 16, 2024
969a706
type hack so that the code for the new apps-engine update is still se…
pierre-lehnen-rc Jul 16, 2024
4a3e26d
updated types used on the "Calls" sidebar to reflect that it can now …
pierre-lehnen-rc Jul 16, 2024
229697e
Added a button to join the call discussion on the list of calls
pierre-lehnen-rc Jul 16, 2024
fccbe5d
removed condition added based on a false assumption
pierre-lehnen-rc Jul 16, 2024
4bfdcf8
Merge branch 'develop' into feat/videoconf-persistent-chat
d-gubert Jul 17, 2024
86c5d7e
wip
ggazzo Jul 17, 2024
29c5b1b
apps-engine version
pierre-lehnen-rc Jul 17, 2024
c7ea1bb
..
ggazzo Jul 17, 2024
58f79df
..
ggazzo Jul 17, 2024
5e79768
rollback changes related to assigning the drid of the videoconf messa…
pierre-lehnen-rc Jul 17, 2024
5b4ef26
ts
pierre-lehnen-rc Jul 17, 2024
e140180
mapped the expected route path params for each room type
pierre-lehnen-rc Jul 17, 2024
b315ac0
use the id received from the backend, just in case we received a room…
pierre-lehnen-rc Jul 17, 2024
c81467d
Merge branch 'chore/useGoToRoom' into feat/videoconf-persistent-chat
pierre-lehnen-rc Jul 17, 2024
614c1d3
open the discussion
pierre-lehnen-rc Jul 17, 2024
381950f
changeset
pierre-lehnen-rc Jul 17, 2024
1d72e2a
Merge branch 'develop' into feat/videoconf-persistent-chat
pierre-lehnen-rc Jul 17, 2024
90e0641
oops
pierre-lehnen-rc Jul 17, 2024
a8deb4e
Merge branch 'develop' into feat/videoconf-persistent-chat
pierre-lehnen-rc Jul 17, 2024
3729678
Merge branch 'chore/useGoToRoom' into feat/videoconf-persistent-chat
pierre-lehnen-rc Jul 17, 2024
8f9ca8c
lint after merge
pierre-lehnen-rc Jul 17, 2024
5548b6b
Merge branch 'develop' into feat/videoconf-persistent-chat
pierre-lehnen-rc Jul 22, 2024
cd2d0f1
Update apps/meteor/server/services/video-conference/service.ts
pierre-lehnen-rc Jul 22, 2024
0a9e7bc
code review changes
pierre-lehnen-rc Jul 22, 2024
e3206dd
adding tests
pierre-lehnen-rc Jul 22, 2024
700603f
installation test
pierre-lehnen-rc Jul 22, 2024
5591615
skip persistent chat tests on CE
pierre-lehnen-rc Jul 22, 2024
c436fc2
additional tests
pierre-lehnen-rc Jul 23, 2024
bc69685
Merge branch 'develop' into feat/videoconf-persistent-chat
kodiakhq[bot] Jul 23, 2024
09aec45
Merge branch 'develop' into feat/videoconf-persistent-chat
kodiakhq[bot] Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/nice-laws-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'rocketchat-services': minor
'@rocket.chat/core-services': minor
'@rocket.chat/model-typings': minor
'@rocket.chat/ui-video-conf': minor
'@rocket.chat/core-typings': minor
'@rocket.chat/ui-contexts': minor
'@rocket.chat/models': minor
'@rocket.chat/ui-kit': minor
'@rocket.chat/i18n': minor
'@rocket.chat/meteor': minor
---

New Feature: Video Conference Persistent Chat.
This feature provides a discussion id for conference provider apps to store the chat messages exchanged during the conferences, so that those users may then access those messages again at any time through Rocket.Chat.
4 changes: 4 additions & 0 deletions apps/meteor/app/apps/server/bridges/videoConferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export class AppVideoConferenceBridge extends VideoConferenceBridge {
if (data.status > oldData.status) {
await VideoConf.setStatus(call._id, data.status);
}

if (data.discussionRid !== oldData.discussionRid) {
await VideoConf.assignDiscussionToConference(call._id, data.discussionRid);
}
}

protected async registerProvider(info: IVideoConfProvider, appId: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { Messages, Rooms } from '@rocket.chat/models';
import { Messages, Rooms, VideoConference } from '@rocket.chat/models';

import { callbacks } from '../../../../lib/callbacks';
import { broadcastMessageFromData } from '../../../../server/modules/watchers/lib/messages';
Expand Down Expand Up @@ -108,6 +108,8 @@ callbacks.add(
},
},
);

await VideoConference.unsetDiscussionRid(drid);
return drid;
},
callbacks.priority.LOW,
Expand Down
18 changes: 12 additions & 6 deletions apps/meteor/app/lib/server/functions/addUserToRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ import { notifyOnRoomChangedById } from '../lib/notifyListener';

export const addUserToRoom = async function (
rid: string,
user: Pick<IUser, '_id' | 'username'> | string,
user: Pick<IUser, '_id'> | string,
inviter?: Pick<IUser, '_id' | 'username'>,
silenced?: boolean,
{
skipSystemMessage,
skipAlertSound,
}: {
skipSystemMessage?: boolean;
skipAlertSound?: boolean;
} = {},
): Promise<boolean | undefined> {
const now = new Date();
const room = await Rooms.findOneById(rid);
Expand All @@ -43,12 +49,12 @@ export const addUserToRoom = async function (
}

try {
await callbacks.run('federation.beforeAddUserToARoom', { user, inviter }, room);
await callbacks.run('federation.beforeAddUserToARoom', { user: userToBeAdded, inviter }, room);
} catch (error) {
throw new Meteor.Error((error as any)?.message);
}

await callbacks.run('beforeAddedToRoom', { user: userToBeAdded, inviter: userToBeAdded });
await callbacks.run('beforeAddedToRoom', { user: userToBeAdded, inviter });

// Check if user is already in room
const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, userToBeAdded._id);
Expand Down Expand Up @@ -79,7 +85,7 @@ export const addUserToRoom = async function (
await Subscriptions.createWithRoomAndUser(room, userToBeAdded as IUser, {
ts: now,
open: true,
alert: true,
alert: !skipAlertSound,
unread: 1,
userMentions: 1,
groupMentions: 0,
Expand All @@ -93,7 +99,7 @@ export const addUserToRoom = async function (
throw new Meteor.Error('error-invalid-user', 'Cannot add an user to a room without a username');
}

if (!silenced) {
if (!skipSystemMessage) {
if (inviter) {
const extraData = {
ts: now,
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/slackbridge/server/SlackAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -1341,7 +1341,7 @@ export default class SlackAdapter {
const user = (await this.rocket.findUser(member)) || (await this.rocket.addUser(member));
if (user) {
slackLogger.debug('Adding user to room', user.username, rid);
await addUserToRoom(rid, user, null, true);
await addUserToRoom(rid, user, null, { skipSystemMessage: true });
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const useCallsRoomAction = () => {

return {
id: 'calls',
groups: ['channel', 'group', 'team'],
groups: ['channel', 'group', 'team', 'direct', 'direct_multiple'],
icon: 'phone',
title: 'Calls',
...(federated && {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IGroupVideoConference } from '@rocket.chat/core-typings';
import type { VideoConference } from '@rocket.chat/core-typings';
import { Box, States, StatesIcon, StatesTitle, StatesSubtitle, Throbber } from '@rocket.chat/fuselage';
import { useResizeObserver } from '@rocket.chat/fuselage-hooks';
import { useTranslation } from '@rocket.chat/ui-contexts';
Expand All @@ -21,7 +21,7 @@ import VideoConfListItem from './VideoConfListItem';
type VideoConfListProps = {
onClose: () => void;
total: number;
videoConfs: IGroupVideoConference[];
videoConfs: VideoConference[];
loading: boolean;
error?: Error;
reload: () => void;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { IGroupVideoConference } from '@rocket.chat/core-typings';
import type { VideoConference } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Button, Message, Box, Avatar, Palette } from '@rocket.chat/fuselage';
import { Button, Message, Box, Avatar, Palette, IconButton, ButtonGroup } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { UserAvatar } from '@rocket.chat/ui-avatar';
import { useTranslation, useSetting } from '@rocket.chat/ui-contexts';
Expand All @@ -10,14 +10,15 @@ import React from 'react';
import { useVideoConfJoinCall } from '../../../../../contexts/VideoConfContext';
import { useTimeAgo } from '../../../../../hooks/useTimeAgo';
import { VIDEOCONF_STACK_MAX_USERS } from '../../../../../lib/constants';
import { useGoToRoom } from '../../../hooks/useGoToRoom';

const VideoConfListItem = ({
videoConfData,
className = [],
reload,
...props
}: {
videoConfData: IGroupVideoConference;
videoConfData: VideoConference;
className?: string[];
reload: () => void;
}): ReactElement => {
Expand All @@ -32,6 +33,7 @@ const VideoConfListItem = ({
users,
createdAt,
endedAt,
discussionRid,
} = videoConfData;

const joinedUsers = users.filter((user) => user._id !== _id);
Expand All @@ -51,6 +53,8 @@ const VideoConfListItem = ({
return reload();
});

const goToRoom = useGoToRoom();

return (
<Box
color='default'
Expand All @@ -70,9 +74,20 @@ const VideoConfListItem = ({
<Message.Body clamp={2} />
<Box display='flex'></Box>
<Message.Block flexDirection='row' alignItems='center'>
<Button disabled={Boolean(endedAt)} small alignItems='center' display='flex' onClick={handleJoinConference}>
{endedAt ? t('Call_ended') : t('Join_call')}
</Button>
<ButtonGroup>
<Button disabled={Boolean(endedAt)} small alignItems='center' display='flex' onClick={handleJoinConference}>
{endedAt ? t('Call_ended') : t('Join_call')}
</Button>
{discussionRid && (
<IconButton
small
icon='discussion'
data-drid={discussionRid}
title={t('Join_discussion')}
onClick={() => goToRoom(discussionRid)}
/>
)}
</ButtonGroup>
{joinedUsers.length > 0 && (
<Box mis={8} fontScale='c1' display='flex' alignItems='center'>
<Avatar.Stack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { IGroupVideoConference } from '@rocket.chat/core-typings';
import type { VideoConference } from '@rocket.chat/core-typings';

import { RecordList } from '../../../../../lib/lists/RecordList';

export class VideoConfRecordList extends RecordList<IGroupVideoConference> {
protected compare(a: IGroupVideoConference, b: IGroupVideoConference): number {
export class VideoConfRecordList extends RecordList<VideoConference> {
protected compare(a: VideoConference, b: VideoConference): number {
return b.createdAt.getTime() - a.createdAt.getTime();
}
}
19 changes: 19 additions & 0 deletions apps/meteor/ee/server/settings/video-conference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ export function addSettings(): Promise<void> {
public: true,
invalidValue: true,
});

const discussionsEnabled = { _id: 'Discussion_enabled', value: true };

await this.add('VideoConf_Enable_Persistent_Chat', false, {
type: 'boolean',
public: true,
invalidValue: false,
alert: 'VideoConf_Enable_Persistent_Chat_Alert',
enableQuery: [discussionsEnabled],
});

const persistentChatEnabled = { _id: 'VideoConf_Enable_Persistent_Chat', value: true };

await this.add('VideoConf_Persistent_Chat_Discussion_Name', 'Conference Call Chat History', {
type: 'string',
public: true,
invalidValue: 'Conference Call Chat History',
enableQuery: [discussionsEnabled, persistentChatEnabled],
});
},
);
});
Expand Down
5 changes: 4 additions & 1 deletion apps/meteor/lib/callbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ interface EventLikeCallbackSignatures {
) => void;
'livechat.afterAgentRemoved': (params: { agent: Pick<IUser, '_id' | 'username'> }) => void;
'afterAddedToRoom': (params: { user: IUser; inviter?: IUser }, room: IRoom) => void;
'beforeAddedToRoom': (params: { user: AtLeast<IUser, '_id' | 'federated' | 'roles'>; inviter: IUser }) => void;
'beforeAddedToRoom': (params: {
user: AtLeast<IUser, '_id' | 'federated' | 'roles'>;
inviter: AtLeast<IUser, '_id' | 'username'>;
}) => void;
'afterCreateDirectRoom': (params: IRoom, second: { members: IUser[]; creatorId: IUser['_id'] }) => void;
'beforeDeleteRoom': (params: IRoom) => void;
'beforeJoinDefaultChannels': (user: IUser) => void;
Expand Down
22 changes: 22 additions & 0 deletions apps/meteor/server/models/raw/VideoConference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class VideoConferenceRaw extends BaseRaw<VideoConference> implements IVid
return [
{ key: { rid: 1, createdAt: 1 }, unique: false },
{ key: { type: 1, status: 1 }, unique: false },
{ key: { discussionRid: 1 }, unique: false },
];
}

Expand Down Expand Up @@ -263,4 +264,25 @@ export class VideoConferenceRaw extends BaseRaw<VideoConference> implements IVid
},
);
}

public async setDiscussionRidById(callId: string, discussionRid: IRoom['_id']): Promise<void> {
await this.updateOne({ _id: callId }, { $set: { discussionRid } });
}

public async unsetDiscussionRidById(callId: string): Promise<void> {
await this.updateOne({ _id: callId }, { $unset: { discussionRid: true } });
}

public async unsetDiscussionRid(discussionRid: IRoom['_id']): Promise<void> {
await this.updateMany(
{
discussionRid,
},
{
$unset: {
discussionRid: 1,
},
},
);
}
}
9 changes: 6 additions & 3 deletions apps/meteor/server/services/room/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ export class RoomService extends ServiceClassInternal implements IRoomService {

async addUserToRoom(
roomId: string,
user: Pick<IUser, '_id' | 'username'> | string,
user: Pick<IUser, '_id'> | string,
inviter?: Pick<IUser, '_id' | 'username'>,
silenced?: boolean,
options?: {
skipSystemMessage?: boolean;
skipAlertSound?: boolean;
},
): Promise<boolean | undefined> {
return addUserToRoom(roomId, user, inviter, silenced);
return addUserToRoom(roomId, user, inviter, options);
}

async removeUserFromRoom(roomId: string, user: IUser, options?: { byUser: IUser }): Promise<void> {
Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/server/services/team/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ export class TeamService extends ServiceClassInternal implements ITeamService {

for await (const member of members) {
const user = (await Users.findOneById(member.userId, { projection: { username: 1 } })) as Pick<IUser, '_id' | 'username'>;
await addUserToRoom(team.roomId, user, createdBy, false);
await addUserToRoom(team.roomId, user, createdBy, { skipSystemMessage: false });

if (member.roles) {
await this.addRolesToMember(teamId, member.userId, member.roles);
Expand Down Expand Up @@ -826,7 +826,7 @@ export class TeamService extends ServiceClassInternal implements ITeamService {
return;
}

await addUserToRoom(team.roomId, user, inviter, false);
await addUserToRoom(team.roomId, user, inviter, { skipSystemMessage: false });
}),
);
}
Expand Down Expand Up @@ -977,7 +977,7 @@ export class TeamService extends ServiceClassInternal implements ITeamService {
// at this point, users are already part of the team so we won't check for membership
for await (const user of users) {
// add each user to the default room
await addUserToRoom(room._id, user, inviter, false);
await addUserToRoom(room._id, user, inviter, { skipSystemMessage: false });
}
});
}
Expand Down
Loading
Loading