Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for m.call.invite events in the timeline and as a last room message #3181

Merged
merged 1 commit into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bindings/matrix-sdk-ffi/src/timeline/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl TimelineItemContent {
}
}
Content::Poll(poll_state) => TimelineItemContentKind::from(poll_state.results()),
Content::CallInvite => TimelineItemContentKind::CallInvite,
Content::UnableToDecrypt(msg) => {
TimelineItemContentKind::UnableToDecrypt { msg: EncryptedMessage::new(msg) }
}
Expand Down Expand Up @@ -113,6 +114,7 @@ pub enum TimelineItemContentKind {
end_time: Option<u64>,
has_been_edited: bool,
},
CallInvite,
UnableToDecrypt {
msg: EncryptedMessage,
},
Expand Down
13 changes: 8 additions & 5 deletions crates/matrix-sdk-base/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,11 +720,14 @@ impl BaseClient {
// We found an event we can decrypt
if let Ok(any_sync_event) = decrypted.event.deserialize() {
// We can deserialize it to find its type
if let PossibleLatestEvent::YesRoomMessage(_) =
is_suitable_for_latest_event(&any_sync_event)
{
// The event is the right type for us to use as latest_event
return Some((Box::new(LatestEvent::new(decrypted)), i));
match is_suitable_for_latest_event(&any_sync_event) {
PossibleLatestEvent::YesRoomMessage(_)
| PossibleLatestEvent::YesPoll(_)
| PossibleLatestEvent::YesCallInvite(_) => {
// The event is the right type for us to use as latest_event
return Some((Box::new(LatestEvent::new(decrypted)), i));
}
_ => (),
}
}
}
Expand Down
41 changes: 39 additions & 2 deletions crates/matrix-sdk-base/src/latest_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use ruma::events::{
poll::unstable_start::SyncUnstablePollStartEvent, room::message::SyncRoomMessageEvent,
AnySyncMessageLikeEvent, AnySyncTimelineEvent,
};
use ruma::{events::relation::RelationType, MxcUri, OwnedEventId};
use ruma::{
events::{call::invite::SyncCallInviteEvent, relation::RelationType},
MxcUri, OwnedEventId,
};
use serde::{Deserialize, Serialize};

use crate::MinimalRoomMemberEvent;
Expand All @@ -25,6 +28,10 @@ pub enum PossibleLatestEvent<'a> {
YesRoomMessage(&'a SyncRoomMessageEvent),
/// This message is suitable - it is a poll
YesPoll(&'a SyncUnstablePollStartEvent),

/// This message is suitable - it is a call invite
YesCallInvite(&'a SyncCallInviteEvent),

// Later: YesState(),
// Later: YesReaction(),
/// Not suitable - it's a state event
Expand Down Expand Up @@ -67,6 +74,10 @@ pub fn is_suitable_for_latest_event(event: &AnySyncTimelineEvent) -> PossibleLat
PossibleLatestEvent::YesPoll(poll)
}

AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::CallInvite(invite)) => {
PossibleLatestEvent::YesCallInvite(invite)
}

// Encrypted events are not suitable
AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomEncrypted(_)) => {
PossibleLatestEvent::NoEncrypted
Expand Down Expand Up @@ -243,6 +254,10 @@ mod tests {
use matrix_sdk_common::deserialized_responses::SyncTimelineEvent;
use ruma::{
events::{
call::{
invite::{CallInviteEventContent, SyncCallInviteEvent},
SessionDescription,
},
poll::unstable_start::{
NewUnstablePollStartEventContent, SyncUnstablePollStartEvent, UnstablePollAnswer,
UnstablePollStartContentBlock,
Expand All @@ -268,7 +283,7 @@ mod tests {
},
owned_event_id, owned_mxc_uri, owned_user_id,
serde::Raw,
MilliSecondsSinceUnixEpoch, UInt,
MilliSecondsSinceUnixEpoch, UInt, VoipVersionId,
};
use serde_json::json;

Expand Down Expand Up @@ -321,6 +336,28 @@ mod tests {
assert_eq!(m.content.poll_start().question.text, "do you like rust?");
}

#[test]
fn call_invites_are_suitable() {
let event = AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::CallInvite(
SyncCallInviteEvent::Original(OriginalSyncMessageLikeEvent {
content: CallInviteEventContent::new(
"call_id".into(),
UInt::new(123).unwrap(),
SessionDescription::new("".into(), "".into()),
VoipVersionId::V1,
),
event_id: owned_event_id!("$1"),
sender: owned_user_id!("@a:b.c"),
origin_server_ts: MilliSecondsSinceUnixEpoch(UInt::new(2123).unwrap()),
unsigned: MessageLikeUnsigned::new(),
}),
));
assert_let!(
PossibleLatestEvent::YesCallInvite(SyncMessageLikeEvent::Original(_)) =
is_suitable_for_latest_event(&event)
);
}

#[test]
fn different_types_of_messagelike_are_unsuitable() {
let event = AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::Sticker(
Expand Down
6 changes: 4 additions & 2 deletions crates/matrix-sdk-base/src/sliding_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,8 +585,10 @@ async fn cache_latest_events(
for event in events.iter().rev() {
if let Ok(timeline_event) = event.event.deserialize() {
match is_suitable_for_latest_event(&timeline_event) {
PossibleLatestEvent::YesRoomMessage(_) | PossibleLatestEvent::YesPoll(_) => {
// m.room.message or m.poll.start - we found one! Store it.
PossibleLatestEvent::YesRoomMessage(_)
| PossibleLatestEvent::YesPoll(_)
| PossibleLatestEvent::YesCallInvite(_) => {
// We found a suitable latest event. Store it.

// In order to make the latest event fast to read, we want to keep the
// associated sender in cache. This is a best-effort to gather enough
Expand Down
4 changes: 4 additions & 0 deletions crates/matrix-sdk-ui/src/timeline/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
) => self.handle_poll_start(c, should_add),
AnyMessageLikeEventContent::UnstablePollResponse(c) => self.handle_poll_response(c),
AnyMessageLikeEventContent::UnstablePollEnd(c) => self.handle_poll_end(c),
AnyMessageLikeEventContent::CallInvite(_) => {
self.add(should_add, TimelineItemContent::CallInvite);
}

// TODO
_ => {
debug!(
Expand Down
18 changes: 18 additions & 0 deletions crates/matrix-sdk-ui/src/timeline/event_item/content/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use imbl::Vector;
use matrix_sdk_base::latest_event::{is_suitable_for_latest_event, PossibleLatestEvent};
use ruma::{
events::{
call::invite::SyncCallInviteEvent,
policy::rule::{
room::PolicyRuleRoomEventContent, server::PolicyRuleServerEventContent,
user::PolicyRuleUserEventContent,
Expand Down Expand Up @@ -106,6 +107,9 @@ pub enum TimelineItemContent {

/// An `m.poll.start` event.
Poll(PollState),

/// An `m.call.invite` event
CallInvite,
}

impl TimelineItemContent {
Expand All @@ -122,6 +126,9 @@ impl TimelineItemContent {
PossibleLatestEvent::YesPoll(poll) => {
Some(Self::from_suitable_latest_poll_event_content(poll))
}
PossibleLatestEvent::YesCallInvite(call_invite) => {
Some(Self::from_suitable_latest_call_invite_content(call_invite))
}
PossibleLatestEvent::NoUnsupportedEventType => {
// TODO: when we support state events in message previews, this will need change
warn!("Found a state event cached as latest_event! ID={}", event.event_id());
Expand Down Expand Up @@ -189,6 +196,15 @@ impl TimelineItemContent {
}
}

fn from_suitable_latest_call_invite_content(
event: &SyncCallInviteEvent,
) -> TimelineItemContent {
match event {
SyncCallInviteEvent::Original(_) => TimelineItemContent::CallInvite,
SyncCallInviteEvent::Redacted(_) => TimelineItemContent::RedactedMessage,
}
}

/// If `self` is of the [`Message`][Self::Message] variant, return the inner
/// [`Message`].
pub fn as_message(&self) -> Option<&Message> {
Expand Down Expand Up @@ -228,6 +244,7 @@ impl TimelineItemContent {
TimelineItemContent::FailedToParseMessageLike { .. }
| TimelineItemContent::FailedToParseState { .. } => "an event that couldn't be parsed",
TimelineItemContent::Poll(_) => "a poll",
TimelineItemContent::CallInvite => "a call invite",
}
}

Expand Down Expand Up @@ -306,6 +323,7 @@ impl TimelineItemContent {
| Self::RedactedMessage
| Self::Sticker(_)
| Self::Poll(_)
| Self::CallInvite
| Self::UnableToDecrypt(_) => Self::RedactedMessage,
Self::MembershipChange(ev) => Self::MembershipChange(ev.redact(room_version)),
Self::ProfileChange(ev) => Self::ProfileChange(ev.redact()),
Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk-ui/src/timeline/inner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ pub fn default_event_filter(event: &AnySyncTimelineEvent, room_version: &RoomVer
| AnyMessageLikeEventContent::UnstablePollStart(
UnstablePollStartEventContent::New(_),
)
| AnyMessageLikeEventContent::CallInvite(_)
| AnyMessageLikeEventContent::RoomEncrypted(_) => true,

_ => false,
Expand Down
3 changes: 3 additions & 0 deletions crates/matrix-sdk-ui/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,9 @@ impl Timeline {
TimelineItemContent::Poll(poll_state) => AnyMessageLikeEventContent::UnstablePollStart(
UnstablePollStartEventContent::New(poll_state.into()),
),
TimelineItemContent::CallInvite => {
error_return!("Retrying call events is not currently supported");
}
};

debug!("Retrying failed local echo");
Expand Down
Loading