@@ -971,6 +971,10 @@ impl Widget for RoomScreen {
971971 // ▼ (down arrow) = collapsed/closed - items are hidden
972972 let button_text = if * open { "▲" } else { "▼" } ;
973973 wr. button ( id ! ( collapsible_button) ) . set_text ( cx, button_text) ;
974+ // If the last item is a group of small state events, scroll to the end when it is expanded.
975+ if range. end == tl_state. items . len ( ) && * open {
976+ portal_list. smooth_scroll_to_end ( cx, 90.0 , None ) ;
977+ }
974978 }
975979 }
976980 }
@@ -1634,18 +1638,20 @@ impl RoomScreen {
16341638 tl. content_drawn_since_last_update . remove ( changed_indices. clone ( ) ) ;
16351639 tl. profile_drawn_since_last_update . remove ( changed_indices. clone ( ) ) ;
16361640 }
1637- // Calculate the shift amount based on the difference between old and new lengths
1638- let old_len = tl. items . len ( ) ;
1639- let new_len = new_items. len ( ) ;
1640- let shift = new_len as i32 - old_len as i32 ;
1641- tl. items = new_items;
1642- // Apply the shift to the small_state_groups.
1643- for ( range, _) in & mut tl. small_state_groups {
1644- let new_start = ( range. start as i32 + shift) . max ( 0 ) as usize ;
1645- let new_end = ( range. end as i32 + shift) . max ( 0 ) as usize ;
1646- * range = new_start..new_end;
1641+ // Handles item_id changes whenever there is a backward pagination.
1642+ if !is_append {
1643+ // Calculate the shift amount based on the difference between old and new lengths
1644+ let old_len = tl. items . len ( ) ;
1645+ let new_len = new_items. len ( ) ;
1646+ let shift = new_len as i32 - old_len as i32 ;
1647+ tl. items = new_items;
1648+ // Apply the shift to the small_state_groups.
1649+ for ( range, _) in & mut tl. small_state_groups {
1650+ let new_start = ( range. start as i32 + shift) . max ( 0 ) as usize ;
1651+ let new_end = ( range. end as i32 + shift) . max ( 0 ) as usize ;
1652+ * range = new_start..new_end;
1653+ }
16471654 }
1648-
16491655 done_loading = true ;
16501656 }
16511657 TimelineUpdate :: NewUnreadMessagesCount ( unread_messages_count) => {
@@ -4024,7 +4030,7 @@ fn is_small_state_event(
40244030/// Dynamically updates small state groups as timeline items are processed.
40254031/// This function is called during populate_small_state_event to build groups on-demand.
40264032/// Since iteration starts from the biggest item_id and goes backwards, we handle reverse grouping.
4027- /// Returns a tuple whether to display the message, and whether to display the debug button and whether collapsible list is expanded.
4033+ /// Returns a tuple whether to display the message, and whether to display the collapsible button and whether collapsible list is expanded.
40284034fn update_small_state_groups_for_item (
40294035 item_id : usize ,
40304036 current_item : & EventTimelineItem ,
@@ -4040,21 +4046,17 @@ fn update_small_state_groups_for_item(
40404046
40414047 // check if the next item (item_id + 1) is a small state event to continue grouping
40424048 let next_item_is_small_state = next_item
4043- . and_then ( |timeline_item|
4044- match timeline_item. kind ( ) {
4045- TimelineItemKind :: Event ( event_tl_item) => Some ( event_tl_item) ,
4046- _ => None
4047- }
4048- )
4049+ . and_then ( |timeline_item| match timeline_item. kind ( ) {
4050+ TimelineItemKind :: Event ( event_tl_item) => Some ( event_tl_item) ,
4051+ _ => None ,
4052+ } )
40494053 . map ( is_small_state_event)
40504054 . unwrap_or ( false ) ;
40514055 let previous_item_is_small_state = previous_item
4052- . and_then ( |timeline_item|
4053- match timeline_item. kind ( ) {
4054- TimelineItemKind :: Event ( event_tl_item) => Some ( event_tl_item) ,
4055- _ => None
4056- }
4057- )
4056+ . and_then ( |timeline_item| match timeline_item. kind ( ) {
4057+ TimelineItemKind :: Event ( event_tl_item) => Some ( event_tl_item) ,
4058+ _ => None ,
4059+ } )
40584060 . map ( is_small_state_event)
40594061 . unwrap_or ( false ) ;
40604062 if !previous_item_is_small_state && !next_item_is_small_state {
@@ -4070,7 +4072,7 @@ fn update_small_state_groups_for_item(
40704072 return ( * is_open, false , * is_open) ; // Item is in group but not at start, no debug button
40714073 }
40724074 }
4073-
4075+
40744076 // Since we're iterating backwards (from highest to lowest item_id),
40754077 for ( range, is_open) in small_state_groups. iter_mut ( ) {
40764078 if range. start == item_id + 1 {
@@ -4083,7 +4085,8 @@ fn update_small_state_groups_for_item(
40834085 }
40844086 }
40854087 if next_item_is_small_state {
4086- small_state_groups. push ( ( item_id..item_id + 1 , false ) ) ;
4088+ // Plus to include the next item into the group.
4089+ small_state_groups. push ( ( item_id..( item_id + 2 ) , false ) ) ;
40874090 }
40884091 ( false , false , false ) // Return collapsed state, no debug button
40894092}
@@ -4323,22 +4326,29 @@ fn populate_small_state_event(
43234326 // - opened: whether this individual item should be rendered (based on group state)
43244327 // - show_collapsible_button: true if this item is the first in a collapsible group
43254328 // - expanded: current expansion state of the group (for button text)
4326- let ( opened, show_collapsible_button, expanded) = update_small_state_groups_for_item ( item_id, event_tl_item, prev_event, next_event, small_state_groups) ;
4327-
4329+ let ( opened, show_collapsible_button, expanded) = update_small_state_groups_for_item (
4330+ item_id,
4331+ event_tl_item,
4332+ prev_event,
4333+ next_event,
4334+ small_state_groups,
4335+ ) ;
43284336 // Only show the collapsible button on the first item of each group
4329- item. button ( id ! ( collapsible_button) ) . set_visible ( cx, show_collapsible_button) ;
4330-
4337+ item. button ( id ! ( collapsible_button) )
4338+ . set_visible ( cx, show_collapsible_button) ;
4339+
43314340 // Render logic based on group state
43324341 if opened {
43334342 // This item should be visible - set appropriate button text if this is a group leader
43344343 if show_collapsible_button {
43354344 // Update button text to show current group state:
43364345 // ▲ = group is expanded (click to collapse)
4337- // ▼ = group is collapsed (click to expand)
4346+ // ▼ = group is collapsed (click to expand)
43384347 let button_text = if expanded { "▲" } else { "▼" } ;
4339- item. button ( id ! ( collapsible_button) ) . set_text ( cx, button_text) ;
4348+ item. button ( id ! ( collapsible_button) )
4349+ . set_text ( cx, button_text) ;
43404350 }
4341-
4351+
43424352 // Render the actual event content
43434353 event_content. populate_item_content (
43444354 cx,
0 commit comments