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 asdefaultslot
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:
import React, { useState, useEffect, useRef } from 'react';
import {
  Page,
  Navbar,
  Messagebar,
  Link,
  MessagebarAttachments,
  MessagebarAttachment,
  MessagebarSheet,
  MessagebarSheetImage,
  Messages,
  MessagesTitle,
  Message,
  f7,
  f7ready,
} 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,
          {
            text: answer,
            type: 'received',
            name: person.name,
            avatar: person.avatar,
          },
        ]);
        responseInProgress.current = false;
      }, 4000);
    }, 1000);
  };
  return (
    
      
    <Page>
      <Navbar title="Messages" backLink="Back"></Navbar>
      <Messagebar
        placeholder={placeholder()}
        attachmentsVisible={attachmentsVisible()}
        sheetVisible={sheetVisible}
        value={messageText}
        onInput={(e) => setMessageText(e.target.value)}
      >
        <Link
          iconIos="f7:camera_fill"
          iconAurora="f7:camera_fill"
          iconMd="material:camera_alt"
          slot="inner-start"
          onClick={() => {
            setSheetVisible(!sheetVisible);
          }}
        />
        <Link
          iconIos="f7:arrow_up_circle_fill"
          iconAurora="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>
      
    
  );
};



