Advertisement
Guest User

Untitled

a guest
Dec 9th, 2018
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* eslint-disable react/prop-types */
  2. import React, { Component, Fragment } from 'react';
  3. import { connect } from 'react-redux';
  4. import PropTypes from 'prop-types';
  5. import classNames from 'classnames';
  6.  
  7. import ChatInput from './ChatInput';
  8. import { TypingIndicator } from './TypingIndicator';
  9. import styles from './Chat.scss';
  10. import { ChatMessagesGroup } from './ChatMessagesGroup';
  11. import * as interviewActions from '../reducers/interviewActions';
  12. import { STATUS } from '../../../constants/statuses';
  13. import { setSectionControl } from '../../../redux/modules/view';
  14. import { TYPES } from '../../../constants/chat';
  15. import { ArrowIcon } from '../../../components/IconsSvg/ArrowIcon';
  16. import EditAnswerModal from '../../../components/modals/EditAnswerModal';
  17.  
  18. const HIDE_DISTANCE = 50;
  19. const SET_STICKY_BUTTON_DISTANCE = 1;
  20.  
  21. const isMultipleChoice = (type, answers) => (
  22.   type === TYPES.MULTI || (type === TYPES.SINGLE && answers.length > 3)
  23. );
  24.  
  25. const mapStateToProps = ({
  26.   lang,
  27.   view: { stickyButton, showScrollButton, showEditModal },
  28.   interview: { triageCurrentQuestion: { type }, triageInterviewId, triageMessages }
  29. }) => ({ lang, stickyButton, type, showScrollButton, showEditModal, triageInterviewId, triageMessages });
  30.  
  31. const mapDispatchToProps = dispatch => ({
  32.   fetchIntroMessages: lastMessage => dispatch(interviewActions.sendChatIntroMessages(lastMessage)),
  33.   setScrollButton: showScrollButton => dispatch(setSectionControl({ showScrollButton })),
  34.   setStickyButton: stickyButton => dispatch(setSectionControl({ stickyButton })),
  35.   setEditModal: showEditModal => dispatch(setSectionControl({ showEditModal })),
  36.   askForChildren: () => dispatch(interviewActions.askForChildren()),
  37.   goBackToMessage: (index, message) => dispatch(interviewActions.goBackToMessage(index, message)),
  38.   setTriageCurrentQuestion: question => dispatch(interviewActions.setTriageCurrentQuestion(question)),
  39. });
  40.  
  41. @connect(mapStateToProps, mapDispatchToProps)
  42. export class Chat extends Component {
  43.   static propTypes = {
  44.     messages: PropTypes.array,
  45.     inputLocked: PropTypes.bool,
  46.     previousAppointmentMessages: PropTypes.array,
  47.     currentQuestion: PropTypes.shape({
  48.       type: PropTypes.string,
  49.       answers: PropTypes.array,
  50.     }),
  51.   };
  52.  
  53.   static defaultProps = {
  54.     onSend() {},
  55.     messages: [],
  56.     inputLocked: false,
  57.     currentQuestion: {
  58.       type: 'text',
  59.       answers: [],
  60.     },
  61.     previousAppointmentMessages: [],
  62.   };
  63.  
  64.   constructor() {
  65.     super();
  66.     this.chat = React.createRef();
  67.   }
  68.  
  69.   componentWillReceiveProps(newProps) {
  70.     const { answers } = newProps.currentQuestion;
  71.     const answersChanged = answers !== this.props.currentQuestion.answers;
  72.     if (answersChanged) {
  73.       this.props.setStickyButton(false);
  74.     }
  75.   }
  76.  
  77.   setButtons = () => {
  78.     if (!this.chat) return;
  79.     const { setScrollButton, setStickyButton } = this.props;
  80.     const { scrollHeight, scrollTop, clientHeight } = this.chat;
  81.  
  82.     const distanceToBottom = scrollHeight - scrollTop - clientHeight;
  83.     const shouldSetScrollButton = distanceToBottom > HIDE_DISTANCE;
  84.     const shouldSetStickyButton = distanceToBottom <= SET_STICKY_BUTTON_DISTANCE;
  85.     setScrollButton(shouldSetScrollButton);
  86.     setStickyButton(shouldSetStickyButton);
  87.   };
  88.  
  89.   scrollToBottom = () => {
  90.     this.chat.scrollTo({
  91.       behavior: 'smooth',
  92.       left: 0,
  93.       top: this.chat.scrollHeight
  94.     });
  95.   };
  96.  
  97.   nextMessage = () => {
  98.     const { messages, fetchIntroMessages } = this.props;
  99.     fetchIntroMessages(messages[messages.length - 1]);
  100.   };
  101.  
  102.   editAnswer = async (message) => {
  103.     const { triageMessages, askForChildren, setTriageCurrentQuestion, goBackToMessage, setEditModal } = this.props;
  104.     setEditModal(false);
  105.     const questions = await interviewActions.getAllQuestions();
  106.     const question = questions[questions.findIndex((q => q.id === message.questionId))];
  107.     const messageIndex = triageMessages.findIndex((m => m.id === message.id)) - 1;
  108.     if (message.questionId === 'child') {
  109.       askForChildren();
  110.     }
  111.     if (question) {
  112.       setTriageCurrentQuestion(question);
  113.     }
  114.     goBackToMessage(messageIndex, triageMessages[messageIndex]);
  115.   };
  116.  
  117.   renderMessages = () => {
  118.     const { messages = [], directToAsync, endedAt } = this.props;
  119.  
  120.     return (
  121.       <ChatMessagesGroup
  122.         messages={messages}
  123.         endedAt={endedAt}
  124.         directToAsync={directToAsync}
  125.       />
  126.     );
  127.   };
  128.   renderPreviousAppointmentMessages = () => {
  129.     const { previousAppointmentMessages = [] } = this.props;
  130.  
  131.     return previousAppointmentMessages.map(({ messages: prevMessages, appointment }, i) => (
  132.       <ChatMessagesGroup
  133.         key={i}
  134.         messages={prevMessages}
  135.         endedAt={appointment.endedAt}
  136.         directToAsync={appointment.directToAsync}
  137.       />
  138.     ));
  139.   };
  140.   render() {
  141.     const {
  142.       lang,
  143.       onSend,
  144.       onType,
  145.       status,
  146.       isLocked,
  147.       isTyping,
  148.       inputLocked,
  149.       messages = [],
  150.       isChatFinished,
  151.       onChatEndClick,
  152.       onUploadPicture,
  153.       isDoctorMeeting,
  154.       currentUploadProgress,
  155.       isBotAnswerInProgress,
  156.       currentQuestion: { type: inputType, answers, skippable, max, min },
  157.       stickyButton,
  158.       showScrollButton,
  159.       showEditModal
  160.     } = this.props;
  161.  
  162.     const isScrollButton = isMultipleChoice(inputType, answers) && showScrollButton && !stickyButton;
  163.     const isDisabled = isLocked || isBotAnswerInProgress || isChatFinished;
  164.     const isLastMessageIntro =
  165.       messages.length > 0 && messages[messages.length - 1] && messages[messages.length - 1].type === 'object';
  166.     return (
  167.       <Fragment>
  168.         <div
  169.           className={classNames(styles.chat, { [styles.multi]: isMultipleChoice(inputType, answers) })}
  170.           onScroll={!stickyButton ? this.setButtons : null}
  171.           ref={node => { this.chat = node; }}
  172.         >
  173.           {/* chatMessagesWrapper is needed because scrolling component needs
  174.           flex-direction: column-reverse in order the be scrolled down by default
  175.           on resize */}
  176.  
  177.           <div className={styles.chatMessagesWrapper}>
  178.             <div className={styles.chatMessages}>
  179.               {this.renderPreviousAppointmentMessages()}
  180.               {this.renderMessages()}
  181.               {isTyping && <TypingIndicator />}
  182.             </div>
  183.           </div>
  184.           {isLastMessageIntro && (
  185.             <button
  186.               className={styles.continueChatButton}
  187.               onClick={this.nextMessage}
  188.             >
  189.               {messages[messages.length - 1].buttonCopy}
  190.             </button>
  191.           )}
  192.           {(isDoctorMeeting &&
  193.           status === STATUS.FULFILLED) && (
  194.               <button className={styles.closeChatButton} onClick={onChatEndClick}>
  195.                 {lang.chat_close}
  196.               </button>
  197.             )}
  198.           {(!inputLocked && inputType) && (
  199.             <ChatInput
  200.               setButtons={this.setButtons}
  201.               onSend={onSend}
  202.               type={inputType}
  203.               onChange={onType}
  204.               answers={answers}
  205.               skippable={skippable}
  206.               disabled={isDisabled}
  207.               onUploadPicture={onUploadPicture}
  208.               currentUploadProgress={currentUploadProgress}
  209.               min={min}
  210.               max={max}
  211.             />
  212.           )}
  213.         </div>
  214.         <div className={classNames(
  215.           styles.buttonScrollContainer,
  216.           { [styles.hide]: !isScrollButton, [styles.show]: isScrollButton }
  217.         )}
  218.         >
  219.           <div onClick={this.scrollToBottom} className={styles.buttonScroll}>
  220.             <ArrowIcon direction="down" />
  221.           </div>
  222.         </div>
  223.         {showEditModal && <EditAnswerModal editAnswer={this.editAnswer} />}
  224.         <div id="sticky-button-root" />
  225.       </Fragment>
  226.     );
  227.   }
  228. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement