MM-42360 - Calls Mobile: Implement raise hand (#6069)
* implement raise hand * added tests; updated tests * spread syntax for combined styles -> array syntax * add useCallbacks, update test snapshots * fix dep array * fix dep array * sort participants based on muted and raisedHand * sort participants by name, then by state * fix screen sharing message * fix test snapshots * add monitor indicator; tweak ui * linting
This commit is contained in:
parent
df7945318e
commit
e086b19d1e
20 changed files with 818 additions and 330 deletions
|
|
@ -35,7 +35,7 @@ import {
|
|||
handleCallChannelEnabled,
|
||||
handleCallChannelDisabled,
|
||||
handleCallScreenOn,
|
||||
handleCallScreenOff,
|
||||
handleCallScreenOff, handleCallUserRaiseHand, handleCallUserUnraiseHand,
|
||||
} from '@mmproducts/calls/store/actions/websockets';
|
||||
import {getChannelSinceValue} from '@utils/channels';
|
||||
import {semverFromServerVersion} from '@utils/general';
|
||||
|
|
@ -467,6 +467,10 @@ function handleEvent(msg: WebSocketMessage) {
|
|||
return dispatch(handleCallScreenOn(msg));
|
||||
case WebsocketEvents.CALLS_SCREEN_OFF:
|
||||
return dispatch(handleCallScreenOff(msg));
|
||||
case WebsocketEvents.CALLS_USER_RAISE_HAND:
|
||||
return dispatch(handleCallUserRaiseHand(msg));
|
||||
case WebsocketEvents.CALLS_USER_UNRAISE_HAND:
|
||||
return dispatch(handleCallUserUnraiseHand(msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,5 +62,7 @@ const WebsocketEvents = {
|
|||
CALLS_CALL_START: 'custom_com.mattermost.calls_call_start',
|
||||
CALLS_SCREEN_ON: 'custom_com.mattermost.calls_user_screen_on',
|
||||
CALLS_SCREEN_OFF: 'custom_com.mattermost.calls_user_screen_off',
|
||||
CALLS_USER_RAISE_HAND: 'custom_com.mattermost.calls_user_raise_hand',
|
||||
CALLS_USER_UNRAISE_HAND: 'custom_com.mattermost.calls_user_unraise_hand',
|
||||
};
|
||||
export default WebsocketEvents;
|
||||
|
|
|
|||
|
|
@ -40,23 +40,23 @@ exports[`CallAvatar should match snapshot muted 1`] = `
|
|||
/>
|
||||
<CompassIcon
|
||||
name="microphone-off"
|
||||
size={16}
|
||||
size={12}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "black",
|
||||
"borderColor": "black",
|
||||
"borderRadius": 12,
|
||||
"borderRadius": 10,
|
||||
"borderWidth": 2,
|
||||
"bottom": -5,
|
||||
"color": "white",
|
||||
"height": 24,
|
||||
"height": 20,
|
||||
"overflow": "hidden",
|
||||
"padding": 2,
|
||||
"padding": 1,
|
||||
"position": "absolute",
|
||||
"right": -5,
|
||||
"textAlign": "center",
|
||||
"textAlignVertical": "center",
|
||||
"width": 24,
|
||||
"width": 20,
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
|
@ -170,23 +170,23 @@ exports[`CallAvatar should match snapshot unmuted 1`] = `
|
|||
/>
|
||||
<CompassIcon
|
||||
name="microphone"
|
||||
size={16}
|
||||
size={12}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#3DB887",
|
||||
"borderColor": "black",
|
||||
"borderRadius": 12,
|
||||
"borderRadius": 10,
|
||||
"borderWidth": 2,
|
||||
"bottom": -5,
|
||||
"color": "white",
|
||||
"height": 24,
|
||||
"height": 20,
|
||||
"overflow": "hidden",
|
||||
"padding": 2,
|
||||
"padding": 1,
|
||||
"position": "absolute",
|
||||
"right": -5,
|
||||
"textAlign": "center",
|
||||
"textAlignVertical": "center",
|
||||
"width": 24,
|
||||
"width": 20,
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {View, StyleSheet} from 'react-native';
|
||||
import {View, StyleSheet, Text, Platform} from 'react-native';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
|
|
@ -11,11 +11,17 @@ type Props = {
|
|||
userId?: string;
|
||||
volume: number;
|
||||
muted?: boolean;
|
||||
sharingScreen?: boolean;
|
||||
raisedHand?: boolean;
|
||||
size?: 'm' | 'l';
|
||||
}
|
||||
|
||||
const getStyleSheet = (props: Props) => {
|
||||
const baseSize = props.size === 'm' || !props.size ? 40 : 72;
|
||||
const smallIcon = props.size === 'm' || !props.size;
|
||||
const widthHeight = smallIcon ? 20 : 24;
|
||||
const borderRadius = smallIcon ? 10 : 12;
|
||||
const padding = smallIcon ? 1 : 2;
|
||||
|
||||
return StyleSheet.create({
|
||||
pictureHalo: {
|
||||
|
|
@ -42,10 +48,10 @@ const getStyleSheet = (props: Props) => {
|
|||
position: 'absolute',
|
||||
bottom: -5,
|
||||
right: -5,
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: 12,
|
||||
padding: 2,
|
||||
width: widthHeight,
|
||||
height: widthHeight,
|
||||
borderRadius,
|
||||
padding,
|
||||
backgroundColor: props.muted ? 'black' : '#3DB887',
|
||||
borderColor: 'black',
|
||||
borderWidth: 2,
|
||||
|
|
@ -54,32 +60,95 @@ const getStyleSheet = (props: Props) => {
|
|||
textAlignVertical: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
raisedHand: {
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
top: 0,
|
||||
right: -5,
|
||||
backgroundColor: 'black',
|
||||
borderColor: 'black',
|
||||
borderRadius,
|
||||
padding,
|
||||
borderWidth: 2,
|
||||
width: widthHeight,
|
||||
height: widthHeight,
|
||||
fontSize: smallIcon ? 10 : 12,
|
||||
...Platform.select(
|
||||
{
|
||||
android: {
|
||||
padding: 4,
|
||||
color: 'rgb(255, 188, 66)',
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
screenSharing: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: -5,
|
||||
width: widthHeight,
|
||||
height: widthHeight,
|
||||
borderRadius,
|
||||
padding: padding + 1,
|
||||
backgroundColor: '#D24B4E',
|
||||
borderColor: 'black',
|
||||
borderWidth: 2,
|
||||
color: 'white',
|
||||
textAlign: 'center',
|
||||
textAlignVertical: 'center',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const CallAvatar = (props: Props) => {
|
||||
const style = getStyleSheet(props);
|
||||
const profileSize = props.size === 'm' || !props.size ? 40 : 72;
|
||||
const iconSize = props.size === 'm' || !props.size ? 12 : 16;
|
||||
|
||||
// Only show one or the other.
|
||||
let topRightIcon: JSX.Element | null = null;
|
||||
if (props.sharingScreen) {
|
||||
topRightIcon = (
|
||||
<CompassIcon
|
||||
name={'monitor'}
|
||||
size={iconSize}
|
||||
style={style.screenSharing}
|
||||
/>
|
||||
);
|
||||
} else if (props.raisedHand) {
|
||||
topRightIcon = (
|
||||
<Text style={style.raisedHand}>
|
||||
{'✋'}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.pictureHalo}>
|
||||
<View style={style.pictureHalo2}>
|
||||
<View style={style.picture}>
|
||||
{props.userId ?
|
||||
<ProfilePicture
|
||||
userId={props.userId}
|
||||
size={props.size === 'm' || !props.size ? 40 : 72}
|
||||
showStatus={false}
|
||||
/> :
|
||||
<CompassIcon
|
||||
name='account-outline'
|
||||
size={props.size === 'm' || !props.size ? 40 : 72}
|
||||
/>
|
||||
{
|
||||
props.userId ?
|
||||
<ProfilePicture
|
||||
userId={props.userId}
|
||||
size={profileSize}
|
||||
showStatus={false}
|
||||
/> :
|
||||
<CompassIcon
|
||||
name='account-outline'
|
||||
size={profileSize}
|
||||
/>
|
||||
}
|
||||
{props.muted !== undefined &&
|
||||
{
|
||||
props.muted !== undefined &&
|
||||
<CompassIcon
|
||||
name={props.muted ? 'microphone-off' : 'microphone'}
|
||||
size={16}
|
||||
size={iconSize}
|
||||
style={style.mute}
|
||||
/>}
|
||||
/>
|
||||
}
|
||||
{topRightIcon}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
|||
35
app/products/calls/components/raised_hand_icon.tsx
Normal file
35
app/products/calls/components/raised_hand_icon.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {StyleProp, View, ViewStyle} from 'react-native';
|
||||
import Svg, {Path} from 'react-native-svg';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
fill?: string;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
svgStyle?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
export default function RaisedHandIcon({width = 25, height = 27, style, svgStyle, ...props}: Props) {
|
||||
return (
|
||||
<View style={style}>
|
||||
<Svg
|
||||
{...props}
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 25 27'
|
||||
style={svgStyle}
|
||||
>
|
||||
|
||||
<Path
|
||||
d='M24.6999 6.15117V21.873C24.6999 23.0918 24.2733 24.1074 23.4202 24.9199C22.5671 25.773 21.5515 26.1996 20.3733 26.1996H12.4515C11.2327 26.1996 10.1968 25.773 9.34365 24.9199L0.87334 16.2668L2.2749 14.9262C2.51865 14.723 2.80303 14.6215 3.12803 14.6215C3.37178 14.6215 3.59521 14.6824 3.79834 14.8043L8.42959 17.4855V4.5668C8.42959 4.11992 8.59209 3.73398 8.91709 3.40898C9.24209 3.08398 9.62803 2.92148 10.0749 2.92148C10.5218 2.92148 10.9077 3.08398 11.2327 3.40898C11.5577 3.73398 11.7202 4.11992 11.7202 4.5668V12.123H12.7562V1.82461C12.7562 1.37773 12.9187 1.01211 13.2437 0.727734C13.5687 0.402734 13.9546 0.240234 14.4015 0.240234C14.8483 0.240234 15.2343 0.402734 15.5593 0.727734C15.8843 1.01211 16.0468 1.37773 16.0468 1.82461V12.123H17.1437V2.92148C17.1437 2.47461 17.2858 2.08867 17.5702 1.76367C17.8952 1.43867 18.2812 1.27617 18.728 1.27617C19.1749 1.27617 19.5608 1.43867 19.8858 1.76367C20.2108 2.08867 20.3733 2.47461 20.3733 2.92148V12.123H21.4702V6.15117C21.4702 5.7043 21.6124 5.33867 21.8968 5.0543C22.2218 4.7293 22.6077 4.5668 23.0546 4.5668C23.5015 4.5668 23.8874 4.7293 24.2124 5.0543C24.5374 5.33867 24.6999 5.7043 24.6999 6.15117Z'
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
35
app/products/calls/components/unraised_hand_icon.tsx
Normal file
35
app/products/calls/components/unraised_hand_icon.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {StyleProp, View, ViewStyle} from 'react-native';
|
||||
import Svg, {Path} from 'react-native-svg';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
fill?: string;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
svgStyle?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
export default function UnraisedHandIcon({width = 24, height = 24, style, svgStyle, ...props}: Props) {
|
||||
return (
|
||||
<View style={style}>
|
||||
<Svg
|
||||
{...props}
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox='0 0 24 24'
|
||||
style={svgStyle}
|
||||
>
|
||||
|
||||
<Path
|
||||
d='M20.84 22.73L19.17 21.06C17.7 22.85 15.5 24 13 24C9.74 24 6.81 22 5.6 19L2.57 11.37C2.26 10.58 3 9.79 3.81 10.05L4.6 10.31C5.16 10.5 5.62 10.92 5.84 11.47L7.25 15H8V9.89L1.11 3L2.39 1.73L22.11 21.46L20.84 22.73M14 1.25C14 .56 13.44 0 12.75 0S11.5 .56 11.5 1.25V8.3L14 10.8V1.25M21 16V5.75C21 5.06 20.44 4.5 19.75 4.5S18.5 5.06 18.5 5.75V12H17.5V2.75C17.5 2.06 16.94 1.5 16.25 1.5S15 2.06 15 2.75V11.8L20.83 17.63C20.94 17.11 21 16.56 21 16M10.5 3.25C10.5 2.56 9.94 2 9.25 2S8 2.56 8 3.25V4.8L10.5 7.3V3.25Z'
|
||||
/>
|
||||
</Svg>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +94,18 @@ export async function newClient(channelID: string, closeCb: () => void, setScree
|
|||
}
|
||||
};
|
||||
|
||||
const raiseHand = () => {
|
||||
if (ws) {
|
||||
ws.send('raise_hand');
|
||||
}
|
||||
};
|
||||
|
||||
const unraiseHand = () => {
|
||||
if (ws) {
|
||||
ws.send('unraise_hand');
|
||||
}
|
||||
};
|
||||
|
||||
ws.on('error', (err) => {
|
||||
console.log('WS (CALLS) ERROR', err); // eslint-disable-line no-console
|
||||
ws.close();
|
||||
|
|
@ -179,6 +191,8 @@ export async function newClient(channelID: string, closeCb: () => void, setScree
|
|||
mute,
|
||||
unmute,
|
||||
waitForReady,
|
||||
raiseHand,
|
||||
unraiseHand,
|
||||
};
|
||||
|
||||
return client;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ exports[`CallScreen Landscape should match snapshot 1`] = `
|
|||
"flexDirection": "row",
|
||||
"height": 64,
|
||||
"padding": 0,
|
||||
"paddingLeft": 14,
|
||||
"paddingRight": 14,
|
||||
"paddingTop": 10,
|
||||
"position": "absolute",
|
||||
"top": -1000,
|
||||
"width": "100%",
|
||||
|
|
@ -101,6 +104,8 @@ exports[`CallScreen Landscape should match snapshot 1`] = `
|
|||
>
|
||||
<CallAvatar
|
||||
muted={false}
|
||||
raisedHand={false}
|
||||
sharingScreen={false}
|
||||
size="l"
|
||||
userId="user-1-id"
|
||||
volume={0}
|
||||
|
|
@ -130,6 +135,8 @@ exports[`CallScreen Landscape should match snapshot 1`] = `
|
|||
>
|
||||
<CallAvatar
|
||||
muted={true}
|
||||
raisedHand={false}
|
||||
sharingScreen={false}
|
||||
size="l"
|
||||
userId="user-2-id"
|
||||
volume={0}
|
||||
|
|
@ -215,21 +222,28 @@ exports[`CallScreen Landscape should match snapshot 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="message-text-outline"
|
||||
name="volume-high"
|
||||
size={24}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
|
|
@ -239,7 +253,7 @@ exports[`CallScreen Landscape should match snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Chat thread
|
||||
Speaker
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -251,23 +265,39 @@ exports[`CallScreen Landscape should match snapshot 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="volume-high"
|
||||
size={24}
|
||||
<RaisedHandIcon
|
||||
fill="#ffffff"
|
||||
height={24}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 34,
|
||||
},
|
||||
]
|
||||
}
|
||||
svgStyle={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
"position": "relative",
|
||||
"right": 13,
|
||||
"top": -12,
|
||||
}
|
||||
}
|
||||
width={24}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
|
|
@ -276,7 +306,7 @@ exports[`CallScreen Landscape should match snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Speaker
|
||||
Raise hand
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -386,6 +416,9 @@ exports[`CallScreen Landscape should match snapshot with screenshare 1`] = `
|
|||
"flexDirection": "row",
|
||||
"height": 64,
|
||||
"padding": 0,
|
||||
"paddingLeft": 14,
|
||||
"paddingRight": 14,
|
||||
"paddingTop": 10,
|
||||
"position": "absolute",
|
||||
"top": -1000,
|
||||
"width": "100%",
|
||||
|
|
@ -455,7 +488,7 @@ exports[`CallScreen Landscape should match snapshot with screenshare 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
You are seeing User 2 screen
|
||||
You are viewing User 2's screen
|
||||
</Text>
|
||||
</Pressable>
|
||||
<View
|
||||
|
|
@ -527,21 +560,28 @@ exports[`CallScreen Landscape should match snapshot with screenshare 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="message-text-outline"
|
||||
name="volume-high"
|
||||
size={24}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
|
|
@ -551,7 +591,7 @@ exports[`CallScreen Landscape should match snapshot with screenshare 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Chat thread
|
||||
Speaker
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -563,23 +603,39 @@ exports[`CallScreen Landscape should match snapshot with screenshare 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="volume-high"
|
||||
size={24}
|
||||
<RaisedHandIcon
|
||||
fill="#ffffff"
|
||||
height={24}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 34,
|
||||
},
|
||||
]
|
||||
}
|
||||
svgStyle={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
"position": "relative",
|
||||
"right": 13,
|
||||
"top": -12,
|
||||
}
|
||||
}
|
||||
width={24}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
|
|
@ -588,7 +644,7 @@ exports[`CallScreen Landscape should match snapshot with screenshare 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Speaker
|
||||
Raise hand
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -695,7 +751,9 @@ exports[`CallScreen Portrait should match snapshot 1`] = `
|
|||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"padding": 14,
|
||||
"paddingLeft": 14,
|
||||
"paddingRight": 14,
|
||||
"paddingTop": 10,
|
||||
"width": "100%",
|
||||
"zIndex": 4,
|
||||
}
|
||||
|
|
@ -767,6 +825,8 @@ exports[`CallScreen Portrait should match snapshot 1`] = `
|
|||
>
|
||||
<CallAvatar
|
||||
muted={false}
|
||||
raisedHand={false}
|
||||
sharingScreen={false}
|
||||
size="l"
|
||||
userId="user-1-id"
|
||||
volume={0}
|
||||
|
|
@ -796,6 +856,8 @@ exports[`CallScreen Portrait should match snapshot 1`] = `
|
|||
>
|
||||
<CallAvatar
|
||||
muted={true}
|
||||
raisedHand={false}
|
||||
sharingScreen={false}
|
||||
size="l"
|
||||
userId="user-2-id"
|
||||
volume={0}
|
||||
|
|
@ -914,21 +976,28 @@ exports[`CallScreen Portrait should match snapshot 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="message-text-outline"
|
||||
name="volume-high"
|
||||
size={24}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
|
|
@ -938,7 +1007,7 @@ exports[`CallScreen Portrait should match snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Chat thread
|
||||
Speaker
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -950,23 +1019,39 @@ exports[`CallScreen Portrait should match snapshot 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="volume-high"
|
||||
size={24}
|
||||
<RaisedHandIcon
|
||||
fill="#ffffff"
|
||||
height={24}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 34,
|
||||
},
|
||||
]
|
||||
}
|
||||
svgStyle={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
"position": "relative",
|
||||
"right": 13,
|
||||
"top": -12,
|
||||
}
|
||||
}
|
||||
width={24}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
|
|
@ -975,7 +1060,7 @@ exports[`CallScreen Portrait should match snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Speaker
|
||||
Raise hand
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -1045,7 +1130,9 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"padding": 14,
|
||||
"paddingLeft": 14,
|
||||
"paddingRight": 14,
|
||||
"paddingTop": 10,
|
||||
"width": "100%",
|
||||
"zIndex": 4,
|
||||
}
|
||||
|
|
@ -1117,6 +1204,8 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
>
|
||||
<CallAvatar
|
||||
muted={false}
|
||||
raisedHand={false}
|
||||
sharingScreen={false}
|
||||
size="m"
|
||||
userId="user-1-id"
|
||||
volume={0}
|
||||
|
|
@ -1146,6 +1235,8 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
>
|
||||
<CallAvatar
|
||||
muted={true}
|
||||
raisedHand={false}
|
||||
sharingScreen={true}
|
||||
size="m"
|
||||
userId="user-2-id"
|
||||
volume={0}
|
||||
|
|
@ -1193,7 +1284,7 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
You are seeing User 2 screen
|
||||
You are viewing User 2's screen
|
||||
</Text>
|
||||
</Pressable>
|
||||
<View
|
||||
|
|
@ -1298,21 +1389,28 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="message-text-outline"
|
||||
name="volume-high"
|
||||
size={24}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
|
|
@ -1322,7 +1420,7 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Chat thread
|
||||
Speaker
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -1334,23 +1432,39 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="volume-high"
|
||||
size={24}
|
||||
<RaisedHandIcon
|
||||
fill="#ffffff"
|
||||
height={24}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 34,
|
||||
},
|
||||
]
|
||||
}
|
||||
svgStyle={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
"position": "relative",
|
||||
"right": 13,
|
||||
"top": -12,
|
||||
}
|
||||
}
|
||||
width={24}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
|
|
@ -1359,7 +1473,7 @@ exports[`CallScreen Portrait should match snapshot with screenshare 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
Speaker
|
||||
Raise hand
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -1432,6 +1546,9 @@ exports[`CallScreen should show controls in landscape view on click the screen s
|
|||
"flexDirection": "row",
|
||||
"height": 64,
|
||||
"padding": 0,
|
||||
"paddingLeft": 14,
|
||||
"paddingRight": 14,
|
||||
"paddingTop": 10,
|
||||
"position": "absolute",
|
||||
"top": 0,
|
||||
"width": "100%",
|
||||
|
|
@ -1501,7 +1618,7 @@ exports[`CallScreen should show controls in landscape view on click the screen s
|
|||
}
|
||||
}
|
||||
>
|
||||
You are seeing User 2 screen
|
||||
You are viewing User 2's screen
|
||||
</Text>
|
||||
</Pressable>
|
||||
<View
|
||||
|
|
@ -1573,21 +1690,28 @@ exports[`CallScreen should show controls in landscape view on click the screen s
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="message-text-outline"
|
||||
name="volume-high"
|
||||
size={24}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
|
|
@ -1597,7 +1721,7 @@ exports[`CallScreen should show controls in landscape view on click the screen s
|
|||
}
|
||||
}
|
||||
>
|
||||
Chat thread
|
||||
Speaker
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -1609,23 +1733,39 @@ exports[`CallScreen should show controls in landscape view on click the screen s
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="volume-high"
|
||||
size={24}
|
||||
<RaisedHandIcon
|
||||
fill="#ffffff"
|
||||
height={24}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 34,
|
||||
},
|
||||
]
|
||||
}
|
||||
svgStyle={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
"position": "relative",
|
||||
"right": 13,
|
||||
"top": -12,
|
||||
}
|
||||
}
|
||||
width={24}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
|
|
@ -1634,7 +1774,7 @@ exports[`CallScreen should show controls in landscape view on click the screen s
|
|||
}
|
||||
}
|
||||
>
|
||||
Speaker
|
||||
Raise hand
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -1744,6 +1884,9 @@ exports[`CallScreen should show controls in landscape view on click the users li
|
|||
"flexDirection": "row",
|
||||
"height": 64,
|
||||
"padding": 0,
|
||||
"paddingLeft": 14,
|
||||
"paddingRight": 14,
|
||||
"paddingTop": 10,
|
||||
"position": "absolute",
|
||||
"top": 0,
|
||||
"width": "100%",
|
||||
|
|
@ -1817,6 +1960,8 @@ exports[`CallScreen should show controls in landscape view on click the users li
|
|||
>
|
||||
<CallAvatar
|
||||
muted={false}
|
||||
raisedHand={false}
|
||||
sharingScreen={false}
|
||||
size="l"
|
||||
userId="user-1-id"
|
||||
volume={0}
|
||||
|
|
@ -1846,6 +1991,8 @@ exports[`CallScreen should show controls in landscape view on click the users li
|
|||
>
|
||||
<CallAvatar
|
||||
muted={true}
|
||||
raisedHand={false}
|
||||
sharingScreen={false}
|
||||
size="l"
|
||||
userId="user-2-id"
|
||||
volume={0}
|
||||
|
|
@ -1931,21 +2078,28 @@ exports[`CallScreen should show controls in landscape view on click the users li
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="message-text-outline"
|
||||
name="volume-high"
|
||||
size={24}
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
}
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"color": "#ffffff",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
|
|
@ -1955,7 +2109,7 @@ exports[`CallScreen should show controls in landscape view on click the users li
|
|||
}
|
||||
}
|
||||
>
|
||||
Chat thread
|
||||
Speaker
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
@ -1967,23 +2121,39 @@ exports[`CallScreen should show controls in landscape view on click the users li
|
|||
"flexDirection": "column",
|
||||
}
|
||||
}
|
||||
testID="toggle-speakerphone"
|
||||
>
|
||||
<CompassIcon
|
||||
name="volume-high"
|
||||
size={24}
|
||||
<RaisedHandIcon
|
||||
fill="#ffffff"
|
||||
height={24}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
},
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 34,
|
||||
},
|
||||
]
|
||||
}
|
||||
svgStyle={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255,255,255,0.12)",
|
||||
"borderRadius": 34,
|
||||
"color": "#ffffff",
|
||||
"height": 68,
|
||||
"margin": 10,
|
||||
"overflow": "hidden",
|
||||
"padding": 22,
|
||||
"width": 68,
|
||||
"position": "relative",
|
||||
"right": 13,
|
||||
"top": -12,
|
||||
}
|
||||
}
|
||||
width={24}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
|
|
@ -1992,7 +2162,7 @@ exports[`CallScreen should show controls in landscape view on click the users li
|
|||
}
|
||||
}
|
||||
>
|
||||
Speaker
|
||||
Raise hand
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
|
|
|
|||
|
|
@ -45,6 +45,26 @@ describe('CallScreen', () => {
|
|||
screenOn: '',
|
||||
threadId: false,
|
||||
},
|
||||
participants: [{
|
||||
id: 'user-1-id',
|
||||
muted: false,
|
||||
isTalking: false,
|
||||
profile: {
|
||||
id: 'user-1-id',
|
||||
username: 'user-1-username',
|
||||
nickname: 'User 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'user-2-id',
|
||||
muted: true,
|
||||
isTalking: true,
|
||||
profile: {
|
||||
id: 'user-2-id',
|
||||
username: 'user-2-username',
|
||||
nickname: 'User 2',
|
||||
},
|
||||
}],
|
||||
currentParticipant: {
|
||||
id: 'user-2-id',
|
||||
muted: true,
|
||||
|
|
|
|||
|
|
@ -15,14 +15,15 @@ import {
|
|||
} from 'react-native';
|
||||
import {RTCView} from 'react-native-webrtc';
|
||||
|
||||
import {showModalOverCurrentContext, mergeNavigationOptions, popTopScreen, goToScreen} from '@actions/navigation';
|
||||
import {showModalOverCurrentContext, mergeNavigationOptions, popTopScreen} from '@actions/navigation';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {WebsocketEvents} from '@constants';
|
||||
import {THREAD} from '@constants/screen';
|
||||
import {ActionFunc, GenericAction} from '@mm-redux/types/actions';
|
||||
import {displayUsername} from '@mm-redux/utils/user_utils';
|
||||
import CallAvatar from '@mmproducts/calls/components/call_avatar';
|
||||
import CallDuration from '@mmproducts/calls/components/call_duration';
|
||||
import RaisedHandIcon from '@mmproducts/calls/components/raised_hand_icon';
|
||||
import UnraisedHandIcon from '@mmproducts/calls/components/unraised_hand_icon';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type {Theme} from '@mm-redux/types/theme';
|
||||
|
|
@ -35,9 +36,12 @@ type Props = {
|
|||
unmuteMyself: (channelId: string) => GenericAction;
|
||||
setSpeakerphoneOn: (newState: boolean) => GenericAction;
|
||||
leaveCall: () => ActionFunc;
|
||||
raiseHand: () => GenericAction;
|
||||
unraiseHand: () => GenericAction;
|
||||
};
|
||||
theme: Theme;
|
||||
call: Call | null;
|
||||
participants: CallParticipant[];
|
||||
currentParticipant: CallParticipant;
|
||||
teammateNameDisplay: string;
|
||||
screenShareURL: string;
|
||||
|
|
@ -72,7 +76,9 @@ const getStyleSheet = makeStyleSheetFromTheme((props: any) => {
|
|||
const header: any = {
|
||||
flexDirection: 'row',
|
||||
width: '100%',
|
||||
padding: 14,
|
||||
paddingTop: 10,
|
||||
paddingLeft: 14,
|
||||
paddingRight: 14,
|
||||
...Platform.select({
|
||||
android: {
|
||||
elevation: 4,
|
||||
|
|
@ -165,6 +171,18 @@ const getStyleSheet = makeStyleSheetFromTheme((props: any) => {
|
|||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
},
|
||||
handIcon: {
|
||||
borderRadius: 34,
|
||||
padding: 34,
|
||||
margin: 10,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: props.currentParticipant?.raisedHand ? 'rgba(255, 188, 66, 0.16)' : 'rgba(255,255,255,0.12)',
|
||||
},
|
||||
handIconSvgStyle: {
|
||||
position: 'relative',
|
||||
top: -12,
|
||||
right: 13,
|
||||
},
|
||||
speakerphoneIcon: {
|
||||
color: props.speakerphoneOn ? 'black' : props.theme.sidebarText,
|
||||
backgroundColor: props.speakerphoneOn ? 'white' : 'rgba(255,255,255,0.12)',
|
||||
|
|
@ -259,7 +277,11 @@ const CallScreen = (props: Props) => {
|
|||
|
||||
const showOtherActions = () => {
|
||||
const screen = 'CallOtherActions';
|
||||
const passProps = {};
|
||||
const passProps = {
|
||||
theme: props.theme,
|
||||
channelId: props.call?.channelId,
|
||||
rootId: props.call?.threadId,
|
||||
};
|
||||
|
||||
Keyboard.dismiss();
|
||||
const otherActionsRequest = requestAnimationFrame(() => {
|
||||
|
|
@ -275,14 +297,6 @@ const CallScreen = (props: Props) => {
|
|||
props.actions.leaveCall();
|
||||
}, [props.actions.leaveCall]);
|
||||
|
||||
const openThreadHandler = useCallback(() => {
|
||||
const passProps = {
|
||||
channelId: props.call?.channelId,
|
||||
rootId: props.call?.threadId,
|
||||
};
|
||||
goToScreen(THREAD, '', passProps);
|
||||
}, [props.call]);
|
||||
|
||||
const muteUnmuteHandler = useCallback(() => {
|
||||
if (props.call) {
|
||||
if (props.currentParticipant?.muted) {
|
||||
|
|
@ -293,9 +307,17 @@ const CallScreen = (props: Props) => {
|
|||
}
|
||||
}, [props.call?.channelId, props.currentParticipant]);
|
||||
|
||||
const toggleSpeakerphoneHandler = () => {
|
||||
const toggleSpeakerphoneHandler = useCallback(() => {
|
||||
props.actions.setSpeakerphoneOn(!props.speakerphoneOn);
|
||||
};
|
||||
}, [props.speakerphoneOn]);
|
||||
|
||||
const toggleRaiseHand = useCallback(() => {
|
||||
if (props.currentParticipant?.raisedHand > 0) {
|
||||
props.actions.unraiseHand();
|
||||
} else {
|
||||
props.actions.raiseHand();
|
||||
}
|
||||
}, [props.currentParticipant?.raisedHand]);
|
||||
|
||||
const toggleControlsInLandscape = useCallback(() => {
|
||||
setShowControlsInLandscape(!showControlsInLandscape);
|
||||
|
|
@ -317,9 +339,9 @@ const CallScreen = (props: Props) => {
|
|||
streamURL={props.screenShareURL}
|
||||
style={style.screenShareImage}
|
||||
/>
|
||||
<Text
|
||||
style={style.screenShareText}
|
||||
>{`You are seeing ${displayUsername(props.call.participants[props.call.screenOn].profile, props.teammateNameDisplay)} screen`}</Text>
|
||||
<Text style={style.screenShareText}>
|
||||
{`You are viewing ${displayUsername(props.call.participants[props.call.screenOn].profile, props.teammateNameDisplay)}'s screen`}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
|
@ -336,7 +358,7 @@ const CallScreen = (props: Props) => {
|
|||
onPress={toggleControlsInLandscape}
|
||||
style={style.users}
|
||||
>
|
||||
{Object.values(props.call.participants).map((user) => {
|
||||
{props.participants.map((user) => {
|
||||
return (
|
||||
<View
|
||||
style={style.user}
|
||||
|
|
@ -346,10 +368,12 @@ const CallScreen = (props: Props) => {
|
|||
userId={user.id}
|
||||
volume={speaker && speaker.id === user.id ? 1 : 0}
|
||||
muted={user.muted}
|
||||
sharingScreen={user.id === props.call?.screenOn}
|
||||
raisedHand={Boolean(user.raisedHand)}
|
||||
size={props.call?.screenOn ? 'm' : 'l'}
|
||||
/>
|
||||
<Text style={style.username}>
|
||||
{displayUsername(props.call?.participants[user.id].profile, props.teammateNameDisplay)}
|
||||
{displayUsername(user.profile, props.teammateNameDisplay)}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -359,6 +383,8 @@ const CallScreen = (props: Props) => {
|
|||
);
|
||||
}
|
||||
|
||||
const HandIcon = props.currentParticipant?.raisedHand ? UnraisedHandIcon : RaisedHandIcon;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={style.wrapper}>
|
||||
<View style={style.container}>
|
||||
|
|
@ -410,17 +436,6 @@ const CallScreen = (props: Props) => {
|
|||
/>
|
||||
<Text style={style.buttonText}>{'Leave'}</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
style={style.button}
|
||||
onPress={openThreadHandler}
|
||||
>
|
||||
<CompassIcon
|
||||
name='message-text-outline'
|
||||
size={24}
|
||||
style={style.buttonIcon}
|
||||
/>
|
||||
<Text style={style.buttonText}>{'Chat thread'}</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
testID={'toggle-speakerphone'}
|
||||
style={style.button}
|
||||
|
|
@ -429,10 +444,25 @@ const CallScreen = (props: Props) => {
|
|||
<CompassIcon
|
||||
name={'volume-high'}
|
||||
size={24}
|
||||
style={{...style.buttonIcon, ...style.speakerphoneIcon}}
|
||||
style={[style.buttonIcon, style.speakerphoneIcon]}
|
||||
/>
|
||||
<Text style={style.buttonText}>{'Speaker'}</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
style={style.button}
|
||||
onPress={toggleRaiseHand}
|
||||
>
|
||||
<HandIcon
|
||||
fill={props.currentParticipant?.raisedHand ? 'rgb(255, 188, 66)' : props.theme.sidebarText}
|
||||
height={24}
|
||||
width={24}
|
||||
style={[style.buttonIcon, style.handIcon]}
|
||||
svgStyle={style.handIconSvgStyle}
|
||||
/>
|
||||
<Text style={style.buttonText}>
|
||||
{props.currentParticipant?.raisedHand ? 'Lower hand' : 'Raise hand'}
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
style={style.button}
|
||||
onPress={showOtherActions}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,19 @@
|
|||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators, Dispatch} from 'redux';
|
||||
|
||||
import {General} from '@mm-redux/constants';
|
||||
import {getTheme, getTeammateNameDisplaySetting} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
|
||||
import {muteMyself, unmuteMyself, leaveCall, setSpeakerphoneOn} from '@mmproducts/calls//store/actions/calls';
|
||||
import {
|
||||
muteMyself,
|
||||
unmuteMyself,
|
||||
leaveCall,
|
||||
setSpeakerphoneOn,
|
||||
raiseHand,
|
||||
unraiseHand,
|
||||
} from '@mmproducts/calls//store/actions/calls';
|
||||
import {getCurrentCall, getScreenShareURL, isSpeakerphoneOn} from '@mmproducts/calls/store/selectors/calls';
|
||||
import {sortParticipants} from '@mmproducts/calls/utils';
|
||||
|
||||
import CallScreen from './call_screen';
|
||||
|
||||
|
|
@ -15,10 +24,12 @@ import type {GlobalState} from '@mm-redux/types/store';
|
|||
function mapStateToProps(state: GlobalState) {
|
||||
const currentCall = getCurrentCall(state);
|
||||
const currentUserId = getCurrentUserId(state);
|
||||
const teammateNameDisplay = getTeammateNameDisplaySetting(state) || General.TEAMMATE_NAME_DISPLAY.SHOW_USERNAME;
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
call: currentCall,
|
||||
teammateNameDisplay: getTeammateNameDisplaySetting(state),
|
||||
teammateNameDisplay,
|
||||
participants: sortParticipants(teammateNameDisplay, currentCall?.participants, currentCall?.screenOn),
|
||||
currentParticipant: currentCall && currentCall.participants[currentUserId],
|
||||
screenShareURL: getScreenShareURL(state),
|
||||
speakerphoneOn: isSpeakerphoneOn(state),
|
||||
|
|
@ -32,6 +43,8 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
|||
unmuteMyself,
|
||||
setSpeakerphoneOn,
|
||||
leaveCall,
|
||||
raiseHand,
|
||||
unraiseHand,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,79 +43,9 @@ exports[`CallOtherActions should match snapshot 1`] = `
|
|||
>
|
||||
<Action
|
||||
destructive={false}
|
||||
icon="account-plus-outline"
|
||||
icon="message-text-outline"
|
||||
onPress={[Function]}
|
||||
text="Add participants"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Action
|
||||
destructive={false}
|
||||
icon="link-variant"
|
||||
onPress={[Function]}
|
||||
text="Copy call link"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
"buttonBg": "#1c58d9",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3f4350",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#d24b4e",
|
||||
"errorTextColor": "#d24b4e",
|
||||
"linkColor": "#386fe5",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionColor": "#1e325c",
|
||||
"mentionHighlightBg": "#ffd470",
|
||||
"mentionHighlightLink": "#1b1d22",
|
||||
"newMessageSeparator": "#cc8f00",
|
||||
"onlineIndicator": "#3db887",
|
||||
"sidebarBg": "#1e325c",
|
||||
"sidebarHeaderBg": "#192a4d",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarTeamBarBg": "#14213e",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#5d89ea",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#28427b",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Denim",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Action
|
||||
destructive={false}
|
||||
icon="send-outline"
|
||||
onPress={[Function]}
|
||||
text="Give Feedback"
|
||||
text="Chat thread"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc1f",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useCallback} from 'react';
|
||||
import React from 'react';
|
||||
import {injectIntl} from 'react-intl';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import {dismissModal} from '@actions/navigation';
|
||||
import {dismissModal, goToScreen} from '@actions/navigation';
|
||||
import SlideUpPanel from '@components/slide_up_panel';
|
||||
import {THREAD} from '@constants/screen';
|
||||
|
||||
import Action from './action';
|
||||
|
||||
|
|
@ -13,21 +14,19 @@ import type {Theme} from '@mm-redux/types/theme';
|
|||
|
||||
type Props = {
|
||||
theme: Theme;
|
||||
channelId?: string;
|
||||
rootId?: string;
|
||||
}
|
||||
|
||||
const CallOtherActions = ({theme}: Props) => {
|
||||
const CallOtherActions = ({theme, channelId, rootId}: Props) => {
|
||||
const close = () => {
|
||||
dismissModal();
|
||||
};
|
||||
|
||||
// TODO: Implement this whenever we support participants invitation to calls
|
||||
const addParticipants = useCallback(() => null, []);
|
||||
|
||||
// TODO: Implement this whenever we support calls links
|
||||
const copyCallLink = useCallback(() => null, []);
|
||||
|
||||
// TODO: Implement this whenever we support give feedback
|
||||
const giveFeedback = useCallback(() => null, []);
|
||||
const chatThread = () => {
|
||||
goToScreen(THREAD, '', {channelId, rootId});
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
|
|
@ -38,23 +37,9 @@ const CallOtherActions = ({theme}: Props) => {
|
|||
>
|
||||
<Action
|
||||
destructive={false}
|
||||
icon='account-plus-outline'
|
||||
onPress={addParticipants}
|
||||
text='Add participants'
|
||||
theme={theme}
|
||||
/>
|
||||
<Action
|
||||
destructive={false}
|
||||
icon='link-variant'
|
||||
onPress={copyCallLink}
|
||||
text='Copy call link'
|
||||
theme={theme}
|
||||
/>
|
||||
<Action
|
||||
destructive={false}
|
||||
icon='send-outline'
|
||||
onPress={giveFeedback}
|
||||
text='Give Feedback'
|
||||
icon='message-text-outline'
|
||||
onPress={chatThread}
|
||||
text='Chat thread'
|
||||
theme={theme}
|
||||
/>
|
||||
</SlideUpPanel>
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ export default keyMirror({
|
|||
RECEIVED_UNMUTE_USER_CALL: null,
|
||||
RECEIVED_VOICE_ON_USER_CALL: null,
|
||||
RECEIVED_VOICE_OFF_USER_CALL: null,
|
||||
RECEIVED_RAISE_HAND_CALL: null,
|
||||
RECEIVED_UNRAISE_HAND_CALL: null,
|
||||
RECEIVED_RAISE_HAND: null,
|
||||
RECEIVED_UNRAISE_HAND: null,
|
||||
SET_SCREENSHARE_URL: null,
|
||||
SET_SPEAKERPHONE: null,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ export function loadCalls(): ActionFunc {
|
|||
participants: channel.call.users.reduce((prev: Dictionary<CallParticipant>, cur: string, curIdx: number) => {
|
||||
const profile = getState().entities.users.profiles[cur];
|
||||
const muted = channel.call.states && channel.call.states[curIdx] ? !channel.call.states[curIdx].unmuted : true;
|
||||
prev[cur] = {id: cur, muted, isTalking: false, profile};
|
||||
const raised_hand = channel.call.states && channel.call.states[curIdx] ? channel.call.states[curIdx].raised_hand : 0;
|
||||
prev[cur] = {id: cur, muted, raisedHand: raised_hand, isTalking: false, profile};
|
||||
return prev;
|
||||
}, {}),
|
||||
channelId: channel.channel_id,
|
||||
|
|
@ -155,6 +156,22 @@ export function unmuteMyself(): GenericAction {
|
|||
return {type: 'empty'};
|
||||
}
|
||||
|
||||
export function raiseHand(): GenericAction {
|
||||
if (ws) {
|
||||
ws.raiseHand();
|
||||
}
|
||||
|
||||
return {type: 'empty'};
|
||||
}
|
||||
|
||||
export function unraiseHand(): GenericAction {
|
||||
if (ws) {
|
||||
ws.unraiseHand();
|
||||
}
|
||||
|
||||
return {type: 'empty'};
|
||||
}
|
||||
|
||||
export function setSpeakerphoneOn(newState: boolean): GenericAction {
|
||||
InCallManager.setSpeakerphoneOn(newState);
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -81,3 +81,17 @@ export function handleCallScreenOff(msg: WebSocketMessage): GenericAction {
|
|||
data: {channelId: msg.broadcast.channel_id, userId: msg.data.userID},
|
||||
};
|
||||
}
|
||||
|
||||
export function handleCallUserRaiseHand(msg: WebSocketMessage): GenericAction {
|
||||
return {
|
||||
type: CallsTypes.RECEIVED_RAISE_HAND,
|
||||
data: {channelId: msg.broadcast.channel_id, userId: msg.data.userID, ts: msg.data.raised_hand},
|
||||
};
|
||||
}
|
||||
|
||||
export function handleCallUserUnraiseHand(msg: WebSocketMessage): GenericAction {
|
||||
return {
|
||||
type: CallsTypes.RECEIVED_UNRAISE_HAND,
|
||||
data: {channelId: msg.broadcast.channel_id, userId: msg.data.userID, ts: msg.data.raised_hand},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import callsReducer from './calls';
|
|||
describe('Reducers.calls.calls', () => {
|
||||
const call1 = {
|
||||
participants: {
|
||||
'user-1': {id: 'user-1', muted: false, isTalking: false, profile: {id: 'user-1'}},
|
||||
'user-2': {id: 'user-2', muted: true, isTalking: true, profile: {id: 'user-2'}},
|
||||
'user-1': {id: 'user-1', muted: false, raisedHand: 0, isTalking: false, profile: {id: 'user-1'}},
|
||||
'user-2': {id: 'user-2', muted: true, raisedHand: 0, isTalking: true, profile: {id: 'user-2'}},
|
||||
},
|
||||
channelId: 'channel-1',
|
||||
startTime: 123,
|
||||
|
|
@ -21,8 +21,8 @@ describe('Reducers.calls.calls', () => {
|
|||
};
|
||||
const call2 = {
|
||||
participants: {
|
||||
'user-3': {id: 'user-3', muted: false, isTalking: false, profile: {id: 'user-3'}},
|
||||
'user-4': {id: 'user-4', muted: true, isTalking: true, profile: {id: 'user-4'}},
|
||||
'user-3': {id: 'user-3', muted: false, raisedHand: 0, isTalking: false, profile: {id: 'user-3'}},
|
||||
'user-4': {id: 'user-4', muted: true, raisedHand: 0, isTalking: true, profile: {id: 'user-4'}},
|
||||
},
|
||||
channelId: 'channel-2',
|
||||
startTime: 123,
|
||||
|
|
@ -32,8 +32,8 @@ describe('Reducers.calls.calls', () => {
|
|||
};
|
||||
const call3 = {
|
||||
participants: {
|
||||
'user-5': {id: 'user-5', muted: false, isTalking: false, profile: {id: 'user-5'}},
|
||||
'user-6': {id: 'user-6', muted: true, isTalking: true, profile: {id: 'user-6'}},
|
||||
'user-5': {id: 'user-5', muted: false, raisedHand: 0, isTalking: false, profile: {id: 'user-5'}},
|
||||
'user-6': {id: 'user-6', muted: true, raisedHand: 0, isTalking: true, profile: {id: 'user-6'}},
|
||||
},
|
||||
channelId: 'channel-3',
|
||||
startTime: 123,
|
||||
|
|
@ -71,7 +71,7 @@ describe('Reducers.calls.calls', () => {
|
|||
{
|
||||
'channel-1': {
|
||||
participants: {
|
||||
'user-2': {id: 'user-2', muted: true, isTalking: true, profile: {id: 'user-2'}},
|
||||
'user-2': {id: 'user-2', muted: true, raisedHand: 0, isTalking: true, profile: {id: 'user-2'}},
|
||||
},
|
||||
channelId: 'channel-1',
|
||||
startTime: 123,
|
||||
|
|
@ -105,9 +105,9 @@ describe('Reducers.calls.calls', () => {
|
|||
{
|
||||
'channel-1': {
|
||||
participants: {
|
||||
'user-1': {id: 'user-1', muted: false, isTalking: false, profile: {id: 'user-1'}},
|
||||
'user-2': {id: 'user-2', muted: true, isTalking: true, profile: {id: 'user-2'}},
|
||||
'user-3': {id: 'user-3', muted: true, isTalking: false, profile: {id: 'user-3'}},
|
||||
'user-1': {id: 'user-1', muted: false, raisedHand: 0, isTalking: false, profile: {id: 'user-1'}},
|
||||
'user-2': {id: 'user-2', muted: true, raisedHand: 0, isTalking: true, profile: {id: 'user-2'}},
|
||||
'user-3': {id: 'user-3', muted: true, raisedHand: 0, isTalking: false, profile: {id: 'user-3'}},
|
||||
},
|
||||
channelId: 'channel-1',
|
||||
startTime: 123,
|
||||
|
|
@ -209,6 +209,74 @@ describe('Reducers.calls.calls', () => {
|
|||
state = callsReducer(initialState, testAction);
|
||||
assert.deepEqual(state.calls, initialState.calls);
|
||||
});
|
||||
it('RECEIVED_RAISE_HAND', async () => {
|
||||
const initialState = {calls: {'channel-1': call1}};
|
||||
const testAction = {
|
||||
type: CallsTypes.RECEIVED_RAISE_HAND,
|
||||
data: {channelId: 'channel-1', userId: 'user-2', ts: 345},
|
||||
};
|
||||
let state = callsReducer(initialState, testAction);
|
||||
assert.deepEqual(
|
||||
state.calls,
|
||||
{
|
||||
'channel-1': {
|
||||
participants: {
|
||||
'user-1': {id: 'user-1', muted: false, raisedHand: 0, isTalking: false, profile: {id: 'user-1'}},
|
||||
'user-2': {id: 'user-2', muted: true, raisedHand: 345, isTalking: true, profile: {id: 'user-2'}},
|
||||
},
|
||||
channelId: 'channel-1',
|
||||
startTime: 123,
|
||||
speakers: ['user-2'],
|
||||
screenOn: false,
|
||||
threadId: 'thread-1',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
testAction.data = {channelId: 'invalid-channel', userId: 'user-1', ts: 345};
|
||||
|
||||
state = callsReducer(initialState, testAction);
|
||||
assert.deepEqual(state.calls, {'channel-1': call1});
|
||||
});
|
||||
it('RECEIVED_UNRAISE_HAND', async () => {
|
||||
const initialState = {
|
||||
calls: {
|
||||
'channel-1': {
|
||||
...call1,
|
||||
participants: {
|
||||
...call1.participants,
|
||||
'user-1': {...call1.participants['user-1'], raisedHand: 345},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const testAction = {
|
||||
type: CallsTypes.RECEIVED_UNRAISE_HAND,
|
||||
data: {channelId: 'channel-1', userId: 'user-1', ts: 0},
|
||||
};
|
||||
let state = callsReducer(initialState, testAction);
|
||||
assert.deepEqual(
|
||||
state.calls,
|
||||
{
|
||||
'channel-1': {
|
||||
participants: {
|
||||
'user-1': {id: 'user-1', muted: false, raisedHand: 0, isTalking: false, profile: {id: 'user-1'}},
|
||||
'user-2': {id: 'user-2', muted: true, raisedHand: 0, isTalking: true, profile: {id: 'user-2'}},
|
||||
},
|
||||
channelId: 'channel-1',
|
||||
startTime: 123,
|
||||
speakers: ['user-2'],
|
||||
screenOn: false,
|
||||
threadId: 'thread-1',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
testAction.data = {channelId: 'invalid-channel', userId: 'user-1', ts: 0};
|
||||
|
||||
state = callsReducer(initialState, testAction);
|
||||
assert.deepEqual(state.calls, initialState.calls);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Reducers.calls.joined', () => {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ function calls(state: Dictionary<Call> = {}, action: GenericAction) {
|
|||
id: userId,
|
||||
muted: true,
|
||||
isTalking: false,
|
||||
raisedHand: 0,
|
||||
profile,
|
||||
};
|
||||
const nextState = {...state};
|
||||
|
|
@ -88,6 +89,36 @@ function calls(state: Dictionary<Call> = {}, action: GenericAction) {
|
|||
nextState[channelId] = channelUpdate;
|
||||
return nextState;
|
||||
}
|
||||
case CallsTypes.RECEIVED_RAISE_HAND: {
|
||||
const {channelId, userId, ts} = action.data;
|
||||
if (!state[channelId]) {
|
||||
return state;
|
||||
}
|
||||
if (!state[channelId].participants[userId]) {
|
||||
return state;
|
||||
}
|
||||
const userUpdate = {...state[channelId].participants[userId], raisedHand: ts};
|
||||
const channelUpdate = {...state[channelId], participants: {...state[channelId].participants}};
|
||||
channelUpdate.participants[userId] = userUpdate;
|
||||
const nextState = {...state};
|
||||
nextState[channelId] = channelUpdate;
|
||||
return nextState;
|
||||
}
|
||||
case CallsTypes.RECEIVED_UNRAISE_HAND: {
|
||||
const {channelId, userId, ts} = action.data;
|
||||
if (!state[channelId]) {
|
||||
return state;
|
||||
}
|
||||
if (!state[channelId].participants[userId]) {
|
||||
return state;
|
||||
}
|
||||
const userUpdate = {...state[channelId].participants[userId], raisedHand: ts};
|
||||
const channelUpdate = {...state[channelId], participants: {...state[channelId].participants}};
|
||||
channelUpdate.participants[userId] = userUpdate;
|
||||
const nextState = {...state};
|
||||
nextState[channelId] = channelUpdate;
|
||||
return nextState;
|
||||
}
|
||||
case CallsTypes.RECEIVED_CHANNEL_CALL_SCREEN_ON: {
|
||||
const {channelId, userId} = action.data;
|
||||
if (!state[channelId]) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export type CallParticipant = {
|
|||
id: string;
|
||||
muted: boolean;
|
||||
isTalking: boolean;
|
||||
raisedHand: number;
|
||||
profile: UserProfile;
|
||||
}
|
||||
|
||||
|
|
|
|||
50
app/products/calls/utils.ts
Normal file
50
app/products/calls/utils.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Dictionary} from '@mm-redux/types/utilities';
|
||||
import {displayUsername} from '@mm-redux/utils/user_utils';
|
||||
import {CallParticipant} from '@mmproducts/calls/store/types/calls';
|
||||
|
||||
export function sortParticipants(teammateNameDisplay: string, participants?: Dictionary<CallParticipant>, presenterID?: string): CallParticipant[] {
|
||||
if (!participants) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const users = Object.values(participants);
|
||||
|
||||
return users.sort(sortByName(teammateNameDisplay)).sort(sortByState(presenterID));
|
||||
}
|
||||
|
||||
const sortByName = (teammateNameDisplay: string) => {
|
||||
return (a: CallParticipant, b: CallParticipant) => {
|
||||
const nameA = displayUsername(a.profile, teammateNameDisplay);
|
||||
const nameB = displayUsername(b.profile, teammateNameDisplay);
|
||||
return nameA.localeCompare(nameB);
|
||||
};
|
||||
};
|
||||
|
||||
const sortByState = (presenterID?: string) => {
|
||||
return (a: CallParticipant, b: CallParticipant) => {
|
||||
if (a.id === presenterID) {
|
||||
return -1;
|
||||
} else if (b.id === presenterID) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!a.muted && b.muted) {
|
||||
return -1;
|
||||
} else if (!b.muted && a.muted) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a.raisedHand && !b.raisedHand) {
|
||||
return -1;
|
||||
} else if (b.raisedHand && !a.raisedHand) {
|
||||
return 1;
|
||||
} else if (a.raisedHand && b.raisedHand) {
|
||||
return a.raisedHand - b.raisedHand;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
Loading…
Reference in a new issue