[MM-57460] - Calls: Host controls, add mute all to participant list (#7945)

* add mute all to participant list

* i18n

* hostControlsAvailable

* mute-all -> mute-others
This commit is contained in:
Christopher Poile 2024-05-10 16:51:11 -04:00 committed by GitHub
parent 7fbd2e52e2
commit 59db1839ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 88 additions and 3 deletions

View file

@ -642,6 +642,17 @@ export const hostMuteSession = async (serverUrl: string, callId: string, session
}
};
export const hostMuteOthers = async (serverUrl: string, callId: string) => {
try {
const client = NetworkManager.getClient(serverUrl);
return client.hostMuteOthers(callId);
} catch (error) {
logDebug('error on hostMuteOthers', getFullErrorMessage(error));
await forceLogoutIfNecessary(serverUrl, error);
return error;
}
};
export const hostStopScreenshare = async (serverUrl: string, callId: string, sessionId: string) => {
try {
const client = NetworkManager.getClient(serverUrl);

View file

@ -19,6 +19,7 @@ export interface ClientCallsMix {
dismissCall: (channelId: string) => Promise<ApiResp>;
hostMake: (callId: string, newHostId: string) => Promise<ApiResp>;
hostMute: (callId: string, sessionId: string) => Promise<ApiResp>;
hostMuteOthers: (callId: string) => Promise<ApiResp>;
hostScreenOff: (callId: string, sessionId: string) => Promise<ApiResp>;
hostLowerHand: (callId: string, sessionId: string) => Promise<ApiResp>;
hostRemove: (callId: string, sessionId: string) => Promise<ApiResp>;
@ -131,6 +132,13 @@ const ClientCalls = (superclass: any) => class extends superclass {
);
};
hostMuteOthers = async (callId: string) => {
return this.doFetch(
`${this.getCallsRoute()}/calls/${callId}/host/mute-others`,
{method: 'post'},
);
};
hostScreenOff = async (callId: string, sessionId: string) => {
return this.doFetch(
`${this.getCallsRoute()}/calls/${callId}/host/screen-off`,

View file

@ -7,6 +7,7 @@ import {distinctUntilChanged, switchMap} from 'rxjs/operators';
import {observeCallDatabase, observeCurrentSessionsDict} from '@calls/observers';
import {ParticipantsList} from '@calls/screens/participants_list/participants_list';
import {observeCurrentCall} from '@calls/state';
import {observeTeammateNameDisplay} from '@queries/servers/user';
const enhanced = withObservables([], () => {
@ -14,10 +15,22 @@ const enhanced = withObservables([], () => {
switchMap((db) => (db ? observeTeammateNameDisplay(db) : of$(''))),
distinctUntilChanged(),
);
const callServerUrl = observeCurrentCall().pipe(
switchMap((call) => of$(call?.serverUrl)),
);
const callChannelId = observeCurrentCall().pipe(
switchMap((call) => of$(call?.channelId)),
);
const callUserId = observeCurrentCall().pipe(
switchMap((call) => of$(call?.myUserId)),
);
return {
sessionsDict: observeCurrentSessionsDict(),
teammateNameDisplay,
callServerUrl,
callChannelId,
callUserId,
};
});

View file

@ -4,14 +4,16 @@
import {BottomSheetFlatList} from '@gorhom/bottom-sheet';
import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {type ListRenderItemInfo, useWindowDimensions, View} from 'react-native';
import {type ListRenderItemInfo, Text, TouchableOpacity, useWindowDimensions, View} from 'react-native';
import {FlatList} from 'react-native-gesture-handler';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {useHostMenus} from '@calls/hooks';
import {hostMuteOthers} from '@calls/actions/calls';
import {useHostControlsAvailable, useHostMenus} from '@calls/hooks';
import {Participant} from '@calls/screens/participants_list/participant';
import Pill from '@calls/screens/participants_list/pill';
import {sortSessions} from '@calls/utils';
import CompassIcon from '@components/compass_icon';
import FormattedText from '@components/formatted_text';
import {Screens} from '@constants';
import {useTheme} from '@context/theme';
@ -31,11 +33,15 @@ type Props = {
closeButtonId: string;
sessionsDict: Dictionary<CallSession>;
teammateNameDisplay: string;
callServerUrl?: string;
callChannelId?: string;
callUserId?: string;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
header: {
paddingBottom: 12,
paddingRight: 2,
flexDirection: 'row',
gap: 8,
alignItems: 'center',
@ -44,12 +50,35 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
color: theme.centerChannelColor,
...typography('Heading', 600, 'SemiBold'),
},
muteButton: {
flexDirection: 'row',
padding: 6,
gap: 6,
alignItems: 'center',
marginLeft: 'auto',
},
muteIcon: {
color: theme.buttonBg,
},
muteText: {
color: theme.buttonBg,
...typography('Body', 100, 'SemiBold'),
marginTop: -2,
},
}));
export const ParticipantsList = ({closeButtonId, sessionsDict, teammateNameDisplay}: Props) => {
export const ParticipantsList = ({
closeButtonId,
sessionsDict,
teammateNameDisplay,
callServerUrl,
callChannelId,
callUserId,
}: Props) => {
const intl = useIntl();
const theme = useTheme();
const {onPress} = useHostMenus();
const hostControlsAvailable = useHostControlsAvailable();
const {bottom} = useSafeAreaInsets();
const {height} = useWindowDimensions();
const isTablet = useIsTablet();
@ -63,6 +92,14 @@ export const ParticipantsList = ({closeButtonId, sessionsDict, teammateNameDispl
if (sessions.length > MIN_ROWS && snapPoint1 < snapPoint2) {
snapPoints.push(snapPoint2);
}
const otherUnMuted = useMemo(() => sessions.some((s) => !s.muted && s.userId !== callUserId), [sessions, callUserId]);
const muteOthersPress = useCallback(async () => {
if (!callServerUrl || !callChannelId) {
return;
}
hostMuteOthers(callServerUrl, callChannelId);
}, [callServerUrl, callChannelId]);
const renderItem = useCallback(({item}: ListRenderItemInfo<CallSession>) => (
<Participant
@ -83,6 +120,21 @@ export const ParticipantsList = ({closeButtonId, sessionsDict, teammateNameDispl
defaultMessage={'Participants'}
/>
<Pill text={sessions.length}/>
{hostControlsAvailable && otherUnMuted &&
<TouchableOpacity
style={styles.muteButton}
onPress={muteOthersPress}
>
<CompassIcon
size={16}
name={'microphone-off'}
style={styles.muteIcon}
/>
<Text style={styles.muteText}>
{intl.formatMessage({id: 'mobile.calls_mute_others', defaultMessage: 'Mute others'})}
</Text>
</TouchableOpacity>
}
</View>
<List
data={sessions}

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_others": "Mute others",
"mobile.calls_mute_participant": "Mute participant",
"mobile.calls_name_is_talking_postfix": "is talking...",
"mobile.calls_name_started_call": "{name} started a call",