Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 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
afbac0d
using rangemap
alanpoon Jan 2, 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);
}
104 changes: 95 additions & 9 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 @@ -324,17 +326,16 @@ live_design! {
SmallStateEvent = <View> {
width: Fill,
height: Fit,
flow: Right,
margin: { top: 4.0, bottom: 4.0}
padding: { top: 1.0, bottom: 1.0, right: 10.0 }
flow: Down,
spacing: 0.0
cursor: Default

small_state_header = <SmallStateHeader> { }
body = <View> {
width: Fill,
height: Fit
flow: Right,
padding: { left: 7.0, top: 2.0, bottom: 2.0 }
margin: { top: 4.0, bottom: 4.0}
padding: { left: 7.0, top: 2.0, bottom: 2.0, right: 10.0 }
spacing: 5.0

left_container = <View> {
Expand Down Expand Up @@ -368,6 +369,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 @@ -673,6 +675,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 @@ -995,35 +1012,43 @@ impl Widget for RoomScreen {
item_id,
room_id,
event_tl_item,
tl_items,
poll_state,
item_drawn_status,
&mut tl_state.group_manager,
),
MsgLikeKind::Redacted => populate_small_state_event(
cx,
list,
item_id,
room_id,
event_tl_item,
tl_items,
&RedactedMessageEventMarker,
item_drawn_status,
&mut tl_state.group_manager,
),
MsgLikeKind::UnableToDecrypt(utd) => populate_small_state_event(
cx,
list,
item_id,
room_id,
event_tl_item,
tl_items,
utd,
item_drawn_status,
&mut tl_state.group_manager,
),
MsgLikeKind::Other(other) => populate_small_state_event(
cx,
list,
item_id,
room_id,
event_tl_item,
tl_items,
other,
item_drawn_status,
&mut tl_state.group_manager,
),
},
TimelineItemContent::MembershipChange(membership_change) => populate_small_state_event(
Expand All @@ -1032,26 +1057,32 @@ impl Widget for RoomScreen {
item_id,
room_id,
event_tl_item,
tl_items,
membership_change,
item_drawn_status,
&mut tl_state.group_manager,
),
TimelineItemContent::ProfileChange(profile_change) => populate_small_state_event(
cx,
list,
item_id,
room_id,
event_tl_item,
tl_items,
profile_change,
item_drawn_status,
&mut tl_state.group_manager,
),
TimelineItemContent::OtherState(other) => populate_small_state_event(
cx,
list,
item_id,
room_id,
event_tl_item,
tl_items,
other,
item_drawn_status,
&mut tl_state.group_manager,
),
unhandled => {
let item = list.item(cx, item_id, id!(SmallStateEvent));
Expand Down Expand Up @@ -1259,9 +1290,27 @@ 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);
}
// Handle index changes and group updates based on the update type
if clear_cache {
// Clear cache indicates all indices changed (typically back pagination)
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,
);
}

tl.items = new_items;

// Update small state groups based on changed indices
tl.group_manager.analyze_and_update_groups(
&tl.items,
changed_indices.clone(),
clear_cache,
);
done_loading = true;
}
TimelineUpdate::NewUnreadMessagesCount(unread_messages_count) => {
Expand Down Expand Up @@ -2066,6 +2115,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 +2672,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 @@ -3962,9 +4015,13 @@ fn populate_small_state_event(
item_id: usize,
room_id: &OwnedRoomId,
event_tl_item: &EventTimelineItem,
tl_items: &Vector<Arc<TimelineItem>>,
event_content: &impl SmallStateEventContent,
item_drawn_status: ItemDrawnStatus,
group_manager: &mut small_state_group_manager::SmallStateGroupManager,
) -> (WidgetRef, ItemDrawnStatus) {
let prev_event = item_id.checked_sub(1).and_then(|i| tl_items.get(i));
let next_event = item_id.checked_add(1).and_then(|i| tl_items.get(i));
let mut new_drawn_status = item_drawn_status;
let (item, existed) = list.item_with_existed(cx, item_id, id!(SmallStateEvent));
// The content of a small state event view may depend on the profile info,
Expand Down Expand Up @@ -4000,9 +4057,27 @@ fn populate_small_state_event(
new_drawn_status.profile_drawn = profile_drawn;
username
});
// Dynamically determine group membership and update groups
// Returns: (show, show_collapsible_button, expanded)
// - show: whether this individual item should be rendered (based on group state)
// - show_collapsible_button: true if this item is the first in a collapsible group
// - expanded: current expansion state of the group (for button text)
let user_event = small_state_group_manager::convert_event_tl_item_to_user_event(event_tl_item, item_id);
let is_previous_small_state = small_state_group_manager::is_small_state(prev_event);
let is_next_small_state = small_state_group_manager::is_small_state(next_event);

let result = group_manager.compute_group_state(
username.clone(),
&user_event,
is_previous_small_state,
is_next_small_state,
);

// Proceed to draw the actual event content.
event_content.populate_item_content(
let (show, collapsible_button) = (result.show, result.collapsible_button);
// Only show the collapsible button on the first item of each group
item.view(ids!(small_state_header.collapsible_button_container))
.set_visible(cx, collapsible_button != small_state_group_manager::CollapsibleButton::None);
let (item, new_drawn_status) = event_content.populate_item_content(
cx,
list,
item_id,
Expand All @@ -4011,7 +4086,18 @@ fn populate_small_state_event(
&username,
item_drawn_status,
new_drawn_status,
)
);
// Handle rendering logic for group state using centralized function
group_manager.render_collapsible_button_and_body(
cx,
&item,
item_id,
show,
collapsible_button,
room_id,
);

(item, new_drawn_status)
}


Expand Down
Loading