[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:
parent
ce485bbde0
commit
2eb8adb3f6
8 changed files with 170 additions and 11 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue