message-list.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import moment from 'moment'
  2. import type { Message as MessageType } from '@/features/chat/context/chat-context'
  3. import { useUserContext } from '@/shared/context/user-context'
  4. import { User } from '../../../../../types/user'
  5. import MessageGroup from '@/features/chat/components/message-group'
  6. const FIVE_MINUTES = 5 * 60 * 1000
  7. const TIMESTAMP_GROUP_SIZE = FIVE_MINUTES
  8. function formatTimestamp(date: moment.MomentInput) {
  9. if (!date) {
  10. return 'N/A'
  11. } else {
  12. return `${moment(date).format('h:mm a')} ${moment(date).calendar()}`
  13. }
  14. }
  15. interface MessageListProps {
  16. messages: MessageType[]
  17. resetUnreadMessages(...args: unknown[]): unknown
  18. }
  19. type MessageGroupType = {
  20. messages: MessageType[]
  21. id: string
  22. user?: User
  23. }
  24. // Group messages by the same author that were sent within 5 minutes of each
  25. // other
  26. function groupMessages(messages: MessageType[]) {
  27. const groups: MessageGroupType[] = []
  28. let currentGroup: MessageGroupType | null = null
  29. let previousMessage: MessageType | null = null
  30. for (const message of messages) {
  31. if (message.deleted) {
  32. continue
  33. }
  34. if (
  35. currentGroup &&
  36. previousMessage &&
  37. !message.pending &&
  38. message.user &&
  39. message.user.id &&
  40. message.user.id === previousMessage.user?.id &&
  41. message.timestamp - previousMessage.timestamp < TIMESTAMP_GROUP_SIZE
  42. ) {
  43. currentGroup.messages.push(message)
  44. } else {
  45. currentGroup = {
  46. messages: [message],
  47. id: String(message.timestamp),
  48. user: message.user,
  49. }
  50. groups.push(currentGroup)
  51. }
  52. previousMessage = message
  53. }
  54. return groups
  55. }
  56. function MessageList({ messages, resetUnreadMessages }: MessageListProps) {
  57. const user = useUserContext()
  58. function shouldRenderDate(messageIndex: number) {
  59. if (messageIndex === 0) {
  60. return true
  61. } else {
  62. const message = messages[messageIndex]
  63. const previousMessage = messages[messageIndex - 1]
  64. return (
  65. message.timestamp &&
  66. previousMessage.timestamp &&
  67. message.timestamp - previousMessage.timestamp > FIVE_MINUTES
  68. )
  69. }
  70. }
  71. const messageGroups = groupMessages(messages)
  72. return (
  73. // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
  74. <ul
  75. className="list-unstyled"
  76. onClick={resetUnreadMessages}
  77. onKeyDown={resetUnreadMessages}
  78. >
  79. {messageGroups.map((group, index) => (
  80. <li key={group.id} className="message">
  81. {shouldRenderDate(index) && (
  82. <div className="date">
  83. <time
  84. dateTime={
  85. group.messages[0].timestamp
  86. ? moment(group.messages[0].timestamp).format()
  87. : undefined
  88. }
  89. >
  90. {formatTimestamp(group.messages[0].timestamp)}
  91. </time>
  92. </div>
  93. )}
  94. <MessageGroup
  95. messages={group.messages}
  96. user={group.user}
  97. fromSelf={user ? group.user?.id === user.id : false}
  98. />
  99. </li>
  100. ))}
  101. </ul>
  102. )
  103. }
  104. export default MessageList