[MM-57958] Calls: Host controls: stop screenshare, mute, lower hand (#7929)

* add muteSession & stopScreenshare controls; obey mute host ctrl

* lower hand host control; obey lower hand ws msg

* no need to await host api call
This commit is contained in:
Christopher Poile 2024-05-06 15:14:40 -04:00 committed by GitHub
parent ce485bbde0
commit 2eb8adb3f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 170 additions and 11 deletions

View file

@ -34,6 +34,8 @@ import {
handleCallUserUnraiseHand,
handleCallUserVoiceOff,
handleCallUserVoiceOn,
handleHostLowerHand,
handleHostMute,
handleUserDismissedNotification,
} from '@calls/connection/websocket_event_handlers';
import {isSupportedServerCalls} from '@calls/utils';
@ -453,6 +455,12 @@ export async function handleEvent(serverUrl: string, msg: WebSocketMessage) {
case WebsocketEvents.CALLS_CAPTION:
handleCallCaption(serverUrl, msg);
break;
case WebsocketEvents.CALLS_HOST_MUTE:
handleHostMute(serverUrl, msg);
break;
case WebsocketEvents.CALLS_HOST_LOWER_HAND:
handleHostLowerHand(serverUrl, msg);
break;
case WebsocketEvents.GROUP_RECEIVED:
handleGroupReceivedEvent(serverUrl, msg);

View file

@ -88,6 +88,8 @@ const WebsocketEvents = {
CALLS_HOST_CHANGED: `custom_${Calls.PluginId}_call_host_changed`,
CALLS_USER_DISMISSED_NOTIFICATION: `custom_${Calls.PluginId}_user_dismissed_notification`,
CALLS_CAPTION: `custom_${Calls.PluginId}_caption`,
CALLS_HOST_MUTE: `custom_${Calls.PluginId}_host_mute`,
CALLS_HOST_LOWER_HAND: `custom_${Calls.PluginId}_host_lower_hand`,
GROUP_RECEIVED: 'received_group',
GROUP_MEMBER_ADD: 'group_member_add',

View file

@ -620,13 +620,47 @@ const handleEndCall = async (serverUrl: string, channelId: string, currentUserId
);
};
export const makeHost = async (serverUrl: string, callId: string, newHostId: string) => {
export const hostMake = async (serverUrl: string, callId: string, newHostId: string) => {
try {
const client = NetworkManager.getClient(serverUrl);
return client.makeHost(callId, newHostId);
return client.hostMake(callId, newHostId);
} catch (error) {
logDebug('error on makeHost', getFullErrorMessage(error));
logDebug('error on hostMake', getFullErrorMessage(error));
await forceLogoutIfNecessary(serverUrl, error);
return error;
}
};
export const hostMuteSession = async (serverUrl: string, callId: string, sessionId: string) => {
try {
const client = NetworkManager.getClient(serverUrl);
return client.hostMute(callId, sessionId);
} catch (error) {
logDebug('error on hostMute', getFullErrorMessage(error));
await forceLogoutIfNecessary(serverUrl, error);
return error;
}
};
export const hostStopScreenshare = async (serverUrl: string, callId: string, sessionId: string) => {
try {
const client = NetworkManager.getClient(serverUrl);
return client.hostScreenOff(callId, sessionId);
} catch (error) {
logDebug('error on hostStopScreenshare', getFullErrorMessage(error));
await forceLogoutIfNecessary(serverUrl, error);
return error;
}
};
export const hostLowerHand = async (serverUrl: string, callId: string, sessionId: string) => {
try {
const client = NetworkManager.getClient(serverUrl);
return client.hostLowerHand(callId, sessionId);
} catch (error) {
logDebug('error on hostLowerHand', getFullErrorMessage(error));
await forceLogoutIfNecessary(serverUrl, error);
return error;
}
};

View file

@ -17,7 +17,10 @@ export interface ClientCallsMix {
startCallRecording: (callId: string) => Promise<ApiResp | CallJobState>;
stopCallRecording: (callId: string) => Promise<ApiResp | CallJobState>;
dismissCall: (channelId: string) => Promise<ApiResp>;
makeHost: (callId: string, newHostId: string) => Promise<ApiResp>;
hostMake: (callId: string, newHostId: string) => Promise<ApiResp>;
hostMute: (callId: string, sessionId: string) => Promise<ApiResp>;
hostScreenOff: (callId: string, sessionId: string) => Promise<ApiResp>;
hostLowerHand: (callId: string, sessionId: string) => Promise<ApiResp>;
}
const ClientCalls = (superclass: any) => class extends superclass {
@ -107,7 +110,7 @@ const ClientCalls = (superclass: any) => class extends superclass {
);
};
makeHost = async (callId: string, newHostId: string) => {
hostMake = async (callId: string, newHostId: string) => {
return this.doFetch(
`${this.getCallsRoute()}/calls/${callId}/host/make`,
{
@ -116,6 +119,36 @@ const ClientCalls = (superclass: any) => class extends superclass {
},
);
};
hostMute = async (callId: string, sessionId: string) => {
return this.doFetch(
`${this.getCallsRoute()}/calls/${callId}/host/mute`,
{
method: 'post',
body: {session_id: sessionId},
},
);
};
hostScreenOff = async (callId: string, sessionId: string) => {
return this.doFetch(
`${this.getCallsRoute()}/calls/${callId}/host/screen-off`,
{
method: 'post',
body: {session_id: sessionId},
},
);
};
hostLowerHand = async (callId: string, sessionId: string) => {
return this.doFetch(
`${this.getCallsRoute()}/calls/${callId}/host/lower-hand`,
{
method: 'post',
body: {session_id: sessionId},
},
);
};
};
export default ClientCalls;

View file

@ -4,10 +4,12 @@
import {DeviceEventEmitter} from 'react-native';
import {fetchUsersByIds} from '@actions/remote/user';
import {muteMyself, unraiseHand} from '@calls/actions';
import {
callEnded,
callStarted,
getCallsConfig,
getCurrentCall,
receivedCaption,
removeIncomingCall,
setCallScreenOff,
@ -29,7 +31,7 @@ import Calls from '@constants/calls';
import DatabaseManager from '@database/manager';
import {getCurrentUserId} from '@queries/servers/system';
import type {CallRecordingStateData} from '@calls/types/calls';
import type {CallRecordingStateData, HostControlsLowerHandMsgData, HostControlsMsgData} from '@calls/types/calls';
import type {
CallHostChangedData,
CallJobState,
@ -204,3 +206,25 @@ export const handleUserDismissedNotification = async (serverUrl: string, msg: We
export const handleCallCaption = (serverUrl: string, msg: WebSocketMessage<LiveCaptionData>) => {
receivedCaption(serverUrl, msg.data);
};
export const handleHostMute = async (serverUrl: string, msg: WebSocketMessage<HostControlsMsgData>) => {
const currentCall = getCurrentCall();
if (currentCall?.serverUrl !== serverUrl ||
currentCall?.channelId !== msg.data.channel_id ||
currentCall?.mySessionId !== msg.data.session_id) {
return;
}
muteMyself();
};
export const handleHostLowerHand = async (serverUrl: string, msg: WebSocketMessage<HostControlsLowerHandMsgData>) => {
const currentCall = getCurrentCall();
if (currentCall?.serverUrl !== serverUrl ||
currentCall?.channelId !== msg.data.channel_id ||
currentCall?.mySessionId !== msg.data.session_id) {
return;
}
unraiseHand();
};

View file

@ -6,7 +6,7 @@ import {useIntl} from 'react-intl';
import {StyleSheet} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {makeHost} from '@calls/actions/calls';
import {hostLowerHand, hostMake, hostMuteSession, hostStopScreenshare} from '@calls/actions/calls';
import SlideUpPanelItem from '@components/slide_up_panel_item';
import {Screens} from '@constants';
import BottomSheet from '@screens/bottom_sheet';
@ -43,19 +43,40 @@ export const HostControls = ({
const intl = useIntl();
const {bottom} = useSafeAreaInsets();
const sharingScreen = currentCall.screenOn === session.sessionId;
const makeHostPress = useCallback(async () => {
await makeHost(currentCall.serverUrl, currentCall.channelId, session.userId);
hostMake(currentCall.serverUrl, currentCall.channelId, session.userId);
await dismissBottomSheet();
}, [currentCall, session.userId]);
}, [currentCall.serverUrl, currentCall.channelId, session.userId]);
const mutePress = useCallback(async () => {
hostMuteSession(currentCall.serverUrl, currentCall.channelId, session.sessionId);
await dismissBottomSheet();
}, [currentCall.serverUrl, currentCall.channelId, session.sessionId]);
const lowerHandPress = useCallback(async () => {
hostLowerHand(currentCall.serverUrl, currentCall.channelId, session.sessionId);
await dismissBottomSheet();
}, [currentCall.serverUrl, currentCall.channelId, session.sessionId]);
const stopScreensharePress = useCallback(async () => {
hostStopScreenshare(currentCall.serverUrl, currentCall.channelId, session.sessionId);
await dismissBottomSheet();
}, [currentCall.serverUrl, currentCall.id, session.sessionId]);
const snapPoints = useMemo(() => {
const items = 1 + (session.muted ? 0 : 1) + (sharingScreen ? 1 : 0) + (session.raisedHand ? 1 : 0);
return [
1,
bottomSheetSnapPoint(1, ITEM_HEIGHT, bottom) + TITLE_HEIGHT,
bottomSheetSnapPoint(items, ITEM_HEIGHT, bottom) + TITLE_HEIGHT,
];
}, [bottom]);
}, [bottom, session.muted, sharingScreen, session.raisedHand]);
const makeHostText = intl.formatMessage({id: 'mobile.calls_make_host', defaultMessage: 'Make host'});
const muteText = intl.formatMessage({id: 'mobile.calls_mute_participant', defaultMessage: 'Mute participant'});
const lowerHandText = intl.formatMessage({id: 'mobile.calls_lower_hand', defaultMessage: 'Lower hand'});
const stopScreenshareText = intl.formatMessage({id: 'mobile.calls_stop_screenshare', defaultMessage: 'Stop screen share'});
const renderContent = () => {
if (!session?.userModel) {
@ -74,6 +95,30 @@ export const HostControls = ({
user={session.userModel}
hideGuestTags={hideGuestTags}
/>
{!session.muted &&
<SlideUpPanelItem
leftIcon={'microphone-off'}
leftIconStyles={styles.iconStyle}
onPress={mutePress}
text={muteText}
/>
}
{Boolean(session.raisedHand) &&
<SlideUpPanelItem
leftIcon={'hand-right-outline-off'}
leftIconStyles={styles.iconStyle}
onPress={lowerHandPress}
text={lowerHandText}
/>
}
{sharingScreen &&
<SlideUpPanelItem
leftIcon={'monitor-off'}
leftIconStyles={styles.iconStyle}
onPress={stopScreensharePress}
text={stopScreenshareText}
/>
}
<SlideUpPanelItem
leftIcon={'monitor-account'}
leftIconStyles={styles.iconStyle}

View file

@ -236,3 +236,14 @@ export type CallRecordingStateData = {
recState: CallRecordingState;
callID: string;
}
// TODO: MM-57919, refactor wsmsg data to calls-common
export type HostControlsMsgData = {
channel_id: string;
session_id: string;
}
export type HostControlsLowerHandMsgData = HostControlsMsgData & {
call_id: string;
host_id: string;
}

View file

@ -486,6 +486,7 @@
"mobile.calls_mic_error": "To participate, open Settings to grant Mattermost access to your microphone.",
"mobile.calls_more": "More",
"mobile.calls_mute": "Mute",
"mobile.calls_mute_participant": "Mute participant",
"mobile.calls_name_is_talking_postfix": "is talking...",
"mobile.calls_name_started_call": "{name} started a call",
"mobile.calls_noone_talking": "No one is talking",
@ -522,6 +523,7 @@
"mobile.calls_start_call": "Start Call",
"mobile.calls_start_call_exists": "A call is already ongoing in the channel.",
"mobile.calls_stop_recording": "Stop Recording",
"mobile.calls_stop_screenshare": "Stop screen share",
"mobile.calls_tablet": "Tablet",
"mobile.calls_thread": "Thread",
"mobile.calls_unmute": "Unmute",