@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
16
16
limitations under the License.
17
17
*/
18
18
19
- import React , { createRef } from "react" ;
19
+ import React , { createRef , useState } from "react" ;
20
20
import classNames from "classnames" ;
21
21
import AccessibleTooltipButton from "../../elements/AccessibleTooltipButton" ;
22
22
import CallContextMenu from "../../context_menus/CallContextMenu" ;
@@ -28,8 +28,11 @@ import {
28
28
alwaysAboveRightOf ,
29
29
ChevronFace ,
30
30
ContextMenuTooltipButton ,
31
+ useContextMenu ,
31
32
} from '../../../structures/ContextMenu' ;
32
33
import { _t } from "../../../../languageHandler" ;
34
+ import DeviceContextMenu from "../../context_menus/DeviceContextMenu" ;
35
+ import { MediaDeviceKindEnum } from "../../../../MediaDeviceHandler" ;
33
36
34
37
// Height of the header duplicated from CSS because we need to subtract it from our max
35
38
// height to get the max height of the video
@@ -39,15 +42,22 @@ const TOOLTIP_Y_OFFSET = -24;
39
42
40
43
const CONTROLS_HIDE_DELAY = 2000 ;
41
44
42
- interface IButtonProps {
45
+ interface IButtonProps extends Omit < React . ComponentProps < typeof AccessibleTooltipButton > , "title" > {
43
46
state : boolean ;
44
47
className : string ;
45
- onLabel : string ;
46
- offLabel : string ;
47
- onClick : ( ) => void ;
48
+ onLabel ? : string ;
49
+ offLabel ? : string ;
50
+ onClick : ( event : React . MouseEvent ) => void ;
48
51
}
49
52
50
- const CallViewToggleButton : React . FC < IButtonProps > = ( { state : isOn , className, onLabel, offLabel, onClick } ) => {
53
+ const CallViewToggleButton : React . FC < IButtonProps > = ( {
54
+ children,
55
+ state : isOn ,
56
+ className,
57
+ onLabel,
58
+ offLabel,
59
+ ...props
60
+ } ) => {
51
61
const classes = classNames ( "mx_CallViewButtons_button" , className , {
52
62
mx_CallViewButtons_button_on : isOn ,
53
63
mx_CallViewButtons_button_off : ! isOn ,
@@ -56,11 +66,48 @@ const CallViewToggleButton: React.FC<IButtonProps> = ({ state: isOn, className,
56
66
return (
57
67
< AccessibleTooltipButton
58
68
className = { classes }
59
- onClick = { onClick }
60
69
title = { isOn ? onLabel : offLabel }
61
70
alignment = { Alignment . Top }
62
71
yOffset = { TOOLTIP_Y_OFFSET }
63
- />
72
+ { ...props }
73
+ >
74
+ { children }
75
+ </ AccessibleTooltipButton >
76
+ ) ;
77
+ } ;
78
+
79
+ interface IDropdownButtonProps extends IButtonProps {
80
+ deviceKinds : MediaDeviceKindEnum [ ] ;
81
+ }
82
+
83
+ const CallViewDropdownButton : React . FC < IDropdownButtonProps > = ( { state, deviceKinds, ...props } ) => {
84
+ const [ menuDisplayed , buttonRef , openMenu , closeMenu ] = useContextMenu ( ) ;
85
+ const [ hoveringDropdown , setHoveringDropdown ] = useState ( false ) ;
86
+
87
+ const classes = classNames ( "mx_CallViewButtons_button" , "mx_CallViewButtons_dropdownButton" , {
88
+ mx_CallViewButtons_dropdownButton_collapsed : ! menuDisplayed ,
89
+ } ) ;
90
+
91
+ const onClick = ( event : React . MouseEvent ) : void => {
92
+ event . stopPropagation ( ) ;
93
+ openMenu ( ) ;
94
+ } ;
95
+
96
+ return (
97
+ < CallViewToggleButton inputRef = { buttonRef } forceHide = { menuDisplayed || hoveringDropdown } state = { state } { ...props } >
98
+ < CallViewToggleButton
99
+ className = { classes }
100
+ onClick = { onClick }
101
+ onHover = { ( hovering ) => setHoveringDropdown ( hovering ) }
102
+ state = { state }
103
+ />
104
+ { menuDisplayed && < DeviceContextMenu
105
+ { ...alwaysAboveRightOf ( buttonRef . current ?. getBoundingClientRect ( ) ) }
106
+
107
+ onFinished = { closeMenu }
108
+ deviceKinds = { deviceKinds }
109
+ /> }
110
+ </ CallViewToggleButton >
64
111
) ;
65
112
} ;
66
113
@@ -221,19 +268,21 @@ export default class CallViewButtons extends React.Component<IProps, IState> {
221
268
alignment = { Alignment . Top }
222
269
yOffset = { TOOLTIP_Y_OFFSET }
223
270
/> }
224
- < CallViewToggleButton
271
+ < CallViewDropdownButton
225
272
state = { ! this . props . buttonsState . micMuted }
226
273
className = "mx_CallViewButtons_button_mic"
227
274
onLabel = { _t ( "Mute the microphone" ) }
228
275
offLabel = { _t ( "Unmute the microphone" ) }
229
276
onClick = { this . props . handlers . onMicMuteClick }
277
+ deviceKinds = { [ MediaDeviceKindEnum . AudioInput , MediaDeviceKindEnum . AudioOutput ] }
230
278
/>
231
- { this . props . buttonsVisibility . vidMute && < CallViewToggleButton
279
+ { this . props . buttonsVisibility . vidMute && < CallViewDropdownButton
232
280
state = { ! this . props . buttonsState . vidMuted }
233
281
className = "mx_CallViewButtons_button_vid"
234
282
onLabel = { _t ( "Stop the camera" ) }
235
283
offLabel = { _t ( "Start the camera" ) }
236
284
onClick = { this . props . handlers . onVidMuteClick }
285
+ deviceKinds = { [ MediaDeviceKindEnum . VideoInput ] }
237
286
/> }
238
287
{ this . props . buttonsVisibility . screensharing && < CallViewToggleButton
239
288
state = { this . props . buttonsState . screensharing }
0 commit comments