@@ -753,3 +753,144 @@ async fn test_focused_timeline_handles_other_thread_event_when_forcing_threaded_
753753 assert_let ! ( VectorDiff :: PushBack { value: item } = & timeline_updates[ 0 ] ) ;
754754 assert_eq ! ( item. as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) , "Next" ) ;
755755}
756+
757+ #[ async_test]
758+ async fn test_focused_timeline_filters_out_threaded_events ( ) {
759+ let room_id = room_id ! ( "!a98sd12bjh:example.org" ) ;
760+ let server = MatrixMockServer :: new ( ) . await ;
761+ let client = server. client_builder ( ) . build ( ) . await ;
762+ let user_id = client. user_id ( ) . unwrap ( ) ;
763+
764+ let prev_thread_event_id = event_id ! ( "$prev:example.org" ) ;
765+ let root_thread_id = event_id ! ( "$root-id:example.org" ) ;
766+
767+ let f = EventFactory :: new ( ) . room ( room_id) . sender ( user_id) ;
768+ let focus_event = f. text_msg ( "Hey" ) . into_event ( ) ;
769+ let focus_event_id = focus_event. event_id ( ) . unwrap ( ) . clone ( ) ;
770+
771+ // Mock the initial /context request to check if the event is in a thread.
772+ let events_before = vec ! [
773+ f. text_msg( "Unrelated before 1" ) . into_event( ) ,
774+ f. text_msg( "Unrelated before 2" ) . into_event( ) ,
775+ f. text_msg( "Thread before" ) . in_thread( root_thread_id, prev_thread_event_id) . into_event( ) ,
776+ ] ;
777+ let events_after = vec ! [
778+ f. text_msg( "Unrelated after 1" ) . into_event( ) ,
779+ f. text_msg( "Unrelated after 2" ) . into_event( ) ,
780+ f. text_msg( "Thread after" ) . in_thread( root_thread_id, prev_thread_event_id) . into_event( ) ,
781+ ] ;
782+ server
783+ . mock_room_event_context ( )
784+ . room ( room_id)
785+ . ok ( RoomContextResponseTemplate :: new ( focus_event)
786+ . events_before ( events_before)
787+ . events_after ( events_after)
788+ . start ( "prev_token_1" )
789+ . end ( "next_token_1" ) )
790+ . mock_once ( )
791+ . mount ( )
792+ . await ;
793+
794+ let focus = TimelineFocus :: Event {
795+ target : focus_event_id,
796+ num_context_events : 10 ,
797+ thread_mode : TimelineEventFocusThreadMode :: Automatic { hide_threaded_events : true } ,
798+ } ;
799+
800+ let room = server. sync_joined_room ( & client, room_id) . await ;
801+ let timeline = TimelineBuilder :: new ( & room)
802+ . with_focus ( focus)
803+ . build ( )
804+ . await
805+ . expect ( "Could not build focused timeline" ) ;
806+
807+ assert ! (
808+ timeline. live_back_pagination_status( ) . await . is_none( ) ,
809+ "there should be no live back-pagination status for a focused timeline"
810+ ) ;
811+
812+ let ( items, mut timeline_stream) = timeline. subscribe ( ) . await ;
813+ // The 2 unrelated events before + 2 after + the item + the date divider
814+ assert_eq ! ( items. len( ) , 6 ) ;
815+ assert ! ( items[ 0 ] . is_date_divider( ) ) ;
816+ assert_eq ! (
817+ items[ 1 ] . as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) ,
818+ "Unrelated before 2"
819+ ) ;
820+ assert_eq ! (
821+ items[ 2 ] . as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) ,
822+ "Unrelated before 1"
823+ ) ;
824+ assert_eq ! ( items[ 3 ] . as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) , "Hey" ) ;
825+ assert_eq ! (
826+ items[ 4 ] . as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) ,
827+ "Unrelated after 1"
828+ ) ;
829+ assert_eq ! (
830+ items[ 5 ] . as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) ,
831+ "Unrelated after 2"
832+ ) ;
833+
834+ // We paginate back once
835+ server
836+ . mock_room_messages ( )
837+ . match_from ( "prev_token_1" )
838+ . ok ( RoomMessagesResponseTemplate {
839+ chunk : vec ! [
840+ f. text_msg( "Prev" )
841+ . sender( user_id)
842+ . in_thread( root_thread_id, prev_thread_event_id)
843+ . into_raw_timeline( ) ,
844+ f. text_msg( "Prev no thread" ) . sender( user_id) . into_raw_timeline( ) ,
845+ ] ,
846+ start : "prev_token_1" . to_owned ( ) ,
847+ end : Some ( "prev_token_2" . to_owned ( ) ) ,
848+ state : Vec :: new ( ) ,
849+ delay : None ,
850+ } )
851+ . mock_once ( )
852+ . mount ( )
853+ . await ;
854+
855+ let start_of_timeline =
856+ timeline. paginate_backwards ( 10 ) . await . expect ( "Could not paginate backwards" ) ;
857+ assert ! ( !start_of_timeline) ;
858+
859+ // Only the non-threaded event is inserted at the start.
860+ assert_let_timeout ! ( Some ( timeline_updates) = timeline_stream. next( ) ) ;
861+ assert_eq ! ( timeline_updates. len( ) , 1 ) ;
862+ // The new item loaded is inserted at the start, just after the date divider.
863+ assert_let ! ( VectorDiff :: Insert { index: 1 , value: item } = & timeline_updates[ 0 ] ) ;
864+ assert_eq ! ( item. as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) , "Prev no thread" ) ;
865+
866+ // Then we paginate forwards
867+ server
868+ . mock_room_messages ( )
869+ . match_from ( "next_token_1" )
870+ . ok ( RoomMessagesResponseTemplate {
871+ chunk : vec ! [
872+ f. text_msg( "Next no thread" ) . sender( user_id) . into_raw_timeline( ) ,
873+ f. text_msg( "Next1" )
874+ . sender( user_id)
875+ . in_thread( root_thread_id, prev_thread_event_id)
876+ . into_raw_timeline( ) ,
877+ ] ,
878+ start : "next_token_1" . to_owned ( ) ,
879+ end : Some ( "next_token_2" . to_owned ( ) ) ,
880+ state : Vec :: new ( ) ,
881+ delay : None ,
882+ } )
883+ . mock_once ( )
884+ . mount ( )
885+ . await ;
886+
887+ let end_of_timeline =
888+ timeline. paginate_forwards ( 10 ) . await . expect ( "Could not paginate forwards" ) ;
889+ assert ! ( !end_of_timeline) ;
890+
891+ // Only the non-threaded event is pushed to the end.
892+ assert_let_timeout ! ( Some ( timeline_updates) = timeline_stream. next( ) ) ;
893+ assert_eq ! ( timeline_updates. len( ) , 1 ) ;
894+ assert_let ! ( VectorDiff :: PushBack { value: item } = & timeline_updates[ 0 ] ) ;
895+ assert_eq ! ( item. as_event( ) . unwrap( ) . content( ) . as_message( ) . unwrap( ) . body( ) , "Next no thread" ) ;
896+ }
0 commit comments