Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* eslint-disable react/prop-types */
- import React, { Component, Fragment } from 'react';
- import { connect } from 'react-redux';
- import PropTypes from 'prop-types';
- import classNames from 'classnames';
- import ChatInput from './ChatInput';
- import { TypingIndicator } from './TypingIndicator';
- import styles from './Chat.scss';
- import { ChatMessagesGroup } from './ChatMessagesGroup';
- import * as interviewActions from '../reducers/interviewActions';
- import { STATUS } from '../../../constants/statuses';
- import { setSectionControl } from '../../../redux/modules/view';
- import { TYPES } from '../../../constants/chat';
- import { ArrowIcon } from '../../../components/IconsSvg/ArrowIcon';
- import EditAnswerModal from '../../../components/modals/EditAnswerModal';
- const HIDE_DISTANCE = 50;
- const SET_STICKY_BUTTON_DISTANCE = 1;
- const isMultipleChoice = (type, answers) => (
- type === TYPES.MULTI || (type === TYPES.SINGLE && answers.length > 3)
- );
- const mapStateToProps = ({
- lang,
- view: { stickyButton, showScrollButton, showEditModal },
- interview: { triageCurrentQuestion: { type }, triageInterviewId, triageMessages }
- }) => ({ lang, stickyButton, type, showScrollButton, showEditModal, triageInterviewId, triageMessages });
- const mapDispatchToProps = dispatch => ({
- fetchIntroMessages: lastMessage => dispatch(interviewActions.sendChatIntroMessages(lastMessage)),
- setScrollButton: showScrollButton => dispatch(setSectionControl({ showScrollButton })),
- setStickyButton: stickyButton => dispatch(setSectionControl({ stickyButton })),
- setEditModal: showEditModal => dispatch(setSectionControl({ showEditModal })),
- askForChildren: () => dispatch(interviewActions.askForChildren()),
- goBackToMessage: (index, message) => dispatch(interviewActions.goBackToMessage(index, message)),
- setTriageCurrentQuestion: question => dispatch(interviewActions.setTriageCurrentQuestion(question)),
- });
- @connect(mapStateToProps, mapDispatchToProps)
- export class Chat extends Component {
- static propTypes = {
- messages: PropTypes.array,
- inputLocked: PropTypes.bool,
- previousAppointmentMessages: PropTypes.array,
- currentQuestion: PropTypes.shape({
- type: PropTypes.string,
- answers: PropTypes.array,
- }),
- };
- static defaultProps = {
- onSend() {},
- messages: [],
- inputLocked: false,
- currentQuestion: {
- type: 'text',
- answers: [],
- },
- previousAppointmentMessages: [],
- };
- constructor() {
- super();
- this.chat = React.createRef();
- }
- componentWillReceiveProps(newProps) {
- const { answers } = newProps.currentQuestion;
- const answersChanged = answers !== this.props.currentQuestion.answers;
- if (answersChanged) {
- this.props.setStickyButton(false);
- }
- }
- setButtons = () => {
- if (!this.chat) return;
- const { setScrollButton, setStickyButton } = this.props;
- const { scrollHeight, scrollTop, clientHeight } = this.chat;
- const distanceToBottom = scrollHeight - scrollTop - clientHeight;
- const shouldSetScrollButton = distanceToBottom > HIDE_DISTANCE;
- const shouldSetStickyButton = distanceToBottom <= SET_STICKY_BUTTON_DISTANCE;
- setScrollButton(shouldSetScrollButton);
- setStickyButton(shouldSetStickyButton);
- };
- scrollToBottom = () => {
- this.chat.scrollTo({
- behavior: 'smooth',
- left: 0,
- top: this.chat.scrollHeight
- });
- };
- nextMessage = () => {
- const { messages, fetchIntroMessages } = this.props;
- fetchIntroMessages(messages[messages.length - 1]);
- };
- editAnswer = async (message) => {
- const { triageMessages, askForChildren, setTriageCurrentQuestion, goBackToMessage, setEditModal } = this.props;
- setEditModal(false);
- const questions = await interviewActions.getAllQuestions();
- const question = questions[questions.findIndex((q => q.id === message.questionId))];
- const messageIndex = triageMessages.findIndex((m => m.id === message.id)) - 1;
- if (message.questionId === 'child') {
- askForChildren();
- }
- if (question) {
- setTriageCurrentQuestion(question);
- }
- goBackToMessage(messageIndex, triageMessages[messageIndex]);
- };
- renderMessages = () => {
- const { messages = [], directToAsync, endedAt } = this.props;
- return (
- <ChatMessagesGroup
- messages={messages}
- endedAt={endedAt}
- directToAsync={directToAsync}
- />
- );
- };
- renderPreviousAppointmentMessages = () => {
- const { previousAppointmentMessages = [] } = this.props;
- return previousAppointmentMessages.map(({ messages: prevMessages, appointment }, i) => (
- <ChatMessagesGroup
- key={i}
- messages={prevMessages}
- endedAt={appointment.endedAt}
- directToAsync={appointment.directToAsync}
- />
- ));
- };
- render() {
- const {
- lang,
- onSend,
- onType,
- status,
- isLocked,
- isTyping,
- inputLocked,
- messages = [],
- isChatFinished,
- onChatEndClick,
- onUploadPicture,
- isDoctorMeeting,
- currentUploadProgress,
- isBotAnswerInProgress,
- currentQuestion: { type: inputType, answers, skippable, max, min },
- stickyButton,
- showScrollButton,
- showEditModal
- } = this.props;
- const isScrollButton = isMultipleChoice(inputType, answers) && showScrollButton && !stickyButton;
- const isDisabled = isLocked || isBotAnswerInProgress || isChatFinished;
- const isLastMessageIntro =
- messages.length > 0 && messages[messages.length - 1] && messages[messages.length - 1].type === 'object';
- return (
- <Fragment>
- <div
- className={classNames(styles.chat, { [styles.multi]: isMultipleChoice(inputType, answers) })}
- onScroll={!stickyButton ? this.setButtons : null}
- ref={node => { this.chat = node; }}
- >
- {/* chatMessagesWrapper is needed because scrolling component needs
- flex-direction: column-reverse in order the be scrolled down by default
- on resize */}
- <div className={styles.chatMessagesWrapper}>
- <div className={styles.chatMessages}>
- {this.renderPreviousAppointmentMessages()}
- {this.renderMessages()}
- {isTyping && <TypingIndicator />}
- </div>
- </div>
- {isLastMessageIntro && (
- <button
- className={styles.continueChatButton}
- onClick={this.nextMessage}
- >
- {messages[messages.length - 1].buttonCopy}
- </button>
- )}
- {(isDoctorMeeting &&
- status === STATUS.FULFILLED) && (
- <button className={styles.closeChatButton} onClick={onChatEndClick}>
- {lang.chat_close}
- </button>
- )}
- {(!inputLocked && inputType) && (
- <ChatInput
- setButtons={this.setButtons}
- onSend={onSend}
- type={inputType}
- onChange={onType}
- answers={answers}
- skippable={skippable}
- disabled={isDisabled}
- onUploadPicture={onUploadPicture}
- currentUploadProgress={currentUploadProgress}
- min={min}
- max={max}
- />
- )}
- </div>
- <div className={classNames(
- styles.buttonScrollContainer,
- { [styles.hide]: !isScrollButton, [styles.show]: isScrollButton }
- )}
- >
- <div onClick={this.scrollToBottom} className={styles.buttonScroll}>
- <ArrowIcon direction="down" />
- </div>
- </div>
- {showEditModal && <EditAnswerModal editAnswer={this.editAnswer} />}
- <div id="sticky-button-root" />
- </Fragment>
- );
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement