Skip to content

Commit

Permalink
feat: add support for m.call.invite events in the timeline and as a…
Browse files Browse the repository at this point in the history
… last room message
  • Loading branch information
stefanceriu committed Mar 4, 2024
1 parent 4b711e4 commit d9b4623
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 9 deletions.
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

0 comments on commit d9b4623

Please sign in to comment.