@@ -20,7 +20,7 @@ use crate::section::{Section, SectionItem, SectionMap};
2020use crate :: view:: view_from_map_ref;
2121use crate :: {
2222 impl_section_op, subscribe_folder_change, FolderData , ParentChildRelations , SectionChangeSender ,
23- TrashInfo , View , ViewUpdate , ViewsMap , Workspace ,
23+ SpacePermission , TrashInfo , View , ViewUpdate , ViewsMap , Workspace ,
2424} ;
2525
2626#[ derive( Clone , Debug , Serialize , Deserialize , Eq , PartialEq , Hash ) ]
@@ -54,6 +54,7 @@ impl AsRef<str> for UserId {
5454const VIEWS : & str = "views" ;
5555const PARENT_CHILD_VIEW_RELATION : & str = "relation" ;
5656const CURRENT_VIEW : & str = "current_view" ;
57+ const CURRENT_VIEW_FOR_USER : & str = "current_view_for_user" ;
5758
5859pub ( crate ) const FAVORITES_V1 : & str = "favorites" ;
5960const SECTION : & str = "section" ;
@@ -546,7 +547,10 @@ impl FolderBody {
546547 }
547548
548549 meta. insert ( & mut txn, FOLDER_WORKSPACE_ID , workspace_id) ;
549- meta. insert ( & mut txn, CURRENT_VIEW , folder_data. current_view ) ;
550+ // For compatibility with older collab library which doesn't use CURRENT_VIEW_FOR_USER.
551+ meta. insert ( & mut txn, CURRENT_VIEW , folder_data. current_view . clone ( ) ) ;
552+ let current_view_for_user = meta. get_or_init_map ( & mut txn, CURRENT_VIEW_FOR_USER ) ;
553+ current_view_for_user. insert ( & mut txn, uid. as_ref ( ) , folder_data. current_view . clone ( ) ) ;
550554
551555 if let Some ( fav_section) = section. section_op ( & txn, Section :: Favorite ) {
552556 for ( uid, sections) in folder_data. favorites {
@@ -732,12 +736,64 @@ impl FolderBody {
732736 Some ( view)
733737 }
734738
739+ pub fn get_child_of_first_public_view < T : ReadTxn > ( & self , txn : & T ) -> Option < String > {
740+ self
741+ . get_workspace_id ( txn)
742+ . and_then ( |workspace_id| self . views . get_view ( txn, & workspace_id) )
743+ . and_then ( |root_view| {
744+ let first_public_space_view_id_with_child =
745+ root_view
746+ . children
747+ . iter ( )
748+ . find ( |space_id| match self . views . get_view ( txn, space_id) {
749+ Some ( space_view) => {
750+ let is_public_space = space_view
751+ . space_info ( )
752+ . map ( |info| info. space_permission == SpacePermission :: PublicToAll )
753+ . unwrap_or ( false ) ;
754+ let has_children = !space_view. children . is_empty ( ) ;
755+ is_public_space && has_children
756+ } ,
757+ None => false ,
758+ } ) ;
759+ first_public_space_view_id_with_child. map ( |v| v. id . clone ( ) )
760+ } )
761+ . and_then ( |first_public_space_view_id_with_child| {
762+ self
763+ . views
764+ . get_view ( txn, & first_public_space_view_id_with_child)
765+ } )
766+ . and_then ( |first_public_space_view_with_child| {
767+ first_public_space_view_with_child
768+ . children
769+ . iter ( )
770+ . next ( )
771+ . map ( |first_child| first_child. id . clone ( ) )
772+ } )
773+ }
774+
735775 pub fn get_current_view < T : ReadTxn > ( & self , txn : & T ) -> Option < String > {
736- self . meta . get_with_txn ( txn, CURRENT_VIEW )
776+ // Fallback to CURRENT_VIEW if CURRENT_VIEW_FOR_USER is not present. This could happen for
777+ // workspace folder created by older version of the app before CURRENT_VIEW_FOR_USER is introduced.
778+ // If user cannot be found in CURRENT_VIEW_FOR_USER, use the first child of the first public space
779+ // which has children.
780+ let current_view_for_user_map = match self . meta . get ( txn, CURRENT_VIEW_FOR_USER ) {
781+ Some ( YrsValue :: YMap ( map) ) => Some ( map) ,
782+ _ => None ,
783+ } ;
784+ match current_view_for_user_map {
785+ Some ( current_view_for_user) => {
786+ let view_for_user: Option < String > =
787+ current_view_for_user. get_with_txn ( txn, self . uid . as_ref ( ) ) ;
788+ view_for_user. or ( self . get_child_of_first_public_view ( txn) )
789+ } ,
790+ None => self . meta . get_with_txn ( txn, CURRENT_VIEW ) ,
791+ }
737792 }
738793
739794 pub fn set_current_view ( & self , txn : & mut TransactionMut , view : String ) {
740- self . meta . try_update ( txn, CURRENT_VIEW , view) ;
795+ let current_view_for_user = self . meta . get_or_init_map ( txn, CURRENT_VIEW_FOR_USER ) ;
796+ current_view_for_user. try_update ( txn, self . uid . 0 . clone ( ) , view) ;
741797 }
742798}
743799
@@ -761,3 +817,84 @@ pub fn default_folder_data(workspace_id: &str) -> FolderData {
761817 private : HashMap :: new ( ) ,
762818 }
763819}
820+
821+ #[ cfg( test) ]
822+ mod tests {
823+ use collab:: { core:: origin:: CollabOrigin , preclude:: Collab } ;
824+
825+ use crate :: {
826+ Folder , FolderData , RepeatedViewIdentifier , SpaceInfo , UserId , View , ViewIdentifier , Workspace ,
827+ } ;
828+
829+ #[ test]
830+ pub fn test_set_and_get_current_view ( ) {
831+ let current_time = chrono:: Utc :: now ( ) . timestamp ( ) ;
832+ let workspace_id = "1234" ;
833+ let uid = 1 ;
834+ let collab = Collab :: new_with_origin ( CollabOrigin :: Empty , workspace_id, vec ! [ ] , false ) ;
835+ let view_1 = View :: new (
836+ "view_1" . to_string ( ) ,
837+ workspace_id. to_string ( ) ,
838+ "View 1" . to_string ( ) ,
839+ crate :: ViewLayout :: Document ,
840+ Some ( uid) ,
841+ ) ;
842+ let view_1_id = view_1. id . clone ( ) ;
843+ let view_2 = View :: new (
844+ "view_2" . to_string ( ) ,
845+ workspace_id. to_string ( ) ,
846+ "View 2" . to_string ( ) ,
847+ crate :: ViewLayout :: Document ,
848+ Some ( uid) ,
849+ ) ;
850+ let view_2_id = view_2. id . clone ( ) ;
851+ let space_view = View {
852+ id : "space_1_id" . to_string ( ) ,
853+ parent_view_id : workspace_id. to_string ( ) ,
854+ name : "Space 1" . to_string ( ) ,
855+ children : RepeatedViewIdentifier :: new ( vec ! [
856+ ViewIdentifier :: new( view_1_id. clone( ) ) ,
857+ ViewIdentifier :: new( view_2_id. clone( ) ) ,
858+ ] ) ,
859+ created_at : current_time,
860+ is_favorite : false ,
861+ layout : crate :: ViewLayout :: Document ,
862+ icon : None ,
863+ created_by : None ,
864+ last_edited_time : current_time,
865+ last_edited_by : None ,
866+ extra : Some ( serde_json:: to_string ( & SpaceInfo :: default ( ) ) . unwrap ( ) ) ,
867+ } ;
868+ let space_view_id = space_view. id . clone ( ) ;
869+ let workspace = Workspace {
870+ id : workspace_id. to_string ( ) ,
871+ name : "Workspace" . to_string ( ) ,
872+ child_views : RepeatedViewIdentifier :: new ( vec ! [ ViewIdentifier :: new( space_view_id. clone( ) ) ] ) ,
873+ created_at : current_time,
874+ created_by : Some ( uid) ,
875+ last_edited_time : current_time,
876+ last_edited_by : Some ( uid) ,
877+ } ;
878+ let folder_data = FolderData {
879+ workspace,
880+ current_view : view_2. id . clone ( ) ,
881+ views : vec ! [ space_view, view_1, view_2] ,
882+ favorites : Default :: default ( ) ,
883+ recent : Default :: default ( ) ,
884+ trash : Default :: default ( ) ,
885+ private : Default :: default ( ) ,
886+ } ;
887+ let mut folder = Folder :: create ( uid, collab, None , folder_data) ;
888+
889+ folder. set_current_view ( view_2_id. clone ( ) ) ;
890+ assert_eq ! ( folder. get_current_view( ) , Some ( view_2_id. to_string( ) ) ) ;
891+ // First visit from user 2, should return the first child of the first public space with children.
892+ folder. body . uid = UserId :: from ( 2 ) ;
893+ assert_eq ! ( folder. get_current_view( ) , Some ( view_1_id. to_string( ) ) ) ;
894+ folder. set_current_view ( view_1_id. to_string ( ) ) ;
895+ folder. body . uid = UserId :: from ( 1 ) ;
896+ assert_eq ! ( folder. get_current_view( ) , Some ( view_2_id. to_string( ) ) ) ;
897+ folder. body . uid = UserId :: from ( 2 ) ;
898+ assert_eq ! ( folder. get_current_view( ) , Some ( view_1_id. to_string( ) ) ) ;
899+ }
900+ }
0 commit comments