diff --git a/app/containers/UIKit/VideoConferenceBlock/components/CallAgainActionSheet.tsx b/app/containers/UIKit/VideoConferenceBlock/components/StartACallActionSheet.tsx
similarity index 70%
rename from app/containers/UIKit/VideoConferenceBlock/components/CallAgainActionSheet.tsx
rename to app/containers/UIKit/VideoConferenceBlock/components/StartACallActionSheet.tsx
index 781c5b284..2158e3680 100644
--- a/app/containers/UIKit/VideoConferenceBlock/components/CallAgainActionSheet.tsx
+++ b/app/containers/UIKit/VideoConferenceBlock/components/StartACallActionSheet.tsx
@@ -6,7 +6,6 @@ import i18n from '../../../../i18n';
import { getSubscriptionByRoomId } from '../../../../lib/database/services/Subscription';
import { useAppSelector } from '../../../../lib/hooks';
import { getRoomAvatar, getUidDirectMessage } from '../../../../lib/methods/helpers';
-import { videoConfStartAndJoin } from '../../../../lib/methods/videoConf';
import { useTheme } from '../../../../theme';
import { useActionSheet } from '../../../ActionSheet';
import AvatarContainer from '../../../Avatar';
@@ -16,12 +15,12 @@ import { BUTTON_HIT_SLOP } from '../../../message/utils';
import StatusContainer from '../../../Status';
import useStyle from './styles';
-export default function CallAgainActionSheet({ rid }: { rid: string }): React.ReactElement {
+export default function StartACallActionSheet({ rid, initCall }: { rid: string; initCall: Function }): React.ReactElement {
const style = useStyle();
const { colors } = useTheme();
- const [user, setUser] = useState({ username: '', avatar: '', uid: '', rid: '' });
- const [phone, setPhone] = useState(true);
- const [camera, setCamera] = useState(false);
+ const [user, setUser] = useState({ username: '', avatar: '', uid: '' });
+ const [mic, setMic] = useState(true);
+ const [cam, setCam] = useState(false);
const username = useAppSelector(state => state.login.user.username);
const { hideActionSheet } = useActionSheet();
@@ -31,7 +30,7 @@ export default function CallAgainActionSheet({ rid }: { rid: string }): React.Re
const room = await getSubscriptionByRoomId(rid);
const uid = (await getUidDirectMessage(room)) as string;
const avt = getRoomAvatar(room);
- setUser({ uid, username: room?.name || '', avatar: avt, rid: room?.id || '' });
+ setUser({ uid, username: room?.name || '', avatar: avt });
})();
}, [rid]);
@@ -43,25 +42,27 @@ export default function CallAgainActionSheet({ rid }: { rid: string }): React.Re
{i18n.t('Start_a_call')}
setCamera(!camera)}
- style={[style.iconCallContainer, camera && style.enabledBackground, { marginRight: 6 }]}
+ onPress={() => setCam(!cam)}
+ style={[style.iconCallContainer, cam && style.enabledBackground, { marginRight: 6 }]}
hitSlop={BUTTON_HIT_SLOP}
>
-
+
setPhone(!phone)}
- style={[style.iconCallContainer, phone && style.enabledBackground]}
+ onPress={() => setMic(!mic)}
+ style={[style.iconCallContainer, mic && style.enabledBackground]}
hitSlop={BUTTON_HIT_SLOP}
>
-
+
- {user.username}
+
+ {user.username}
+
@@ -70,7 +71,7 @@ export default function CallAgainActionSheet({ rid }: { rid: string }): React.Re
onPress={() => {
hideActionSheet();
setTimeout(() => {
- videoConfStartAndJoin(user.rid, camera);
+ initCall({ cam, mic });
}, 100);
}}
title={i18n.t('Call')}
diff --git a/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceDirect.tsx b/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceDirect.tsx
index 2526a190f..d6fc28662 100644
--- a/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceDirect.tsx
+++ b/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceDirect.tsx
@@ -3,17 +3,16 @@ import { Text } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import i18n from '../../../../i18n';
-import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
+import { videoConfJoin } from '../../../../lib/methods/videoConf';
import useStyle from './styles';
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
const VideoConferenceDirect = React.memo(({ blockId }: { blockId: string }) => {
const style = useStyle();
- const { joinCall } = useVideoConf();
return (
- joinCall(blockId)}>
+ videoConfJoin(blockId)}>
{i18n.t('Join')}
{i18n.t('Waiting_for_answer')}
diff --git a/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceEnded.tsx b/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceEnded.tsx
index 8c8171b3a..b4c95fa8e 100644
--- a/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceEnded.tsx
+++ b/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceEnded.tsx
@@ -6,9 +6,7 @@ import { IUser } from '../../../../definitions';
import { VideoConferenceType } from '../../../../definitions/IVideoConference';
import i18n from '../../../../i18n';
import { useAppSelector } from '../../../../lib/hooks';
-import { useSnaps } from '../../../../lib/hooks/useSnaps';
-import { useActionSheet } from '../../../ActionSheet';
-import CallAgainActionSheet from './CallAgainActionSheet';
+import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
import { CallParticipants, TCallUsers } from './CallParticipants';
import useStyle from './styles';
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
@@ -26,8 +24,7 @@ export default function VideoConferenceEnded({
}): React.ReactElement {
const style = useStyle();
const username = useAppSelector(state => state.login.user.username);
- const { showActionSheet } = useActionSheet();
- const snaps = useSnaps([1250]);
+ const { showInitCallActionSheet } = useVideoConf(rid);
const onlyAuthorOnCall = users.length === 1 && users.some(user => user.username === createdBy.username);
@@ -35,15 +32,7 @@ export default function VideoConferenceEnded({
{type === 'direct' ? (
<>
-
- showActionSheet({
- children: ,
- snaps
- })
- }
- >
+
{createdBy.username === username ? i18n.t('Call_back') : i18n.t('Call_again')}
diff --git a/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceOutgoing.tsx b/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceOutgoing.tsx
index 164e9b3ab..bebf26da1 100644
--- a/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceOutgoing.tsx
+++ b/app/containers/UIKit/VideoConferenceBlock/components/VideoConferenceOutgoing.tsx
@@ -3,18 +3,17 @@ import { Text } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import i18n from '../../../../i18n';
-import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
+import { videoConfJoin } from '../../../../lib/methods/videoConf';
import { CallParticipants, TCallUsers } from './CallParticipants';
import useStyle from './styles';
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
export default function VideoConferenceOutgoing({ users, blockId }: { users: TCallUsers; blockId: string }): React.ReactElement {
const style = useStyle();
- const { joinCall } = useVideoConf();
return (
- joinCall(blockId)}>
+ videoConfJoin(blockId)}>
{i18n.t('Join')}
diff --git a/app/containers/UIKit/VideoConferenceBlock/components/styles.ts b/app/containers/UIKit/VideoConferenceBlock/components/styles.ts
index d8258e24d..df607ff8d 100644
--- a/app/containers/UIKit/VideoConferenceBlock/components/styles.ts
+++ b/app/containers/UIKit/VideoConferenceBlock/components/styles.ts
@@ -100,7 +100,8 @@ export default function useStyle() {
actionSheetUsername: {
fontSize: 16,
...sharedStyles.textBold,
- color: colors.passcodePrimary
+ color: colors.passcodePrimary,
+ flexShrink: 1
},
enabledBackground: {
backgroundColor: colors.conferenceCallEnabledIconBackground
diff --git a/app/containers/UIKit/utils.ts b/app/containers/UIKit/utils.ts
index f127f8068..b56eaaec1 100644
--- a/app/containers/UIKit/utils.ts
+++ b/app/containers/UIKit/utils.ts
@@ -2,7 +2,7 @@
import { BlockContext } from '@rocket.chat/ui-kit';
import React, { useContext, useState } from 'react';
-import { useVideoConf } from '../../lib/hooks/useVideoConf';
+import { videoConfJoin } from '../../lib/methods/videoConf';
import { IText } from './interfaces';
export const textParser = ([{ text }]: IText[]) => text;
@@ -40,7 +40,6 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
const { value = initialValue } = values[actionId] || {};
const [loading, setLoading] = useState(false);
- const { joinCall } = useVideoConf();
const error = errors && actionId && errors[actionId];
@@ -58,7 +57,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
try {
if (appId === 'videoconf-core' && blockId) {
setLoading(false);
- return joinCall(blockId);
+ return videoConfJoin(blockId);
}
await action({
blockId,
diff --git a/app/containers/message/CallButton.tsx b/app/containers/message/CallButton.tsx
index 9a81ca7ce..265ebb268 100644
--- a/app/containers/message/CallButton.tsx
+++ b/app/containers/message/CallButton.tsx
@@ -10,12 +10,12 @@ import { themes } from '../../lib/constants';
import { IMessageCallButton } from './interfaces';
import { useTheme } from '../../theme';
-const CallButton = React.memo(({ callJitsi }: IMessageCallButton) => {
+const CallButton = React.memo(({ handleEnterCall }: IMessageCallButton) => {
const { theme } = useTheme();
return (
void;
onReactionLongPress?: (item: TAnyMessageModel) => void;
navToRoomInfo: (navParam: IRoomInfoParam) => void;
- callJitsi?: () => void;
+ handleEnterCall?: () => void;
blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
onAnswerButtonPress?: (message: string, tmid?: string, tshow?: boolean) => void;
threadBadgeColor?: string;
@@ -69,7 +69,6 @@ class MessageContainer extends React.Component null,
onLongPress: () => {},
- callJitsi: () => {},
blockAction: () => {},
archived: false,
broadcast: false,
@@ -338,7 +337,7 @@ class MessageContainer extends React.Component void;
+ handleEnterCall?: () => void;
}
export interface IMessageContent {
diff --git a/app/definitions/IVideoConference.ts b/app/definitions/IVideoConference.ts
index af689be47..1ba428cb3 100644
--- a/app/definitions/IVideoConference.ts
+++ b/app/definitions/IVideoConference.ts
@@ -4,37 +4,34 @@ import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
import type { IMessage } from './IMessage';
-export enum VideoConferenceStatus {
+export declare enum VideoConferenceStatus {
CALLING = 0,
STARTED = 1,
EXPIRED = 2,
ENDED = 3,
DECLINED = 4
}
-
-export type DirectCallInstructions = {
+export declare type DirectCallInstructions = {
type: 'direct';
- callee: IUser['_id'];
+ calleeId: IUser['_id'];
callId: string;
};
-
-export type ConferenceInstructions = {
+export declare type ConferenceInstructions = {
type: 'videoconference';
callId: string;
rid: IRoom['_id'];
};
-
-export type LivechatInstructions = {
+export declare type LivechatInstructions = {
type: 'livechat';
callId: string;
};
-
-export type VideoConferenceType = DirectCallInstructions['type'] | ConferenceInstructions['type'] | LivechatInstructions['type'];
-
+export declare type VideoConferenceType =
+ | DirectCallInstructions['type']
+ | ConferenceInstructions['type']
+ | LivechatInstructions['type'];
export interface IVideoConferenceUser extends Pick, '_id' | 'username' | 'name' | 'avatarETag'> {
ts: Date;
}
-
export interface IVideoConference extends IRocketChatRecord {
type: VideoConferenceType;
rid: string;
@@ -45,51 +42,68 @@ export interface IVideoConference extends IRocketChatRecord {
ended?: IMessage['_id'];
};
url?: string;
-
- createdBy: Pick;
+ createdBy: Pick, '_id' | 'username' | 'name'>;
createdAt: Date;
-
- endedBy?: Pick;
+ endedBy?: Pick, '_id' | 'username' | 'name'>;
endedAt?: Date;
-
providerName: string;
providerData?: Record;
-
ringing?: boolean;
}
-
export interface IDirectVideoConference extends IVideoConference {
type: 'direct';
}
-
export interface IGroupVideoConference extends IVideoConference {
type: 'videoconference';
anonymousUsers: number;
title: string;
}
-
export interface ILivechatVideoConference extends IVideoConference {
type: 'livechat';
}
-
-export type VideoConference = IDirectVideoConference | IGroupVideoConference | ILivechatVideoConference;
-
-export type VideoConferenceInstructions = DirectCallInstructions | ConferenceInstructions | LivechatInstructions;
-
-export const isDirectVideoConference = (call: VideoConference | undefined | null): call is IDirectVideoConference =>
- call?.type === 'direct';
-
-export const isGroupVideoConference = (call: VideoConference | undefined | null): call is IGroupVideoConference =>
- call?.type === 'videoconference';
-
-export const isLivechatVideoConference = (call: VideoConference | undefined | null): call is ILivechatVideoConference =>
- call?.type === 'livechat';
-
-type GroupVideoConferenceCreateData = Omit & { createdBy: IUser['_id'] };
-type DirectVideoConferenceCreateData = Omit & { createdBy: IUser['_id'] };
-type LivechatVideoConferenceCreateData = Omit & { createdBy: IUser['_id'] };
-
-export type VideoConferenceCreateData = AtLeast<
+export declare type VideoConference = IDirectVideoConference | IGroupVideoConference | ILivechatVideoConference;
+export declare type VideoConferenceInstructions = DirectCallInstructions | ConferenceInstructions | LivechatInstructions;
+export declare const isDirectVideoConference: (call: VideoConference | undefined | null) => call is IDirectVideoConference;
+export declare const isGroupVideoConference: (call: VideoConference | undefined | null) => call is IGroupVideoConference;
+export declare const isLivechatVideoConference: (call: VideoConference | undefined | null) => call is ILivechatVideoConference;
+declare type GroupVideoConferenceCreateData = Omit & {
+ createdBy: IUser['_id'];
+};
+declare type DirectVideoConferenceCreateData = Omit & {
+ createdBy: IUser['_id'];
+};
+declare type LivechatVideoConferenceCreateData = Omit & {
+ createdBy: IUser['_id'];
+};
+export declare type VideoConferenceCreateData = AtLeast<
DirectVideoConferenceCreateData | GroupVideoConferenceCreateData | LivechatVideoConferenceCreateData,
'createdBy' | 'type' | 'rid' | 'providerName' | 'providerData'
>;
+
+export type VideoConferenceCapabilities = {
+ mic?: boolean;
+ cam?: boolean;
+ title?: boolean;
+};
+
+export type VideoConfStartProps = { roomId: string; title?: string; allowRinging?: boolean };
+
+export type VideoConfJoinProps = {
+ callId: string;
+ state?: {
+ mic?: boolean;
+ cam?: boolean;
+ };
+};
+
+export type VideoConfCancelProps = {
+ callId: string;
+};
+
+export type VideoConfListProps = {
+ roomId: string;
+ count?: number;
+ offset?: number;
+};
+
+export type VideoConfInfoProps = { callId: string };
diff --git a/app/definitions/rest/v1/videoConference.ts b/app/definitions/rest/v1/videoConference.ts
index ba681df28..2d93cd7b6 100644
--- a/app/definitions/rest/v1/videoConference.ts
+++ b/app/definitions/rest/v1/videoConference.ts
@@ -1,27 +1,45 @@
-import { VideoConference } from '../../IVideoConference';
+import {
+ VideoConfCancelProps,
+ VideoConference,
+ VideoConferenceCapabilities,
+ VideoConferenceInstructions,
+ VideoConfInfoProps,
+ VideoConfJoinProps,
+ VideoConfListProps,
+ VideoConfStartProps
+} from '../../IVideoConference';
+import { PaginatedResult } from '../helpers/PaginatedResult';
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 };
+ POST: (params: VideoConfStartProps) => { data: VideoConferenceInstructions & { providerName: string } };
+ };
+
+ 'video-conference.join': {
+ POST: (params: VideoConfJoinProps) => { url: string; providerName: string };
};
'video-conference.cancel': {
- POST: (params: { callId: string }) => void;
+ POST: (params: VideoConfCancelProps) => void;
};
'video-conference.info': {
- GET: (params: { callId: string }) => VideoConference & {
- capabilities: {
- mic?: boolean;
- cam?: boolean;
- title?: boolean;
- };
- };
+ GET: (params: VideoConfInfoProps) => VideoConference & { capabilities: VideoConferenceCapabilities };
+ };
+
+ 'video-conference.list': {
+ GET: (params: VideoConfListProps) => PaginatedResult<{ data: VideoConference[] }>;
+ };
+
+ 'video-conference.capabilities': {
+ GET: () => { providerName: string; capabilities: VideoConferenceCapabilities };
+ };
+
+ 'video-conference.providers': {
+ GET: () => { data: { key: string; label: string }[] };
+ };
+
+ 'video-conference/jitsi.update-timeout': {
+ POST: (params: { roomId: string }) => void;
};
};
diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json
index 20f4360dd..c695d3c63 100644
--- a/app/i18n/locales/en.json
+++ b/app/i18n/locales/en.json
@@ -877,6 +877,18 @@
"Reply_in_direct_message": "Reply in Direct Message",
"room_archived": "archived room",
"room_unarchived": "unarchived room",
+ "no-videoconf-provider-app-header": "Conference call not available",
+ "no-videoconf-provider-app-body": "Conference call apps can be installed in the Rocket.Chat marketplace by a workspace admin.",
+ "admin-no-videoconf-provider-app-header": "Conference call not enabled",
+ "admin-no-videoconf-provider-app-body": "Conference call apps are available in the Rocket.Chat marketplace.",
+ "no-active-video-conf-provider-header": "Conference call not enabled",
+ "no-active-video-conf-provider-body": "A workspace admin needs to enable the conference call feature first.",
+ "admin-no-active-video-conf-provider-header": "Conference call not enabled",
+ "admin-no-active-video-conf-provider-body": "Configure conference calls in order to make it available on this workspace.",
+ "video-conf-provider-not-configured-header": "Conference call not enabled",
+ "video-conf-provider-not-configured-body": "A workspace admin needs to enable the conference calls feature first.",
+ "admin-video-conf-provider-not-configured-header": "Conference call not enabled",
+ "admin-video-conf-provider-not-configured-body": "Configure conference calls in order to make it available on this workspace.",
"Presence_Cap_Warning_Title": "User status temporarily disabled",
"Presence_Cap_Warning_Description": "Active connections have reached the limit for the workspace, thus the service that handles user status is disabled. It can be re-enabled manually in workspace settings.",
"Learn_more": "Learn more"
diff --git a/app/lib/hooks/useVideoConf.ts b/app/lib/hooks/useVideoConf.ts
deleted file mode 100644
index 02aba7c7c..000000000
--- a/app/lib/hooks/useVideoConf.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useCallback } from 'react';
-
-import { TActionSheetOptionsItem, useActionSheet } from '../../containers/ActionSheet';
-import i18n from '../../i18n';
-import { videoConfJoin } from '../methods/videoConf';
-
-export const useVideoConf = (): { joinCall: (blockId: string) => void } => {
- const { showActionSheet } = useActionSheet();
-
- const joinCall = useCallback(blockId => {
- 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 { joinCall };
-};
diff --git a/app/lib/hooks/useVideoConf.tsx b/app/lib/hooks/useVideoConf.tsx
new file mode 100644
index 000000000..6b79f59eb
--- /dev/null
+++ b/app/lib/hooks/useVideoConf.tsx
@@ -0,0 +1,113 @@
+import React, { useEffect, useState } from 'react';
+import { Q } from '@nozbe/watermelondb';
+
+import { useActionSheet } from '../../containers/ActionSheet';
+import StartACallActionSheet from '../../containers/UIKit/VideoConferenceBlock/components/StartACallActionSheet';
+import { ISubscription, SubscriptionType, TSubscriptionModel } from '../../definitions';
+import i18n from '../../i18n';
+import { getUserSelector } from '../../selectors/login';
+import database from '../database';
+import { getSubscriptionByRoomId } from '../database/services/Subscription';
+import { callJitsi } from '../methods';
+import { compareServerVersion, showErrorAlert } from '../methods/helpers';
+import { videoConfStartAndJoin } from '../methods/videoConf';
+import { Services } from '../services';
+import { useAppSelector } from './useAppSelector';
+import { useSnaps } from './useSnaps';
+
+const availabilityErrors = {
+ NOT_CONFIGURED: 'video-conf-provider-not-configured',
+ NOT_ACTIVE: 'no-active-video-conf-provider',
+ NO_APP: 'no-videoconf-provider-app'
+} as const;
+
+const handleErrors = (isAdmin: boolean, error: typeof availabilityErrors[keyof typeof availabilityErrors]) => {
+ if (isAdmin) return showErrorAlert(i18n.t(`admin-${error}-body`), i18n.t(`admin-${error}-header`));
+ return showErrorAlert(i18n.t(`${error}-body`), i18n.t(`${error}-header`));
+};
+
+export const useVideoConf = (rid: string): { showInitCallActionSheet: () => Promise; showCallOption: boolean } => {
+ const [showCallOption, setShowCallOption] = useState(false);
+
+ const serverVersion = useAppSelector(state => state.server.version);
+ const jitsiEnabled = useAppSelector(state => state.settings.Jitsi_Enabled);
+ const jitsiEnableTeams = useAppSelector(state => state.settings.Jitsi_Enable_Teams);
+ const jitsiEnableChannels = useAppSelector(state => state.settings.Jitsi_Enable_Channels);
+ const user = useAppSelector(state => getUserSelector(state));
+
+ const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
+
+ const { showActionSheet } = useActionSheet();
+ const snaps = useSnaps([1250]);
+
+ const handleShowCallOption = (room: TSubscriptionModel) => {
+ if (isServer5OrNewer) return setShowCallOption(true);
+ const isJitsiDisabledForTeams = room.teamMain && !jitsiEnableTeams;
+ const isJitsiDisabledForChannels = !room.teamMain && (room.t === 'p' || room.t === 'c') && !jitsiEnableChannels;
+
+ if (room.t === SubscriptionType.DIRECT) return setShowCallOption(!!jitsiEnabled);
+ if (room.t === SubscriptionType.CHANNEL) return setShowCallOption(!isJitsiDisabledForChannels);
+ if (room.t === SubscriptionType.GROUP) return setShowCallOption(!isJitsiDisabledForTeams);
+
+ return setShowCallOption(false);
+ };
+
+ const canInitAnCall = async () => {
+ if (isServer5OrNewer) {
+ try {
+ await Services.videoConferenceGetCapabilities();
+ return true;
+ } catch (error: any) {
+ const isAdmin = !!['admin'].find(role => user.roles?.includes(role));
+ switch (error?.error) {
+ case availabilityErrors.NOT_CONFIGURED:
+ return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
+ case availabilityErrors.NOT_ACTIVE:
+ return handleErrors(isAdmin, availabilityErrors.NOT_ACTIVE);
+ case availabilityErrors.NO_APP:
+ return handleErrors(isAdmin, availabilityErrors.NO_APP);
+ default:
+ return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
+ }
+ }
+ }
+ return true;
+ };
+
+ const initCall = async ({ cam, mic }: { cam: boolean; mic: boolean }) => {
+ if (isServer5OrNewer) return videoConfStartAndJoin({ rid, cam, mic });
+ const room = (await getSubscriptionByRoomId(rid)) as ISubscription;
+ callJitsi({ room, cam });
+ };
+
+ const showInitCallActionSheet = async () => {
+ const canInit = await canInitAnCall();
+ if (canInit) {
+ showActionSheet({
+ children: ,
+ snaps
+ });
+ }
+ };
+
+ const initSubscription = () => {
+ try {
+ const db = database.active;
+ const observeSubCollection = db.get('subscriptions').query(Q.where('rid', rid)).observe();
+ const subObserveQuery = observeSubCollection.subscribe(data => {
+ if (data[0]) {
+ handleShowCallOption(data[0]);
+ subObserveQuery.unsubscribe();
+ }
+ });
+ } catch (e) {
+ console.log("observeSubscriptions: Can't find subscription to observe");
+ }
+ };
+
+ useEffect(() => {
+ initSubscription();
+ }, []);
+
+ return { showInitCallActionSheet, showCallOption };
+};
diff --git a/app/lib/methods/callJitsi.ts b/app/lib/methods/callJitsi.ts
index b6e836408..dfcdcc071 100644
--- a/app/lib/methods/callJitsi.ts
+++ b/app/lib/methods/callJitsi.ts
@@ -46,8 +46,8 @@ export function callJitsiWithoutServer(path: string): void {
Navigation.navigate('JitsiMeetView', { url, onlyAudio: false });
}
-export async function callJitsi(room: ISubscription, onlyAudio = false): Promise {
- logEvent(onlyAudio ? events.RA_JITSI_AUDIO : events.RA_JITSI_VIDEO);
+export async function callJitsi({ room, cam = false }: { room: ISubscription; cam?: boolean }): Promise {
+ logEvent(cam ? events.RA_JITSI_AUDIO : events.RA_JITSI_VIDEO);
const url = await jitsiURL({ room });
- Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid: room?.rid });
+ Navigation.navigate('JitsiMeetView', { url, onlyAudio: cam, rid: room?.rid });
}
diff --git a/app/lib/methods/videoConf.ts b/app/lib/methods/videoConf.ts
index 255d18688..3385ea7ef 100644
--- a/app/lib/methods/videoConf.ts
+++ b/app/lib/methods/videoConf.ts
@@ -19,9 +19,9 @@ const handleBltPermission = async (): Promise => {
return [PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION];
};
-export const videoConfJoin = async (callId: string, cam: boolean) => {
+export const videoConfJoin = async (callId: string, cam?: boolean, mic?: boolean): Promise => {
try {
- const result = await Services.videoConferenceJoin(callId, cam);
+ const result = await Services.videoConferenceJoin(callId, cam, mic);
if (result.success) {
if (isAndroid) {
const bltPermission = await handleBltPermission();
@@ -44,11 +44,11 @@ export const videoConfJoin = async (callId: string, cam: boolean) => {
}
};
-export const videoConfStartAndJoin = async (rid: string, cam: boolean) => {
+export const videoConfStartAndJoin = async ({ rid, cam, mic }: { rid: string; cam?: boolean; mic?: boolean }): Promise => {
try {
- const videoConfResponse: any = await Services.videoConferenceStart(rid);
+ const videoConfResponse = await Services.videoConferenceStart(rid);
if (videoConfResponse.success) {
- videoConfJoin(videoConfResponse.data.callId, cam);
+ videoConfJoin(videoConfResponse.data.callId, cam, mic);
}
} catch (e) {
showErrorAlert(i18n.t('error-init-video-conf'));
diff --git a/app/lib/methods/videoConfTimer.ts b/app/lib/methods/videoConfTimer.ts
new file mode 100644
index 000000000..46bcd3ed1
--- /dev/null
+++ b/app/lib/methods/videoConfTimer.ts
@@ -0,0 +1,27 @@
+import BackgroundTimer from 'react-native-background-timer';
+
+import { Services } from '../services';
+
+let interval: number | null = null;
+
+export const initVideoConfTimer = (rid: string): void => {
+ if (rid) {
+ Services.updateJitsiTimeout(rid).catch((e: unknown) => console.log(e));
+ if (interval) {
+ BackgroundTimer.clearInterval(interval);
+ BackgroundTimer.stopBackgroundTimer();
+ interval = null;
+ }
+ interval = BackgroundTimer.setInterval(() => {
+ Services.updateJitsiTimeout(rid).catch((e: unknown) => console.log(e));
+ }, 10000);
+ }
+};
+
+export const endVideoConfTimer = (): void => {
+ if (interval) {
+ BackgroundTimer.clearInterval(interval);
+ interval = null;
+ BackgroundTimer.stopBackgroundTimer();
+ }
+};
diff --git a/app/lib/services/restApi.ts b/app/lib/services/restApi.ts
index eefb89831..d73c1d9df 100644
--- a/app/lib/services/restApi.ts
+++ b/app/lib/services/restApi.ts
@@ -936,8 +936,10 @@ 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 videoConferenceJoin = (callId: string, cam?: boolean, mic?: boolean) =>
+ sdk.post('video-conference.join', { callId, state: { cam: !!cam, mic: mic === undefined ? true : mic } });
+
+export const videoConferenceGetCapabilities = () => sdk.get('video-conference.capabilities');
export const videoConferenceStart = (roomId: string) => sdk.post('video-conference.start', { roomId });
diff --git a/app/views/JitsiMeetView.android.tsx b/app/views/JitsiMeetView.android.tsx
index ce54691c1..45265494d 100644
--- a/app/views/JitsiMeetView.android.tsx
+++ b/app/views/JitsiMeetView.android.tsx
@@ -1,14 +1,13 @@
+import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import React from 'react';
import { BackHandler, NativeEventSubscription } from 'react-native';
-import BackgroundTimer from 'react-native-background-timer';
import { isAppInstalled, openAppWithUri } from 'react-native-send-intent';
import WebView from 'react-native-webview';
import { WebViewMessage, WebViewNavigation } from 'react-native-webview/lib/WebViewTypes';
-import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import { IBaseScreen } from '../definitions';
import { events, logEvent } from '../lib/methods/helpers/log';
-import { Services } from '../lib/services';
+import { endVideoConfTimer, initVideoConfTimer } from '../lib/methods/videoConfTimer';
import { ChatsStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
@@ -20,7 +19,6 @@ class JitsiMeetView extends React.Component {
private rid: string;
private url: string;
private videoConf: boolean;
- private jitsiTimeout: number | null;
private backHandler!: NativeEventSubscription;
constructor(props: TJitsiMeetViewProps) {
@@ -28,7 +26,6 @@ class JitsiMeetView extends React.Component {
this.rid = props.route.params?.rid;
this.url = props.route.params?.url;
this.videoConf = !!props.route.params?.videoConf;
- this.jitsiTimeout = null;
}
componentDidMount() {
@@ -50,10 +47,8 @@ class JitsiMeetView extends React.Component {
componentWillUnmount() {
logEvent(this.videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
- if (this.jitsiTimeout && !this.videoConf) {
- BackgroundTimer.clearInterval(this.jitsiTimeout);
- this.jitsiTimeout = null;
- BackgroundTimer.stopBackgroundTimer();
+ if (!this.videoConf) {
+ endVideoConfTimer();
}
this.backHandler.remove();
deactivateKeepAwake();
@@ -64,15 +59,7 @@ class JitsiMeetView extends React.Component {
onConferenceJoined = () => {
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);
- BackgroundTimer.stopBackgroundTimer();
- this.jitsiTimeout = null;
- }
- this.jitsiTimeout = BackgroundTimer.setInterval(() => {
- Services.updateJitsiTimeout(this.rid).catch((e: unknown) => console.log(e));
- }, 10000);
+ initVideoConfTimer(this.rid);
}
};
@@ -90,7 +77,7 @@ class JitsiMeetView extends React.Component {
render() {
return (
this.onNavigationStateChange(nativeEvent)}
onNavigationStateChange={this.onNavigationStateChange}
style={{ flex: 1 }}
diff --git a/app/views/JitsiMeetView.ios.tsx b/app/views/JitsiMeetView.ios.tsx
index effbe2331..d4f0c4c72 100644
--- a/app/views/JitsiMeetView.ios.tsx
+++ b/app/views/JitsiMeetView.ios.tsx
@@ -9,6 +9,7 @@ import { useAppSelector } from '../lib/hooks';
import { events, logEvent } from '../lib/methods/helpers/log';
import { getUserSelector } from '../selectors/login';
import { ChatsStackParamList } from '../stacks/types';
+import { endVideoConfTimer, initVideoConfTimer } from '../lib/methods/videoConfTimer';
const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLFragment: string) =>
`${baseUrl}/avatar/${url}?format=png&width=${uriSize}&height=${uriSize}${avatarAuthURLFragment}`;
@@ -16,7 +17,7 @@ const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLF
const JitsiMeetView = (): React.ReactElement => {
const { goBack } = useNavigation();
const {
- params: { url, onlyAudio, videoConf }
+ params: { url, onlyAudio, videoConf, rid }
} = useRoute>();
const user = useAppSelector(state => getUserSelector(state));
const baseUrl = useAppSelector(state => state.server.server);
@@ -60,8 +61,10 @@ const JitsiMeetView = (): React.ReactElement => {
}
};
logEvent(videoConf ? events.LIVECHAT_VIDEOCONF_JOIN : events.JM_CONFERENCE_JOIN);
+ if (!videoConf) initVideoConfTimer(rid);
await JitsiMeet.launchJitsiMeetView(conferenceOptions);
logEvent(videoConf ? events.LIVECHAT_VIDEOCONF_TERMINATE : events.JM_CONFERENCE_TERMINATE);
+ if (!videoConf) endVideoConfTimer();
goBack();
};
diff --git a/app/views/RoomActionsView/components/CallSection.tsx b/app/views/RoomActionsView/components/CallSection.tsx
new file mode 100644
index 000000000..180bbbb52
--- /dev/null
+++ b/app/views/RoomActionsView/components/CallSection.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+import * as List from '../../../containers/List';
+import i18n from '../../../i18n';
+import { useVideoConf } from '../../../lib/hooks/useVideoConf';
+
+export default function CallSection({ rid }: { rid: string }): React.ReactElement | null {
+ const { showCallOption, showInitCallActionSheet } = useVideoConf(rid);
+
+ if (showCallOption)
+ return (
+
+
+ }
+ showActionIndicator
+ />
+
+
+ );
+ return null;
+}
diff --git a/app/views/RoomActionsView/index.tsx b/app/views/RoomActionsView/index.tsx
index 9c3a4eaaa..8e0a409b6 100644
--- a/app/views/RoomActionsView/index.tsx
+++ b/app/views/RoomActionsView/index.tsx
@@ -33,7 +33,7 @@ import sharedStyles from '../Styles';
import styles from './styles';
import { ERoomType } from '../../definitions/ERoomType';
import { E2E_ROOM_TYPES, SWITCH_TRACK_COLOR, themes } from '../../lib/constants';
-import { callJitsi, getPermalinkChannel } from '../../lib/methods';
+import { getPermalinkChannel } from '../../lib/methods';
import {
canAutoTranslate as canAutoTranslateMethod,
getRoomAvatar,
@@ -48,9 +48,9 @@ import { getSubscriptionByRoomId } from '../../lib/database/services/Subscriptio
import { IActionSheetProvider, withActionSheet } from '../../containers/ActionSheet';
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
import { closeLivechat } from '../../lib/methods/helpers/closeLivechat';
-import { videoConfStartAndJoin } from '../../lib/methods/videoConf';
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
import { ILivechatTag } from '../../definitions/ILivechatTag';
+import CallSection from './components/CallSection';
interface IOnPressTouch {
(item: { route?: T; params?: ChatsStackParamList[T]; event?: Function }): void;
@@ -730,16 +730,6 @@ class RoomActionsView extends React.Component {
- 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;
@@ -815,63 +805,6 @@ class RoomActionsView extends React.Component {
- const { room } = this.state;
- 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;
-
- 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;
- }
-
- return (
-
-
- this.startVideoConf({ video: false })}
- testID='room-actions-voice'
- left={() => }
- showActionIndicator
- />
-
- this.startVideoConf({ video: true })}
- testID='room-actions-video'
- left={() => }
- showActionIndicator
- />
-
-
- );
- };
-
renderE2EEncryption = () => {
const { room } = this.state;
const { encryptionEnabled } = this.props;
@@ -1108,7 +1041,7 @@ class RoomActionsView extends React.Component
{this.renderRoomInfo()}
- {this.renderJitsi()}
+
{this.renderE2EEncryption()}
@@ -1299,13 +1232,6 @@ class RoomActionsView extends React.Component ({
userId: getUserSelector(state).id,
- 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,
diff --git a/app/views/RoomInfoView/components/UserInfoButton.tsx b/app/views/RoomInfoView/components/UserInfoButton.tsx
new file mode 100644
index 000000000..7185c8112
--- /dev/null
+++ b/app/views/RoomInfoView/components/UserInfoButton.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { Text } from 'react-native';
+import { BorderlessButton } from 'react-native-gesture-handler';
+
+import { CustomIcon, TIconsName } from '../../../containers/CustomIcon';
+import styles from '../styles';
+import { useTheme } from '../../../theme';
+import { useVideoConf } from '../../../lib/hooks/useVideoConf';
+import i18n from '../../../i18n';
+import { useAppSelector } from '../../../lib/hooks';
+import { compareServerVersion } from '../../../lib/methods/helpers';
+
+// TODO: change other icons on future
+function UserInfoButton({
+ danger,
+ iconName,
+ onPress,
+ label,
+ showIcon
+}: {
+ danger?: boolean;
+ iconName: TIconsName;
+ onPress?: (prop: any) => void;
+ label: string;
+ showIcon?: boolean;
+}): React.ReactElement | null {
+ const { colors } = useTheme();
+ const color = danger ? colors.dangerColor : colors.actionTintColor;
+
+ if (showIcon)
+ return (
+
+
+ {label}
+
+ );
+ return null;
+}
+
+export function CallButton({ rid, isDirect }: { rid: string; isDirect: boolean }): React.ReactElement | null {
+ const { showCallOption, showInitCallActionSheet } = useVideoConf(rid);
+ const serverVersion = useAppSelector(state => state.server.version);
+ const greaterThanFive = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
+
+ const showIcon = greaterThanFive ? showCallOption : showCallOption && isDirect;
+
+ return ;
+}
diff --git a/app/views/RoomInfoView/index.tsx b/app/views/RoomInfoView/index.tsx
index 2744c0cc6..d57a23bae 100644
--- a/app/views/RoomInfoView/index.tsx
+++ b/app/views/RoomInfoView/index.tsx
@@ -1,43 +1,43 @@
+import { CompositeNavigationProp, RouteProp } from '@react-navigation/native';
+import { StackNavigationProp } from '@react-navigation/stack';
+import isEmpty from 'lodash/isEmpty';
import React from 'react';
import { ScrollView, Text, View } from 'react-native';
import { BorderlessButton } from 'react-native-gesture-handler';
import { connect } from 'react-redux';
-import UAParser from 'ua-parser-js';
-import isEmpty from 'lodash/isEmpty';
-import { StackNavigationProp } from '@react-navigation/stack';
-import { CompositeNavigationProp, RouteProp } from '@react-navigation/native';
import { Observable, Subscription } from 'rxjs';
+import UAParser from 'ua-parser-js';
-import { CustomIcon, TIconsName } from '../../containers/CustomIcon';
-import Status from '../../containers/Status';
import Avatar from '../../containers/Avatar';
-import sharedStyles from '../Styles';
-import RoomTypeIcon from '../../containers/RoomTypeIcon';
-import I18n from '../../i18n';
+import { CustomIcon, TIconsName } from '../../containers/CustomIcon';
import * as HeaderButton from '../../containers/HeaderButton';
-import StatusBar from '../../containers/StatusBar';
-import log, { events, logEvent } from '../../lib/methods/helpers/log';
-import { themes } from '../../lib/constants';
-import { TSupportedThemes, withTheme } from '../../theme';
import { MarkdownPreview } from '../../containers/markdown';
-import { LISTENER } from '../../containers/Toast';
-import EventEmitter from '../../lib/methods/helpers/events';
+import RoomTypeIcon from '../../containers/RoomTypeIcon';
import SafeAreaView from '../../containers/SafeAreaView';
-import { goRoom } from '../../lib/methods/helpers/goRoom';
-import Navigation from '../../lib/navigation/appNavigation';
-import Livechat from './Livechat';
-import Channel from './Channel';
-import Direct from './Direct';
-import styles from './styles';
-import { ChatsStackParamList } from '../../stacks/types';
-import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
-import { SubscriptionType, TSubscriptionModel, ISubscription, IUser, IApplicationState } from '../../definitions';
+import Status from '../../containers/Status';
+import StatusBar from '../../containers/StatusBar';
+import { LISTENER } from '../../containers/Toast';
+import { IApplicationState, ISubscription, IUser, SubscriptionType, TSubscriptionModel } from '../../definitions';
import { ILivechatVisitor } from '../../definitions/ILivechatVisitor';
-import { callJitsi } from '../../lib/methods';
-import { getRoomTitle, getUidDirectMessage, hasPermission } from '../../lib/methods/helpers';
-import { Services } from '../../lib/services';
+import I18n from '../../i18n';
+import { themes } from '../../lib/constants';
import { getSubscriptionByRoomId } from '../../lib/database/services/Subscription';
+import { getRoomTitle, getUidDirectMessage, hasPermission } from '../../lib/methods/helpers';
+import EventEmitter from '../../lib/methods/helpers/events';
+import { goRoom } from '../../lib/methods/helpers/goRoom';
import { handleIgnore } from '../../lib/methods/helpers/handleIgnore';
+import log, { events, logEvent } from '../../lib/methods/helpers/log';
+import Navigation from '../../lib/navigation/appNavigation';
+import { Services } from '../../lib/services';
+import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
+import { ChatsStackParamList } from '../../stacks/types';
+import { TSupportedThemes, withTheme } from '../../theme';
+import sharedStyles from '../Styles';
+import Channel from './Channel';
+import { CallButton } from './components/UserInfoButton';
+import Direct from './Direct';
+import Livechat from './Livechat';
+import styles from './styles';
interface IGetRoomTitle {
room: ISubscription;
@@ -386,11 +386,6 @@ class RoomInfoView extends React.Component {
- const { room } = this.state;
- callJitsi(room);
- };
-
handleBlockUser = async (rid: string, blocked: string, block: boolean) => {
logEvent(events.RI_TOGGLE_BLOCK_USER);
try {
@@ -425,8 +420,7 @@ class RoomInfoView extends React.Component {
- const { roomFromRid, roomUser } = this.state;
- const { jitsiEnabled } = this.props;
+ const { roomFromRid, roomUser, room } = this.state;
const isFromDm = roomFromRid?.rid ? new RegExp(roomUser._id).test(roomFromRid.rid) : false;
const isDirectFromSaved = this.isDirect && this.fromRid && roomFromRid;
@@ -442,9 +436,7 @@ class RoomInfoView extends React.Component
{this.renderButton(() => this.handleCreateDirectMessage(this.goRoom), 'message', I18n.t('Message'))}
- {jitsiEnabled && this.isDirect
- ? this.renderButton(() => this.handleCreateDirectMessage(this.videoCall), 'camera', I18n.t('Video_call'))
- : null}
+
{isDirectFromSaved && !isFromDm && !isDmWithMyself
? this.renderButton(
() => handleIgnore(roomUser._id, !isIgnored, roomFromRid.rid),
diff --git a/app/views/RoomView/RightButtons.tsx b/app/views/RoomView/RightButtons.tsx
index 22001d5ee..a37b5d11f 100644
--- a/app/views/RoomView/RightButtons.tsx
+++ b/app/views/RoomView/RightButtons.tsx
@@ -10,8 +10,7 @@ import * as HeaderButton from '../../containers/HeaderButton';
import database from '../../lib/database';
import { getUserSelector } from '../../selectors/login';
import { events, logEvent } from '../../lib/methods/helpers/log';
-import { isTeamRoom } from '../../lib/methods/helpers/room';
-import { IApplicationState, SubscriptionType, TMessageModel, TSubscriptionModel } from '../../definitions';
+import { IApplicationState, ISubscription, SubscriptionType, TMessageModel, TSubscriptionModel } from '../../definitions';
import { ChatsStackParamList } from '../../stacks/types';
import { TActionSheetOptionsItem } from '../../containers/ActionSheet';
import i18n from '../../i18n';
@@ -20,12 +19,11 @@ import { onHoldLivechat, returnLivechat } from '../../lib/services/restApi';
import { closeLivechat as closeLivechatService } from '../../lib/methods/helpers/closeLivechat';
import { Services } from '../../lib/services';
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
+import HeaderCallButton from './components/HeaderCallButton';
-interface IRightButtonsProps {
+interface IRightButtonsProps extends Pick {
userId?: string;
threadsEnabled: boolean;
- rid?: string;
- t: string;
tmid?: string;
teamId?: string;
isMasterDetail: boolean;
@@ -43,6 +41,7 @@ interface IRightButtonsProps {
livechatRequestComment: boolean;
showActionSheet: Function;
departmentId?: string;
+ rid?: string;
}
interface IRigthButtonsState {
@@ -338,7 +337,7 @@ class RightButtonsContainer extends Component
- {isTeamRoom({ teamId, joined }) ? (
-
- ) : null}
+ {rid ? : null}
{threadsEnabled ? (
;
+ return null;
+}
diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx
index 586c9ddef..c51d009d8 100644
--- a/app/views/RoomView/index.tsx
+++ b/app/views/RoomView/index.tsx
@@ -627,7 +627,7 @@ class RoomView extends React.Component {
joined={joined}
status={room.status}
omnichannelPermissions={omnichannelPermissions}
- t={this.t || t}
+ t={(this.t || t) as SubscriptionType}
encrypted={encrypted}
navigation={navigation}
toggleFollowThread={this.toggleFollowThread}
@@ -1229,14 +1229,15 @@ class RoomView extends React.Component {
});
};
- handleCallJitsi = () => {
+ // OLD METHOD - support versions before 5.0.0
+ handleEnterCall = () => {
const { room } = this.state;
if ('id' in room) {
const { jitsiTimeout } = room;
if (jitsiTimeout && jitsiTimeout < new Date()) {
showErrorAlert(I18n.t('Call_already_ended'));
} else {
- callJitsi(room);
+ callJitsi({ room });
}
}
};
@@ -1382,7 +1383,7 @@ class RoomView extends React.Component {
autoTranslateLanguage={'id' in room ? room.autoTranslateLanguage : undefined}
navToRoomInfo={this.navToRoomInfo}
getCustomEmoji={this.getCustomEmoji}
- callJitsi={this.handleCallJitsi}
+ handleEnterCall={this.handleEnterCall}
blockAction={this.blockAction}
threadBadgeColor={this.getBadgeColor(item?.id)}
toggleFollowThread={this.toggleFollowThread}