message-list.tsx 3.3 KB

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