Skip to content

Commit c44f833

Browse files
committed
Make SpaceLobbyScreen tree generation much more efficient (less cloning)
Move `AvatarStatus` out of `user_profile` module since it's now used for not just User avatars but also Room/Space avatars.
1 parent fc5216c commit c44f833

8 files changed

Lines changed: 127 additions & 79 deletions

File tree

src/home/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use makepad_widgets::{Cx, LiveRegister};
1+
use makepad_widgets::Cx;
22

33
pub mod add_room;
44
pub mod edited_indicator;
@@ -34,7 +34,6 @@ pub fn live_design(cx: &mut Cx) {
3434
location_preview::live_design(cx);
3535
add_room::live_design(cx);
3636
space_lobby::live_design(cx);
37-
space_lobby::DrawTreeLine::live_register(cx);
3837
rooms_list_entry::live_design(cx);
3938
rooms_list_header::live_design(cx);
4039
rooms_list::live_design(cx);

src/home/navigation_tab_bar.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
use makepad_widgets::*;
3232
use crate::{
3333
avatar_cache::{self, AvatarCacheEntry}, login::login_screen::LoginAction, logout::logout_confirm_modal::LogoutAction, profile::{
34-
user_profile::{AvatarState, UserProfile},
34+
user_profile::UserProfile,
3535
user_profile_cache::{self, UserProfileUpdate},
3636
}, shared::{
37-
avatar::AvatarWidgetExt,
37+
avatar::{AvatarState, AvatarWidgetExt},
3838
callout_tooltip::{CalloutTooltipOptions, TooltipAction, TooltipPosition},
3939
styles::*,
4040
verification_badge::VerificationBadgeWidgetExt,

src/home/room_screen.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ use ruma::{OwnedUserId, events::{AnySyncMessageLikeEvent, AnySyncTimelineEvent,
2626

2727
use crate::{
2828
app::AppStateAction, avatar_cache, event_preview::{plaintext_body_of_timeline_item, text_preview_of_encrypted_message, text_preview_of_member_profile_change, text_preview_of_other_message_like, text_preview_of_other_state, text_preview_of_room_membership_change, text_preview_of_timeline_item}, home::{edited_indicator::EditedIndicatorWidgetRefExt, link_preview::{LinkPreviewCache, LinkPreviewRef, LinkPreviewWidgetRefExt}, loading_pane::{LoadingPaneState, LoadingPaneWidgetExt}, room_image_viewer::{get_image_name_and_filesize, populate_matrix_image_modal}, rooms_list::RoomsListRef, tombstone_footer::SuccessorRoomDetails}, media_cache::{MediaCache, MediaCacheEntry}, profile::{
29-
user_profile::{AvatarState, ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt},
29+
user_profile::{ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt},
3030
user_profile_cache,
3131
},
3232
room::{BasicRoomDetails, room_input_bar::RoomInputBarState, typing_notice::TypingNoticeWidgetExt},
3333
shared::{
34-
avatar::AvatarWidgetRefExt, callout_tooltip::{CalloutTooltipOptions, TooltipAction, TooltipPosition}, confirmation_modal::ConfirmationModalContent, html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt, RobrixHtmlLinkAction}, image_viewer::{ImageViewerAction, ImageViewerMetaData, LoadState}, jump_to_bottom_button::{JumpToBottomButtonWidgetExt, UnreadMessageCount}, popup_list::{PopupItem, PopupKind, enqueue_popup_notification}, restore_status_view::RestoreStatusViewWidgetExt, styles::*, text_or_image::{TextOrImageAction, TextOrImageRef, TextOrImageWidgetRefExt}, timestamp::TimestampWidgetRefExt
34+
avatar::{AvatarState, AvatarWidgetRefExt}, callout_tooltip::{CalloutTooltipOptions, TooltipAction, TooltipPosition}, confirmation_modal::ConfirmationModalContent, html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt, RobrixHtmlLinkAction}, image_viewer::{ImageViewerAction, ImageViewerMetaData, LoadState}, jump_to_bottom_button::{JumpToBottomButtonWidgetExt, UnreadMessageCount}, popup_list::{PopupItem, PopupKind, enqueue_popup_notification}, restore_status_view::RestoreStatusViewWidgetExt, styles::*, text_or_image::{TextOrImageAction, TextOrImageRef, TextOrImageWidgetRefExt}, timestamp::TimestampWidgetRefExt
3535
},
3636
sliding_sync::{BackwardsPaginateUntilEventRequest, MatrixRequest, PaginationDirection, TimelineEndpoints, TimelineRequestSender, UserPowerLevels, get_client, submit_async_request, take_timeline_endpoints}, utils::{self, ImageFormat, MEDIA_THUMBNAIL_FORMAT, RoomNameId, unix_time_millis_to_datetime}
3737
};

src/home/space_lobby.rs

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
use std::collections::{HashMap, HashSet};
1010
use imbl::Vector;
1111
use makepad_widgets::*;
12-
use matrix_sdk::ruma::OwnedRoomId;
12+
use matrix_sdk::{RoomState, ruma::OwnedRoomId};
1313
use matrix_sdk_ui::spaces::SpaceRoom;
14+
use ruma::room::JoinRuleSummary;
1415
use tokio::sync::mpsc::UnboundedSender;
1516
use crate::{
16-
home::rooms_list::RoomsListRef,
17-
shared::avatar::{AvatarWidgetExt, AvatarWidgetRefExt},
18-
space_service_sync::{SpaceRequest, SpaceRoomExt, SpaceRoomListAction},
19-
utils::{self, RoomNameId},
17+
home::rooms_list::RoomsListRef, shared::avatar::{AvatarState, AvatarWidgetExt, AvatarWidgetRefExt}, space_service_sync::{SpaceRequest, SpaceRoomExt, SpaceRoomListAction}, utils::{self, RoomNameId}
2018
};
2119

2220

@@ -618,15 +616,63 @@ impl Widget for RoomEntry {
618616
}
619617
}
620618

619+
/// The subset of info in [`SpaceRoom`] that we display for each room/space.
620+
struct SpaceRoomInfo {
621+
id: OwnedRoomId,
622+
name: String,
623+
#[allow(unused)]
624+
topic: Option<String>,
625+
#[allow(unused)]
626+
room_avatar: AvatarState,
627+
num_joined_members: u64,
628+
#[allow(unused)]
629+
state: Option<RoomState>,
630+
#[allow(unused)]
631+
join_rule: Option<JoinRuleSummary>,
632+
/// If `Some`, this is a space. If `None`, it's a room.
633+
children_count: Option<u64>,
634+
}
635+
impl SpaceRoomInfo {
636+
fn is_space(&self) -> bool {
637+
self.children_count.is_some()
638+
}
639+
}
640+
impl From<&SpaceRoom> for SpaceRoomInfo {
641+
fn from(space_room: &SpaceRoom) -> Self {
642+
SpaceRoomInfo {
643+
id: space_room.room_id.clone(),
644+
name: space_room.display_name.clone(),
645+
topic: space_room.topic.clone(),
646+
room_avatar: AvatarState::Known(space_room.avatar_url.clone()),
647+
num_joined_members: space_room.num_joined_members,
648+
state: space_room.state.clone(),
649+
join_rule: space_room.join_rule.clone(),
650+
children_count: space_room.is_space().then_some(space_room.children_count),
651+
}
652+
}
653+
}
654+
impl From<SpaceRoom> for SpaceRoomInfo {
655+
fn from(space_room: SpaceRoom) -> Self {
656+
SpaceRoomInfo {
657+
children_count: space_room.is_space().then_some(space_room.children_count),
658+
id: space_room.room_id,
659+
name: space_room.display_name,
660+
topic: space_room.topic,
661+
room_avatar: AvatarState::Known(space_room.avatar_url),
662+
num_joined_members: space_room.num_joined_members,
663+
state: space_room.state,
664+
join_rule: space_room.join_rule,
665+
}
666+
}
667+
}
621668

622669
/// An entry in the tree to be displayed.
623-
#[derive(Clone, Debug)]
624670
#[allow(clippy::large_enum_variant)]
625671
enum TreeEntry {
626672
/// A regular space or room entry.
627673
Item {
628-
/// The detailed info about this space or room.
629-
info: SpaceRoom,
674+
/// The info needed to display this space or room.
675+
info: SpaceRoomInfo,
630676
/// The nesting level (0 = direct child of the displayed space).
631677
level: usize,
632678
/// Whether this entry is the last child of its parent.
@@ -685,7 +731,7 @@ impl Widget for SpaceLobbyScreen {
685731
if let SubspaceEntryAction::Clicked { space_id: room_id } = action.as_widget_action().cast() {
686732
self.toggle_space_expansion(cx, &room_id);
687733
}
688-
734+
689735
// Handle RoomEntry clicks
690736
if let RoomEntryAction::Clicked { room_id: _ } = action.as_widget_action().cast() {
691737
// TODO: Navigate to the room
@@ -728,7 +774,7 @@ impl Widget for SpaceLobbyScreen {
728774
if info.is_space() {
729775
let item = list.item(cx, item_id, id!(subspace_entry));
730776
if let Some(mut inner) = item.borrow_mut::<SubspaceEntry>() {
731-
inner.space_id = Some(info.room_id.clone());
777+
inner.space_id = Some(info.id.clone());
732778
}
733779

734780
// Configure tree lines
@@ -740,21 +786,21 @@ impl Widget for SpaceLobbyScreen {
740786
}
741787

742788
// Expand icon
743-
let is_expanded = self.expanded_spaces.contains(&info.room_id);
789+
let is_expanded = self.expanded_spaces.contains(&info.id);
744790
let angle = if is_expanded { 180.0 } else { 90.0 };
745791
item.icon(ids!(expand_icon)).apply_over(cx, live! {
746792
draw_icon: { rotation_angle: (angle) }
747793
});
748794

749795
// Avatar
750796
let avatar_ref = item.avatar(ids!(avatar));
751-
let first_char = utils::user_name_first_letter(&info.display_name);
797+
let first_char = utils::user_name_first_letter(&info.name);
752798
avatar_ref.show_text(cx, None, None, first_char.unwrap_or("#"));
753799

754800
// Text
755-
item.label(ids!(content.name_label)).set_text(cx, &info.display_name);
756-
let info_text = if info.children_count > 0 {
757-
format!("{} members · {} rooms", info.num_joined_members, info.children_count)
801+
item.label(ids!(content.name_label)).set_text(cx, &info.name);
802+
let info_text = if let Some(c) = info.children_count && c > 0 {
803+
format!("{} members · {} rooms", info.num_joined_members, c)
758804
} else {
759805
format!("{} members", info.num_joined_members)
760806
};
@@ -764,7 +810,7 @@ impl Widget for SpaceLobbyScreen {
764810
} else {
765811
let item = list.item(cx, item_id, id!(room_entry));
766812
if let Some(mut inner) = item.borrow_mut::<RoomEntry>() {
767-
inner.room_id = Some(info.room_id.clone());
813+
inner.room_id = Some(info.id.clone());
768814
}
769815

770816
// Configure tree lines
@@ -777,11 +823,11 @@ impl Widget for SpaceLobbyScreen {
777823

778824
// Avatar
779825
let avatar_ref = item.avatar(ids!(avatar));
780-
let first_char = utils::user_name_first_letter(&info.display_name);
826+
let first_char = utils::user_name_first_letter(&info.name);
781827
avatar_ref.show_text(cx, None, None, first_char.unwrap_or("#"));
782828

783829
// Text
784-
item.label(ids!(content.name_label)).set_text(cx, &info.display_name);
830+
item.label(ids!(content.name_label)).set_text(cx, &info.name);
785831
let info_text = format!("{} members", info.num_joined_members);
786832
item.label(ids!(content.info_label)).set_text(cx, &info_text);
787833

@@ -882,8 +928,8 @@ impl SpaceLobbyScreen {
882928
/// can be displayed in the SpaceLobbyScreen's PortalList.
883929
//
884930
// Note: this is intentionally *not* a method (it doesn't take &mut self),
885-
// in order to make it possible to recursively call it while borrowing only select
886-
// fields of `Self`.
931+
// in order to make it possible to recursively call it while immutably borrowing
932+
// only select fields of `Self`.
887933
fn build_tree_for_space(
888934
children_cache: &HashMap<OwnedRoomId, Vector<SpaceRoom>>,
889935
expanded_spaces: &HashSet<OwnedRoomId>,
@@ -907,11 +953,11 @@ impl SpaceLobbyScreen {
907953

908954

909955
let count = sorted_children.len();
910-
for (i, child) in sorted_children.iter().enumerate() {
956+
for (i, child) in sorted_children.into_iter().enumerate() {
911957
let is_last = i == count - 1;
912958

913959
tree_entries.push(TreeEntry::Item {
914-
info: (*child).clone(),
960+
info: SpaceRoomInfo::from(child),
915961
level,
916962
is_last,
917963
parent_mask,

src/profile/user_profile.rs

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,13 @@
1-
use std::{borrow::Cow, ops::{Deref, DerefMut}, sync::Arc};
1+
//! Widgets and types related to displaying info about a user profile.
2+
3+
use std::{borrow::Cow, ops::{Deref, DerefMut}};
24
use makepad_widgets::*;
3-
use matrix_sdk::{room::{RoomMember, RoomMemberRole}, ruma::{events::room::member::MembershipState, OwnedMxcUri, OwnedRoomId, OwnedUserId}};
5+
use matrix_sdk::{room::{RoomMember, RoomMemberRole}, ruma::{events::room::member::MembershipState, OwnedRoomId, OwnedUserId}};
46
use crate::{
5-
avatar_cache::{self, AvatarCacheEntry}, shared::{avatar::AvatarWidgetExt, popup_list::{enqueue_popup_notification, PopupItem, PopupKind}}, sliding_sync::{current_user_id, is_user_ignored, submit_async_request, MatrixRequest}, utils
7+
avatar_cache::{self, AvatarCacheEntry}, shared::{avatar::{AvatarState, AvatarWidgetExt}, popup_list::{PopupItem, PopupKind, enqueue_popup_notification}}, sliding_sync::{MatrixRequest, current_user_id, is_user_ignored, submit_async_request}, utils
68
};
79
use super::user_profile_cache;
810

9-
/// The currently-known state of a user's avatar.
10-
#[derive(Clone)]
11-
pub enum AvatarState {
12-
/// It isn't yet known if this user has an avatar.
13-
Unknown,
14-
/// It is known that this user does or does not have an avatar.
15-
Known(Option<OwnedMxcUri>),
16-
/// This user does have an avatar, and it has been fetched successfully.
17-
Loaded(Arc<[u8]>),
18-
/// This user does have an avatar, but we failed to fetch it.
19-
Failed,
20-
}
21-
impl std::fmt::Debug for AvatarState {
22-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23-
match self {
24-
AvatarState::Unknown => write!(f, "Unknown"),
25-
AvatarState::Known(Some(_)) => write!(f, "Known(Some)"),
26-
AvatarState::Known(None) => write!(f, "Known(None)"),
27-
AvatarState::Loaded(data) => write!(f, "Loaded({} bytes)", data.len()),
28-
AvatarState::Failed => write!(f, "Failed"),
29-
}
30-
}
31-
}
32-
impl AvatarState {
33-
/// Returns the avatar data, if in the `Loaded` state.
34-
pub fn data(&self) -> Option<&Arc<[u8]>> {
35-
if let AvatarState::Loaded(data) = self {
36-
Some(data)
37-
} else {
38-
None
39-
}
40-
}
41-
42-
/// Returns the avatar URI, if in the `Known` state and it exists.
43-
pub fn uri(&self) -> Option<&OwnedMxcUri> {
44-
if let AvatarState::Known(Some(uri)) = self {
45-
Some(uri)
46-
} else {
47-
None
48-
}
49-
}
50-
}
5111

5212
/// Information retrieved about a user: their displayable name, ID, and known avatar state.
5313
#[derive(Clone, Debug)]

src/profile/user_profile_cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use makepad_widgets::{warning, Cx, SignalToUI};
77
use matrix_sdk::{room::RoomMember, ruma::{OwnedRoomId, OwnedUserId, UserId}};
88
use std::{cell::RefCell, collections::{btree_map::Entry, BTreeMap}};
99

10-
use crate::{profile::user_profile::AvatarState, sliding_sync::{submit_async_request, MatrixRequest}};
10+
use crate::{shared::avatar::AvatarState, sliding_sync::{submit_async_request, MatrixRequest}};
1111

1212
use super::user_profile::UserProfile;
1313

src/shared/avatar.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ use std::sync::Arc;
1111
use makepad_widgets::*;
1212
use matrix_sdk::{ruma::{EventId, OwnedRoomId, OwnedUserId, RoomId, UserId}};
1313
use matrix_sdk_ui::timeline::{Profile, TimelineDetails};
14+
use ruma::OwnedMxcUri;
1415

1516
use crate::{
16-
avatar_cache::{self, AvatarCacheEntry}, profile::{user_profile::{AvatarState, ShowUserProfileAction, UserProfile, UserProfileAndRoomId}, user_profile_cache}, sliding_sync::{submit_async_request, MatrixRequest}, utils
17+
avatar_cache::{self, AvatarCacheEntry}, profile::{user_profile::{ShowUserProfileAction, UserProfile, UserProfileAndRoomId}, user_profile_cache}, sliding_sync::{submit_async_request, MatrixRequest}, utils
1718
};
1819

1920
live_design! {
@@ -450,3 +451,47 @@ impl From<(OwnedUserId, Option<String>, OwnedRoomId, Arc<[u8]>)> for AvatarImage
450451
Self { user_id, username, room_id, img_data }
451452
}
452453
}
454+
455+
456+
/// The currently-known state of an avatar for a user, room, or space.
457+
#[derive(Clone)]
458+
pub enum AvatarState {
459+
/// It isn't yet known if this user/room/space has an avatar.
460+
Unknown,
461+
/// It is known that this user/room/space does or does not have an avatar.
462+
Known(Option<OwnedMxcUri>),
463+
/// The avatar is known to exist and has been fetched successfully.
464+
Loaded(Arc<[u8]>),
465+
/// The avatar is known to exist but could not be fetched.
466+
Failed,
467+
}
468+
impl std::fmt::Debug for AvatarState {
469+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470+
match self {
471+
AvatarState::Unknown => write!(f, "Unknown"),
472+
AvatarState::Known(Some(_)) => write!(f, "Known(Some)"),
473+
AvatarState::Known(None) => write!(f, "Known(None)"),
474+
AvatarState::Loaded(data) => write!(f, "Loaded({} bytes)", data.len()),
475+
AvatarState::Failed => write!(f, "Failed"),
476+
}
477+
}
478+
}
479+
impl AvatarState {
480+
/// Returns the avatar data, if in the `Loaded` state.
481+
pub fn data(&self) -> Option<&Arc<[u8]>> {
482+
if let AvatarState::Loaded(data) = self {
483+
Some(data)
484+
} else {
485+
None
486+
}
487+
}
488+
489+
/// Returns the avatar URI, if in the `Known` state and it exists.
490+
pub fn uri(&self) -> Option<&OwnedMxcUri> {
491+
if let AvatarState::Known(Some(uri)) = self {
492+
Some(uri)
493+
} else {
494+
None
495+
}
496+
}
497+
}

src/sliding_sync.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ use crate::{
3232
app::AppStateAction, app_data_dir, avatar_cache::AvatarUpdate, event_preview::text_preview_of_timeline_item, home::{
3333
add_room::KnockResultAction, invite_screen::{JoinRoomResultAction, LeaveRoomResultAction}, link_preview::{LinkPreviewData, LinkPreviewDataNonNumeric, LinkPreviewRateLimitResponse}, room_screen::TimelineUpdate, rooms_list::{self, InvitedRoomInfo, InviterInfo, JoinedRoomInfo, RoomsListUpdate, enqueue_rooms_list_update}, rooms_list_header::RoomsListHeaderAction, tombstone_footer::SuccessorRoomDetails
3434
}, login::login_screen::LoginAction, logout::{logout_confirm_modal::LogoutAction, logout_state_machine::{LogoutConfig, is_logout_in_progress, logout_with_state_machine}}, media_cache::{MediaCacheEntry, MediaCacheEntryRef}, persistence::{self, ClientSessionPersisted, load_app_state}, profile::{
35-
user_profile::{AvatarState, UserProfile},
35+
user_profile::UserProfile,
3636
user_profile_cache::{UserProfileUpdate, enqueue_user_profile_update},
3737
}, room::{FetchedRoomAvatar, FetchedRoomPreview, RoomPreviewAction}, shared::{
38-
html_or_plaintext::MatrixLinkPillState,
39-
jump_to_bottom_button::UnreadMessageCount,
40-
popup_list::{PopupItem, PopupKind, enqueue_popup_notification}
38+
avatar::AvatarState, html_or_plaintext::MatrixLinkPillState, jump_to_bottom_button::UnreadMessageCount, popup_list::{PopupItem, PopupKind, enqueue_popup_notification}
4139
}, space_service_sync::space_service_loop, utils::{self, AVATAR_THUMBNAIL_FORMAT, RoomNameId, avatar_from_room_name}, verification::add_verification_event_handlers_and_sync_client
4240
};
4341

0 commit comments

Comments
 (0)