chat-pane.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import React, { useEffect } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { useTranslation } from 'react-i18next'
  4. import MessageList from './message-list'
  5. import MessageInput from './message-input'
  6. import InfiniteScroll from './infinite-scroll'
  7. import Icon from '../../../shared/components/icon'
  8. import { useLayoutContext } from '../../../shared/context/layout-context'
  9. import { useApplicationContext } from '../../../shared/context/application-context'
  10. import withErrorBoundary from '../../../infrastructure/error-boundary'
  11. import { useChatContext } from '../context/chat-context'
  12. function ChatPane() {
  13. const { t } = useTranslation()
  14. const { chatIsOpen } = useLayoutContext({ chatIsOpen: PropTypes.bool })
  15. const { user } = useApplicationContext()
  16. const {
  17. status,
  18. messages,
  19. initialMessagesLoaded,
  20. atEnd,
  21. loadInitialMessages,
  22. loadMoreMessages,
  23. sendMessage,
  24. markMessagesAsRead,
  25. } = useChatContext()
  26. useEffect(() => {
  27. if (chatIsOpen && !initialMessagesLoaded) {
  28. loadInitialMessages()
  29. }
  30. }, [chatIsOpen, loadInitialMessages, initialMessagesLoaded])
  31. const shouldDisplayPlaceholder = status !== 'pending' && messages.length === 0
  32. const messageContentCount = messages.reduce(
  33. (acc, { contents }) => acc + contents.length,
  34. 0
  35. )
  36. return (
  37. <aside className="chat">
  38. <InfiniteScroll
  39. atEnd={atEnd}
  40. className="messages"
  41. fetchData={loadMoreMessages}
  42. isLoading={status === 'pending'}
  43. itemCount={messageContentCount}
  44. >
  45. <div>
  46. <h2 className="sr-only">{t('chat')}</h2>
  47. {status === 'pending' && <LoadingSpinner />}
  48. {shouldDisplayPlaceholder && <Placeholder />}
  49. <MessageList
  50. messages={messages}
  51. userId={user.id}
  52. resetUnreadMessages={markMessagesAsRead}
  53. />
  54. </div>
  55. </InfiniteScroll>
  56. <MessageInput
  57. resetUnreadMessages={markMessagesAsRead}
  58. sendMessage={sendMessage}
  59. />
  60. </aside>
  61. )
  62. }
  63. function LoadingSpinner() {
  64. const { t } = useTranslation()
  65. return (
  66. <div className="loading">
  67. <Icon type="fw" modifier="refresh" spin />
  68. {` ${t('loading')}…`}
  69. </div>
  70. )
  71. }
  72. function Placeholder() {
  73. const { t } = useTranslation()
  74. return (
  75. <>
  76. <div className="no-messages text-center small">{t('no_messages')}</div>
  77. <div className="first-message text-center">
  78. {t('send_first_message')}
  79. <br />
  80. <Icon type="arrow-down" />
  81. </div>
  82. </>
  83. )
  84. }
  85. export default withErrorBoundary(ChatPane)