Home Reference Source

src/components/MessageList/index.js

// @ts-check
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import AvatarContainer from '../AvatarContainer';
import MessageContainer from '../MessageContainer';
import Message from '../Message';

import * as messageActions from '../../actions/messages';

import Avatar from '../../../es/themes/default/components/Avatar/index';
import networkManager from '../../../es/utils/network';

const mapStateToProps = ({ messages }) => ({
    messages: messages.messages,
    messageQueue: messages.messageQueue
});

const mapDispatchToProps = (dispatch, ownProps) => ({
    delayedMessageAdd: message => {
        dispatch(messageActions.delayedMessageAdd(message));
    },
    submitHandler: text => {
        dispatch(messageActions.messageSend({ text }));
    }
});

class MessageList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            offset: 0
        };
    }

    componentDidUpdate() {
        this._last &&
            this._last.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'end' });

        let { messageQueue, delayedMessageAdd } = this.props;

        messageQueue.length && delayedMessageAdd(messageQueue[messageQueue.length - 1]);
    }

    render() {
        let { theme, messages, messageQueue, submitHandler } = this.props;
        return (
            <ul className="ChatContainer-content" ref={ref => (this._ref = ref)}>
                <div className="MessagesList">
                    {messages.map((message, i) => (
                        <li
                            key={i}
                            ref={ref => {
                                if (i === messages.length - 1) {
                                    this._last = ref;
                                }
                            }}
                        >
                            {message.origin === 'remote' && (
                                <AvatarContainer AvatarComponent={theme.AvatarComponent} />
                            )}
                            <MessageContainer key="text" {...message}>
                                {message.pages.map((page, i) => (
                                    <Message
                                        key={i}
                                        page={page}
                                        isLocal={message.origin === 'local'}
                                        {...theme}
                                        submitHandler={submitHandler}
                                    />
                                ))}
                            </MessageContainer>
                            {message.buttons &&
                                message.buttonStyle === 'default' && (
                                    <MessageContainer key="buttons" {...message}>
                                        {message.buttons.map((button, i) => (
                                            <theme.ButtonComponent
                                                key={i}
                                                text={button.text}
                                                onClick={() => submitHandler(button.text)}
                                            />
                                        ))}
                                    </MessageContainer>
                                )}
                            {messageQueue.length && i === messages.length - 1
                                ? [
                                      <AvatarContainer
                                          key="avatar"
                                          AvatarComponent={theme.AvatarComponent}
                                      />,
                                      <MessageContainer key="typing">
                                          <theme.TypingIndicatorComponent />
                                      </MessageContainer>
                                  ]
                                : null}
                        </li>
                    ))}
                </div>
            </ul>
        );
    }
}

MessageList.PropTypes = {
    messages: PropTypes.object,
    theme: PropTypes.shape({
        ImageComponent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
        InputComponent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
        MessageComponent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
        TextComponent: PropTypes.oneOfType([PropTypes.element, PropTypes.func])
    })
};

export default connect(mapStateToProps, mapDispatchToProps)(MessageList);