
Messages Component
Messages React component represents Messages component.
Messages Components
There are following components included:
**Messages**/**F7Messages**- main Messages container**Message**/**F7Message**- single message element**MessagesTitle**/**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 - duration - number scroll duration in ms - position - number scroll position in px |
| .showTyping(message); | Show typing message indicator - message - object - parameters of message to add |
| .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 end**start**- 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">element**content-end**- element will be inserted in the end and direct child of the<div class="message-content">element**bubble-start**- element will be inserted in the beginning and direct child of the<div class="message-bubble">element**bubble-end**- element will be inserted in the end and direct child of the<div class="message-bubble">element. Same as**default**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 header**footer**- element will be inserted in message footer**text**- element will be inserted in message text**name**- element will be inserted in message name**image**- element will be inserted in message image (supposed tp be an<img>element)**text-header**- element will be inserted in message text header**text-footer**- element will be inserted in message text footer
1. <Message
2. type="sent"
3. text="Hello World"
4. name="John Doe"
5. avatar="path/to/image.jpg"
6. >
7. <div slot="start">Start</div>
8. <div slot="end">End</div>
9. <div slot="content-start">Content Start</div>
10. <div slot="content-end">Content End</div>
11. <div slot="bubble-start">Bubble Start</div>
12. <div slot="bubble-end">Bubble End</div>
13. </Message>
15. {/* Renders to: */}
17. <div class="message message-sent">
18. <div>Start</div>
19. <div class="message-avatar" style="background-image: url(path/to/image.jpg);"></div>
20. <div class="message-content">
21. <div>Content Start</div>
22. <div class="message-name">John Doe</div>
23. <div class="message-bubble">
24. <div>Bubble Start</div>
25. <div class="message-text">Hello World</div>
26. <div>Bubble End</div>
27. </div>
28. <div>Content End</div>
29. </div>
30. <div>End</div>
31. </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:
1. export default class extends React.Component {
2. constructor(props) {
3. super(props);
5. this.state = {
6. attachments: [],
7. sheetVisible: false,
8. typingMessage: null,
9. messagesData: [
10. {
11. type: 'sent',
12. text: 'Hi, Kate',
13. },
14. {
15. type: 'sent',
16. text: 'How are you?',
17. },
18. {
19. name: 'Kate',
20. type: 'received',
21. text: 'Hi, I am good!',
22. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
23. },
24. {
25. name: 'Blue Ninja',
26. type: 'received',
27. text: 'Hi there, I am also fine, thanks! And how are you?',
28. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
29. },
30. {
31. type: 'sent',
32. text: 'Hey, Blue Ninja! Glad to see you ;)',
33. },
34. {
35. type: 'sent',
36. text: 'Hey, look, cutest kitten ever!',
37. },
38. {
39. type: 'sent',
40. image: 'https://cdn.framework7.io/placeholder/cats-200x260-4.jpg',
42. },
43. {
44. name: 'Kate',
45. type: 'received',
46. text: 'Nice!',
47. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
48. },
49. {
50. name: 'Kate',
51. type: 'received',
52. text: 'Like it very much!',
53. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
54. },
55. {
56. name: 'Blue Ninja',
57. type: 'received',
58. text: 'Awesome!',
59. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
60. },
61. ],
62. images: [
63. 'https://cdn.framework7.io/placeholder/cats-300x300-1.jpg',
64. 'https://cdn.framework7.io/placeholder/cats-200x300-2.jpg',
65. 'https://cdn.framework7.io/placeholder/cats-400x300-3.jpg',
66. 'https://cdn.framework7.io/placeholder/cats-300x150-4.jpg',
67. 'https://cdn.framework7.io/placeholder/cats-150x300-5.jpg',
68. 'https://cdn.framework7.io/placeholder/cats-300x300-6.jpg',
69. 'https://cdn.framework7.io/placeholder/cats-300x300-7.jpg',
70. 'https://cdn.framework7.io/placeholder/cats-200x300-8.jpg',
71. 'https://cdn.framework7.io/placeholder/cats-400x300-9.jpg',
72. 'https://cdn.framework7.io/placeholder/cats-300x150-10.jpg',
73. ],
74. people: [
75. {
76. name: 'Kate Johnson',
77. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-9.jpg',
78. },
79. {
80. name: 'Blue Ninja',
81. avatar: 'https://cdn.framework7.io/placeholder/people-100x100-7.jpg',
82. },
83. ],
84. answers: [
85. 'Yes!',
86. 'No',
87. 'Hm...',
88. 'I am not sure',
89. 'And what about you?',
90. 'May be ;)',
91. 'Lorem ipsum dolor sit amet, consectetur',
92. 'What?',
93. 'Are you sure?',
94. 'Of course',
95. 'Need to think about it',
96. 'Amazing!!!',
97. ],
98. responseInProgress: false,
99. }
100. }
101. render() {
102. return (
103. <Page>
104. <Navbar title="Messsages"></Navbar>
106. <Messagebar
107. placeholder={this.placeholder}
108. ref={(el) => {this.messagebarComponent = el}}
109. attachmentsVisible={this.attachmentsVisible}
110. sheetVisible={this.state.sheetVisible}
111. >
112. <Link
113. iconIos="f7:camera_fill"
114. iconAurora="f7:camera_fill"
115. iconMd="material:camera_alt"
116. slot="inner-start"
117. onClick={() => {this.setState({sheetVisible: !this.state.sheetVisible})}}
118. ></Link>
119. <Link
120. iconIos="f7:arrow_up_fill"
121. iconAurora="f7:arrow_up_fill"
122. iconMd="material:send"
123. slot="inner-end"
124. onClick={this.sendMessage.bind(this)}
125. ></Link>
126. <MessagebarAttachments>
127. {this.state.attachments.map((image, index) => (
128. <MessagebarAttachment
129. key={index}
130. image={image}
131. onAttachmentDelete={() => this.deleteAttachment(image)}
132. ></MessagebarAttachment>
133. ))}
134. </MessagebarAttachments>
135. <MessagebarSheet>
136. {this.state.images.map((image, index) => (
137. <MessagebarSheetImage
138. key={index}
139. image={image}
140. checked={this.state.attachments.indexOf(image) >= 0}
141. onChange={this.handleAttachment.bind(this)}
142. ></MessagebarSheetImage>
143. ))}
144. </MessagebarSheet>
145. </Messagebar>
147. <Messages ref={(el) => {this.messagesComponent = el}}>
148. <MessagesTitle><b>Sunday, Feb 9,</b> 12:58</MessagesTitle>
150. {this.state.messagesData.map((message, index) => (
151. <Message
152. key={index}
153. type={message.type}
154. image={message.image}
155. name={message.name}
156. avatar={message.avatar}
157. first={this.isFirstMessage(message, index)}
158. last={this.isLastMessage(message, index)}
159. tail={this.isTailMessage(message, index)}
160. >
161. {message.text && (
162. <span slot="text" dangerouslySetInnerHTML={{__html: message.text}} />
163. )}
164. </Message>
165. ))}
166. {this.state.typingMessage && (
167. <Message
168. type="received"
169. typing={true}
170. first={true}
171. last={true}
172. tail={true}
173. header={`${this.state.typingMessage.name} is typing`}
174. avatar={this.state.typingMessage.avatar}
175. ></Message>
176. )}
177. </Messages>
178. </Page>
179. )
180. }
182. get attachmentsVisible() {
183. const self = this;
184. return self.state.attachments.length > 0;
185. }
186. get placeholder() {
187. const self = this;
188. return self.state.attachments.length > 0 ? 'Add comment or Send' : 'Message';
189. }
190. componentDidMount() {
191. const self = this;
192. self.$f7ready(() => {
193. self.messagebar = self.messagebarComponent.f7Messagebar;
194. self.messages = self.messagesComponent.f7Messages;
195. });
196. }
197. isFirstMessage(message, index) {
198. const self = this;
199. const previousMessage = self.state.messagesData[index - 1];
200. if (message.isTitle) return false;
201. if (!previousMessage || previousMessage.type !== message.type || previousMessage.name !== message.name) return true;
202. return false;
203. }
204. isLastMessage(message, index) {
205. const self = this;
206. const nextMessage = self.state.messagesData[index + 1];
207. if (message.isTitle) return false;
208. if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
209. return false;
210. }
211. isTailMessage(message, index) {
212. const self = this;
213. const nextMessage = self.state.messagesData[index + 1];
214. if (message.isTitle) return false;
215. if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
216. return false;
217. }
218. deleteAttachment(image) {
219. const self = this;
220. const attachments = self.state.attachments;
221. const index = attachments.indexOf(image);
222. attachments.splice(index, 1);
223. self.setState({ attachments });
224. }
225. handleAttachment(e) {
226. const self = this;
227. const attachments = self.state.attachments;
228. const index = self.$$(e.target).parents('label.checkbox').index();
229. const image = self.state.images[index];
230. if (e.target.checked) {
231. // Add to attachments
232. attachments.unshift(image);
233. } else {
234. // Remove from attachments
235. attachments.splice(attachments.indexOf(image), 1);
236. }
237. self.setState({ attachments });
238. }
239. sendMessage() {
240. const self = this;
241. const text = self.messagebar.getValue().replace(/\n/g, '<br>').trim();
242. const messagesToSend = [];
243. self.state.attachments.forEach((attachment) => {
244. messagesToSend.push({
245. image: attachment,
246. });
247. });
248. if (text.trim().length) {
249. messagesToSend.push({
250. text,
251. });
252. }
253. if (messagesToSend.length === 0) {
254. return;
255. }
257. self.setState({
258. // Reset attachments
259. attachments: [],
260. // Hide sheet
261. sheetVisible: false,
262. // Send message
263. messagesData: [...self.state.messagesData, ...messagesToSend],
264. });
265. self.messagebar.clear();
267. // Focus area
268. if (text.length) self.messagebar.focus();
270. // Mock response
271. if (self.state.responseInProgress) return;
272. self.setState({
273. responseInProgress: true,
274. })
275. setTimeout(() => {
276. const answer = self.state.answers[Math.floor(Math.random() * self.state.answers.length)];
277. const person = self.state.people[Math.floor(Math.random() * self.state.people.length)];
278. self.setState({
279. typingMessage: {
280. name: person.name,
281. avatar: person.avatar,
282. },
283. });
284. setTimeout(() => {
285. self.setState({
286. messagesData: [...self.state.messagesData, {
287. text: answer,
288. type: 'received',
289. name: person.name,
290. avatar: person.avatar,
291. }],
292. typingMessage: null,
293. responseInProgress: false,
294. });
295. }, 4000);
296. }, 1000);
297. }
298. };
