add 'Scroll to the end of the conversation' bar
Change-Id: I63e41433db4999d021bc13375cf5b7ba07843c3d
diff --git a/client/src/components/ConversationView.tsx b/client/src/components/ConversationView.tsx
index a269c89..1242541 100644
--- a/client/src/components/ConversationView.tsx
+++ b/client/src/components/ConversationView.tsx
@@ -101,31 +101,25 @@
return (
<Stack height="100%">
- <Stack padding="16px">
- <ConversationHeader
- account={account}
- members={conversation.getMembers()}
- adminTitle={conversation.infos.title as string}
- conversationId={conversationId}
- />
- </Stack>
+ <ConversationHeader
+ account={account}
+ members={conversation.getMembers()}
+ adminTitle={conversation.infos.title as string}
+ conversationId={conversationId}
+ />
<Divider
sx={{
borderTop: '1px solid #E5E5E5',
}}
/>
- <Stack flex={1} overflow="auto" direction="column-reverse" padding="0px 16px">
- <MessageList account={account} members={conversation.getMembers()} messages={messages} />
- </Stack>
+ <MessageList account={account} members={conversation.getMembers()} messages={messages} />
<Divider
sx={{
margin: '30px 16px 0px 16px',
borderTop: '1px solid #E5E5E5',
}}
/>
- <Stack padding="16px">
- <SendMessageForm account={account} members={conversation.getMembers()} onSend={sendMessage} />
- </Stack>
+ <SendMessageForm account={account} members={conversation.getMembers()} onSend={sendMessage} />
</Stack>
);
};
@@ -173,7 +167,7 @@
};
return (
- <Stack direction="row">
+ <Stack direction="row" padding="16px">
<Stack flex={1} justifyContent="center" whiteSpace="nowrap" overflow="hidden">
<Typography variant="h3" textOverflow="ellipsis">
{title}
diff --git a/client/src/components/MessageList.tsx b/client/src/components/MessageList.tsx
index 6e5e233..7b9bf2d 100644
--- a/client/src/components/MessageList.tsx
+++ b/client/src/components/MessageList.tsx
@@ -15,10 +15,15 @@
* License along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
-import { Stack } from '@mui/system';
+import { Typography } from '@mui/material';
+import { Box, Stack } from '@mui/system';
import { Account, ConversationMember, Message } from 'jami-web-common';
+import { MutableRefObject, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Waypoint } from 'react-waypoint';
import { MessageRow } from './Message';
+import { ArrowDownIcon } from './SvgIcon';
interface MessageListProps {
account: Account;
@@ -27,31 +32,82 @@
}
export default function MessageList({ account, members, messages }: MessageListProps) {
+ const [showScrollButton, setShowScrollButton] = useState(false);
+ const listBottomRef = useRef<HTMLElement>();
+
return (
- <Stack direction="column-reverse">
- {
- // most recent messages first
- messages.map((message, index) => {
- const isAccountMessage = message.author === account.getUri();
- let author;
- if (isAccountMessage) {
- author = account;
- } else {
- const member = members.find((member) => message.author === member.contact.getUri());
- author = member?.contact;
+ <>
+ <Stack flex={1} overflow="auto" padding="0px 16px" direction="column-reverse">
+ {/* Here is the bottom of the list of messages because of 'column-reverse' */}
+ <Box ref={listBottomRef} />
+ <Waypoint
+ onEnter={() => setShowScrollButton(false)}
+ onLeave={() => setShowScrollButton(true)}
+ bottomOffset="-100px"
+ />
+ <Stack direction="column-reverse">
+ {
+ // most recent messages first
+ messages.map((message, index) => {
+ const isAccountMessage = message.author === account.getUri();
+ let author;
+ if (isAccountMessage) {
+ author = account;
+ } else {
+ const member = members.find((member) => message.author === member.contact.getUri());
+ author = member?.contact;
+ }
+ if (!author) {
+ return null;
+ }
+ const props = {
+ messageIndex: index,
+ messages,
+ isAccountMessage,
+ author,
+ };
+ return <MessageRow key={message.id} {...props} />;
+ })
}
- if (!author) {
- return null;
- }
- const props = {
- messageIndex: index,
- messages,
- isAccountMessage,
- author,
- };
- return <MessageRow key={message.id} {...props} />;
- })
- }
- </Stack>
+ </Stack>
+ <Waypoint onEnter={() => console.log('should load more messages')} topOffset="-200px" />
+ </Stack>
+ {showScrollButton && (
+ <Box position="relative">
+ <Box position="absolute" bottom="10px" left="50%" sx={{ transform: 'translate(-50%)' }}>
+ <ScrollToEndButton listBottomRef={listBottomRef} />
+ </Box>
+ </Box>
+ )}
+ </>
);
}
+
+interface ScrollToEndButtonProps {
+ listBottomRef: MutableRefObject<HTMLElement | undefined>;
+}
+
+const ScrollToEndButton = ({ listBottomRef }: ScrollToEndButtonProps) => {
+ const { t } = useTranslation();
+ const textColor = 'white';
+ return (
+ <Stack
+ direction="row"
+ borderRadius="5px"
+ height="30px"
+ alignItems="center"
+ padding="0 16px"
+ spacing="12px"
+ sx={{
+ backgroundColor: '#005699', // Should be same color as message bubble
+ cursor: 'pointer',
+ }}
+ onClick={() => listBottomRef.current?.scrollIntoView({ behavior: 'smooth' })}
+ >
+ <ArrowDownIcon sx={{ fontSize: '12px', color: textColor }} />
+ <Typography variant="caption" fontWeight="bold" color={textColor}>
+ {t('messages_scroll_to_end')}
+ </Typography>
+ </Stack>
+ );
+};
diff --git a/client/src/components/SendMessageForm.tsx b/client/src/components/SendMessageForm.tsx
index 7230dd0..f003aae 100644
--- a/client/src/components/SendMessageForm.tsx
+++ b/client/src/components/SendMessageForm.tsx
@@ -55,7 +55,7 @@
);
return (
- <Stack component="form" onSubmit={handleSubmit} direction="row" alignItems="center" spacing="20px">
+ <Stack component="form" onSubmit={handleSubmit} direction="row" alignItems="center" spacing="20px" padding="16px">
<UploadFileButton />
<RecordVoiceMessageButton />
<RecordVideoMessageButton />
diff --git a/client/src/components/SvgIcon.tsx b/client/src/components/SvgIcon.tsx
index 9e560dc..0b8b9ed 100644
--- a/client/src/components/SvgIcon.tsx
+++ b/client/src/components/SvgIcon.tsx
@@ -64,6 +64,17 @@
);
};
+export const ArrowDownIcon = (props: SvgIconProps) => {
+ return (
+ <SvgIcon {...props} viewBox="0 0 8 11.607">
+ <path
+ fillRule="evenodd"
+ d="m4.353 11.43 3.441-3.5.058-.058a.564.564 0 0 0-.058-.816.62.62 0 0 0-.816.058l-2.45 2.508V.583A.551.551 0 0 0 3.945 0a.551.551 0 0 0-.581.583v9.039L.912 7.114a.613.613 0 0 0-.758 0 .621.621 0 0 0 0 .816l3.441 3.5a.452.452 0 0 0 .583.117c.058 0 .117-.058.175-.117"
+ />
+ </SvgIcon>
+ );
+};
+
export const ArrowLeftCurved = (props: SvgIconProps) => {
return (
<SvgIcon {...props} viewBox="0 0 16 8.814">
diff --git a/client/src/locale/en/translation.json b/client/src/locale/en/translation.json
index bef16ed..049bc55 100644
--- a/client/src/locale/en/translation.json
+++ b/client/src/locale/en/translation.json
@@ -26,6 +26,8 @@
"message_input_placeholder_four": "Write to {{member0}}, {{member1}}, {{member2}}, +1 other member",
"message_input_placeholder_more": "Write to {{member0}}, {{member1}}, {{member2}}, +{{excess}} other members",
+ "messages_scroll_to_end": "Scroll to the end of the conversation",
+
"username_input_default_helper_text": "",
"username_input_success_helper_text": "Username available",
"username_input_taken_helper_text": "Username already taken",
diff --git a/client/src/locale/en/translation_old.json b/client/src/locale/en/translation_old.json
new file mode 100644
index 0000000..a1a3e9a
--- /dev/null
+++ b/client/src/locale/en/translation_old.json
@@ -0,0 +1,15 @@
+{
+ "password_input_default_helper_text": "",
+ "password_input_medium_helper_text": "Medium",
+ "password_input_registration_failed_helper_text": "Choose another password!",
+ "password_input_strong_helper_text": "Strong",
+ "password_input_too_weak_helper_text": "Too weak",
+ "password_input_weak_helper_text": "Weak",
+ "severity_error": "Error",
+ "severity_success": "Success",
+ "username_input_default_helper_text": "",
+ "username_input_invalid_helper_text": "Username doesn't follow required pattern",
+ "username_input_registration_failed_helper_text": "Username not correct!",
+ "username_input_success_helper_text": "Username available",
+ "username_input_taken_helper_text": "Username already taken"
+}
diff --git a/client/src/locale/fr/translation.json b/client/src/locale/fr/translation.json
index 97a1cf6..346f0aa 100644
--- a/client/src/locale/fr/translation.json
+++ b/client/src/locale/fr/translation.json
@@ -24,7 +24,9 @@
"message_input_placeholder_two": "Écrire à {{member0}} et {{member1}}",
"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",
+ "message_input_placeholder_more": "Écrire à {{member0}}, {{member1}}, {{member2}}, +{{excess}} autres membres",
+
+ "messages_scroll_to_end": "Faire défiler jusqu'à la fin de la conversation",
"share_screen": "Partager votre écran",
"share_screen_area": "Partager une partie de l'écran",