diff --git a/app/containers/UIKit/utils.ts b/app/containers/UIKit/utils.ts
index 0336e6cf..86839d86 100644
--- a/app/containers/UIKit/utils.ts
+++ b/app/containers/UIKit/utils.ts
@@ -3,6 +3,9 @@ import React, { useContext, useState } from 'react';
import { BlockContext } from '@rocket.chat/ui-kit';
import { IText } from './interfaces';
+import { videoConfJoin } from '../../lib/methods/videoConf';
+import { TActionSheetOptionsItem, useActionSheet } from '../ActionSheet';
+import i18n from '../../i18n';
export const textParser = ([{ text }]: IText[]) => text;
@@ -32,12 +35,14 @@ interface IUseBlockContext {
actionId: string;
appId?: string;
initialValue?: string;
+ url?: string;
}
export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUseBlockContext, context: BlockContext): TReturn => {
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
const { value = initialValue } = values[actionId] || {};
const [loading, setLoading] = useState(false);
+ const { showActionSheet } = useActionSheet();
const error = errors && actionId && errors[actionId];
@@ -53,6 +58,23 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
async ({ value }: any) => {
setLoading(true);
try {
+ if (appId === 'videoconf-core' && blockId) {
+ setLoading(false);
+ const options: TActionSheetOptionsItem[] = [
+ {
+ title: i18n.t('Video_call'),
+ icon: 'camera',
+ onPress: () => videoConfJoin(blockId, true)
+ },
+ {
+ title: i18n.t('Voice_call'),
+ icon: 'microphone',
+ onPress: () => videoConfJoin(blockId, false)
+ }
+ ];
+ showActionSheet({ options });
+ return;
+ }
await action({
blockId,
appId: appId || appIdFromContext,
diff --git a/app/definitions/rest/v1/videoConference.ts b/app/definitions/rest/v1/videoConference.ts
index a7a1e3f7..e919c9bd 100644
--- a/app/definitions/rest/v1/videoConference.ts
+++ b/app/definitions/rest/v1/videoConference.ts
@@ -2,4 +2,10 @@ export type VideoConferenceEndpoints = {
'video-conference/jitsi.update-timeout': {
POST: (params: { roomId: string }) => void;
};
+ 'video-conference.join': {
+ POST: (params: { callId: string; state: { cam: boolean } }) => { url: string; providerName: string };
+ };
+ 'video-conference.start': {
+ POST: (params: { roomId: string }) => { url: string };
+ };
};
diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json
index 977e9be9..fd5b8569 100644
--- a/app/i18n/locales/en.json
+++ b/app/i18n/locales/en.json
@@ -839,5 +839,6 @@
"Mark_as_unread_Info": "Display room as unread when there are unread messages",
"Show_badge_for_mentions": "Show badge for mentions",
"Show_badge_for_mentions_Info": "Display badge for direct mentions only",
+ "error-init-video-conf": "Error starting video call",
"totp-invalid": "Code or password invalid"
}
\ No newline at end of file
diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json
index a064932e..60c556e4 100644
--- a/app/i18n/locales/pt-BR.json
+++ b/app/i18n/locales/pt-BR.json
@@ -759,6 +759,7 @@
"Unsupported_format": "Formato não suportado",
"Downloaded_file": "Arquivo baixado",
"Error_Download_file": "Erro ao baixar o arquivo",
+ "error-init-video-conf": "Erro ao iniciar chamada de video"
"added__roomName__to_team": "#{{roomName}} adicionada a esta equipe",
"Added__username__to_team": "@{{user_added}} adicionado a esta equipe",
"Converted__roomName__to_team": "#{{roomName}} convertida em equipe",
diff --git a/app/lib/constants/defaultSettings.ts b/app/lib/constants/defaultSettings.ts
index 3edb1070..90b928d9 100644
--- a/app/lib/constants/defaultSettings.ts
+++ b/app/lib/constants/defaultSettings.ts
@@ -212,6 +212,18 @@ export const defaultSettings = {
Accounts_AvatarExternalProviderUrl: {
type: 'valueAsString'
},
+ VideoConf_Enable_DMs: {
+ type: 'valueAsBoolean'
+ },
+ VideoConf_Enable_Channels: {
+ type: 'valueAsBoolean'
+ },
+ VideoConf_Enable_Groups: {
+ type: 'valueAsBoolean'
+ },
+ VideoConf_Enable_Teams: {
+ type: 'valueAsBoolean'
+ },
Accounts_AllowDeleteOwnAccount: {
type: 'valueAsBoolean'
}
diff --git a/app/lib/methods/helpers/log/events.ts b/app/lib/methods/helpers/log/events.ts
index 51cb8cf1..ea4e57f4 100644
--- a/app/lib/methods/helpers/log/events.ts
+++ b/app/lib/methods/helpers/log/events.ts
@@ -351,6 +351,10 @@ export default {
TC_TOGGLE_AUTOJOIN: 'tc_toggle_autojoin',
TC_TOGGLE_AUTOJOIN_F: 'tc_toggle_autojoin_f',
+ // LIVECHAT VIDEOCONF
+ LIVECHAT_VIDEOCONF_JOIN: 'livechat_videoconf_join',
+ LIVECHAT_VIDEOCONF_TERMINATE: 'livechat_videoconf_terminate',
+
// DELETE OWN ACCOUNT ACCOUNT
DELETE_OWN_ACCOUNT: 'delete_own_account',
DELETE_OWN_ACCOUNT_F: 'delete_own_account_f'
diff --git a/app/lib/methods/videoConf.ts b/app/lib/methods/videoConf.ts
new file mode 100644
index 00000000..f10b2ad1
--- /dev/null
+++ b/app/lib/methods/videoConf.ts
@@ -0,0 +1,35 @@
+import navigation from '../navigation/appNavigation';
+import openLink from './helpers/openLink';
+import { Services } from '../services';
+import log from './helpers/log';
+import { showErrorAlert } from './helpers';
+import i18n from '../../i18n';
+
+export const videoConfJoin = async (callId: string, cam: boolean) => {
+ try {
+ const result = await Services.videoConferenceJoin(callId, cam);
+ if (result.success) {
+ const { url, providerName } = result;
+ if (providerName === 'jitsi') {
+ navigation.navigate('JitsiMeetView', { url, onlyAudio: !cam, videoConf: true });
+ } else {
+ openLink(url);
+ }
+ }
+ } catch (e) {
+ showErrorAlert(i18n.t('error-init-video-conf'));
+ log(e);
+ }
+};
+
+export const videoConfStartAndJoin = async (rid: string, cam: boolean) => {
+ try {
+ const videoConfResponse: any = await Services.videoConferenceStart(rid);
+ if (videoConfResponse.success) {
+ videoConfJoin(videoConfResponse.data.callId, cam);
+ }
+ } catch (e) {
+ showErrorAlert(i18n.t('error-init-video-conf'));
+ log(e);
+ }
+};
diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts
index 5d7007c5..566c55c6 100644
--- a/app/lib/services/restApi.ts
+++ b/app/lib/services/restApi.ts
@@ -918,6 +918,12 @@ export function getUserInfo(userId: string) {
export const toggleFavorite = (roomId: string, favorite: boolean) => sdk.post('rooms.favorite', { roomId, favorite });
+
+export const videoConferenceJoin = (callId: string, cam: boolean) =>
+ sdk.post('video-conference.join', { callId, state: { cam } });
+
+export const videoConferenceStart = (roomId: string) => sdk.post('video-conference.start', { roomId });
+
export const saveUserProfileMethod = (
params: IProfileParams,
customFields = {},
@@ -930,3 +936,4 @@ export const saveUserProfileMethod = (
export const deleteOwnAccount = (password: string, confirmRelinquish = false): any =>
// RC 0.67.0
sdk.post('users.deleteOwnAccount', { password, confirmRelinquish });
+
diff --git a/app/stacks/InsideStack.tsx b/app/stacks/InsideStack.tsx
index e5130374..6a525d57 100644
--- a/app/stacks/InsideStack.tsx
+++ b/app/stacks/InsideStack.tsx
@@ -136,6 +136,7 @@ const ChatsStackNavigator = () => {
+
);
};
@@ -325,7 +326,6 @@ const InsideStackNavigator = () => {
-
);
};
diff --git a/app/stacks/types.ts b/app/stacks/types.ts
index aa96c982..6265b3aa 100644
--- a/app/stacks/types.ts
+++ b/app/stacks/types.ts
@@ -163,6 +163,12 @@ export type ChatsStackParamList = {
cannedResponse: ICannedResponse;
room: ISubscription;
};
+ JitsiMeetView: {
+ rid: string;
+ url: string;
+ onlyAudio?: boolean;
+ videoConf?: boolean;
+ };
};
export type ProfileStackParamList = {
@@ -259,11 +265,6 @@ export type InsideStackParamList = {
ModalBlockView: {
data: any; // TODO: Change;
};
- JitsiMeetView: {
- rid: string;
- url: string;
- onlyAudio?: boolean;
- };
};
export type OutsideParamList = {
diff --git a/app/views/JitsiMeetView.tsx b/app/views/JitsiMeetView.tsx
index 0a87f1bf..c2e8d2d9 100644
--- a/app/views/JitsiMeetView.tsx
+++ b/app/views/JitsiMeetView.tsx
@@ -9,7 +9,7 @@ import ActivityIndicator from '../containers/ActivityIndicator';
import { events, logEvent } from '../lib/methods/helpers/log';
import { isAndroid, isIOS } from '../lib/methods/helpers';
import { withTheme } from '../theme';
-import { InsideStackParamList } from '../stacks/types';
+import { ChatsStackParamList } from '../stacks/types';
import { IApplicationState, IUser, IBaseScreen } from '../definitions';
import { Services } from '../lib/services';
@@ -24,7 +24,7 @@ interface IJitsiMeetViewState {
loading: boolean;
}
-interface IJitsiMeetViewProps extends IBaseScreen {
+interface IJitsiMeetViewProps extends IBaseScreen {
baseUrl: string;
user: IUser;
}
@@ -32,12 +32,14 @@ interface IJitsiMeetViewProps extends IBaseScreen {
private rid: string;
private url: string;
+ private videoConf: boolean;
private jitsiTimeout: number | null;
constructor(props: IJitsiMeetViewProps) {
super(props);
this.rid = props.route.params?.rid;
this.url = props.route.params?.url;
+ this.videoConf = !!props.route.params?.videoConf;
this.jitsiTimeout = null;
const { user, baseUrl } = props;
@@ -71,7 +73,7 @@ class JitsiMeetView extends React.Component {
- logEvent(events.JM_CONFERENCE_JOIN);
- if (this.rid) {
+ logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_JOIN : events.JM_CONFERENCE_JOIN);
+ if (this.rid && !this.videoConf) {
Services.updateJitsiTimeout(this.rid).catch((e: unknown) => console.log(e));
if (this.jitsiTimeout) {
BackgroundTimer.clearInterval(this.jitsiTimeout);
@@ -101,7 +103,7 @@ class JitsiMeetView extends React.Component {
- logEvent(events.JM_CONFERENCE_TERMINATE);
+ logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
const { navigation } = this.props;
navigation.pop();
};
diff --git a/app/views/RoomActionsView/index.tsx b/app/views/RoomActionsView/index.tsx
index 2f93d636..5d088422 100644
--- a/app/views/RoomActionsView/index.tsx
+++ b/app/views/RoomActionsView/index.tsx
@@ -44,6 +44,7 @@ import {
} from '../../lib/methods/helpers';
import { Services } from '../../lib/services';
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
+import { videoConfStartAndJoin } from '../../lib/methods/videoConf';
interface IOnPressTouch {
(item: { route?: T; params?: ChatsStackParamList[T]; event?: Function }): void;
@@ -68,6 +69,10 @@ interface IRoomActionsViewProps extends IBaseScreen {
+ const { room } = this.state;
+ const { serverVersion } = this.props;
+ if (compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0')) {
+ videoConfStartAndJoin(room.rid, video);
+ } else {
+ callJitsi(room, !video);
+ }
+ };
+
renderRoomInfo = () => {
const { room, member } = this.state;
const { rid, name, t, topic, source } = room;
@@ -810,12 +825,35 @@ class RoomActionsView extends React.Component {
const { room } = this.state;
- const { jitsiEnabled, jitsiEnableTeams, jitsiEnableChannels } = this.props;
+ const {
+ jitsiEnabled,
+ jitsiEnableTeams,
+ jitsiEnableChannels,
+ serverVersion,
+ videoConf_Enable_DMs,
+ videoConf_Enable_Channels,
+ videoConf_Enable_Groups,
+ videoConf_Enable_Teams
+ } = this.props;
const isJitsiDisabledForTeams = room.teamMain && !jitsiEnableTeams;
const isJitsiDisabledForChannels = !room.teamMain && (room.t === 'p' || room.t === 'c') && !jitsiEnableChannels;
- if (!jitsiEnabled || isJitsiDisabledForTeams || isJitsiDisabledForChannels) {
+ const isVideoConfDisabledForTeams = !!room.teamMain && !videoConf_Enable_Teams;
+ const isVideoConfDisabledForChannels = !room.teamMain && room.t === 'c' && !videoConf_Enable_Channels;
+ const isVideoConfDisabledForGroups = !room.teamMain && room.t === 'p' && !videoConf_Enable_Groups;
+ const isVideoConfDisabledForDirect = !room.teamMain && room.t === 'd' && !videoConf_Enable_DMs;
+
+ if (compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0')) {
+ if (
+ isVideoConfDisabledForTeams ||
+ isVideoConfDisabledForChannels ||
+ isVideoConfDisabledForGroups ||
+ isVideoConfDisabledForDirect
+ ) {
+ return null;
+ }
+ } else if (!jitsiEnabled || isJitsiDisabledForTeams || isJitsiDisabledForChannels) {
return null;
}
@@ -824,7 +862,7 @@ class RoomActionsView extends React.Component
callJitsi(room, true)}
+ onPress={() => this.startVideoConf({ video: false })}
testID='room-actions-voice'
left={() => }
showActionIndicator
@@ -832,7 +870,7 @@ class RoomActionsView extends React.Component
callJitsi(room)}
+ onPress={() => this.startVideoConf({ video: true })}
testID='room-actions-video'
left={() => }
showActionIndicator
@@ -1309,6 +1347,10 @@ const mapStateToProps = (state: IApplicationState) => ({
jitsiEnabled: (state.settings.Jitsi_Enabled || false) as boolean,
jitsiEnableTeams: (state.settings.Jitsi_Enable_Teams || false) as boolean,
jitsiEnableChannels: (state.settings.Jitsi_Enable_Channels || false) as boolean,
+ videoConf_Enable_DMs: (state.settings.VideoConf_Enable_DMs ?? true) as boolean,
+ videoConf_Enable_Channels: (state.settings.VideoConf_Enable_Channels ?? true) as boolean,
+ videoConf_Enable_Groups: (state.settings.VideoConf_Enable_Groups ?? true) as boolean,
+ videoConf_Enable_Teams: (state.settings.VideoConf_Enable_Teams ?? true) as boolean,
encryptionEnabled: state.encryption.enabled,
serverVersion: state.server.version,
isMasterDetail: state.app.isMasterDetail,