
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: 'http://lorempixel.com/100/100/people/9',
23. },
24. {
25. name: 'Blue Ninja',
26. type: 'received',
27. text: 'Hi there, I am also fine, thanks! And how are you?',
28. avatar: 'http://lorempixel.com/100/100/people/7',
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: 'http://lorempixel.com/200/260/cats/4/',
42. },
43. {
44. name: 'Kate',
45. type: 'received',
46. text: 'Nice!',
47. avatar: 'http://lorempixel.com/100/100/people/9',
48. },
49. {
50. name: 'Kate',
51. type: 'received',
52. text: 'Like it very much!',
53. avatar: 'http://lorempixel.com/100/100/people/9',
54. },
55. {
56. name: 'Blue Ninja',
57. type: 'received',
58. text: 'Awesome!',
59. avatar: 'http://lorempixel.com/100/100/people/7',
60. },
61. ],
62. images: [
63. 'http://lorempixel.com/300/300/cats/1/',
64. 'http://lorempixel.com/200/300/cats/2/',
65. 'http://lorempixel.com/400/300/cats/3/',
66. 'http://lorempixel.com/300/150/cats/4/',
67. 'http://lorempixel.com/150/300/cats/5/',
68. 'http://lorempixel.com/300/300/cats/6/',
69. 'http://lorempixel.com/300/300/cats/7/',
70. 'http://lorempixel.com/200/300/cats/8/',
71. 'http://lorempixel.com/400/300/cats/9/',
72. 'http://lorempixel.com/300/150/cats/10/',
73. ],
74. people: [
75. {
76. name: 'Kate Johnson',
77. avatar: 'http://lorempixel.com/100/100/people/9',
78. },
79. {
80. name: 'Blue Ninja',
81. avatar: 'http://lorempixel.com/100/100/people/7',
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. iconMd="material:camera_alt"
115. slot="inner-start"
116. onClick={() => {this.setState({sheetVisible: !this.state.sheetVisible})}}
117. ></Link>
118. <Link
119. iconIos="f7:arrow_up_fill"
120. iconMd="material:send"
121. slot="inner-end"
122. onClick={this.sendMessage.bind(this)}
123. ></Link>
124. <MessagebarAttachments>
125. {this.state.attachments.map((image, index) => (
126. <MessagebarAttachment
127. key={index}
128. image={image}
129. onAttachmentDelete={() => this.deleteAttachment(image)}
130. ></MessagebarAttachment>
131. ))}
132. </MessagebarAttachments>
133. <MessagebarSheet>
134. {this.state.images.map((image, index) => (
135. <MessagebarSheetImage
136. key={index}
137. image={image}
138. checked={this.state.attachments.indexOf(image) >= 0}
139. onChange={this.handleAttachment.bind(this)}
140. ></MessagebarSheetImage>
141. ))}
142. </MessagebarSheet>
143. </Messagebar>
145. <Messages ref={(el) => {this.messagesComponent = el}}>
146. <MessagesTitle><b>Sunday, Feb 9,</b> 12:58</MessagesTitle>
148. {this.state.messagesData.map((message, index) => (
149. <Message
150. key={index}
151. type={message.type}
152. image={message.image}
153. name={message.name}
154. avatar={message.avatar}
155. first={this.isFirstMessage(message, index)}
156. last={this.isLastMessage(message, index)}
157. tail={this.isTailMessage(message, index)}
158. >
159. {message.text && (
160. <span slot="text" dangerouslySetInnerHTML={{__html: message.text}} />
161. )}
162. </Message>
163. ))}
164. {this.state.typingMessage && (
165. <Message
166. type="received"
167. typing={true}
168. first={true}
169. last={true}
170. tail={true}
171. header={`${this.state.typingMessage.name} is typing`}
172. avatar={this.state.typingMessage.avatar}
173. ></Message>
174. )}
175. </Messages>
176. </Page>
177. )
178. }
180. get attachmentsVisible() {
181. const self = this;
182. return self.state.attachments.length > 0;
183. }
184. get placeholder() {
185. const self = this;
186. return self.state.attachments.length > 0 ? 'Add comment or Send' : 'Message';
187. }
188. componentDidMount() {
189. const self = this;
190. self.$f7ready(() => {
191. self.messagebar = self.messagebarComponent.f7Messagebar;
192. self.messages = self.messagesComponent.f7Messages;
193. });
194. }
195. isFirstMessage(message, index) {
196. const self = this;
197. const previousMessage = self.state.messagesData[index - 1];
198. if (message.isTitle) return false;
199. if (!previousMessage || previousMessage.type !== message.type || previousMessage.name !== message.name) return true;
200. return false;
201. }
202. isLastMessage(message, index) {
203. const self = this;
204. const nextMessage = self.state.messagesData[index + 1];
205. if (message.isTitle) return false;
206. if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
207. return false;
208. }
209. isTailMessage(message, index) {
210. const self = this;
211. const nextMessage = self.state.messagesData[index + 1];
212. if (message.isTitle) return false;
213. if (!nextMessage || nextMessage.type !== message.type || nextMessage.name !== message.name) return true;
214. return false;
215. }
216. deleteAttachment(image) {
217. const self = this;
218. const attachments = self.state.attachments;
219. const index = attachments.indexOf(image);
220. attachments.splice(index, 1);
221. self.setState({ attachments });
222. }
223. handleAttachment(e) {
224. const self = this;
225. const attachments = self.state.attachments;
226. const index = self.$$(e.target).parents('label.checkbox').index();
227. const image = self.state.images[index];
228. if (e.target.checked) {
229. // Add to attachments
230. attachments.unshift(image);
231. } else {
232. // Remove from attachments
233. attachments.splice(attachments.indexOf(image), 1);
234. }
235. self.setState({ attachments });
236. }
237. sendMessage() {
238. const self = this;
239. const text = self.messagebar.getValue().replace(/\n/g, '<br>').trim();
240. const messagesToSend = [];
241. self.state.attachments.forEach((attachment) => {
242. messagesToSend.push({
243. image: attachment,
244. });
245. });
246. if (text.trim().length) {
247. messagesToSend.push({
248. text,
249. });
250. }
251. if (messagesToSend.length === 0) {
252. return;
253. }
255. self.setState({
256. // Reset attachments
257. attachments: [],
258. // Hide sheet
259. sheetVisible: false,
260. // Send message
261. messagesData: [...self.state.messagesData, ...messagesToSend],
262. });
263. self.messagebar.clear();
265. // Focus area
266. if (text.length) self.messagebar.focus();
268. // Mock response
269. if (self.state.responseInProgress) return;
270. self.setState({
271. responseInProgress: true,
272. })
273. setTimeout(() => {
274. const answer = self.state.answers[Math.floor(Math.random() * self.state.answers.length)];
275. const person = self.state.people[Math.floor(Math.random() * self.state.people.length)];
276. self.setState({
277. typingMessage: {
278. name: person.name,
279. avatar: person.avatar,
280. },
281. });
282. setTimeout(() => {
283. self.setState({
284. messagesData: [...self.state.messagesData, {
285. text: answer,
286. type: 'received',
287. name: person.name,
288. avatar: person.avatar,
289. }],
290. typingMessage: null,
291. responseInProgress: false,
292. });
293. }, 4000);
294. }, 1000);
295. }
296. };
