[NEW] Basic support for Video Conf (#4307)
* create livechat video conf feature * add handler to call url * remove webview and change to openUrl * Checking settings * stash * add action sheet on click init and ad more handlers * fix logic and call to create a video conf * change JitsiMeetView from InsideStack to ChatStack to remove modal animation * fix error logic * fix stack * fix comma Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
fc0d7e2ed3
commit
58a15b23b5
|
@ -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,
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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 });
|
||||
|
||||
|
|
|
@ -136,6 +136,7 @@ const ChatsStackNavigator = () => {
|
|||
<ChatsStack.Screen name='QueueListView' component={QueueListView} />
|
||||
<ChatsStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
|
||||
<ChatsStack.Screen name='CannedResponseDetail' component={CannedResponseDetail} />
|
||||
<ChatsStack.Screen name='JitsiMeetView' component={JitsiMeetView} options={{ headerShown: false }} />
|
||||
</ChatsStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
@ -325,7 +326,6 @@ const InsideStackNavigator = () => {
|
|||
<InsideStack.Screen name='StatusView' component={StatusView} />
|
||||
<InsideStack.Screen name='ShareView' component={ShareView} />
|
||||
<InsideStack.Screen name='ModalBlockView' component={ModalBlockView} options={ModalBlockView.navigationOptions} />
|
||||
<InsideStack.Screen name='JitsiMeetView' component={JitsiMeetView} options={{ headerShown: false }} />
|
||||
</InsideStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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<InsideStackParamList, 'JitsiMeetView'> {
|
||||
interface IJitsiMeetViewProps extends IBaseScreen<ChatsStackParamList, 'JitsiMeetView'> {
|
||||
baseUrl: string;
|
||||
user: IUser;
|
||||
}
|
||||
|
@ -32,12 +32,14 @@ interface IJitsiMeetViewProps extends IBaseScreen<InsideStackParamList, 'JitsiMe
|
|||
class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewState> {
|
||||
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<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
|
||||
componentWillUnmount() {
|
||||
logEvent(events.JM_CONFERENCE_TERMINATE);
|
||||
if (this.jitsiTimeout) {
|
||||
if (this.jitsiTimeout && !this.videoConf) {
|
||||
BackgroundTimer.clearInterval(this.jitsiTimeout);
|
||||
this.jitsiTimeout = null;
|
||||
BackgroundTimer.stopBackgroundTimer();
|
||||
|
@ -86,8 +88,8 @@ class JitsiMeetView extends React.Component<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
// Jitsi Update Timeout needs to be called every 10 seconds to make sure
|
||||
// call is not ended and is available to web users.
|
||||
onConferenceJoined = () => {
|
||||
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<IJitsiMeetViewProps, IJitsiMeetViewS
|
|||
};
|
||||
|
||||
onConferenceTerminated = () => {
|
||||
logEvent(events.JM_CONFERENCE_TERMINATE);
|
||||
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
|
||||
const { navigation } = this.props;
|
||||
navigation.pop();
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
<T extends keyof ChatsStackParamList>(item: { route?: T; params?: ChatsStackParamList[T]; event?: Function }): void;
|
||||
|
@ -68,6 +69,10 @@ interface IRoomActionsViewProps extends IBaseScreen<ChatsStackParamList, 'RoomAc
|
|||
addTeamChannelPermission?: string[];
|
||||
convertTeamPermission?: string[];
|
||||
viewCannedResponsesPermission?: string[];
|
||||
videoConf_Enable_DMs: boolean;
|
||||
videoConf_Enable_Channels: boolean;
|
||||
videoConf_Enable_Groups: boolean;
|
||||
videoConf_Enable_Teams: boolean;
|
||||
}
|
||||
|
||||
interface IRoomActionsViewState {
|
||||
|
@ -734,6 +739,16 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
}
|
||||
};
|
||||
|
||||
startVideoConf = ({ video }: { video: boolean }): void => {
|
||||
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<IRoomActionsViewProps, IRoomAction
|
|||
|
||||
renderJitsi = () => {
|
||||
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<IRoomActionsViewProps, IRoomAction
|
|||
<List.Separator />
|
||||
<List.Item
|
||||
title='Voice_call'
|
||||
onPress={() => callJitsi(room, true)}
|
||||
onPress={() => this.startVideoConf({ video: false })}
|
||||
testID='room-actions-voice'
|
||||
left={() => <List.Icon name='phone' />}
|
||||
showActionIndicator
|
||||
|
@ -832,7 +870,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
<List.Separator />
|
||||
<List.Item
|
||||
title='Video_call'
|
||||
onPress={() => callJitsi(room)}
|
||||
onPress={() => this.startVideoConf({ video: true })}
|
||||
testID='room-actions-video'
|
||||
left={() => <List.Icon name='camera' />}
|
||||
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,
|
||||
|
|
Loading…
Reference in New Issue