Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7003e1a
collapsible small state improvement
alanpoon Sep 11, 2025
d5da199
Merge branch 'main' into collapsibe_small_state_#118
alanpoon Sep 28, 2025
b8f04ac
Merge branch 'main' into collapsible_small_state_#118_2
alanpoon Oct 10, 2025
1a79bc3
summary text
alanpoon Oct 10, 2025
688d355
summary_text
alanpoon Oct 11, 2025
faaa5f2
add debug
alanpoon Oct 13, 2025
c1e4061
Fix user_events iteration in summary text
alanpoon Oct 13, 2025
f9c760f
remove empty space
alanpoon Oct 14, 2025
0747521
Do not show collapsible button when range is <=2
alanpoon Oct 15, 2025
7df3f34
not_empty
alanpoon Oct 15, 2025
b9a3eb0
fix with content_drawn
alanpoon Oct 16, 2025
5d9ab19
before changing hashmap key from sender to state_key
alanpoon Oct 17, 2025
2562019
collapsible
alanpoon Oct 17, 2025
f218892
after some AI
alanpoon Oct 18, 2025
65e55ad
minor improvement
alanpoon Oct 18, 2025
899668c
Merge branch 'main' into collapsible_small_state_#118_2
alanpoon Nov 5, 2025
32726d2
added CreationCollapsibleList
alanpoon Nov 28, 2025
4714160
Merge branch 'main' into collapsible_small_state_#118_2
alanpoon Dec 1, 2025
5eafd5b
Added small state group manager
alanpoon Dec 5, 2025
ee7ebab
optimize function name
alanpoon Dec 8, 2025
6d446f7
added TimelineGroupService
alanpoon Dec 8, 2025
826ac32
Merge branch 'main' into collapsible_small_state_#118_2
alanpoon Dec 23, 2025
e9869d4
SmallStateGroup improvement
alanpoon Dec 25, 2025
b3fbd7f
Merge branch 'main' into collapsible_small_state_#118_2
alanpoon Dec 25, 2025
74a9767
fix clippy
alanpoon Dec 25, 2025
b346b81
fix typo
alanpoon Dec 25, 2025
3b76b26
compute_group_state_2
alanpoon Jan 5, 2026
9127772
compute_group_state early
alanpoon Jan 6, 2026
5d6d59f
added smallstate header
alanpoon Jan 7, 2026
080c81e
draw
alanpoon Jan 7, 2026
2ac0664
loop
alanpoon Jan 7, 2026
d792363
fix check_group_header_status
alanpoon Jan 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/home/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod loading_pane;
pub mod location_preview;
pub mod main_desktop_ui;
pub mod main_mobile_ui;
pub mod small_state_group_manager;
pub mod room_screen;
pub mod room_read_receipt;
pub mod rooms_list;
Expand Down Expand Up @@ -51,4 +52,5 @@ pub fn live_design(cx: &mut Cx) {
light_themed_dock::live_design(cx);
event_reaction_list::live_design(cx);
link_preview::live_design(cx);
small_state_group_manager::live_design(cx);
}
79 changes: 74 additions & 5 deletions src/home/room_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use crate::{
};
use crate::home::event_reaction_list::ReactionListWidgetRefExt;
use crate::home::room_read_receipt::AvatarRowWidgetRefExt;
use crate::home::small_state_group_manager;
use crate::room::room_input_bar::RoomInputBarWidgetExt;
use crate::shared::mentionable_text_input::MentionableTextInputAction;

Expand Down Expand Up @@ -78,6 +79,7 @@ live_design! {
use crate::rooms_list::*;
use crate::shared::restore_status_view::*;
use crate::home::link_preview::LinkPreview;
use crate::home::small_state_group_manager::SmallStateHeader;
use link::tsp_link::TspSignIndicator;

COLOR_BG = #xfff8ee
Expand Down Expand Up @@ -368,6 +370,7 @@ live_design! {
}
text: ""
}

// Center the Avatar vertically with respect to the SmallStateEvent content.
avatar_row = <AvatarRow> { margin: {top: -1.0} }
}
Expand Down Expand Up @@ -468,6 +471,7 @@ live_design! {
Empty = <Empty> {}
DateDivider = <DateDivider> {}
ReadMarker = <ReadMarker> {}
SmallStateHeader = <SmallStateHeader> {}
}

// A jump to bottom button (with an unread message badge) that is shown
Expand Down Expand Up @@ -673,6 +677,21 @@ impl Widget for RoomScreen {
);
continue;
}
// Handle collapsible button click in SmallStateEvent
if wr.button(ids!(collapsible_button)).clicked(actions) {
if let Some(tl_state) = &mut self.tl_state {
small_state_group_manager::handle_collapsible_button_click(
cx,
&wr,
index,
&portal_list,
&mut tl_state.group_manager,
&mut tl_state.content_drawn_since_last_update,
&mut tl_state.profile_drawn_since_last_update,
tl_state.items.len(),
);
}
}
}

