Messages Component
- Messages Components
- Messages Properties
- Messages Methods
- Messages Events
- Messages Slots
- Access To Messages Instance
- Examples
Messages React component represents Messages component.
Messages Components
There are following components included:
Messages
/F7Messages
- main Messages containerMessage
/F7Message
- single message elementMessagesTitle
/F7MessagesTitle
- 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 |
<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 Methods
<Messages> methods | |
---|---|
.scroll(duration, position); | Scroll messages to top/bottom depending on newMessagesFirst parameter
|
.showTyping(message); | Show typing message indicator
|
.hideTyping() | Hide typing message indicator |
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 tp 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>
Access To Messages Instance
If you use automatic initalization to init Messages (with init={true}
prop) and need to use Messages API you can access its initialized instance by accessing .f7Messages
component's property.
Examples
Here is how the full example of Messages page where it can be used together with Messagebar:
export default class extends React.Component {
constructor(props) {
super(props);
this.state = {
attachments: [],
sheetVisible: false,
typingMessage: null,
messagesData: [
{
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',
},
],
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',
],
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',
},
],
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!!!',
],
responseInProgress: false,
}
}
render() {
return (
<Page>
<Navbar title="Messages"></Navbar>
<Messagebar
placeholder={this.placeholder}
ref={(el) => {this.messagebarComponent = el}}
attachmentsVisible={this.attachmentsVisible}
sheetVisible={this.state.sheetVisible}
>
<Link
iconIos="f7:camera_fill"
iconAurora="f7:camera_fill"
iconMd="material:camera_alt"
slot="inner-start"
onClick={() => {this.setState({sheetVisible: !this.state.sheetVisible})}}
></Link>
<Link
iconIos="f7:arrow_up_circle_fill"
iconAurora="f7:arrow_up_circle_fill"
iconMd="material:send"
slot="inner-end"
onClick={this.sendMessage.bind(this)}
></Link>
<MessagebarAttachments>
{this.state.attachments.map((image, index) => (
<MessagebarAttachment
key={index}
image={image}
onAttachmentDelete={() => this.deleteAttachment(image)}
></MessagebarAttachment>
))}
</MessagebarAttachments>
<MessagebarSheet>
{this.state.images.map((image, index) => (
<MessagebarSheetImage
key={index}
image={image}
checked={this.state.attachments.indexOf(image) >= 0}
onChange={this.handleAttachment.bind(this)}
></MessagebarSheetImage>
))}
</MessagebarSheet>
</Messagebar>
<Messages ref={(el) => {this.messagesComponent = el}}>
<MessagesTitle><b>Sunday, Feb 9,</b> 12:58</MessagesTitle>
{this.state.messagesData.map((message, index) => (
<Message
key={index}
type={message.type}
image={message.image}
name={message.name}
avatar={message.avatar}
first={this.isFirstMessage(message, index)}
last={this.isLastMessage(message, index)}
tail={this.isTailMessage(message, index)}
>
{message.text && (
<span slot="text" dangerouslySetInnerHTML={{__html: message.text}} />
)}
</Message>
))}
{this.state.typingMessage && (
<Message
type="received"
typing={true}
first={true}
last={true}
tail={true}
header={`${this.state.typingMessage.name} is typing`}
avatar={this.state.typingMessage.avatar}
></Message>
)}
</Messages>
</Page>
)
}
get attachmentsVisible() {
const self = this;
return self.state.attachments.length > 0;
}
get placeholder() {
const self = this;
return self.state.attachments.length > 0 ? 'Add comment or Send' : 'Message';
}
componentDidMount() {
const self = this;
self.$f7ready(() => {
self.messagebar = self.messagebarComponent.f7Messagebar;
self.messages = self.messagesComponent.f7Messages;
});
}
isFirstMessage(message, index) {
const self = this;
const previousMessage = self.state.messagesData[index - 1];
if (message.isTitle) return false;
if (!previousMessage || previousMessage.type !== message.type || previousMessage.name !== message.name) return true;
return false;
}
isLastMessage(message, index) {
const self = this;
const nextMessage = self.state.messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
return false;
}
isTailMessage(message, index) {
const self = this;
const nextMessage = self.state.messagesData[index + 1];
if (message.isTitle) return false;
if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
return false;
}
deleteAttachment(image) {
const self = this;
const attachments = self.state.attachments;
const index = attachments.indexOf(image);
attachments.splice(index, 1);
self.setState({ attachments });
}
handleAttachment(e) {
const self = this;
const attachments = self.state.attachments;
const index = self.$$(e.target).parents('label.checkbox').index();
const image = self.state.images[index];
if (e.target.checked) {
// Add to attachments
attachments.unshift(image);
} else {
// Remove from attachments
attachments.splice(attachments.indexOf(image), 1);
}
self.setState({ attachments });
}
sendMessage() {
const self = this;
const text = self.messagebar.getValue().replace(/\n/g, '<br>').trim();
const messagesToSend = [];
self.state.attachments.forEach((attachment) => {
messagesToSend.push({
image: attachment,
});
});
if (text.trim().length) {
messagesToSend.push({
text,
});
}
if (messagesToSend.length === 0) {
return;
}
self.setState({
// Reset attachments
attachments: [],
// Hide sheet
sheetVisible: false,
// Send message
messagesData: [...self.state.messagesData, ...messagesToSend],
});
self.messagebar.clear();
// Focus area
if (text.length) self.messagebar.focus();
// Mock response
if (self.state.responseInProgress) return;
self.setState({
responseInProgress: true,
})
setTimeout(() => {
const answer = self.state.answers[Math.floor(Math.random() * self.state.answers.length)];
const person = self.state.people[Math.floor(Math.random() * self.state.people.length)];
self.setState({
typingMessage: {
name: person.name,
avatar: person.avatar,
},
});
setTimeout(() => {
self.setState({
messagesData: [...self.state.messagesData, {
text: answer,
type: 'received',
name: person.name,
avatar: person.avatar,
}],
typingMessage: null,
responseInProgress: false,
});
}, 4000);
}, 1000);
}
};