66//! that allows the user to click on it to show the `SpaceLobby`.
77//!
88
9- use std:: { collections:: { HashMap , HashSet } , sync:: Arc } ;
9+ use std:: collections:: { HashMap , HashSet } ;
10+ use imbl:: Vector ;
1011use makepad_widgets:: * ;
1112use matrix_sdk:: ruma:: OwnedRoomId ;
13+ use matrix_sdk_ui:: spaces:: SpaceRoom ;
1214use tokio:: sync:: mpsc:: UnboundedSender ;
1315use crate :: {
1416 home:: rooms_list:: RoomsListRef ,
1517 shared:: avatar:: { AvatarWidgetExt , AvatarWidgetRefExt } ,
16- space_service_sync:: { ParentChain , SpaceRequest , SpaceRoomInfo , SpaceRoomListAction } ,
18+ space_service_sync:: { SpaceRequest , SpaceRoomExt , SpaceRoomListAction } ,
1719 utils:: { self , RoomNameId } ,
1820} ;
1921
@@ -433,6 +435,7 @@ live_design! {
433435 // The hierarchical tree list
434436 tree_list = <PortalList > {
435437 keep_invisible: false ,
438+ max_pull_down: 0.0 ,
436439 auto_tail: false ,
437440 width: Fill , height: Fill
438441 flow: Down ,
@@ -518,11 +521,7 @@ pub struct TreeLines {
518521}
519522
520523impl Widget for TreeLines {
521- fn handle_event ( & mut self , cx : & mut Cx , event : & Event , _scope : & mut Scope ) {
522- // No interaction needed
523- let _ = event;
524- let _ = cx;
525- }
524+ fn handle_event ( & mut self , _cx : & mut Cx , _event : & Event , _scope : & mut Scope ) { }
526525
527526 fn draw_walk ( & mut self , cx : & mut Cx2d , _scope : & mut Scope , walk : Walk ) -> DrawStep {
528527 let indent_pixel = ( self . draw_bg . level + 1.0 ) * self . draw_bg . indent_width ;
@@ -539,7 +538,7 @@ impl Widget for TreeLines {
539538pub struct SubspaceEntry {
540539 #[ deref] view : View ,
541540 #[ animator] animator : Animator ,
542- #[ rust] room_id : Option < OwnedRoomId > ,
541+ #[ rust] space_id : Option < OwnedRoomId > ,
543542}
544543
545544/// A clickable entry for a child room.
@@ -553,7 +552,7 @@ pub struct RoomEntry {
553552/// Action emitted when a subspace entry is clicked.
554553#[ derive( Clone , Debug , DefaultNone ) ]
555554pub enum SubspaceEntryAction {
556- Clicked { room_id : OwnedRoomId } ,
555+ Clicked { space_id : OwnedRoomId } ,
557556 None ,
558557}
559558
@@ -575,9 +574,12 @@ impl Widget for SubspaceEntry {
575574 Hit :: FingerHoverOut ( _) => { self . animator_play ( cx, ids ! ( hover. off) ) ; }
576575 Hit :: FingerDown ( _) => { cx. set_key_focus ( self . view . area ( ) ) ; }
577576 Hit :: FingerUp ( fe) if fe. is_over && fe. is_primary_hit ( ) && fe. was_tap ( ) => {
578- if let Some ( room_id) = & self . room_id {
579- cx. widget_action ( self . widget_uid ( ) , & scope. path ,
580- SubspaceEntryAction :: Clicked { room_id : room_id. clone ( ) } ) ;
577+ if let Some ( space_id) = self . space_id . clone ( ) {
578+ cx. widget_action (
579+ self . widget_uid ( ) ,
580+ & scope. path ,
581+ SubspaceEntryAction :: Clicked { space_id } ,
582+ ) ;
581583 }
582584 }
583585 _ => { }
@@ -601,9 +603,9 @@ impl Widget for RoomEntry {
601603 Hit :: FingerHoverOut ( _) => { self . animator_play ( cx, ids ! ( hover. off) ) ; }
602604 Hit :: FingerDown ( _) => { cx. set_key_focus ( self . view . area ( ) ) ; }
603605 Hit :: FingerUp ( fe) if fe. is_over && fe. is_primary_hit ( ) && fe. was_tap ( ) => {
604- if let Some ( room_id) = & self . room_id {
606+ if let Some ( room_id) = self . room_id . clone ( ) {
605607 cx. widget_action ( self . widget_uid ( ) , & scope. path ,
606- RoomEntryAction :: Clicked { room_id : room_id . clone ( ) } ) ;
608+ RoomEntryAction :: Clicked { room_id } ) ;
607609 }
608610 }
609611 _ => { }
@@ -617,13 +619,14 @@ impl Widget for RoomEntry {
617619}
618620
619621
620- /// An entry in the flattened tree to be displayed.
622+ /// An entry in the tree to be displayed.
621623#[ derive( Clone , Debug ) ]
624+ #[ allow( clippy:: large_enum_variant) ]
622625enum TreeEntry {
623626 /// A regular space or room entry.
624627 Item {
625628 /// The detailed info about this space or room.
626- info : SpaceRoomInfo ,
629+ info : SpaceRoom ,
627630 /// The nesting level (0 = direct child of the displayed space).
628631 level : usize ,
629632 /// Whether this entry is the last child of its parent.
@@ -653,12 +656,12 @@ pub struct SpaceLobbyScreen {
653656
654657 /// Cache of detailed children for each space we've fetched.
655658 /// Key is the space_id, value is the list of its direct children.
656- #[ rust] children_cache : HashMap < OwnedRoomId , Arc < Vec < SpaceRoomInfo > > > ,
659+ #[ rust] children_cache : HashMap < OwnedRoomId , Vector < SpaceRoom > > ,
657660
658661 /// The set of space IDs that are currently expanded (showing their children).
659662 #[ rust] expanded_spaces : HashSet < OwnedRoomId > ,
660663
661- /// The flattened list of tree entries to display.
664+ /// The ordered list of children to display in the space tree .
662665 #[ rust] tree_entries : Vec < TreeEntry > ,
663666
664667 /// The set of space IDs that are currently loading their children.
@@ -672,16 +675,14 @@ impl Widget for SpaceLobbyScreen {
672675 fn handle_event ( & mut self , cx : & mut Cx , event : & Event , scope : & mut Scope ) {
673676 self . view . handle_event ( cx, event, scope) ;
674677
675- // Listen for actions from the space service.
676678 if let Event :: Actions ( actions) = event {
677679 for action in actions {
678- // Handle detailed children response
679680 if let Some ( SpaceRoomListAction :: DetailedChildren { space_id, children, .. } ) = action. downcast_ref ( ) {
680- self . handle_detailed_children ( cx, space_id, children) ;
681+ self . update_children_in_space ( cx, space_id, children) ;
681682 }
682683
683684 // Handle SubspaceEntry clicks
684- if let SubspaceEntryAction :: Clicked { room_id } = action. as_widget_action ( ) . cast ( ) {
685+ if let SubspaceEntryAction :: Clicked { space_id : room_id } = action. as_widget_action ( ) . cast ( ) {
685686 self . toggle_space_expansion ( cx, & room_id) ;
686687 }
687688
@@ -724,12 +725,10 @@ impl Widget for SpaceLobbyScreen {
724725 } else if let Some ( entry) = self . tree_entries . get ( item_id) {
725726 match entry {
726727 TreeEntry :: Item { info, level, is_last, parent_mask } => {
727-
728-
729- if info. is_space {
728+ if info. is_space ( ) {
730729 let item = list. item ( cx, item_id, id ! ( subspace_entry) ) ;
731730 if let Some ( mut inner) = item. borrow_mut :: < SubspaceEntry > ( ) {
732- inner. room_id = Some ( info. room_id . clone ( ) ) ;
731+ inner. space_id = Some ( info. room_id . clone ( ) ) ;
733732 }
734733
735734 // Configure tree lines
@@ -750,7 +749,7 @@ impl Widget for SpaceLobbyScreen {
750749 // Avatar
751750 let avatar_ref = item. avatar ( ids ! ( avatar) ) ;
752751 let first_char = utils:: user_name_first_letter ( & info. display_name ) ;
753- avatar_ref. show_text ( cx, None , None , first_char. as_deref ( ) . unwrap_or ( "#" ) ) ;
752+ avatar_ref. show_text ( cx, None , None , first_char. unwrap_or ( "#" ) ) ;
754753
755754 // Text
756755 item. label ( ids ! ( content. name_label) ) . set_text ( cx, & info. display_name ) ;
@@ -779,7 +778,7 @@ impl Widget for SpaceLobbyScreen {
779778 // Avatar
780779 let avatar_ref = item. avatar ( ids ! ( avatar) ) ;
781780 let first_char = utils:: user_name_first_letter ( & info. display_name ) ;
782- avatar_ref. show_text ( cx, None , None , first_char. as_deref ( ) . unwrap_or ( "#" ) ) ;
781+ avatar_ref. show_text ( cx, None , None , first_char. unwrap_or ( "#" ) ) ;
783782
784783 // Text
785784 item. label ( ids ! ( content. name_label) ) . set_text ( cx, & info. display_name ) ;
@@ -819,9 +818,8 @@ impl Widget for SpaceLobbyScreen {
819818impl SpaceLobbyScreen {
820819
821820 /// Handle receiving detailed children for a space.
822- fn handle_detailed_children ( & mut self , cx : & mut Cx , space_id : & OwnedRoomId , children : & Arc < Vec < SpaceRoomInfo > > ) {
823- // Store in cache and remove from loading set
824- self . children_cache . insert ( space_id. clone ( ) , Arc :: clone ( children) ) ;
821+ fn update_children_in_space ( & mut self , cx : & mut Cx , space_id : & OwnedRoomId , children : & Vector < SpaceRoom > ) {
822+ self . children_cache . insert ( space_id. clone ( ) , children. clone ( ) ) ;
825823 self . loading_subspaces . remove ( space_id) ;
826824
827825 // If this is for our displayed space, mark as loaded and rebuild tree
@@ -847,9 +845,12 @@ impl SpaceLobbyScreen {
847845 if !self . children_cache . contains_key ( space_id) {
848846 self . loading_subspaces . insert ( space_id. clone ( ) ) ;
849847 if let Some ( sender) = & self . space_request_sender {
848+ let parent_chain = cx. get_global :: < RoomsListRef > ( )
849+ . get_space_parent_chain ( space_id)
850+ . unwrap_or_default ( ) ;
850851 let _ = sender. send ( SpaceRequest :: GetDetailedChildren {
851852 space_id : space_id. clone ( ) ,
852- parent_chain : ParentChain :: new ( ) ,
853+ parent_chain,
853854 } ) ;
854855 }
855856 }
@@ -861,26 +862,43 @@ impl SpaceLobbyScreen {
861862
862863 /// Rebuild the flattened tree entries based on the current expansion state.
863864 fn rebuild_tree_entries ( & mut self ) {
864- self . tree_entries . clear ( ) ;
865-
866865 let Some ( space_name_id) = & self . space_name_id else { return } ;
867866 let root_space_id = space_name_id. room_id ( ) . clone ( ) ;
868-
869867 // Build tree starting from root
870- self . build_tree_for_space ( & root_space_id, 0 , 0 ) ;
868+ let mut new_tree_entries = Vec :: new ( ) ;
869+ Self :: build_tree_for_space (
870+ & self . children_cache ,
871+ & self . expanded_spaces ,
872+ & self . loading_subspaces ,
873+ & mut new_tree_entries,
874+ & root_space_id,
875+ 0 ,
876+ 0 ,
877+ ) ;
878+ self . tree_entries = new_tree_entries;
871879 }
872880
873- /// Build tree entries for a space and its expanded children.
874- fn build_tree_for_space ( & mut self , space_id : & OwnedRoomId , level : usize , parent_mask : u32 ) {
875- let children = match self . children_cache . get ( space_id) {
876- Some ( c) => Arc :: clone ( c) ,
877- None => return ,
878- } ;
881+ /// Recursively build the tree of spaces and their expanded children such that they
882+ /// can be displayed in the SpaceLobbyScreen's PortalList.
883+ //
884+ // 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`.
887+ fn build_tree_for_space (
888+ children_cache : & HashMap < OwnedRoomId , Vector < SpaceRoom > > ,
889+ expanded_spaces : & HashSet < OwnedRoomId > ,
890+ loading_subspaces : & HashSet < OwnedRoomId > ,
891+ tree_entries : & mut Vec < TreeEntry > ,
892+ space_id : & OwnedRoomId ,
893+ level : usize ,
894+ parent_mask : u32 ,
895+ ) {
896+ let Some ( children) = children_cache. get ( space_id) else { return } ;
879897
880898 // Sort: spaces first, then rooms, both alphabetically
881899 let mut sorted_children: Vec < _ > = children. iter ( ) . collect ( ) ;
882900 sorted_children. sort_by ( |a, b| {
883- match ( a. is_space , b. is_space ) {
901+ match ( a. is_space ( ) , b. is_space ( ) ) {
884902 ( true , false ) => std:: cmp:: Ordering :: Less ,
885903 ( false , true ) => std:: cmp:: Ordering :: Greater ,
886904 _ => a. display_name . to_lowercase ( ) . cmp ( & b. display_name . to_lowercase ( ) ) ,
@@ -892,15 +910,15 @@ impl SpaceLobbyScreen {
892910 for ( i, child) in sorted_children. iter ( ) . enumerate ( ) {
893911 let is_last = i == count - 1 ;
894912
895- self . tree_entries . push ( TreeEntry :: Item {
913+ tree_entries. push ( TreeEntry :: Item {
896914 info : ( * child) . clone ( ) ,
897915 level,
898916 is_last,
899917 parent_mask,
900918 } ) ;
901919
902920 // If this is an expanded space, recursively add its children or a loading indicator
903- if child. is_space && self . expanded_spaces . contains ( & child. room_id ) {
921+ if child. is_space ( ) && expanded_spaces. contains ( & child. room_id ) {
904922 // Calculate mask for children:
905923 // If we are NOT the last child, our level needs a continuation line for our children.
906924 // If we ARE the last child, our level does NOT need a line.
@@ -911,11 +929,19 @@ impl SpaceLobbyScreen {
911929 parent_mask | ( 1 << level)
912930 } ;
913931
914- if self . children_cache . contains_key ( & child. room_id ) {
915- self . build_tree_for_space ( & child. room_id , level + 1 , child_mask) ;
916- } else if self . loading_subspaces . contains ( & child. room_id ) {
932+ if children_cache. contains_key ( & child. room_id ) {
933+ Self :: build_tree_for_space (
934+ children_cache,
935+ expanded_spaces,
936+ loading_subspaces,
937+ tree_entries,
938+ & child. room_id ,
939+ level + 1 ,
940+ child_mask,
941+ ) ;
942+ } else if loading_subspaces. contains ( & child. room_id ) {
917943 // Show loading indicator
918- self . tree_entries . push ( TreeEntry :: Loading {
944+ tree_entries. push ( TreeEntry :: Loading {
919945 level : level + 1 ,
920946 parent_mask : child_mask,
921947 } ) ;
@@ -925,56 +951,43 @@ impl SpaceLobbyScreen {
925951 }
926952
927953 pub fn set_displayed_space ( & mut self , cx : & mut Cx , space_name_id : & RoomNameId ) {
928- // If this space is already being displayed, then do nothing.
954+ let space_name = space_name_id. to_string ( ) ;
955+ let parent_name = self . view . label ( ids ! ( header. parent_space_row. parent_name) ) ;
956+ parent_name. set_text ( cx, & space_name) ;
957+
958+ // If this space is already being displayed, then the only thing we may need to do
959+ // is update its name in the top-level header (already done above).
929960 if self . space_name_id . as_ref ( ) . is_some_and ( |sni| sni. room_id ( ) == space_name_id. room_id ( ) ) {
930961 return ;
931962 }
932963
933- // Clear previous state
964+ self . space_name_id = Some ( space_name_id. clone ( ) ) ;
965+ let rooms_list_ref = cx. get_global :: < RoomsListRef > ( ) ;
966+ if let Some ( sender) = rooms_list_ref. get_space_request_sender ( ) {
967+ // Request detailed children for this space so we can start populating it.
968+ let parent_chain_opt = rooms_list_ref. get_space_parent_chain ( space_name_id. room_id ( ) ) ;
969+ let _ = sender. send ( SpaceRequest :: GetDetailedChildren {
970+ space_id : space_name_id. room_id ( ) . clone ( ) ,
971+ parent_chain : parent_chain_opt. unwrap_or_default ( ) ,
972+ } ) ;
973+ self . space_request_sender = Some ( sender) ;
974+ }
975+
934976 self . tree_entries . clear ( ) ;
935977 self . expanded_spaces . clear ( ) ;
936978 self . is_loading = true ;
937979
938- // Set the new space
939- self . space_name_id = Some ( space_name_id. clone ( ) ) ;
940-
941- // Update parent space header
942- let space_name = space_name_id. to_string ( ) ;
943- self . view . label ( ids ! ( header. parent_space_row. parent_name) ) . set_text ( cx, & space_name) ;
944-
945980 // Set parent avatar
946981 let avatar_ref = self . view . avatar ( ids ! ( header. parent_space_row. parent_avatar) ) ;
947982 let first_char = utils:: user_name_first_letter ( & space_name) ;
948- avatar_ref. show_text ( cx, None , None , first_char. as_deref ( ) . unwrap_or ( "#" ) ) ;
949-
950- // Request detailed children for this space
951- if let Some ( sender) = & self . space_request_sender {
952- let _ = sender. send ( SpaceRequest :: GetDetailedChildren {
953- space_id : space_name_id. room_id ( ) . clone ( ) ,
954- parent_chain : ParentChain :: new ( ) ,
955- } ) ;
956- }
957-
983+ avatar_ref. show_text ( cx, None , None , first_char. unwrap_or ( "#" ) ) ;
958984 self . redraw ( cx) ;
959985 }
960-
961- /// Set the space request sender channel.
962- pub fn set_space_request_sender ( & mut self , sender : UnboundedSender < SpaceRequest > ) {
963- self . space_request_sender = Some ( sender) ;
964- }
965986}
966987
967988impl SpaceLobbyScreenRef {
968989 pub fn set_displayed_space ( & self , cx : & mut Cx , space_name_id : & RoomNameId ) {
969990 let Some ( mut inner) = self . borrow_mut ( ) else { return } ;
970-
971- // Get the space request sender from RoomsList via Cx global if needed
972- if inner. space_request_sender . is_none ( ) {
973- if let Some ( sender) = cx. get_global :: < RoomsListRef > ( ) . get_space_request_sender ( ) {
974- inner. space_request_sender = Some ( sender) ;
975- }
976- }
977-
978991 inner. set_displayed_space ( cx, space_name_id) ;
979992 }
980993}
0 commit comments