@@ -92,6 +92,14 @@ type TimelineRenderRow =
9292 | TimelineUnreadRow
9393 | TimelineMessageRowModel ;
9494
95+ type TimelineNonDayRow = TimelineUnreadRow | TimelineMessageRowModel ;
96+
97+ type TimelineDayGroup = {
98+ key : string ;
99+ label : string ;
100+ rows : TimelineNonDayRow [ ] ;
101+ } ;
102+
95103function buildTimelineRenderRows ( {
96104 firstUnreadMessageId,
97105 messages,
@@ -139,6 +147,36 @@ function buildTimelineRenderRows({
139147 return rows ;
140148}
141149
150+ function buildTimelineDayGroups ( rows : TimelineRenderRow [ ] ) : TimelineDayGroup [ ] {
151+ const groups : TimelineDayGroup [ ] = [ ] ;
152+ let currentGroup : TimelineDayGroup | null = null ;
153+
154+ for ( const row of rows ) {
155+ if ( row . type === "day" ) {
156+ currentGroup = {
157+ key : row . key ,
158+ label : row . label ,
159+ rows : [ ] ,
160+ } ;
161+ groups . push ( currentGroup ) ;
162+ continue ;
163+ }
164+
165+ if ( ! currentGroup ) {
166+ currentGroup = {
167+ key : "day-undated" ,
168+ label : "" ,
169+ rows : [ ] ,
170+ } ;
171+ groups . push ( currentGroup ) ;
172+ }
173+
174+ currentGroup . rows . push ( row ) ;
175+ }
176+
177+ return groups ;
178+ }
179+
142180export const TimelineMessageList = React . memo ( function TimelineMessageList ( {
143181 agentPubkeys,
144182 channelId,
@@ -169,39 +207,55 @@ export const TimelineMessageList = React.memo(function TimelineMessageList({
169207 ( ) => buildTimelineRenderRows ( { firstUnreadMessageId, messages } ) ,
170208 [ firstUnreadMessageId , messages ] ,
171209 ) ;
172- return rows . map ( ( row ) => (
173- < TimelineRenderRowView
174- agentPubkeys = { agentPubkeys }
175- allMessages = {
176- row . type === "message" && hasVideoAttachment ( row . message )
177- ? messages
178- : undefined
179- }
180- channelId = { channelId }
181- channelName = { channelName }
182- channelType = { channelType }
183- currentPubkey = { currentPubkey }
184- followThreadById = { followThreadById }
185- highlightedMessageId = { highlightedMessageId }
186- isFollowingThreadById = { isFollowingThreadById }
187- isSendingVideoReviewComment = { isSendingVideoReviewComment }
188- key = { row . key }
189- messageFooters = { messageFooters }
190- onDelete = { onDelete }
191- onEdit = { onEdit }
192- onMarkUnread = { onMarkUnread }
193- onReply = { onReply }
194- onSendVideoReviewComment = { onSendVideoReviewComment }
195- onToggleReaction = { onToggleReaction }
196- profiles = { profiles }
197- row = { row }
198- searchActiveMessageId = { searchActiveMessageId }
199- searchMatchingMessageIds = { searchMatchingMessageIds }
200- searchQuery = { searchQuery }
201- threadUnreadCounts = { threadUnreadCounts }
202- unfollowThreadById = { unfollowThreadById }
203- />
204- ) ) ;
210+ const dayGroups = React . useMemo ( ( ) => buildTimelineDayGroups ( rows ) , [ rows ] ) ;
211+
212+ return (
213+ < div className = "flex flex-col gap-0" >
214+ { dayGroups . map ( ( group ) => (
215+ < section
216+ className = "relative flex flex-col gap-2 before:absolute before:inset-x-0 before:top-4 before:h-px before:bg-border/35 before:content-['']"
217+ data-day-label = { group . label }
218+ data-testid = "message-timeline-day-group"
219+ key = { group . key }
220+ >
221+ { group . label ? < DayDivider label = { group . label } /> : null }
222+ { group . rows . map ( ( row ) => (
223+ < TimelineRenderRowView
224+ agentPubkeys = { agentPubkeys }
225+ allMessages = {
226+ row . type === "message" && hasVideoAttachment ( row . message )
227+ ? messages
228+ : undefined
229+ }
230+ channelId = { channelId }
231+ channelName = { channelName }
232+ channelType = { channelType }
233+ currentPubkey = { currentPubkey }
234+ followThreadById = { followThreadById }
235+ highlightedMessageId = { highlightedMessageId }
236+ isFollowingThreadById = { isFollowingThreadById }
237+ isSendingVideoReviewComment = { isSendingVideoReviewComment }
238+ key = { row . key }
239+ messageFooters = { messageFooters }
240+ onDelete = { onDelete }
241+ onEdit = { onEdit }
242+ onMarkUnread = { onMarkUnread }
243+ onReply = { onReply }
244+ onSendVideoReviewComment = { onSendVideoReviewComment }
245+ onToggleReaction = { onToggleReaction }
246+ profiles = { profiles }
247+ row = { row }
248+ searchActiveMessageId = { searchActiveMessageId }
249+ searchMatchingMessageIds = { searchMatchingMessageIds }
250+ searchQuery = { searchQuery }
251+ threadUnreadCounts = { threadUnreadCounts }
252+ unfollowThreadById = { unfollowThreadById }
253+ />
254+ ) ) }
255+ </ section >
256+ ) ) }
257+ </ div >
258+ ) ;
205259} ) ;
206260
207261type TimelineRenderRowViewProps = Omit <
0 commit comments