Messages Component
Messages React component represents Messages component.
Messages Components
There are following components included:
Messages
- main Messages containerMessage
- single message elementMessagesTitle
- single messages title element
Messages Properties
Prop | Type | Default | Description |
---|---|---|---|
<Messages> properties | |||
init | boolean | true | Initializes Messages component |
newMessagesFirst | boolean | false | Enable if you want to use new messages on top, instead of having them on bottom |
scrollMessages | boolean | true | Enable/disable messages autoscrolling when adding new message |
scrollMessagesOnEdge | boolean | true | If enabled then messages autoscrolling will happen only when user is on top/bottom of the messages view |
typing | boolean | false | Allows to display/toggle typing message indicator |
<Message> properties | |||
type | string | sent | Message type: sent (default) or received |
text | string | Message text | |
avatar | string | Message user's avatar URL | |
name | string | Message user's name | |
image | string | Message image URL | |
header | string | Message header | |
footer | string | Message footer | |
textHeader | string | Message text header | |
textFooter | string | Message text footer | |
first | boolean | false | Defines that the message is first in the conversation |
last | boolean | false | Defines that the message is last in the conversation |
tail | boolean | false | Defines that the message has visual "tail". Usually last message in conversation |
sameName | boolean | false | Defines that this message sender name is the same as on previous message |
sameHeader | boolean | false | Defines that this message header text is the same as on previous message |
sameFooter | boolean | false | Defines that this message footer text is the same as on previous message |
sameAvatar | boolean | false | Defines that this message user's avatar URL is the same as on previous message |
Messages Events
Event | Description |
---|---|
<Message> events | |
click | Event will be triggered when user clicks on message bubble |
clickName | Event will be triggered when user clicks on message user's name |
clickText | Event will be triggered when user clicks on message text |
clickAvatar | Event will be triggered when user clicks on message user's avatar |
clickHeader | Event will be triggered when user clicks on message header |
clickFooter | Event will be triggered when user clicks on message footer |
clickBubble | Event will be triggered when user clicks on message bubble |
Messages Slots
Single message React component (<Message>
) has additional slots for custom elements:
default
- element will be inserted as a child of<div class="message-bubble">
element in the endstart
- element will be inserted in the beginning and direct child of main message element<div class="message">
end
- element will be inserted in the end and direct child of main message element<div class="message">
content-start
- element will be inserted in the beginning and direct child of the<div class="message-content">
elementcontent-end
- element will be inserted in the end and direct child of the<div class="message-content">
elementbubble-start
- element will be inserted in the beginning and direct child of the<div class="message-bubble">
elementbubble-end
- element will be inserted in the end and direct child of the<div class="message-bubble">
element. Same asdefault
slot
The following slots can be used inside of single message instead of same props if you need to pass there more complext layout:
header
- element will be inserted in message headerfooter
- element will be inserted in message footertext
- element will be inserted in message textname
- element will be inserted in message nameimage
- element will be inserted in message image (supposed to be an<img>
element)text-header
- element will be inserted in message text headertext-footer
- element will be inserted in message text footer
<Message
type="sent"
text="Hello World"
name="John Doe"
avatar="path/to/image.jpg"
>
<div slot="start">Start</div>
<div slot="end">End</div>
<div slot="content-start">Content Start</div>
<div slot="content-end">Content End</div>
<div slot="bubble-start">Bubble Start</div>
<div slot="bubble-end">Bubble End</div>
</Message>
{/* Renders to: */}
<div class="message message-sent">
<div>Start</div>
<div class="message-avatar" style="background-image: url(path/to/image.jpg);"></div>
<div class="message-content">
<div>Content Start</div>
<div class="message-name">John Doe</div>
<div class="message-bubble">
<div>Bubble Start</div>
<div class="message-text">Hello World</div>
<div>Bubble End</div>
</div>
<div>Content End</div>
</div>
<div>End</div>
</div>
Examples
Here is how the full example of Messages page where it can be used together with Messagebar:
messages.jsx
import React, { useEffect, useRef, useState } from 'react';
import {
Navbar,
Page,
Messages,
MessagesTitle,
Message,
Messagebar,
Link,
MessagebarAttachments,
MessagebarAttachment,
MessagebarSheet,
MessagebarSheetImage,
f7ready,
f7,
} from 'framework7-react';
export default () => {
const images = [
'https://cdn.framework7.io/placeholder/cats-300x300-1.jpg',
'https://cdn.framework7.io/placeholder/cats-200x300-2.jpg',
'https://cdn.framework7.io/placeholder/cats-400x300-3.jpg',
'https://cdn.framework7.io/placeholder/cats-300x150-4.jpg',
'https://cdn.framework7.io/placeholder/cats-150x300-5.jpg',
'https://cdn.framework7.io/placeholder/cats-300x300-6.jpg',
'https://cdn.framework7.io/placeholder/cats-300x300-7.jpg',
'https://cdn.framework7.io/placeholder/cats-200x300-8.jpg',
'https://cdn.framework7.io/placeholder/cats-400x300-9.jpg',
'https://cdn.framework7.io/placeholder/cats-300x150-10.jpg',
];
const people = [
{
name: 'Kate Johnson',
avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
},
{
name: 'Blue Ninja',
avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
},
];
const answers = [
'Yes!',
'No',
'Hm...',
'I am not sure',
'And what about you?',
'May be ;)',
'Lorem ipsum dolor sit amet, consectetur',
'What?',
'Are you sure?',
'Of course',
'Need to think about it',
'Amazing!!!',
];
const [attachments, setAttachments] = useState([]);
const [sheetVisible, setSheetVisible] = useState(false);
const [typingMessage, setTypingMessage] = useState(null);
const [messageText, setMessageText] = useState('');
const [messagesData, setMessagesData] = useState([
{
type: 'sent',
text: 'Hi, Kate',
},
{
type: 'sent',
text: 'How are you?',
},
{
name: 'Kate',
type: 'received',
text: 'Hi, I am good!',
avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
},
{
name: 'Blue Ninja',
type: 'received',
text: 'Hi there, I am also fine, thanks! And how are you?',
avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
},
{
type: 'sent',
text: 'Hey, Blue Ninja! Glad to see you ;)',
},
{
type: 'sent',
text: 'Hey, look, cutest kitten ever!',
},
{
type: 'sent',
image: 'https://cdn.framework7.io/placeholder/cats-200x260-4.jpg',
},
{
name: 'Kate',
type: 'received',
text: 'Nice!',
avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
},
{
name: 'Kate',
type: 'received',
text: 'Like it very much!',
avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
},
{
name: 'Blue Ninja',
type: 'received',
text: 'Awesome!',
avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
},
]);
const responseInProgress = useRef(false);
const messagebar = useRef(null);
const attachmentsVisible = () => {
return attachments.length > 0;
};
const placeholder = () => {
return attachments.length > 0 ? 'Add comment or Send' : 'Message';
};
useEffect(() => {
f7ready(() => {
messagebar.current = f7.messagebar.get('.messagebar');
});
});
const isFirstMessage = (message, index) => {
const previousMessage = messagesData[index - 1];
if (message.isTitle) return false;
if (
!previousMessage ||
previousMessage.type !== message.type ||
previousMessage.name !== message.name
)
return true;
return false;
};
const isLastMessage = (message, index) => {
const nextMessage = messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name)
return true;
return false;
};
const isTailMessage = (message, index) => {
const nextMessage = messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name)
return true;
return false;
};
const deleteAttachment = (image) => {
const index = attachments.indexOf(image);
attachments.splice(index, 1);
setAttachments([...attachments]);
};
const handleAttachment = (e) => {
const index = f7.$(e.target).parents('label.checkbox').index();
const image = images[index];
if (e.target.checked) {
// Add to attachments
attachments.unshift(image);
} else {
// Remove from attachments
attachments.splice(attachments.indexOf(image), 1);
}
setAttachments([...attachments]);
};
const sendMessage = () => {
const text = messageText.replace(/\n/g, '<br>').trim();
const messagesToSend = [];
attachments.forEach((attachment) => {
messagesToSend.push({
image: attachment,
});
});
if (text.length) {
messagesToSend.push({
text,
});
}
if (messagesToSend.length === 0) {
return;
}
setAttachments([]);
setSheetVisible(false);
setMessagesData([...messagesData, ...messagesToSend]);
setMessageText('');
// Focus area
if (text.length) messagebar.current.focus();
// Mock response
if (responseInProgress.current) return;
responseInProgress.current = true;
setTimeout(() => {
const answer = answers[Math.floor(Math.random() * answers.length)];
const person = people[Math.floor(Math.random() * people.length)];
setTypingMessage({
name: person.name,
avatar: person.avatar,
});
setTimeout(() => {
setTypingMessage(null);
setMessagesData([
...messagesData,
...messagesToSend,
{
text: answer,
type: 'received',
name: person.name,
avatar: person.avatar,
},
]);
responseInProgress.current = false;
}, 4000);
}, 1000);
};
return (
<Page>
<Navbar title="Messages"></Navbar>
<Messagebar
placeholder={placeholder()}
attachmentsVisible={attachmentsVisible()}
sheetVisible={sheetVisible}
value={messageText}
onInput={(e) => setMessageText(e.target.value)}
>
<Link
iconIos="f7:camera_fill"
iconMd="material:camera_alt"
slot="inner-start"
onClick={() => {
setSheetVisible(!sheetVisible);
}}
/>
<Link
iconIos="f7:arrow_up_circle_fill"
iconMd="material:send"
slot="inner-end"
onClick={sendMessage}
/>
<MessagebarAttachments>
{attachments.map((image, index) => (
<MessagebarAttachment
key={index}
image={image}
onAttachmentDelete={() => deleteAttachment(image)}
/>
))}
</MessagebarAttachments>
<MessagebarSheet>
{images.map((image, index) => (
<MessagebarSheetImage
key={index}
image={image}
checked={attachments.indexOf(image) >= 0}
onChange={handleAttachment}
/>
))}
</MessagebarSheet>
</Messagebar>
<Messages>
<MessagesTitle>
<b>Sunday, Feb 9,</b> 12:58
</MessagesTitle>
{messagesData.map((message, index) => (
<Message
key={index}
type={message.type}
image={message.image}
name={message.name}
avatar={message.avatar}
first={isFirstMessage(message, index)}
last={isLastMessage(message, index)}
tail={isTailMessage(message, index)}
>
{message.text && (
<span slot="text" dangerouslySetInnerHTML={{ __html: message.text }} />
)}
</Message>
))}
{typingMessage && (
<Message
type="received"
typing={true}
first={true}
last={true}
tail={true}
header={`${typingMessage.name} is typing`}
avatar={typingMessage.avatar}
/>
)}
</Messages>
</Page>
);
};