Add pending and pending receiving user interfaces
GitLab: #101
GitLab: #102
Change-Id: I202372a92246c72225bb1fe277147fe5ee2f9981
diff --git a/client/src/components/CallButtons.tsx b/client/src/components/CallButtons.tsx
index 2392c00..79d5485 100644
--- a/client/src/components/CallButtons.tsx
+++ b/client/src/components/CallButtons.tsx
@@ -32,7 +32,9 @@
MicroIcon,
MicroOffIcon,
MoreVerticalIcon,
+ PlaceAudioCallIcon,
RecordingIcon,
+ RoundCloseIcon,
ScreenShareArrowIcon,
ScreenShareRegularIcon,
ScreenShareScreenAreaIcon,
@@ -42,23 +44,33 @@
WindowIcon,
} from './SvgIcon';
+type ColoredCallButtonColor = 'red' | 'green';
+
const CallButton = styled((props: ExpandableButtonProps) => {
return <ExpandableButton {...props} />;
})({
+ color: 'white',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.15)',
},
- color: 'white',
});
-const ColoredCallButton = styled((props: ExpandableButtonProps) => {
- return <ExpandableButton {...props} />;
-})({
- color: 'white',
- backgroundColor: '#a30000',
- '&:hover': {
- backgroundColor: '#ff0000',
- },
+const ColoredCallButton = styled(
+ ({
+ ...props
+ }: ExpandableButtonProps & {
+ buttonColor: ColoredCallButtonColor;
+ }) => {
+ return <ExpandableButton {...props} />;
+ }
+)(({ buttonColor }) => {
+ return {
+ color: 'white',
+ backgroundColor: buttonColor === 'green' ? '#183722' : '#5E070D',
+ '&:hover': {
+ backgroundColor: buttonColor === 'green' ? '#0B8271' : '#CC0022',
+ },
+ };
});
export const CallingChatButton = (props: ExpandableButtonProps) => {
@@ -66,7 +78,7 @@
};
export const CallingEndButton = (props: ExpandableButtonProps) => {
- return <ColoredCallButton sx={{}} aria-label="call end" Icon={CallEndIcon} {...props} />;
+ return <ColoredCallButton buttonColor="red" aria-label="call end" Icon={CallEndIcon} {...props} />;
};
export const CallingExtensionButton = (props: ExpandableButtonProps) => {
@@ -159,7 +171,6 @@
export const CallingVideoCameraButton = (props: ExpandableButtonProps) => {
const { isVideoOn, setVideoStatus } = useContext(WebRTCContext);
-
return (
<CallButton
aria-label="camera options"
@@ -176,3 +187,16 @@
/>
);
};
+
+// Calling pending/receiving interface
+export const CallingAnswerAudioButton = (props: ExpandableButtonProps) => {
+ return <ColoredCallButton aria-label="answer audio" buttonColor="green" Icon={PlaceAudioCallIcon} {...props} />;
+};
+
+export const CallingAnswerVideoButton = (props: ExpandableButtonProps) => {
+ return <ColoredCallButton aria-label="answer video" buttonColor="green" Icon={VideoCameraIcon} {...props} />;
+};
+
+export const CallingRefuseButton = (props: ExpandableButtonProps) => {
+ return <ColoredCallButton aria-label="reject" buttonColor="red" Icon={RoundCloseIcon} {...props} />;
+};
diff --git a/client/src/components/ConversationListItem.tsx b/client/src/components/ConversationListItem.tsx
index 9fa1acc..91dd0f1 100644
--- a/client/src/components/ConversationListItem.tsx
+++ b/client/src/components/ConversationListItem.tsx
@@ -147,7 +147,7 @@
closeModalDelete();
};
- const uri = conversation.getId() ? `/conversation/${conversation.getId()}` : `/addContact/${userId}`;
+ const uri = conversation.getId() ? `/conversation/${conversation.getId()}` : `/add-contact/${userId}`;
return (
<div onContextMenu={openMenu}>
<div>
diff --git a/client/src/components/SvgIcon.tsx b/client/src/components/SvgIcon.tsx
index 88423b1..8f83587 100644
--- a/client/src/components/SvgIcon.tsx
+++ b/client/src/components/SvgIcon.tsx
@@ -552,6 +552,23 @@
);
};
+export const PlaceAudioCallIcon = (props: SvgIconProps) => {
+ return (
+ <SvgIcon {...props} viewBox="0 0 24 24">
+ <g id="Icons_Outline" stroke="#ffffff" strokeWidth="1.75" fill="none" fillRule="evenodd">
+ <g id="Phone">
+ <g id="Ico_TEL" transform="translate(3.000000, 3.000000)">
+ <path
+ d="M2.08318114,0.702801822 C3.47221232,-0.375546383 4.67183016,-0.185249641 5.24007015,1.08339531 C6.37655021,3.36695621 7.19734136,4.50873667 6.37655021,5.39678812 C4.86124348,6.41170409 3.78790122,6.91916206 4.35614125,7.99751029 C5.49262131,10.7250969 7.70244359,13.0720901 10.3542304,14.4675995 C11.3644349,15.101922 11.9326749,14.0235738 13.0691549,12.6280643 C14.0162216,11.8668773 15.0895639,12.6914966 17.2993862,14.0235738 C18.4990041,14.7213285 18.5621418,15.8631089 17.4256618,17.1951861 C10.7961949,24.6801913 -5.80904142,6.85572983 2.08318114,0.702801822"
+ id="Path"
+ ></path>
+ </g>
+ </g>
+ </g>
+ </SvgIcon>
+ );
+};
+
export const RecordingIcon = (props: SvgIconProps) => {
return (
<SvgIcon {...props} viewBox="0 0 24 24">
@@ -588,6 +605,15 @@
);
};
+export const RoundCloseIcon = (props: SvgIconProps) => {
+ return (
+ <SvgIcon {...props} viewBox="0 0 24 24">
+ <path fill="none" stroke="none" d="M0 0h24v24H0V0z" />
+ <path d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z" />
+ </SvgIcon>
+ );
+};
+
export const RoundCrossIcon = (props: SvgIconProps) => {
return (
<SvgIcon {...props} viewBox="0 0 16 16">
@@ -763,7 +789,7 @@
export const VideoCameraOffIcon = (props: SvgIconProps) => {
return (
<SvgIcon {...props} viewBox="0 0 24 24">
- <g id="Icones_Outline" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
+ <g id="Icons_Outline" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<g id="Camera-Off" stroke="#ffffff" strokeWidth="1.5">
<line x1="4.5" y1="18.5" x2="15.5" y2="6.5" id="Line" strokeLinecap="round"></line>
<g id="Ico_Camera" transform="translate(3.000000, 5.000000)" fillRule="nonzero" strokeLinejoin="round">
diff --git a/client/src/locale/en/translation.json b/client/src/locale/en/translation.json
index bd21509..e797090 100644
--- a/client/src/locale/en/translation.json
+++ b/client/src/locale/en/translation.json
@@ -1,7 +1,7 @@
{
"severity": "",
"share_screen": "Share your screen",
- "share_window": "",
+ "share_window": "Share window",
"share_screen_area": "Share an area of your screen",
"share_file": "Share a file",
"dummy_option_string": "Test option",
@@ -54,8 +54,12 @@
"message_input_placeholder_two": "Write to {{member0}} and {{member1}}",
"message_input_placeholder_three": "Write to {{member0}}, {{member1}} and {{member2}}",
"message_input_placeholder_four": "Write to {{member0}}, {{member1}}, {{member2}}, +1 other member",
- "message_input_placeholder_more": "Write to {{member0}}, {{member1}}, {{member2}}, +{{excess}} other members",
- "conversation_add_contact": "Add contact",
+ "message_input_placeholder_more": "Write to {{member01}}, {{member1}}, {{member2}}, +{{excess}} other members",
+ "conversation_add_contact": "",
+ "calling": "Calling...",
+ "connecting": "Connecting...",
+ "incoming_call_{medium}": "",
+ "end_call": "End call",
"login_username_not_found": "Username not found",
"login_invalid_password": "Incorrect password",
"login_form_title": "LOGIN",
@@ -71,14 +75,14 @@
"registration_form_submit_button": "REGISTER",
"registration_form_to_login_text": "Already have an account?",
"registration_form_to_login_link": "LOG IN",
- "logout": "LOGOUT",
- "setup_login_title": "Jami web node setup",
- "setup_login_welcome": "Welcome to the Jami web node setup.",
- "setup_login_admin_creation": "Let's start by creating a new administrator account to control access to the server configuration.",
- "password_placeholder": "Password",
- "setup_login_password_placeholder_creation": "New password",
- "setup_login_password_placeholder_repeat": "Repeat password",
- "admin_creation_submit_button": "CREATE ADMIN ACCOUNT",
+ "logout": "",
+ "setup_login_title": "",
+ "setup_login_welcome": "",
+ "setup_login_admin_creation": "",
+ "password_placeholder": "",
+ "setup_login_password_placeholder_creation": "",
+ "setup_login_password_placeholder_repeat": "",
+ "admin_creation_submit_button": "",
"severity_error": "Error",
"severity_success": "Success"
}
diff --git a/client/src/locale/fr/translation.json b/client/src/locale/fr/translation.json
index e61c57a..90818e5 100644
--- a/client/src/locale/fr/translation.json
+++ b/client/src/locale/fr/translation.json
@@ -1,7 +1,7 @@
{
"severity": "",
"share_screen": "Partager votre écran",
- "share_window": "",
+ "share_window": "Partager la fenêtre",
"share_screen_area": "Partager une partie de l'écran",
"share_file": "Partager le fichier",
"dummy_option_string": "Option test",
@@ -55,7 +55,11 @@
"message_input_placeholder_three": "Écrire à {{member0}}, {{member1}} et {{member2}}",
"message_input_placeholder_four": "Écrire à {{member0}}, {{member1}}, {{member2}}, +1 autre membre",
"message_input_placeholder_more": "Écrire à {{member01}}, {{member1}}, {{member2}}, +{{excess}} autres membres",
- "conversation_add_contact": "Ajouter le contact",
+ "conversation_add_contact": "",
+ "calling": "Appel en cours...",
+ "connecting": "Connexion en cours...",
+ "incoming_call_{medium}": "",
+ "end_call": "Fin d'appel",
"login_username_not_found": "Nom d'utilisateur introuvable",
"login_invalid_password": "Mot de passe incorrect",
"login_form_title": "CONNEXION",
@@ -71,14 +75,14 @@
"registration_form_submit_button": "S'INSCRIRE",
"registration_form_to_login_text": "Déjà inscrit?",
"registration_form_to_login_link": "SE CONNECTER",
- "logout": "SE DÉCONNECTER",
- "setup_login_title": "Configuration du noeud web Jami",
- "setup_login_welcome": "Bienvenue à la configuration du noeud web Jami.",
- "setup_login_admin_creation": "Commençons par créer un nouveau compte administrateur pour contrôler l'accès à la configuration du serveur.",
- "password_placeholder": "Mot de passe",
- "setup_login_password_placeholder_creation": "Nouveau mot de passe",
- "setup_login_password_placeholder_repeat": "Répéter le mot de passe",
- "admin_creation_submit_button": "CRÉER UN COMPTE ADMIN",
+ "logout": "",
+ "setup_login_title": "",
+ "setup_login_welcome": "",
+ "setup_login_admin_creation": "",
+ "password_placeholder": "",
+ "setup_login_password_placeholder_creation": "",
+ "setup_login_password_placeholder_repeat": "",
+ "admin_creation_submit_button": "",
"severity_error": "Erreur",
"severity_success": "Succès"
}
diff --git a/client/src/pages/CallPending.tsx b/client/src/pages/CallPending.tsx
new file mode 100644
index 0000000..27d28e5
--- /dev/null
+++ b/client/src/pages/CallPending.tsx
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+import { Box, CircularProgress, Grid, Stack, Typography } from '@mui/material';
+import { Trans } from 'react-i18next';
+
+import {
+ CallingAnswerAudioButton,
+ CallingAnswerVideoButton,
+ CallingEndButton,
+ CallingRefuseButton,
+} from '../components/CallButtons';
+
+export type CallPendingProps = {
+ pending: PendingStatus;
+ caller?: CallerStatus;
+ medium?: CommunicationMedium;
+};
+
+type PendingStatus = 'caller' | 'receiver';
+type CallerStatus = 'calling' | 'connecting';
+type CommunicationMedium = 'audio' | 'video';
+
+const RECEIVER_BUTTONS = [
+ {
+ ButtonComponent: CallingRefuseButton,
+ translationKey: 'refuse_call',
+ },
+ {
+ ButtonComponent: CallingAnswerAudioButton,
+ translationKey: 'accept_call_audio',
+ },
+ {
+ ButtonComponent: CallingAnswerVideoButton,
+ translationKey: 'accept_call_video',
+ },
+];
+
+export const CallPending = (props: CallPendingProps) => {
+ return (
+ <Stack
+ direction="column"
+ justifyContent="center"
+ alignItems="center"
+ height="100%"
+ spacing={4}
+ sx={{
+ backgroundColor: 'black',
+ }}
+ >
+ <Box
+ sx={{
+ position: 'relative',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: '100%',
+ height: '30%',
+ }}
+ >
+ <Box
+ sx={{
+ aspectRatio: '1',
+ height: '100%',
+ position: 'absolute',
+ }}
+ >
+ <CircularProgress
+ disableShrink
+ thickness={1}
+ size="100%"
+ sx={{
+ position: 'absolute',
+ color: 'white',
+ zIndex: 1,
+ }}
+ />
+ <img
+ // TODO: Insert incoming caller icon here
+ style={{
+ position: 'absolute',
+ objectFit: 'cover',
+ width: '100%',
+ height: '100%',
+ maxWidth: '100%',
+ borderRadius: '50%',
+ aspectRatio: '1',
+ }}
+ />
+ </Box>
+ </Box>
+ {props.pending === 'caller' ? (
+ <CallPendingCallerInterface {...props} />
+ ) : (
+ <CallPendingReceiverInterface {...props} />
+ )}
+ </Stack>
+ );
+};
+
+export const CallPendingCallerInterface = ({ caller }: CallPendingProps) => {
+ // TODO: Remove the dummy name
+ const defaultName = 'Alex Thérieur';
+ return (
+ <Stack textAlign="center" spacing={2}>
+ <Typography variant="h1" color="white">
+ {defaultName}
+ </Typography>
+ <Typography variant="h3" color="white">
+ {caller === 'calling' ? <Trans i18nKey="calling" /> : <Trans i18nKey="connecting" />}
+ </Typography>
+ <CallerButtons />
+ </Stack>
+ );
+};
+
+export const CallPendingReceiverInterface = ({ medium }: CallPendingProps) => {
+ // TODO: Remove the dummy name
+ const defaultName = 'Alain Thérieur';
+ return (
+ <Stack textAlign="center" spacing={2}>
+ <Typography variant="h1" color="white">
+ <Trans i18nKey="incoming_call" context={medium} values={{ member0: defaultName }} />
+ </Typography>
+ <ReceiverButtons />
+ </Stack>
+ );
+};
+
+const CallerButtons = () => {
+ return (
+ <Stack textAlign="center" spacing={1}>
+ <CallingEndButton size="large" />
+ <Typography variant="body2" color="white">
+ <Trans i18nKey="end_call" />
+ </Typography>
+ </Stack>
+ );
+};
+
+const ReceiverButtons = () => {
+ return (
+ <Grid container direction="row" padding={2}>
+ {RECEIVER_BUTTONS.map(({ ButtonComponent, translationKey }, i) => (
+ <Grid item xs={4} key={i}>
+ <Stack spacing={1}>
+ <ButtonComponent color="inherit" size="large" />
+ <Typography variant="body2" color="white" textAlign="center" sx={{ opacity: 0.75 }}>
+ <Trans i18nKey={'' + translationKey} />
+ </Typography>
+ </Stack>
+ </Grid>
+ ))}
+ </Grid>
+ );
+};
diff --git a/client/src/router.tsx b/client/src/router.tsx
index f378a37..b372099 100644
--- a/client/src/router.tsx
+++ b/client/src/router.tsx
@@ -49,7 +49,7 @@
</AuthProvider>
}
>
- <Route path="addContact/:contactId" element={<Messenger />} />
+ <Route path="add-contact/:contactId" element={<Messenger />} />
<Route path="conversation/:conversationId" element={<Messenger />} />
<Route path="call/:conversationId" element={<CallInterface />} />
<Route path="settings" element={<AccountSettings />} />