[MM-58581] Calls: End call confirmations (#8004)
* end call confirmations * i18n * remove unneeded asyncs * better alert for android
This commit is contained in:
parent
8cef881db6
commit
1f2a71c499
19 changed files with 209 additions and 38 deletions
|
|
@ -24,7 +24,10 @@ import {
|
|||
handleCallRecordingState,
|
||||
handleCallScreenOff,
|
||||
handleCallScreenOn,
|
||||
handleCallStarted, handleCallUserConnected, handleCallUserDisconnected,
|
||||
handleCallStarted,
|
||||
handleCallState,
|
||||
handleCallUserConnected,
|
||||
handleCallUserDisconnected,
|
||||
handleCallUserJoined,
|
||||
handleCallUserLeft,
|
||||
handleCallUserMuted,
|
||||
|
|
@ -450,6 +453,9 @@ export async function handleEvent(serverUrl: string, msg: WebSocketMessage) {
|
|||
case WebsocketEvents.CALLS_HOST_REMOVED:
|
||||
handleHostRemoved(serverUrl, msg);
|
||||
break;
|
||||
case WebsocketEvents.CALLS_CALL_STATE:
|
||||
handleCallState(serverUrl, msg);
|
||||
break;
|
||||
|
||||
case WebsocketEvents.GROUP_RECEIVED:
|
||||
handleGroupReceivedEvent(serverUrl, msg);
|
||||
|
|
|
|||
|
|
@ -341,6 +341,12 @@ const Post = ({
|
|||
<CallsCustomMessage
|
||||
serverUrl={serverUrl}
|
||||
post={post}
|
||||
|
||||
// Note: the below are provided by the index, but typescript seems to be having problems.
|
||||
otherParticipants={false}
|
||||
isAdmin={false}
|
||||
isHost={false}
|
||||
joiningChannelId={null}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ const WebsocketEvents = {
|
|||
CALLS_HOST_MUTE: `custom_${Calls.PluginId}_host_mute`,
|
||||
CALLS_HOST_LOWER_HAND: `custom_${Calls.PluginId}_host_lower_hand`,
|
||||
CALLS_HOST_REMOVED: `custom_${Calls.PluginId}_host_removed`,
|
||||
CALLS_CALL_STATE: `custom_${Calls.PluginId}_call_state`,
|
||||
|
||||
GROUP_RECEIVED: 'received_group',
|
||||
GROUP_MEMBER_ADD: 'group_member_add',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {forceLogoutIfNecessary} from '@actions/remote/session';
|
|||
import {updateThreadFollowing} from '@actions/remote/thread';
|
||||
import {fetchUsersByIds} from '@actions/remote/user';
|
||||
import {
|
||||
endCallConfirmationAlert,
|
||||
leaveAndJoinWithAlert,
|
||||
needsRecordingErrorAlert,
|
||||
needsRecordingWillBePostedAlert,
|
||||
|
|
@ -28,6 +29,7 @@ import {
|
|||
setScreenShareURL,
|
||||
setSpeakerPhone,
|
||||
} from '@calls/state';
|
||||
import {type AudioDevice, type Call, type CallSession, type CallsConnection, EndCallReturn} from '@calls/types/calls';
|
||||
import {General, Preferences} from '@constants';
|
||||
import Calls from '@constants/calls';
|
||||
import DatabaseManager from '@database/manager';
|
||||
|
|
@ -44,7 +46,6 @@ import {displayUsername, getUserIdFromChannelName, isSystemAdmin} from '@utils/u
|
|||
|
||||
import {newConnection} from '../connection/connection';
|
||||
|
||||
import type {AudioDevice, Call, CallSession, CallsConnection} from '@calls/types/calls';
|
||||
import type {CallChannelState, CallState, EmojiData, SessionState} from '@mattermost/calls/lib/types';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
|
||||
|
|
@ -132,7 +133,7 @@ export const loadCallForChannel = async (serverUrl: string, channelId: string) =
|
|||
fetchUsersByIds(serverUrl, Array.from(ids));
|
||||
}
|
||||
|
||||
setCallForChannel(serverUrl, channelId, resp.enabled, call);
|
||||
setCallForChannel(serverUrl, channelId, call, resp.enabled);
|
||||
|
||||
return {data: {call, enabled: resp.enabled}};
|
||||
};
|
||||
|
|
@ -160,11 +161,12 @@ const convertOldCallToNew = (call: CallState): CallState => {
|
|||
};
|
||||
};
|
||||
|
||||
const createCallAndAddToIds = (channelId: string, call: CallState, ids: Set<string>) => {
|
||||
return {
|
||||
export const createCallAndAddToIds = (channelId: string, call: CallState, ids?: Set<string>) => {
|
||||
// Don't cast so that we get alerted to missing types
|
||||
const convertedCall: Call = {
|
||||
sessions: Object.values(call.sessions).reduce((accum, cur) => {
|
||||
// Add the id to the set of UserModels we want to ensure are loaded.
|
||||
ids.add(cur.user_id);
|
||||
ids?.add(cur.user_id);
|
||||
|
||||
// Create the CallParticipant
|
||||
accum[cur.session_id] = {
|
||||
|
|
@ -184,7 +186,9 @@ const createCallAndAddToIds = (channelId: string, call: CallState, ids: Set<stri
|
|||
hostId: call.host_id,
|
||||
recState: call.recording,
|
||||
dismissed: call.dismissed_notification || {},
|
||||
} as Call;
|
||||
};
|
||||
|
||||
return convertedCall;
|
||||
};
|
||||
|
||||
export const loadConfigAndCalls = async (serverUrl: string, userId: string) => {
|
||||
|
|
@ -302,6 +306,29 @@ export const leaveCall = (err?: Error) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const leaveCallConfirmation = async (
|
||||
intl: IntlShape,
|
||||
otherParticipants: boolean,
|
||||
isAdmin: boolean,
|
||||
isHost: boolean,
|
||||
serverUrl: string,
|
||||
channelId: string,
|
||||
leaveCb?: () => void) => {
|
||||
const showHostControls = (isHost || isAdmin) && otherParticipants;
|
||||
const ret = await endCallConfirmationAlert(intl, showHostControls) as EndCallReturn;
|
||||
switch (ret) {
|
||||
case EndCallReturn.Cancel:
|
||||
return;
|
||||
case EndCallReturn.LeaveCall:
|
||||
leaveCall();
|
||||
leaveCb?.();
|
||||
return;
|
||||
case EndCallReturn.EndCall:
|
||||
endCall(serverUrl, channelId);
|
||||
leaveCb?.();
|
||||
}
|
||||
};
|
||||
|
||||
export const muteMyself = () => {
|
||||
if (connection) {
|
||||
connection.mute();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Alert} from 'react-native';
|
||||
import {Alert, type AlertButton, Platform} from 'react-native';
|
||||
|
||||
import {hasMicrophonePermission, joinCall, leaveCall, unmuteMyself} from '@calls/actions';
|
||||
import {dismissIncomingCall, hostRemove} from '@calls/actions/calls';
|
||||
|
|
@ -15,6 +15,7 @@ import {
|
|||
removeIncomingCall,
|
||||
setMicPermissionsGranted,
|
||||
} from '@calls/state';
|
||||
import {EndCallReturn} from '@calls/types/calls';
|
||||
import {errorAlert} from '@calls/utils';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {getChannelById} from '@queries/servers/channel';
|
||||
|
|
@ -132,7 +133,7 @@ export const leaveAndJoinWithAlert = async (
|
|||
id: 'mobile.post.cancel',
|
||||
defaultMessage: 'Cancel',
|
||||
}),
|
||||
onPress: async () => resolve(false),
|
||||
onPress: () => resolve(false),
|
||||
style: 'destructive',
|
||||
},
|
||||
{
|
||||
|
|
@ -473,3 +474,61 @@ export const removeFromCall = (serverUrl: string, displayName: string, callId: s
|
|||
style: 'destructive',
|
||||
}]);
|
||||
};
|
||||
|
||||
export const endCallConfirmationAlert = (intl: IntlShape, showHostControls: boolean) => {
|
||||
const {formatMessage} = intl;
|
||||
|
||||
const asyncAlert = async () => new Promise((resolve) => {
|
||||
const buttons: AlertButton[] = [{
|
||||
text: formatMessage({
|
||||
id: 'mobile.calls_cancel',
|
||||
defaultMessage: 'Cancel',
|
||||
}),
|
||||
onPress: () => resolve(EndCallReturn.Cancel),
|
||||
style: 'cancel',
|
||||
}, {
|
||||
text: formatMessage({
|
||||
id: 'mobile.calls_host_leave_confirm',
|
||||
defaultMessage: 'Leave call',
|
||||
}),
|
||||
onPress: () => resolve(EndCallReturn.LeaveCall),
|
||||
style: 'destructive',
|
||||
}];
|
||||
const questionMsg = formatMessage({
|
||||
id: 'mobile.calls_host_leave_title',
|
||||
defaultMessage: 'Are you sure you want to leave this call?',
|
||||
});
|
||||
|
||||
if (showHostControls) {
|
||||
const endCallButton: AlertButton = {
|
||||
text: formatMessage({
|
||||
id: 'mobile.calls_host_end_confirm',
|
||||
defaultMessage: 'End call for everyone',
|
||||
}),
|
||||
onPress: () => resolve(EndCallReturn.EndCall),
|
||||
style: 'destructive',
|
||||
};
|
||||
const leaveCallButton = {
|
||||
text: formatMessage({
|
||||
id: 'mobile.calls_host_leave_confirm',
|
||||
defaultMessage: 'Leave call',
|
||||
}),
|
||||
onPress: () => resolve(EndCallReturn.LeaveCall),
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
buttons.splice(1, 1, endCallButton, leaveCallButton);
|
||||
} else {
|
||||
buttons.splice(1, 1, leaveCallButton, endCallButton);
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
Alert.alert(questionMsg, '', buttons);
|
||||
} else {
|
||||
Alert.alert('', questionMsg, buttons);
|
||||
}
|
||||
});
|
||||
|
||||
return asyncAlert();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import React, {useCallback} from 'react';
|
|||
import {useIntl} from 'react-intl';
|
||||
import {Text, TouchableOpacity, View} from 'react-native';
|
||||
|
||||
import {leaveCall} from '@calls/actions';
|
||||
import {leaveCallConfirmation} from '@calls/actions/calls';
|
||||
import {leaveAndJoinWithAlert, showLimitRestrictedAlert} from '@calls/alerts';
|
||||
import {setJoiningChannelId} from '@calls/state';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
|
|
@ -26,11 +26,14 @@ import type UserModel from '@typings/database/models/servers/user';
|
|||
|
||||
type Props = {
|
||||
post: PostModel;
|
||||
currentUser?: UserModel;
|
||||
isMilitaryTime: boolean;
|
||||
joiningChannelId: string | null;
|
||||
otherParticipants: boolean;
|
||||
isAdmin: boolean;
|
||||
isHost: boolean;
|
||||
currentUser?: UserModel;
|
||||
limitRestrictedInfo?: LimitRestrictedInfo;
|
||||
ccChannelId?: string;
|
||||
joiningChannelId: string | null;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
|
|
@ -131,6 +134,9 @@ export const CallsCustomMessage = ({
|
|||
ccChannelId,
|
||||
limitRestrictedInfo,
|
||||
joiningChannelId,
|
||||
otherParticipants,
|
||||
isAdmin,
|
||||
isHost,
|
||||
}: Props) => {
|
||||
const intl = useIntl();
|
||||
const theme = useTheme();
|
||||
|
|
@ -154,9 +160,9 @@ export const CallsCustomMessage = ({
|
|||
setJoiningChannelId(null);
|
||||
}, [limitRestrictedInfo, intl, serverUrl, post.channelId]);
|
||||
|
||||
const leaveHandler = useCallback(() => {
|
||||
leaveCall();
|
||||
}, []);
|
||||
const leaveCallHandler = useCallback(() => {
|
||||
leaveCallConfirmation(intl, otherParticipants, isAdmin, isHost, serverUrl, post.channelId);
|
||||
}, [intl, otherParticipants, isAdmin, isHost, serverUrl, post.channelId]);
|
||||
|
||||
const title = post.props.title ? (
|
||||
<Text style={style.title}>
|
||||
|
|
@ -210,7 +216,7 @@ export const CallsCustomMessage = ({
|
|||
const button = alreadyInTheCall ? (
|
||||
<TouchableOpacity
|
||||
style={[style.callButton, style.leaveCallButton]}
|
||||
onPress={leaveHandler}
|
||||
onPress={leaveCallHandler}
|
||||
>
|
||||
<CompassIcon
|
||||
name='phone-hangup'
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {of as of$} from 'rxjs';
|
|||
import {distinctUntilChanged, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {CallsCustomMessage} from '@calls/components/calls_custom_message/calls_custom_message';
|
||||
import {observeIsCallLimitRestricted} from '@calls/observers';
|
||||
import {observeEndCallDetails, observeIsCallLimitRestricted} from '@calls/observers';
|
||||
import {observeCurrentCall, observeGlobalCallsState} from '@calls/state';
|
||||
import {Preferences} from '@constants';
|
||||
import {getDisplayNamePreferenceAsBool} from '@helpers/api/preference';
|
||||
|
|
@ -52,6 +52,7 @@ const enhanced = withObservables(['post'], ({serverUrl, post, database}: OwnProp
|
|||
limitRestrictedInfo: observeIsCallLimitRestricted(database, serverUrl, post.channelId),
|
||||
ccChannelId,
|
||||
joiningChannelId,
|
||||
...observeEndCallDetails(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import React, {useCallback, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import {leaveCall} from '@calls/actions';
|
||||
import {leaveCallConfirmation} from '@calls/actions/calls';
|
||||
import {leaveAndJoinWithAlert, showLimitRestrictedAlert} from '@calls/alerts';
|
||||
import {useTryCallsFunction} from '@calls/hooks';
|
||||
import Loading from '@components/loading';
|
||||
|
|
@ -23,6 +23,9 @@ export interface Props {
|
|||
alreadyInCall: boolean;
|
||||
dismissChannelInfo: () => void;
|
||||
limitRestrictedInfo: LimitRestrictedInfo;
|
||||
otherParticipants: boolean;
|
||||
isAdmin: boolean;
|
||||
isHost: boolean;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
|
|
@ -53,6 +56,9 @@ const ChannelInfoStartButton = ({
|
|||
alreadyInCall,
|
||||
dismissChannelInfo,
|
||||
limitRestrictedInfo,
|
||||
otherParticipants,
|
||||
isAdmin,
|
||||
isHost,
|
||||
}: Props) => {
|
||||
const intl = useIntl();
|
||||
const theme = useTheme();
|
||||
|
|
@ -66,8 +72,7 @@ const ChannelInfoStartButton = ({
|
|||
|
||||
const toggleJoinLeave = useCallback(async () => {
|
||||
if (alreadyInCall) {
|
||||
leaveCall();
|
||||
dismissChannelInfo();
|
||||
await leaveCallConfirmation(intl, otherParticipants, isAdmin, isHost, serverUrl, channelId, dismissChannelInfo);
|
||||
} else if (isLimitRestricted) {
|
||||
showLimitRestrictedAlert(limitRestrictedInfo, intl);
|
||||
dismissChannelInfo();
|
||||
|
|
@ -79,7 +84,7 @@ const ChannelInfoStartButton = ({
|
|||
setConnecting(false);
|
||||
dismissChannelInfo();
|
||||
}
|
||||
}, [isLimitRestricted, alreadyInCall, dismissChannelInfo, intl, serverUrl, channelId, isACallInCurrentChannel]);
|
||||
}, [isLimitRestricted, alreadyInCall, dismissChannelInfo, intl, serverUrl, channelId, isACallInCurrentChannel, otherParticipants]);
|
||||
|
||||
const [tryJoin, msgPostfix] = useTryCallsFunction(toggleJoinLeave);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {of as of$} from 'rxjs';
|
|||
import {distinctUntilChanged, switchMap} from 'rxjs/operators';
|
||||
|
||||
import ChannelInfoStartButton from '@calls/components/channel_info_start/channel_info_start_button';
|
||||
import {observeIsCallLimitRestricted} from '@calls/observers';
|
||||
import {observeEndCallDetails, observeIsCallLimitRestricted} from '@calls/observers';
|
||||
import {observeChannelsWithCalls, observeCurrentCall} from '@calls/state';
|
||||
|
||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||
|
|
@ -33,6 +33,7 @@ const enhanced = withObservables([], ({serverUrl, channelId, database}: EnhanceP
|
|||
confirmToJoin,
|
||||
alreadyInCall,
|
||||
limitRestrictedInfo: observeIsCallLimitRestricted(database, serverUrl, channelId),
|
||||
...observeEndCallDetails(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import React, {useCallback, useMemo} from 'react';
|
|||
import {useIntl} from 'react-intl';
|
||||
import {View, Text, Pressable, Platform} from 'react-native';
|
||||
|
||||
import {leaveCall, muteMyself, unmuteMyself} from '@calls/actions';
|
||||
import {muteMyself, unmuteMyself} from '@calls/actions';
|
||||
import {leaveCallConfirmation} from '@calls/actions/calls';
|
||||
import {recordingAlert, recordingWillBePostedAlert, recordingErrorAlert} from '@calls/alerts';
|
||||
import CallAvatar from '@calls/components/call_avatar';
|
||||
import CallDuration from '@calls/components/call_duration';
|
||||
|
|
@ -34,6 +35,9 @@ type Props = {
|
|||
teammateNameDisplay: string;
|
||||
micPermissionsGranted: boolean;
|
||||
threadScreen?: boolean;
|
||||
otherParticipants: boolean;
|
||||
isAdmin: boolean;
|
||||
isHost: boolean;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: CallsTheme) => {
|
||||
|
|
@ -139,6 +143,9 @@ const CurrentCallBar = ({
|
|||
teammateNameDisplay,
|
||||
micPermissionsGranted,
|
||||
threadScreen,
|
||||
otherParticipants,
|
||||
isAdmin,
|
||||
isHost,
|
||||
}: Props) => {
|
||||
const theme = useTheme();
|
||||
const serverUrl = useServerUrl();
|
||||
|
|
@ -168,8 +175,8 @@ const CurrentCallBar = ({
|
|||
}, [formatMessage, threadScreen]);
|
||||
|
||||
const leaveCallHandler = useCallback(() => {
|
||||
leaveCall();
|
||||
}, []);
|
||||
leaveCallConfirmation(intl, otherParticipants, isAdmin, isHost, serverUrl, currentCall?.channelId || '');
|
||||
}, [intl, otherParticipants, isAdmin, isHost, serverUrl, currentCall?.channelId]);
|
||||
|
||||
const mySession = currentCall?.sessions[currentCall.mySessionId];
|
||||
|
||||
|
|
@ -210,7 +217,6 @@ const CurrentCallBar = ({
|
|||
|
||||
// The user should receive an alert if all of the following conditions apply:
|
||||
// - Recording has started and recording has not ended.
|
||||
const isHost = Boolean(currentCall?.hostId === mySession?.userId);
|
||||
if (currentCall?.recState?.start_at && !currentCall?.recState?.end_at) {
|
||||
recordingAlert(isHost, EnableTranscriptions, intl);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {withObservables} from '@nozbe/watermelondb/react';
|
|||
import {combineLatest, of as of$} from 'rxjs';
|
||||
import {distinctUntilChanged, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {observeCurrentSessionsDict} from '@calls/observers';
|
||||
import {observeCurrentSessionsDict, observeEndCallDetails} from '@calls/observers';
|
||||
import {observeCurrentCall, observeGlobalCallsState} from '@calls/state';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {observeChannel} from '@queries/servers/channel';
|
||||
|
|
@ -46,6 +46,7 @@ const enhanced = withObservables([], () => {
|
|||
sessionsDict: observeCurrentSessionsDict(),
|
||||
teammateNameDisplay,
|
||||
micPermissionsGranted,
|
||||
...observeEndCallDetails(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {DeviceEventEmitter} from 'react-native';
|
|||
|
||||
import {fetchUsersByIds} from '@actions/remote/user';
|
||||
import {leaveCall, muteMyself, unraiseHand} from '@calls/actions';
|
||||
import {createCallAndAddToIds} from '@calls/actions/calls';
|
||||
import {hostRemovedErr} from '@calls/errors';
|
||||
import {
|
||||
callEnded,
|
||||
|
|
@ -13,6 +14,7 @@ import {
|
|||
getCurrentCall,
|
||||
receivedCaption,
|
||||
removeIncomingCall,
|
||||
setCallForChannel,
|
||||
setCallScreenOff,
|
||||
setCallScreenOn,
|
||||
setCaptioningState,
|
||||
|
|
@ -38,6 +40,8 @@ import type {
|
|||
CallJobState,
|
||||
CallJobStateData,
|
||||
CallStartData,
|
||||
CallState,
|
||||
CallStateData,
|
||||
EmptyData,
|
||||
LiveCaptionData,
|
||||
UserConnectedData,
|
||||
|
|
@ -240,3 +244,11 @@ export const handleHostRemoved = async (serverUrl: string, msg: WebSocketMessage
|
|||
|
||||
leaveCall(hostRemovedErr);
|
||||
};
|
||||
|
||||
export const handleCallState = (serverUrl: string, msg: WebSocketMessage<CallStateData>) => {
|
||||
const callState: CallState = JSON.parse(msg.data.call);
|
||||
const call = createCallAndAddToIds(msg.data.channel_id, callState);
|
||||
|
||||
setCallForChannel(serverUrl, msg.data.channel_id, call);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import Permissions from 'react-native-permissions';
|
|||
|
||||
import {initializeVoiceTrack} from '@calls/actions/calls';
|
||||
import {
|
||||
getCallsConfig, getCurrentCall,
|
||||
getCallsConfig,
|
||||
getCurrentCall,
|
||||
setMicPermissionsGranted,
|
||||
useCallsState,
|
||||
useChannelsWithCalls,
|
||||
|
|
@ -18,6 +19,7 @@ import {
|
|||
useGlobalCallsState,
|
||||
useIncomingCalls,
|
||||
} from '@calls/state';
|
||||
import {type CallSession} from '@calls/types/calls';
|
||||
import {errorAlert, isHostControlsAllowed} from '@calls/utils';
|
||||
import {Screens} from '@constants';
|
||||
import {
|
||||
|
|
@ -37,7 +39,6 @@ import {openAsBottomSheet} from '@screens/navigation';
|
|||
import {getFullErrorMessage} from '@utils/errors';
|
||||
import {isSystemAdmin} from '@utils/user';
|
||||
|
||||
import type {CallSession} from '@calls/types/calls';
|
||||
import type {Client} from '@client/rest';
|
||||
|
||||
export const useTryCallsFunction = (fn: () => void) => {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {observeConfigValue, observeLicense} from '@queries/servers/system';
|
|||
import {queryUsersById} from '@queries/servers/user';
|
||||
import UserModel from '@typings/database/models/servers/user';
|
||||
import {isMinimumServerVersion} from '@utils/helpers';
|
||||
import {isSystemAdmin} from '@utils/user';
|
||||
|
||||
import type {CallSession} from '@calls/types/calls';
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
|
@ -137,3 +138,25 @@ export const observeCallStateInChannel = (serverUrl: string, database: Database,
|
|||
showIncomingCalls,
|
||||
};
|
||||
};
|
||||
|
||||
export const observeEndCallDetails = () => {
|
||||
const cc = observeCurrentCall();
|
||||
const otherParticipants = cc.pipe(
|
||||
switchMap((call) => of$(Object.keys(call?.sessions || {}).length > 1)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isAdmin = cc.pipe(
|
||||
switchMap((call) => of$(isSystemAdmin(call?.sessions[call?.mySessionId || '']?.userModel?.roles || ''))),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
const isHost = cc.pipe(
|
||||
switchMap((call) => of$(call?.hostId === call?.myUserId)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
return {
|
||||
otherParticipants,
|
||||
isAdmin,
|
||||
isHost,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import {Navigation} from 'react-native-navigation';
|
|||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
import {RTCView} from 'react-native-webrtc';
|
||||
|
||||
import {leaveCall, muteMyself, unmuteMyself} from '@calls/actions';
|
||||
import {startCallRecording, stopCallRecording} from '@calls/actions/calls';
|
||||
import {muteMyself, unmuteMyself} from '@calls/actions';
|
||||
import {leaveCallConfirmation, startCallRecording, stopCallRecording} from '@calls/actions/calls';
|
||||
import {recordingAlert, recordingWillBePostedAlert, recordingErrorAlert} from '@calls/alerts';
|
||||
import {AudioDeviceButton} from '@calls/components/audio_device_button';
|
||||
import CallDuration from '@calls/components/call_duration';
|
||||
|
|
@ -88,6 +88,9 @@ export type Props = {
|
|||
fromThreadScreen?: boolean;
|
||||
displayName?: string;
|
||||
isOwnDirectMessage: boolean;
|
||||
otherParticipants: boolean;
|
||||
isAdmin: boolean;
|
||||
isHost: boolean;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: CallsTheme) => ({
|
||||
|
|
@ -311,6 +314,9 @@ const CallScreen = ({
|
|||
fromThreadScreen,
|
||||
displayName,
|
||||
isOwnDirectMessage,
|
||||
otherParticipants,
|
||||
isAdmin,
|
||||
isHost,
|
||||
}: Props) => {
|
||||
const intl = useIntl();
|
||||
const theme = useTheme();
|
||||
|
|
@ -380,9 +386,8 @@ const CallScreen = ({
|
|||
}, []);
|
||||
|
||||
const leaveCallHandler = useCallback(() => {
|
||||
popTopScreen();
|
||||
leaveCall();
|
||||
}, []);
|
||||
leaveCallConfirmation(intl, otherParticipants, isAdmin, isHost, serverUrl, currentCall?.channelId || '', popTopScreen);
|
||||
}, [intl, otherParticipants, isAdmin, isHost, serverUrl, currentCall?.channelId]);
|
||||
|
||||
const muteUnmuteHandler = useCallback(() => {
|
||||
if (mySession?.muted) {
|
||||
|
|
@ -453,7 +458,6 @@ const CallScreen = ({
|
|||
|
||||
// The user should receive a recording alert if all of the following conditions apply:
|
||||
// - Recording has started, recording has not ended
|
||||
const isHost = Boolean(currentCall?.hostId === mySession?.userId);
|
||||
const recording = Boolean(currentCall?.recState?.start_at && !currentCall.recState.end_at);
|
||||
if (recording) {
|
||||
recordingAlert(isHost, EnableTranscriptions, intl);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {withDatabase, withObservables} from '@nozbe/watermelondb/react';
|
|||
import {of as of$, combineLatestWith} from 'rxjs';
|
||||
import {distinctUntilChanged, switchMap} from 'rxjs/operators';
|
||||
|
||||
import {observeCallDatabase, observeCurrentSessionsDict} from '@calls/observers';
|
||||
import {observeCallDatabase, observeCurrentSessionsDict, observeEndCallDetails} from '@calls/observers';
|
||||
import CallScreen from '@calls/screens/call_screen/call_screen';
|
||||
import {observeCurrentCall, observeGlobalCallsState} from '@calls/state';
|
||||
import {General} from '@constants';
|
||||
|
|
@ -53,6 +53,7 @@ const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
|
|||
teammateNameDisplay,
|
||||
displayName,
|
||||
isOwnDirectMessage,
|
||||
...observeEndCallDetails(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -184,15 +184,16 @@ export const removeIncomingCall = (serverUrl: string, callId: string, channelId?
|
|||
setCallsState(serverUrl, {...callsState, calls: nextCalls});
|
||||
};
|
||||
|
||||
export const setCallForChannel = (serverUrl: string, channelId: string, enabled?: boolean, call?: Call) => {
|
||||
export const setCallForChannel = (serverUrl: string, channelId: string, call?: Call, enabled?: boolean) => {
|
||||
const callsState = getCallsState(serverUrl);
|
||||
let nextEnabled = callsState.enabled;
|
||||
if (typeof enabled !== 'undefined') {
|
||||
nextEnabled = {...callsState.enabled, [channelId]: enabled};
|
||||
}
|
||||
|
||||
const nextCalls = {...callsState.calls};
|
||||
let nextCalls = callsState.calls;
|
||||
if (call) {
|
||||
nextCalls = {...callsState.calls};
|
||||
nextCalls[channelId] = call;
|
||||
|
||||
// In case we got a complete update on the currentCall
|
||||
|
|
|
|||
|
|
@ -249,3 +249,9 @@ export type HostControlsLowerHandMsgData = HostControlsMsgData & {
|
|||
call_id: string;
|
||||
host_id: string;
|
||||
}
|
||||
|
||||
export enum EndCallReturn {
|
||||
Cancel,
|
||||
LeaveCall,
|
||||
EndCall,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -445,6 +445,7 @@
|
|||
"mobile.calls_call_ended": "Call ended",
|
||||
"mobile.calls_call_screen": "Call",
|
||||
"mobile.calls_call_thread": "Call Thread",
|
||||
"mobile.calls_cancel": "Cancel",
|
||||
"mobile.calls_captions": "Captions",
|
||||
"mobile.calls_captions_turned_on": "Live captions turned on",
|
||||
"mobile.calls_disable": "Disable calls",
|
||||
|
|
@ -463,6 +464,9 @@
|
|||
"mobile.calls_hide_cc": "Hide live captions",
|
||||
"mobile.calls_host": "host",
|
||||
"mobile.calls_host_controls": "Host controls",
|
||||
"mobile.calls_host_end_confirm": "End call for everyone",
|
||||
"mobile.calls_host_leave_confirm": "Leave call",
|
||||
"mobile.calls_host_leave_title": "Are you sure you want to leave this call?",
|
||||
"mobile.calls_host_rec": "You are recording this meeting. Consider letting everyone know that this meeting is being recorded.",
|
||||
"mobile.calls_host_rec_error": "Please try to record again. You can also contact your system admin for troubleshooting help.",
|
||||
"mobile.calls_host_rec_error_title": "Something went wrong with the recording",
|
||||
|
|
|
|||
Loading…
Reference in a new issue