@@ -431,6 +431,10 @@ impl EventTimelineItem {
431431 false
432432 } else if self . content . is_message ( ) {
433433 true
434+ } else if self . content ( ) . as_live_location_state ( ) . is_some ( ) {
435+ // Live location sharing session (MSC3489) events are state events, not always
436+ // displayed in a timeline, so can't be replied to.
437+ false
434438 } else {
435439 self . latest_json ( ) . is_some ( )
436440 }
@@ -909,3 +913,144 @@ impl From<ShieldStateCode> for TimelineEventShieldStateCode {
909913 }
910914 }
911915}
916+
917+ #[ cfg( test) ]
918+ mod tests {
919+ use std:: time:: Duration ;
920+
921+ use ruma:: {
922+ MilliSecondsSinceUnixEpoch ,
923+ events:: {
924+ AnySyncTimelineEvent ,
925+ beacon_info:: BeaconInfoEventContent ,
926+ room:: message:: { MessageType , RoomMessageEventContent , TextMessageEventContent } ,
927+ } ,
928+ owned_event_id, owned_user_id,
929+ serde:: Raw ,
930+ uint,
931+ } ;
932+ use serde_json:: json;
933+
934+ use super :: {
935+ EventSendState , EventTimelineItem , EventTimelineItemKind , LiveLocationState ,
936+ LocalEventTimelineItem , Message , MsgLikeContent , MsgLikeKind , RemoteEventOrigin ,
937+ RemoteEventTimelineItem , TimelineDetails , TimelineItemContent ,
938+ } ;
939+
940+ fn message_content ( ) -> TimelineItemContent {
941+ TimelineItemContent :: MsgLike ( MsgLikeContent {
942+ kind : MsgLikeKind :: Message ( Message {
943+ msgtype : MessageType :: Text ( TextMessageEventContent :: plain ( "hello" ) ) ,
944+ edited : false ,
945+ mentions : None ,
946+ } ) ,
947+ reactions : Default :: default ( ) ,
948+ thread_root : None ,
949+ in_reply_to : None ,
950+ thread_summary : None ,
951+ } )
952+ }
953+
954+ fn live_location_content ( ) -> TimelineItemContent {
955+ TimelineItemContent :: MsgLike ( MsgLikeContent {
956+ kind : MsgLikeKind :: LiveLocation ( LiveLocationState :: new ( BeaconInfoEventContent :: new (
957+ None ,
958+ Duration :: from_secs ( 300 ) ,
959+ true ,
960+ Some ( MilliSecondsSinceUnixEpoch ( uint ! ( 1 ) ) ) ,
961+ ) ) ) ,
962+ reactions : Default :: default ( ) ,
963+ thread_root : None ,
964+ in_reply_to : None ,
965+ thread_summary : None ,
966+ } )
967+ }
968+
969+ fn remote_item (
970+ content : TimelineItemContent ,
971+ original_json : Option < Raw < AnySyncTimelineEvent > > ,
972+ ) -> EventTimelineItem {
973+ EventTimelineItem :: new (
974+ owned_user_id ! ( "@alice:example.org" ) ,
975+ TimelineDetails :: Unavailable ,
976+ None ,
977+ None ,
978+ MilliSecondsSinceUnixEpoch ( uint ! ( 1 ) ) ,
979+ content,
980+ EventTimelineItemKind :: Remote ( RemoteEventTimelineItem {
981+ event_id : owned_event_id ! ( "$event" ) ,
982+ transaction_id : None ,
983+ read_receipts : Default :: default ( ) ,
984+ is_own : false ,
985+ is_highlighted : false ,
986+ encryption_info : None ,
987+ original_json,
988+ latest_edit_json : None ,
989+ origin : RemoteEventOrigin :: Sync ,
990+ } ) ,
991+ false ,
992+ )
993+ }
994+
995+ fn local_unsent_item ( content : TimelineItemContent ) -> EventTimelineItem {
996+ EventTimelineItem :: new (
997+ owned_user_id ! ( "@alice:example.org" ) ,
998+ TimelineDetails :: Unavailable ,
999+ None ,
1000+ None ,
1001+ MilliSecondsSinceUnixEpoch ( uint ! ( 1 ) ) ,
1002+ content,
1003+ EventTimelineItemKind :: Local ( LocalEventTimelineItem {
1004+ send_state : EventSendState :: NotSentYet { progress : None } ,
1005+ transaction_id : "t0" . into ( ) ,
1006+ send_handle : None ,
1007+ } ) ,
1008+ false ,
1009+ )
1010+ }
1011+
1012+ fn sample_raw_event ( ) -> Raw < AnySyncTimelineEvent > {
1013+ Raw :: from_json_string (
1014+ json ! ( {
1015+ "content" : RoomMessageEventContent :: text_plain( "hi" ) ,
1016+ "type" : "m.room.message" ,
1017+ "event_id" : "$event" ,
1018+ "room_id" : "!room:example.org" ,
1019+ "origin_server_ts" : 1 ,
1020+ "sender" : "@alice:example.org" ,
1021+ } )
1022+ . to_string ( ) ,
1023+ )
1024+ . unwrap ( )
1025+ }
1026+
1027+ #[ test]
1028+ fn cannot_reply_to_local_unsent_events ( ) {
1029+ let item = local_unsent_item ( message_content ( ) ) ;
1030+ assert ! ( !item. can_be_replied_to( ) ) ;
1031+ }
1032+
1033+ #[ test]
1034+ fn can_reply_to_messages ( ) {
1035+ let item = remote_item ( message_content ( ) , None ) ;
1036+ assert ! ( item. can_be_replied_to( ) ) ;
1037+ }
1038+
1039+ #[ test]
1040+ fn cannot_reply_to_live_location_events ( ) {
1041+ let item = remote_item ( live_location_content ( ) , Some ( sample_raw_event ( ) ) ) ;
1042+ assert ! ( !item. can_be_replied_to( ) ) ;
1043+ }
1044+
1045+ #[ test]
1046+ fn cannot_reply_to_non_messages_with_no_json ( ) {
1047+ let item = remote_item ( TimelineItemContent :: CallInvite , None ) ;
1048+ assert ! ( !item. can_be_replied_to( ) ) ;
1049+ }
1050+
1051+ #[ test]
1052+ fn can_reply_to_non_messages_with_json ( ) {
1053+ let item = remote_item ( TimelineItemContent :: CallInvite , Some ( sample_raw_event ( ) ) ) ;
1054+ assert ! ( item. can_be_replied_to( ) ) ;
1055+ }
1056+ }
0 commit comments