self.handle_message_actions(cx, actions, &portal_list, &loading_pane);
Expand Down Expand Up @@ -944,15 +963,31 @@ impl Widget for RoomScreen {
let room_id = &tl_state.room_id;
let tl_items = &tl_state.items;

// Set the portal list's range based on the number of timeline items.
let last_item_id = tl_items.len();
// Set the portal list's range based on the number of timeline items,
// accounting for small state groups (collapsed groups take 1 slot each)
let base_item_count = tl_items.len();
let group_count = tl_state.group_manager.small_state_groups.len();
println!("group_count: {}, base_item_count: {}", group_count, base_item_count);
let last_item_id = base_item_count + group_count;

let list = list_ref.deref_mut();
list.set_item_range(cx, 0, last_item_id);

while let Some(item_id) = list.next_visible_item(cx) {
// Check if this item is a group header and get the count of groups before it
let (is_header, groups_before) = tl_state.group_manager.check_group_header_status(item_id);

if is_header {
println!("is_heade {:?} groups_before {:?}", item_id, groups_before);
// This is the first item in a small state group, populate SmallStateHeader
let (item, _existed) = tl_state.group_manager.populate_small_state_header(cx, list, item_id, groups_before, room_id);
item.draw_all(cx, scope);
continue;
}

let item = {
let tl_idx = item_id;
//println!("groups_before: {} item_id: {}", groups_before, item_id);
let tl_idx = item_id.saturating_sub(groups_before);
let Some(timeline_item) = tl_items.get(tl_idx) else {
// This shouldn't happen (unless the timeline gets corrupted or some other weird error),
// but we can always safely fill the item with an empty widget that takes up no space.
Expand Down Expand Up @@ -1149,10 +1184,21 @@ impl RoomScreen {
portal_list.set_tail_range(true);
jump_to_bottom.update_visibility(cx, true);

// Compute small state groups for initial items
let small_state_events = small_state_group_manager::extract_small_state_events(initial_items.iter().cloned());
if tl.room_id.to_string() == "!MhCFIYPPVRVgyyvRWK:matrix.org" {
println!("FirstUpdate: small_state_events: {:?}", small_state_events);
}
tl.group_manager.compute_group_state_2(small_state_events);
if tl.room_id.to_string() == "!MhCFIYPPVRVgyyvRWK:matrix.org" {
println!("FirstUpdate: computed group state {:?}", tl.group_manager);
}

tl.items = initial_items;
done_loading = true;
}
TimelineUpdate::NewItems { new_items, changed_indices, is_append, clear_cache } => {
return;
if new_items.is_empty() {
if !tl.items.is_empty() {
log!("process_timeline_updates(): timeline (had {} items) was cleared for room {}", tl.items.len(), tl.room_id);
Expand Down Expand Up @@ -1259,8 +1305,28 @@ impl RoomScreen {
} else {
tl.content_drawn_since_last_update.remove(changed_indices.clone());
tl.profile_drawn_since_last_update.remove(changed_indices.clone());
// log!("process_timeline_updates(): changed_indices: {changed_indices:?}, items len: {}\ncontent drawn: {:#?}\nprofile drawn: {:#?}", items.len(), tl.content_drawn_since_last_update, tl.profile_drawn_since_last_update);
}
// Handles item_id changes whenever there is a backward pagination.
if !is_append {
let old_len = tl.items.len();
let new_len = new_items.len();
let shift = new_len.saturating_sub(old_len) as i32;
small_state_group_manager::handle_backward_pagination_index_shift(
shift,
&mut tl.group_manager,
);
}

// Compute small state groups for new items
let small_state_events = small_state_group_manager::extract_small_state_events(new_items.iter().cloned());
if tl.room_id.to_string() == "!UrPVVKTBTiyKLvSgIw:matrix.org" {
println!("NewItems: computed group state for {:?}", small_state_events);
}
tl.group_manager.compute_group_state_2(small_state_events);
if tl.room_id.to_string() == "!UrPVVKTBTiyKLvSgIw:matrix.org" {
println!("NewItems: computed group state for tl.group_manager {:?} done", tl.group_manager);
}

tl.items = new_items;
done_loading = true;
}
Expand Down Expand Up @@ -2066,6 +2132,7 @@ impl RoomScreen {
scrolled_past_read_marker: false,
latest_own_user_receipt: None,
tombstone_info,
group_manager: small_state_group_manager::SmallStateGroupManager::default(),
};
(tl_state, true)
};
Expand Down Expand Up @@ -2622,6 +2689,9 @@ struct TimelineUiState {
/// If `Some`, this room has been tombstoned and the details of its successor room
/// are contained within. If `None`, the room has not been tombstoned.
tombstone_info: Option<SuccessorRoomDetails>,

/// Manager for small state groups, room creation info, and creation collapsible list.
group_manager: small_state_group_manager::SmallStateGroupManager,
}

#[derive(Default, Debug)]
Expand Down Expand Up @@ -4014,7 +4084,6 @@ fn populate_small_state_event(
)
}


/// Returns the display name of the sender of the given `event_tl_item`, if available.
fn get_profile_display_name(event_tl_item: &EventTimelineItem) -> Option<String> {
if let TimelineDetails::Ready(profile) = event_tl_item.sender_profile() {
Expand Down
Loading
Loading