Skip to content

Commit

Permalink
[NEW] Video Conference Message blocks and info action (#27310)
Browse files Browse the repository at this point in the history
Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com>
Co-authored-by: Guilherme Gazzo <5263975+ggazzo@users.noreply.github.com>
Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com>
  • Loading branch information
4 people authored and MartinSchoeler committed Nov 28, 2022
1 parent 40331fc commit 73c491e
Show file tree
Hide file tree
Showing 63 changed files with 1,728 additions and 416 deletions.
2 changes: 1 addition & 1 deletion apps/meteor/app/api/server/v1/videoConference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ API.v1.addRoute(

API.v1.addRoute(
'video-conference.info',
{ authRequired: true, validateParams: isVideoConfInfoProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } },
{ authRequired: true, validateParams: isVideoConfInfoProps, rateLimiterOptions: { numRequestsAllowed: 15, intervalTimeInMS: 3000 } },
{
async get() {
const { callId } = this.queryParams;
Expand Down
39 changes: 35 additions & 4 deletions apps/meteor/client/views/blocks/MessageBlock.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { UIKitIncomingInteractionContainerType } from '@rocket.chat/apps-engine/definition/uikit/UIKitIncomingInteractionContainer';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { UiKitMessage, UiKitComponent, kitContext, messageParser } from '@rocket.chat/fuselage-ui-kit';
import React from 'react';

import * as ActionManager from '../../../app/ui-message/client/ActionManager';
import { useBlockRendered } from '../../components/message/hooks/useBlockRendered';
import { useVideoConfJoinCall, useVideoConfSetPreferences } from '../../contexts/VideoConfContext';
import {
useVideoConfJoinCall,
useVideoConfSetPreferences,
useVideoConfIsCalling,
useVideoConfIsRinging,
useVideoConfDispatchOutgoing,
} from '../../contexts/VideoConfContext';
import { VideoConfManager } from '../../lib/VideoConfManager';
import { renderMessageBody } from '../../lib/utils/renderMessageBody';
import './textParsers';
import { useVideoConfWarning } from '../room/contextualBar/VideoConference/useVideoConfWarning';

// TODO: move this to fuselage-ui-kit itself
const mrkdwn = ({ text } = {}) => text && <span dangerouslySetInnerHTML={{ __html: renderMessageBody({ msg: text }) }} />;
Expand All @@ -16,14 +25,36 @@ function MessageBlock({ mid: _mid, rid, blocks, appId }) {
const { ref, className } = useBlockRendered();
const joinCall = useVideoConfJoinCall();
const setPreferences = useVideoConfSetPreferences();
const isCalling = useVideoConfIsCalling();
const isRinging = useVideoConfIsRinging();
const dispatchWarning = useVideoConfWarning();
const dispatchPopup = useVideoConfDispatchOutgoing();

const handleOpenVideoConf = useMutableCallback(async (rid) => {
if (isCalling || isRinging) {
return;
}

try {
await VideoConfManager.loadCapabilities();
dispatchPopup({ rid });
} catch (error) {
dispatchWarning(error.error);
}
});

const context = {
action: ({ actionId, value, blockId, mid = _mid, appId }, event) => {
if (appId === 'videoconf-core' && actionId === 'join') {
if (appId === 'videoconf-core') {
event.preventDefault();
setPreferences({ mic: true, cam: false });
joinCall(blockId);
return;
if (actionId === 'join') {
return joinCall(blockId);
}

if (actionId === 'callBack') {
return handleOpenVideoConf(blockId);
}
}

ActionManager.triggerBlockAction({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const VideoConfListItem = ({
</Avatar.Stack>
<Box mis='x4'>
{joinedUsers.length > VIDEOCONF_STACK_MAX_USERS
? t('__usersCount__members_joined', { usersCount: joinedUsers.length - VIDEOCONF_STACK_MAX_USERS })
? t('__usersCount__member_joined', { usersCount: joinedUsers.length - VIDEOCONF_STACK_MAX_USERS })
: t('joined')}
</Box>
</Box>
Expand Down
22 changes: 11 additions & 11 deletions apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@
"email": "support@rocket.chat"
},
"devDependencies": {
"@babel/core": "^7.18.9",
"@babel/core": "^7.18.13",
"@babel/eslint-parser": "^7.18.9",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.18.9",
"@babel/preset-env": "^7.18.9",
"@babel/preset-env": "^7.18.10",
"@babel/preset-react": "^7.18.6",
"@babel/register": "^7.18.9",
"@faker-js/faker": "^6.3.1",
Expand Down Expand Up @@ -127,7 +127,7 @@
"@types/prometheus-gc-stats": "^0.6.2",
"@types/proxyquire": "^1.3.28",
"@types/psl": "^1.1.0",
"@types/react": "~17.0.47",
"@types/react": "~17.0.48",
"@types/react-dom": "~17.0.17",
"@types/rewire": "^2.5.28",
"@types/sanitize-html": "^2",
Expand Down Expand Up @@ -156,7 +156,7 @@
"chai-spies": "^1.0.0",
"cross-env": "^7.0.3",
"emojione-assets": "^4.5.0",
"eslint": "^8.20.0",
"eslint": "^8.22.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-playwright": "^0.11.1",
Expand Down Expand Up @@ -205,7 +205,7 @@
"@react-aria/color": "^3.0.0-beta.15",
"@rocket.chat/agenda": "workspace:^",
"@rocket.chat/api-client": "workspace:^",
"@rocket.chat/apps-engine": "latest",
"@rocket.chat/apps-engine": "1.36.0-videoconf",
"@rocket.chat/cas-validate": "workspace:^",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/css-in-js": "0.31.21",
Expand All @@ -214,11 +214,11 @@
"@rocket.chat/forked-matrix-appservice-bridge": "^4.0.1",
"@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.2",
"@rocket.chat/fuselage": "next",
"@rocket.chat/fuselage-hooks": "0.31.21",
"@rocket.chat/fuselage-polyfills": "0.31.21",
"@rocket.chat/fuselage-toastbar": "0.31.21",
"@rocket.chat/fuselage-tokens": "0.31.21",
"@rocket.chat/fuselage-ui-kit": "0.31.21",
"@rocket.chat/fuselage-hooks": "next",
"@rocket.chat/fuselage-polyfills": "next",
"@rocket.chat/fuselage-toastbar": "next",
"@rocket.chat/fuselage-tokens": "next",
"@rocket.chat/fuselage-ui-kit": "workspace:^",
"@rocket.chat/gazzodown": "workspace:^",
"@rocket.chat/icons": "0.31.21",
"@rocket.chat/layout": "next",
Expand All @@ -241,7 +241,7 @@
"@rocket.chat/ui-video-conf": "workspace:^",
"@rocket.chat/web-ui-registration": "workspace:^",
"@slack/rtm-api": "^6.0.0",
"@tanstack/react-query": "^4.0.10",
"@tanstack/react-query": "^4.16.1",
"@types/cookie": "^0.5.1",
"@types/katex": "^0.14.0",
"@types/lodash": "^4.14.182",
Expand Down
9 changes: 8 additions & 1 deletion apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"__count__empty_rooms_will_be_removed_automatically__rooms__": "__count__ empty rooms will be removed automatically:<br/> __rooms__.",
"__count__message_pruned": "__count__ message pruned",
"__count__message_pruned_plural": "__count__ messages pruned",
"__usersCount__members_joined": "+ __usersCount__ members joined",
"__usersCount__member_joined": "+ __usersCount__ member joined",
"__usersCount__member_joined_plural": "+ __usersCount__ members joined",
"__usersCount__people_will_be_invited": "__usersCount__ people will be invited",
"__username__is_no_longer__role__defined_by__user_by_": "__username__ is no longer __role__ by __user_by__",
"__username__was_set__role__by__user_by_": "__username__ was set __role__ by __user_by__",
Expand Down Expand Up @@ -774,6 +775,7 @@
"By_author": "By __author__",
"cache_cleared": "Cache cleared",
"Call": "Call",
"Call_back": "Call back",
"Calling": "Calling",
"Call_Center": "Voice Channel",
"Call_Center_Description": "Configure Rocket.Chat's voice channels",
Expand All @@ -791,7 +793,9 @@
"Call_number_enterprise_only": "Call number (Enterprise Edition only)",
"call-management": "Call Management",
"call-management_description": "Permission to start a meeting",
"Call_ongoing": "Call ongoing",
"Call_unavailable_for_federation": "Call is unavailable for Federated rooms",
"Call_was_not_answered": "Call was not answered",
"Caller": "Caller",
"Caller_Id": "Caller ID",
"Cam_on": "Cam On",
Expand Down Expand Up @@ -5120,6 +5124,8 @@
"Video_Conference": "Conference Call",
"Video_Call_unavailable_for_federation": "Video Call is unavailable for Federated rooms",
"Video_Conferences": "Conference Calls",
"Video_Conference_Info": "Meeting Information",
"Video_Conference_Url": "Meeting URL",
"video-conf-provider-not-configured": "**Conference call not enabled**: A workspace admin needs to enable the conference calls feature first.",
"Video_message": "Video message",
"Videocall_declined": "Video Call Declined.",
Expand Down Expand Up @@ -5287,6 +5293,7 @@
"VoIP_Toggle": "Enable/Disable VoIP",
"Chat_opened_by_visitor": "Chat opened by the visitor",
"Wait_activation_warning": "Before you can login, your account must be manually activated by an administrator.",
"Waiting_for_answer": "Waiting for answer",
"Waiting_queue": "Waiting queue",
"Waiting_queue_message": "Waiting queue message",
"Waiting_queue_message_description": "Message that will be displayed to the visitors when they get in the queue",
Expand Down
14 changes: 0 additions & 14 deletions apps/meteor/server/models/raw/Messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,20 +247,6 @@ export class MessagesRaw extends BaseRaw<IMessage> implements IMessagesModel {
await this.updateOne({ _id }, { $addToSet: { blocks: { $each: blocks } } });
}

async removeVideoConfJoinButton(_id: IMessage['_id']): Promise<void> {
await this.updateOne(
{ _id },
{
$pull: {
blocks: {
appId: 'videoconf-core',
type: 'actions',
} as Required<IMessage>['blocks'][number],
},
},
);
}

async countRoomsWithStarredMessages(options: AggregateOptions): Promise<number> {
const queryResult = await this.col
.aggregate<{ _id: null; total: number }>(
Expand Down
5 changes: 4 additions & 1 deletion apps/meteor/server/models/raw/VideoConference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ export class VideoConferenceRaw extends BaseRaw<VideoConference> implements IVid
});
}

public async addUserById(callId: string, user: Pick<IUser, '_id' | 'name' | 'username' | 'avatarETag'> & { ts?: Date }): Promise<void> {
public async addUserById(
callId: string,
user: Required<Pick<IUser, '_id' | 'name' | 'username' | 'avatarETag'>> & { ts?: Date },
): Promise<void> {
await this.updateOneById(callId, {
$addToSet: {
users: {
Expand Down
33 changes: 33 additions & 0 deletions apps/meteor/server/modules/core-apps/videoconf.module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';

import type { IUiKitCoreApp } from '../../sdk/types/IUiKitCoreApp';
import { VideoConf } from '../../sdk';

Expand All @@ -6,6 +8,7 @@ export class VideoConfModule implements IUiKitCoreApp {

async blockAction(payload: any): Promise<any> {
const {
triggerId,
actionId,
payload: { blockId: callId },
user: { _id: userId },
Expand All @@ -14,5 +17,35 @@ export class VideoConfModule implements IUiKitCoreApp {
if (actionId === 'join') {
VideoConf.join(userId, callId, {});
}

if (actionId === 'info') {
const blocks = await VideoConf.getInfo(callId, userId);

return {
type: 'modal.open',
triggerId,
appId: this.appId,
view: {
appId: this.appId,
type: 'modal',
id: `${callId}-info`,
title: {
type: 'plain_text',
text: TAPi18n.__('Video_Conference_Info'),
emoji: false,
},
close: {
type: 'button',
text: {
type: 'plain_text',
text: TAPi18n.__('Close'),
emoji: false,
},
actionId: 'cancel',
},
blocks,
},
};
}
}
}
6 changes: 6 additions & 0 deletions apps/meteor/server/modules/listeners/listeners.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ export class ListenersModule {
notifications.streamRoomMessage.emitWithoutBroadcast(message.rid, message);
});

service.onEvent('message.update', ({ message }) => {
if (message.rid) {
notifications.streamRoomMessage.emitWithoutBroadcast(message.rid, message);
}
});

service.onEvent('watch.subscriptions', ({ clientAction, subscription }) => {
if (!subscription.u?._id) {
return;
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/server/sdk/lib/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
IWebdavAccount,
ICustomSound,
VoipEventDataSignature,
AtLeast,
UserStatus,
} from '@rocket.chat/core-typings';

Expand Down Expand Up @@ -138,4 +139,5 @@ export type EventSignatures = {
'call.callerhangup'(userId: string, data: { roomId: string }): void;
'watch.pbxevents'(data: { clientAction: ClientAction; data: Partial<IPbxEvent>; id: string }): void;
'connector.statuschanged'(enabled: boolean): void;
'message.update'(data: { message: AtLeast<IMessage, 'rid'> }): void;
};
2 changes: 2 additions & 0 deletions apps/meteor/server/sdk/types/IVideoConfService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
VideoConferenceInstructions,
} from '@rocket.chat/core-typings';
import type { PaginatedResult } from '@rocket.chat/rest-typings';
import type { IBlock } from '@rocket.chat/apps-engine/definition/uikit';

export type VideoConferenceJoinOptions = {
mic?: boolean;
Expand All @@ -18,6 +19,7 @@ export interface IVideoConfService {
create(data: VideoConferenceCreateData, useAppUser?: boolean): Promise<VideoConferenceInstructions>;
start(caller: IUser['_id'], rid: string, options: { title?: string; allowRinging?: boolean }): Promise<VideoConferenceInstructions>;
join(uid: IUser['_id'] | undefined, callId: VideoConference['_id'], options: VideoConferenceJoinOptions): Promise<string>;
getInfo(callId: VideoConference['_id'], uid: IUser['_id'] | undefined): Promise<IBlock[]>;
cancel(uid: IUser['_id'], callId: VideoConference['_id']): Promise<void>;
get(callId: VideoConference['_id']): Promise<Omit<VideoConference, 'providerData'> | null>;
getUnfiltered(callId: VideoConference['_id']): Promise<VideoConference | null>;
Expand Down
Loading

0 comments on commit 73c491e

Please sign in to comment.