chat-pane.js 2.5 KB

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