blob: f6f72752278f57f5ea063bb87f0f55effc32dd06 [file] [log] [blame]
simon1170c322022-10-31 14:51:31 -04001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
simon33c06182022-11-02 17:39:31 -040018
simonf929a362022-11-18 16:53:45 -050019import { IconButton, IconButtonProps, PaletteColor } from '@mui/material';
20import { styled, Theme } from '@mui/material/styles';
simonaccd8022022-11-24 15:04:53 -050021import { useContext, useMemo } from 'react';
simon4e7445c2022-11-16 21:18:46 -050022import { useTranslation } from 'react-i18next';
simon1170c322022-10-31 14:51:31 -040023
simonf929a362022-11-18 16:53:45 -050024import { CallContext } from '../contexts/CallProvider';
25import { ExpandableButton, ExpandableButtonProps, ShapedButtonProps, ToggleIconButton } from './Button';
simon1170c322022-10-31 14:51:31 -040026import {
27 CallEndIcon,
28 ChatBubbleIcon,
29 ExtensionIcon,
simon33c06182022-11-02 17:39:31 -040030 FileIcon,
31 FullScreenIcon,
simon1170c322022-10-31 14:51:31 -040032 GroupAddIcon,
33 MicroIcon,
34 MicroOffIcon,
simon33c06182022-11-02 17:39:31 -040035 MoreVerticalIcon,
simon2d3b6532022-11-08 21:01:57 -050036 PlaceAudioCallIcon,
simon1170c322022-10-31 14:51:31 -040037 RecordingIcon,
simon2d3b6532022-11-08 21:01:57 -050038 RoundCloseIcon,
simon33c06182022-11-02 17:39:31 -040039 ScreenShareArrowIcon,
40 ScreenShareRegularIcon,
41 ScreenShareScreenAreaIcon,
simon1170c322022-10-31 14:51:31 -040042 VideoCameraIcon,
43 VideoCameraOffIcon,
44 VolumeIcon,
Gabriel Rochon8321a0d2022-11-06 23:18:36 -050045 WindowIcon,
simon1170c322022-10-31 14:51:31 -040046} from './SvgIcon';
47
Gabriel Rochon8321a0d2022-11-06 23:18:36 -050048const CallButton = styled((props: ExpandableButtonProps) => {
49 return <ExpandableButton {...props} />;
50})({
simon2d3b6532022-11-08 21:01:57 -050051 color: 'white',
Gabriel Rochon8321a0d2022-11-06 23:18:36 -050052 '&:hover': {
53 backgroundColor: 'rgba(255, 255, 255, 0.15)',
54 },
Gabriel Rochon8321a0d2022-11-06 23:18:36 -050055});
56
simon2d3b6532022-11-08 21:01:57 -050057const ColoredCallButton = styled(
58 ({
simonf929a362022-11-18 16:53:45 -050059 paletteColor,
60 Icon,
simon2d3b6532022-11-08 21:01:57 -050061 ...props
simonf929a362022-11-18 16:53:45 -050062 }: ShapedButtonProps & {
63 paletteColor?: PaletteColor | ((theme: Theme) => PaletteColor);
simon2d3b6532022-11-08 21:01:57 -050064 }) => {
simonf929a362022-11-18 16:53:45 -050065 return (
66 <IconButton {...props} disableRipple={true}>
67 <Icon fontSize="inherit" />
68 </IconButton>
69 );
simon2d3b6532022-11-08 21:01:57 -050070 }
simonf929a362022-11-18 16:53:45 -050071)(({ theme, paletteColor = theme.palette.primary }) => {
72 if (typeof paletteColor === 'function') {
73 paletteColor = paletteColor(theme);
74 }
75
simon2d3b6532022-11-08 21:01:57 -050076 return {
simonf929a362022-11-18 16:53:45 -050077 color: paletteColor.contrastText,
78 backgroundColor: paletteColor.dark,
simon2d3b6532022-11-08 21:01:57 -050079 '&:hover': {
simonf929a362022-11-18 16:53:45 -050080 backgroundColor: paletteColor.main,
simon2d3b6532022-11-08 21:01:57 -050081 },
82 };
Gabriel Rochon8321a0d2022-11-06 23:18:36 -050083});
84
simon33c06182022-11-02 17:39:31 -040085export const CallingChatButton = (props: ExpandableButtonProps) => {
simonf9d78f22022-11-25 15:47:15 -050086 const { setIsChatShown } = useContext(CallContext);
87 return (
88 <CallButton
89 aria-label="chat"
90 Icon={ChatBubbleIcon}
91 onClick={() => {
92 setIsChatShown((v) => !v);
93 }}
94 {...props}
95 />
96 );
simon1170c322022-10-31 14:51:31 -040097};
simon33c06182022-11-02 17:39:31 -040098
99export const CallingEndButton = (props: ExpandableButtonProps) => {
simonaccd8022022-11-24 15:04:53 -0500100 const { endCall } = useContext(CallContext);
simonf929a362022-11-18 16:53:45 -0500101 return (
102 <ColoredCallButton
103 paletteColor={(theme) => theme.palette.error}
104 onClick={() => {
simonaccd8022022-11-24 15:04:53 -0500105 endCall();
simonf929a362022-11-18 16:53:45 -0500106 }}
107 aria-label="call end"
108 Icon={CallEndIcon}
109 {...props}
110 />
111 );
simon1170c322022-10-31 14:51:31 -0400112};
simon33c06182022-11-02 17:39:31 -0400113
114export const CallingExtensionButton = (props: ExpandableButtonProps) => {
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500115 return <CallButton aria-label="extensions" Icon={ExtensionIcon} {...props} />;
simon1170c322022-10-31 14:51:31 -0400116};
simon33c06182022-11-02 17:39:31 -0400117
118export const CallingFullScreenButton = (props: ExpandableButtonProps) => {
simon2a5cf142022-11-25 15:47:35 -0500119 const { setIsFullscreen } = useContext(CallContext);
120 return (
121 <CallButton
122 aria-label="full screen"
123 Icon={FullScreenIcon}
124 onClick={() => {
125 setIsFullscreen((v) => !v);
126 }}
127 {...props}
128 />
129 );
simon1170c322022-10-31 14:51:31 -0400130};
simon33c06182022-11-02 17:39:31 -0400131
132export const CallingGroupButton = (props: ExpandableButtonProps) => {
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500133 return <CallButton aria-label="group options" Icon={GroupAddIcon} {...props} />;
simon1170c322022-10-31 14:51:31 -0400134};
simon33c06182022-11-02 17:39:31 -0400135
136export const CallingMoreVerticalButton = (props: ExpandableButtonProps) => {
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500137 return <CallButton aria-label="more vertical" Icon={MoreVerticalIcon} {...props} />;
simon1170c322022-10-31 14:51:31 -0400138};
simon33c06182022-11-02 17:39:31 -0400139
140export const CallingRecordButton = (props: ExpandableButtonProps) => {
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500141 return <CallButton aria-label="recording options" Icon={RecordingIcon} {...props} />;
simon33c06182022-11-02 17:39:31 -0400142};
143
144export const CallingScreenShareButton = (props: ExpandableButtonProps) => {
simon4e7445c2022-11-16 21:18:46 -0500145 const { t } = useTranslation();
simon1170c322022-10-31 14:51:31 -0400146 return (
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500147 <CallButton
simon33c06182022-11-02 17:39:31 -0400148 aria-label="screen share"
149 Icon={ScreenShareArrowIcon}
150 expandMenuOptions={[
151 {
simon4e7445c2022-11-16 21:18:46 -0500152 description: t('share_screen'),
simon33c06182022-11-02 17:39:31 -0400153 icon: <ScreenShareRegularIcon />,
154 },
155 {
simon4e7445c2022-11-16 21:18:46 -0500156 description: t('share_window'),
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500157 icon: <WindowIcon />,
158 },
159 {
simon4e7445c2022-11-16 21:18:46 -0500160 description: t('share_screen_area'),
simon33c06182022-11-02 17:39:31 -0400161 icon: <ScreenShareScreenAreaIcon />,
162 },
163 {
simon4e7445c2022-11-16 21:18:46 -0500164 description: t('share_file'),
simon33c06182022-11-02 17:39:31 -0400165 icon: <FileIcon />,
166 },
167 ]}
168 {...props}
169 />
simon1170c322022-10-31 14:51:31 -0400170 );
171};
172
simon9a8fe202022-11-15 18:25:49 -0500173const useMediaDeviceExpandMenuOptions = (kind: MediaDeviceKind) => {
simonf929a362022-11-18 16:53:45 -0500174 const { mediaDevices } = useContext(CallContext);
simon9a8fe202022-11-15 18:25:49 -0500175
176 return useMemo(
177 () =>
178 mediaDevices[kind].map((device) => ({
179 key: device.deviceId,
180 description: device.label,
181 })),
182 [mediaDevices, kind]
183 );
184};
185
simon33c06182022-11-02 17:39:31 -0400186export const CallingVolumeButton = (props: ExpandableButtonProps) => {
simon9a8fe202022-11-15 18:25:49 -0500187 const options = useMediaDeviceExpandMenuOptions('audiooutput');
188
simon33c06182022-11-02 17:39:31 -0400189 return (
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500190 <CallButton
simon33c06182022-11-02 17:39:31 -0400191 aria-label="volume options"
192 Icon={VolumeIcon}
193 expandMenuOptions={[
194 {
simon9a8fe202022-11-15 18:25:49 -0500195 options,
simon33c06182022-11-02 17:39:31 -0400196 },
197 ]}
198 {...props}
199 />
200 );
201};
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500202
203export const CallingMicButton = (props: ExpandableButtonProps) => {
simon9a8fe202022-11-15 18:25:49 -0500204 const options = useMediaDeviceExpandMenuOptions('audioinput');
simon1170c322022-10-31 14:51:31 -0400205
206 return (
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500207 <CallButton
simon1170c322022-10-31 14:51:31 -0400208 aria-label="microphone options"
simon9a8fe202022-11-15 18:25:49 -0500209 expandMenuOptions={[
210 {
211 options,
212 },
213 ]}
simonf929a362022-11-18 16:53:45 -0500214 IconButtonComp={ToggleAudioCameraIconButton}
215 {...props}
216 />
217 );
218};
219
220const ToggleAudioCameraIconButton = (props: IconButtonProps) => {
221 const { isAudioOn, setAudioStatus } = useContext(CallContext);
222 return (
223 <ToggleIconButton
224 IconOn={MicroIcon}
225 IconOff={MicroOffIcon}
226 selected={isAudioOn}
227 toggle={() => setAudioStatus(!isAudioOn)}
simon1170c322022-10-31 14:51:31 -0400228 {...props}
229 />
230 );
231};
232
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500233export const CallingVideoCameraButton = (props: ExpandableButtonProps) => {
simon9a8fe202022-11-15 18:25:49 -0500234 const options = useMediaDeviceExpandMenuOptions('videoinput');
235
simon1170c322022-10-31 14:51:31 -0400236 return (
Gabriel Rochon8321a0d2022-11-06 23:18:36 -0500237 <CallButton
simon1170c322022-10-31 14:51:31 -0400238 aria-label="camera options"
simon9a8fe202022-11-15 18:25:49 -0500239 expandMenuOptions={[
240 {
241 options,
242 },
243 ]}
simonf929a362022-11-18 16:53:45 -0500244 IconButtonComp={ToggleVideoCameraIconButton}
245 {...props}
246 />
247 );
248};
249
250const ToggleVideoCameraIconButton = (props: IconButtonProps) => {
251 const { isVideoOn, setVideoStatus } = useContext(CallContext);
252 return (
253 <ToggleIconButton
254 IconOn={VideoCameraIcon}
255 IconOff={VideoCameraOffIcon}
256 selected={isVideoOn}
257 toggle={() => setVideoStatus(!isVideoOn)}
simon1170c322022-10-31 14:51:31 -0400258 {...props}
259 />
260 );
261};
simon2d3b6532022-11-08 21:01:57 -0500262
263// Calling pending/receiving interface
simonf929a362022-11-18 16:53:45 -0500264export const CallingCancelButton = (props: IconButtonProps) => {
simonaccd8022022-11-24 15:04:53 -0500265 const { endCall } = useContext(CallContext);
simonf929a362022-11-18 16:53:45 -0500266
267 return (
268 <ColoredCallButton
269 aria-label="cancel call"
270 onClick={() => {
simonaccd8022022-11-24 15:04:53 -0500271 endCall();
simonf929a362022-11-18 16:53:45 -0500272 }}
273 Icon={CallEndIcon}
274 paletteColor={(theme) => theme.palette.error}
275 {...props}
276 />
277 );
simon2d3b6532022-11-08 21:01:57 -0500278};
279
simonf929a362022-11-18 16:53:45 -0500280export const CallingAnswerAudioButton = (props: IconButtonProps) => {
281 const { acceptCall } = useContext(CallContext);
282
283 return (
284 <ColoredCallButton
285 aria-label="answer call audio"
286 onClick={() => {
MichelleSS55164202022-11-25 18:36:14 -0500287 acceptCall(false);
simonf929a362022-11-18 16:53:45 -0500288 }}
289 Icon={PlaceAudioCallIcon}
290 paletteColor={(theme) => theme.palette.success}
291 {...props}
292 />
293 );
simon2d3b6532022-11-08 21:01:57 -0500294};
295
simonf929a362022-11-18 16:53:45 -0500296export const CallingAnswerVideoButton = (props: IconButtonProps) => {
297 const { acceptCall } = useContext(CallContext);
298
299 return (
300 <ColoredCallButton
301 aria-label="answer call video"
302 onClick={() => {
MichelleSS55164202022-11-25 18:36:14 -0500303 acceptCall(true);
simonf929a362022-11-18 16:53:45 -0500304 }}
305 paletteColor={(theme) => theme.palette.success}
306 Icon={VideoCameraIcon}
307 {...props}
308 />
309 );
310};
311
312export const CallingRefuseButton = (props: IconButtonProps) => {
simonaccd8022022-11-24 15:04:53 -0500313 const { endCall } = useContext(CallContext);
simonf929a362022-11-18 16:53:45 -0500314 return (
315 <ColoredCallButton
316 aria-label="refuse call"
317 onClick={() => {
simonaccd8022022-11-24 15:04:53 -0500318 endCall();
simonf929a362022-11-18 16:53:45 -0500319 }}
320 paletteColor={(theme) => theme.palette.error}
321 Icon={RoundCloseIcon}
322 {...props}
323 />
324 );
simon2d3b6532022-11-08 21:01:57 -0500325};