@@ -11,7 +11,6 @@ import { Alert } from "antd";
11
11
import { List , Set as immutableSet } from "immutable" ;
12
12
import { MutableRefObject , useEffect , useMemo , useRef } from "react" ;
13
13
import { Virtuoso , VirtuosoHandle } from "react-virtuoso" ;
14
-
15
14
import { chatBotName , isChatBot } from "@cocalc/frontend/account/chatbot" ;
16
15
import {
17
16
TypedMap ,
@@ -55,7 +54,6 @@ export function ChatLog(props: Readonly<Props>) {
55
54
project_id ,
56
55
path ,
57
56
) ;
58
- const virtuosoHeightsRef = useRef < { [ index : number ] : number } > ( { } ) ;
59
57
60
58
// see similar code in task list:
61
59
const selectedHashtags0 = useRedux ( [ "selectedHashtags" ] , project_id , path ) ;
@@ -132,11 +130,6 @@ export function ChatLog(props: Readonly<Props>) {
132
130
} ;
133
131
} , [ scrollToBottomRef != null ] ) ;
134
132
135
- const virtuosoScroll = useVirtuosoScrollHook ( {
136
- cacheId : `${ project_id } ${ path } ` ,
137
- initialState : { index : messages . size - 1 , offset : 0 } , // starts scrolled to the newest message.
138
- } ) ;
139
-
140
133
return (
141
134
< >
142
135
{ visibleHashtags . size > 0 && (
@@ -159,90 +152,23 @@ export function ChatLog(props: Readonly<Props>) {
159
152
filterRecentH = { filterRecentH }
160
153
/>
161
154
) }
162
- < Virtuoso
163
- ref = { virtuosoRef }
164
- totalCount = { sortedDates . length }
165
- itemSize = { ( el ) => {
166
- // see comment in jupyter/cell-list.tsx
167
- const h = el . getBoundingClientRect ( ) . height ;
168
- const data = el . getAttribute ( "data-item-index" ) ;
169
- if ( data != null ) {
170
- const index = parseInt ( data ) ;
171
- virtuosoHeightsRef . current [ index ] = h ;
172
- }
173
- return h ;
174
- } }
175
- itemContent = { ( index ) => {
176
- const date = sortedDates [ index ] ;
177
- const message : ChatMessageTyped | undefined = messages . get ( date ) ;
178
- if ( message == null ) {
179
- // shouldn't happen. But we should be robust to such a possibility.
180
- return < div style = { { height : "1px" } } /> ;
181
- }
182
-
183
- const is_thread = isThread ( messages , message ) ;
184
- // if we search for a message, we treat all threads as unfolded
185
- const force_unfold = ! ! search ;
186
- const is_folded =
187
- ! force_unfold && isFolded ( messages , message , account_id ) ;
188
- const is_thread_body = message . get ( "reply_to" ) != null ;
189
- const h = virtuosoHeightsRef . current [ index ] ;
190
-
191
- return (
192
- < div style = { { overflow : "hidden" } } >
193
- < DivTempHeight height = { h ? `${ h } px` : undefined } >
194
- < Message
195
- key = { date }
196
- index = { index }
197
- account_id = { account_id }
198
- user_map = { user_map }
199
- message = { message }
200
- project_id = { project_id }
201
- path = { path }
202
- font_size = { fontSize }
203
- selectedHashtags = { selectedHashtags }
204
- actions = { actions }
205
- is_thread = { is_thread }
206
- is_folded = { is_folded }
207
- force_unfold = { force_unfold }
208
- is_thread_body = { is_thread_body }
209
- is_prev_sender = { isPrevMessageSender (
210
- index ,
211
- sortedDates ,
212
- messages ,
213
- ) }
214
- is_next_sender = { isNextMessageSender (
215
- index ,
216
- sortedDates ,
217
- messages ,
218
- ) }
219
- show_avatar = {
220
- ! isNextMessageSender ( index , sortedDates , messages )
221
- }
222
- mode = { mode }
223
- get_user_name = { ( account_id : string | undefined ) =>
224
- // ATTN: this also works for LLM chat bot IDs, not just account UUIDs
225
- typeof account_id === "string"
226
- ? getUserName ( user_map , account_id )
227
- : "Unknown name"
228
- }
229
- scroll_into_view = { ( ) =>
230
- virtuosoRef . current ?. scrollIntoView ( { index } )
231
- }
232
- allowReply = {
233
- messages . getIn ( [ sortedDates [ index + 1 ] , "reply_to" ] ) == null
234
- }
235
- llm_cost_reply = { llm_cost_reply }
236
- />
237
- </ DivTempHeight >
238
- </ div >
239
- ) ;
240
- } }
241
- rangeChanged = { ( { endIndex } ) => {
242
- // manually scrolling if NOT at the bottom.
243
- manualScrollRef . current = endIndex < sortedDates . length - 1 ;
155
+ < MessageList
156
+ { ...{
157
+ virtuosoRef,
158
+ sortedDates,
159
+ messages,
160
+ search,
161
+ account_id,
162
+ user_map,
163
+ project_id,
164
+ path,
165
+ fontSize,
166
+ selectedHashtags,
167
+ actions,
168
+ llm_cost_reply,
169
+ manualScrollRef,
170
+ mode,
244
171
} }
245
- { ...virtuosoScroll }
246
172
/>
247
173
< Composing
248
174
projectId = { project_id }
@@ -455,3 +381,133 @@ function NotShowing({ num, search, filterRecentH }: NotShowingProps) {
455
381
/>
456
382
) ;
457
383
}
384
+
385
+ export function MessageList ( {
386
+ messages,
387
+ account_id,
388
+ virtuosoRef,
389
+ sortedDates,
390
+ search,
391
+ user_map,
392
+ project_id,
393
+ path,
394
+ fontSize,
395
+ selectedHashtags,
396
+ actions,
397
+ llm_cost_reply,
398
+ manualScrollRef,
399
+ mode,
400
+ } : {
401
+ messages ;
402
+ account_id ;
403
+ user_map ;
404
+ mode ;
405
+ sortedDates ;
406
+ virtuosoRef ?;
407
+ search ?;
408
+ project_id ?;
409
+ path ?;
410
+ fontSize ?;
411
+ selectedHashtags ?;
412
+ actions ?;
413
+ llm_cost_reply ?;
414
+ manualScrollRef ?;
415
+ } ) {
416
+ const virtuosoHeightsRef = useRef < { [ index : number ] : number } > ( { } ) ;
417
+ const virtuosoScroll = useVirtuosoScrollHook ( {
418
+ cacheId : `${ project_id } ${ path } ` ,
419
+ initialState : { index : messages . size - 1 , offset : 0 } , // starts scrolled to the newest message.
420
+ } ) ;
421
+
422
+ return (
423
+ < Virtuoso
424
+ ref = { virtuosoRef }
425
+ totalCount = { sortedDates . length }
426
+ itemSize = { ( el ) => {
427
+ // see comment in jupyter/cell-list.tsx
428
+ const h = el . getBoundingClientRect ( ) . height ;
429
+ const data = el . getAttribute ( "data-item-index" ) ;
430
+ if ( data != null ) {
431
+ const index = parseInt ( data ) ;
432
+ virtuosoHeightsRef . current [ index ] = h ;
433
+ }
434
+ return h ;
435
+ } }
436
+ itemContent = { ( index ) => {
437
+ const date = sortedDates [ index ] ;
438
+ const message : ChatMessageTyped | undefined = messages . get ( date ) ;
439
+ if ( message == null ) {
440
+ // shouldn't happen. But we should be robust to such a possibility.
441
+ return < div style = { { height : "1px" } } /> ;
442
+ }
443
+
444
+ const is_thread = isThread ( messages , message ) ;
445
+ // if we search for a message, we treat all threads as unfolded
446
+ const force_unfold = ! ! search ;
447
+ const is_folded =
448
+ ! force_unfold && isFolded ( messages , message , account_id ) ;
449
+ const is_thread_body = message . get ( "reply_to" ) != null ;
450
+ const h = virtuosoHeightsRef . current [ index ] ;
451
+
452
+ return (
453
+ < div style = { { overflow : "hidden" } } >
454
+ < DivTempHeight height = { h ? `${ h } px` : undefined } >
455
+ < Message
456
+ key = { date }
457
+ index = { index }
458
+ account_id = { account_id }
459
+ user_map = { user_map }
460
+ message = { message }
461
+ project_id = { project_id }
462
+ path = { path }
463
+ font_size = { fontSize }
464
+ selectedHashtags = { selectedHashtags }
465
+ actions = { actions }
466
+ is_thread = { is_thread }
467
+ is_folded = { is_folded }
468
+ force_unfold = { force_unfold }
469
+ is_thread_body = { is_thread_body }
470
+ is_prev_sender = { isPrevMessageSender (
471
+ index ,
472
+ sortedDates ,
473
+ messages ,
474
+ ) }
475
+ is_next_sender = { isNextMessageSender (
476
+ index ,
477
+ sortedDates ,
478
+ messages ,
479
+ ) }
480
+ show_avatar = { ! isNextMessageSender ( index , sortedDates , messages ) }
481
+ mode = { mode }
482
+ get_user_name = { ( account_id : string | undefined ) =>
483
+ // ATTN: this also works for LLM chat bot IDs, not just account UUIDs
484
+ typeof account_id === "string"
485
+ ? getUserName ( user_map , account_id )
486
+ : "Unknown name"
487
+ }
488
+ scroll_into_view = {
489
+ virtuosoRef
490
+ ? ( ) => virtuosoRef . current ?. scrollIntoView ( { index } )
491
+ : undefined
492
+ }
493
+ allowReply = {
494
+ messages . getIn ( [ sortedDates [ index + 1 ] , "reply_to" ] ) == null
495
+ }
496
+ llm_cost_reply = { llm_cost_reply }
497
+ />
498
+ </ DivTempHeight >
499
+ </ div >
500
+ ) ;
501
+ } }
502
+ rangeChanged = {
503
+ manualScrollRef
504
+ ? ( { endIndex } ) => {
505
+ // manually scrolling if NOT at the bottom.
506
+ manualScrollRef . current = endIndex < sortedDates . length - 1 ;
507
+ }
508
+ : undefined
509
+ }
510
+ { ...virtuosoScroll }
511
+ />
512
+ ) ;
513
+ }
0 commit comments