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

chore(room_list): allow knock state event as latest_event #4170

Merged
merged 3 commits into from
Oct 29, 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: 1 addition & 1 deletion bindings/matrix-sdk-ffi/src/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ impl Room {
}

pub async fn get_power_levels(&self) -> Result<RoomPowerLevels, ClientError> {
let power_levels = self.inner.room_power_levels().await?;
let power_levels = self.inner.power_levels().await.map_err(matrix_sdk::Error::from)?;
Ok(RoomPowerLevels::from(power_levels))
}

Expand Down
8 changes: 5 additions & 3 deletions crates/matrix-sdk-base/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,8 @@ impl BaseClient {
room: &Room,
) -> Option<(Box<LatestEvent>, usize)> {
let enc_events = room.latest_encrypted_events();
let power_levels = room.power_levels().await.ok();
let power_levels_info = Some(room.own_user_id()).zip(power_levels.as_ref());

// Walk backwards through the encrypted events, looking for one we can decrypt
for (i, event) in enc_events.iter().enumerate().rev() {
Expand All @@ -802,13 +804,13 @@ impl BaseClient {
// We found an event we can decrypt
if let Ok(any_sync_event) = decrypted.raw().deserialize() {
// We can deserialize it to find its type
match is_suitable_for_latest_event(&any_sync_event) {
match is_suitable_for_latest_event(&any_sync_event, power_levels_info) {
PossibleLatestEvent::YesRoomMessage(_)
| PossibleLatestEvent::YesPoll(_)
| PossibleLatestEvent::YesCallInvite(_)
| PossibleLatestEvent::YesCallNotify(_)
| PossibleLatestEvent::YesSticker(_) => {
// The event is the right type for us to use as latest_event
| PossibleLatestEvent::YesSticker(_)
| PossibleLatestEvent::YesKnockedStateEvent(_) => {
return Some((Box::new(LatestEvent::new(decrypted)), i));
}
_ => (),
Expand Down
8 changes: 8 additions & 0 deletions crates/matrix-sdk-base/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,12 @@ pub enum Error {
/// function with invalid parameters
#[error("receive_all_members function was called with invalid parameters")]
InvalidReceiveMembersParameters,

/// This request failed because the local data wasn't sufficient.
#[error("Local cache doesn't contain all necessary data to perform the action.")]
InsufficientData,

/// There was a [`serde_json`] deserialization error.
#[error(transparent)]
DeserializationError(#[from] serde_json::error::Error),
}
69 changes: 55 additions & 14 deletions crates/matrix-sdk-base/src/latest_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ use ruma::events::{
room::message::SyncRoomMessageEvent,
AnySyncMessageLikeEvent, AnySyncTimelineEvent,
};
use ruma::{events::sticker::SyncStickerEvent, MxcUri, OwnedEventId};
use ruma::{
events::{
room::{
member::{MembershipState, SyncRoomMemberEvent},
power_levels::RoomPowerLevels,
},
sticker::SyncStickerEvent,
AnySyncStateEvent,
},
MxcUri, OwnedEventId, UserId,
};
use serde::{Deserialize, Serialize};

use crate::MinimalRoomMemberEvent;
Expand All @@ -37,6 +47,10 @@ pub enum PossibleLatestEvent<'a> {
/// This message is suitable - it's a call notification
YesCallNotify(&'a SyncCallNotifyEvent),

/// This state event is suitable - it's a knock membership change
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// This state event is suitable - it's a knock membership change
/// This state event is suitable - it's a knock membership change
/// that can be handled by the current user.

/// that can be handled by the current user.
YesKnockedStateEvent(&'a SyncRoomMemberEvent),

// Later: YesState(),
// Later: YesReaction(),
/// Not suitable - it's a state event
Expand All @@ -50,7 +64,10 @@ pub enum PossibleLatestEvent<'a> {
/// Decide whether an event could be stored as the latest event in a room.
/// Returns a LatestEvent representing our decision.
#[cfg(feature = "e2e-encryption")]
pub fn is_suitable_for_latest_event(event: &AnySyncTimelineEvent) -> PossibleLatestEvent<'_> {
pub fn is_suitable_for_latest_event<'a>(
event: &'a AnySyncTimelineEvent,
power_levels_info: Option<(&'a UserId, &'a RoomPowerLevels)>,
) -> PossibleLatestEvent<'a> {
match event {
// Suitable - we have an m.room.message that was not redacted or edited
AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(message)) => {
Expand Down Expand Up @@ -102,8 +119,29 @@ pub fn is_suitable_for_latest_event(event: &AnySyncTimelineEvent) -> PossibleLat
// suitable
AnySyncTimelineEvent::MessageLike(_) => PossibleLatestEvent::NoUnsupportedMessageLikeType,

// We don't currently support state events
AnySyncTimelineEvent::State(_) => PossibleLatestEvent::NoUnsupportedEventType,
// We don't currently support most state events
AnySyncTimelineEvent::State(state) => {
// But we make an exception for knocked state events *if* the current user
// can either accept or decline them
if let AnySyncStateEvent::RoomMember(member) = state {
if matches!(member.membership(), MembershipState::Knock) {
let can_accept_or_decline_knocks = match power_levels_info {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to move this logic to within the RoomPowerLevels, similar to the other can_foos?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's worth it, since it'll only be used here (I hope).

Some((own_user_id, room_power_levels)) => {
room_power_levels.user_can_invite(own_user_id)
|| room_power_levels.user_can_kick(own_user_id)
}
_ => false,
};

// The current user can act on the knock changes, so they should be
// displayed
if can_accept_or_decline_knocks {
return PossibleLatestEvent::YesKnockedStateEvent(member);
}
}
}
PossibleLatestEvent::NoUnsupportedEventType
}
}
}

Expand Down Expand Up @@ -327,7 +365,7 @@ mod tests {
));
assert_let!(
PossibleLatestEvent::YesRoomMessage(SyncMessageLikeEvent::Original(m)) =
is_suitable_for_latest_event(&event)
is_suitable_for_latest_event(&event, None)
);

assert_eq!(m.content.msgtype.msgtype(), "m.image");
Expand All @@ -350,7 +388,7 @@ mod tests {
));
assert_let!(
PossibleLatestEvent::YesPoll(SyncMessageLikeEvent::Original(m)) =
is_suitable_for_latest_event(&event)
is_suitable_for_latest_event(&event, None)
);

assert_eq!(m.content.poll_start().question.text, "do you like rust?");
Expand All @@ -374,7 +412,7 @@ mod tests {
));
assert_let!(
PossibleLatestEvent::YesCallInvite(SyncMessageLikeEvent::Original(_)) =
is_suitable_for_latest_event(&event)
is_suitable_for_latest_event(&event, None)
);
}

Expand All @@ -396,7 +434,7 @@ mod tests {
));
assert_let!(
PossibleLatestEvent::YesCallNotify(SyncMessageLikeEvent::Original(_)) =
is_suitable_for_latest_event(&event)
is_suitable_for_latest_event(&event, None)
);
}

Expand All @@ -417,7 +455,7 @@ mod tests {
));

assert_matches!(
is_suitable_for_latest_event(&event),
is_suitable_for_latest_event(&event, None),
PossibleLatestEvent::YesSticker(SyncStickerEvent::Original(_))
);
}
Expand All @@ -439,7 +477,7 @@ mod tests {
));

assert_matches!(
is_suitable_for_latest_event(&event),
is_suitable_for_latest_event(&event, None),
PossibleLatestEvent::NoUnsupportedMessageLikeType
);
}
Expand Down Expand Up @@ -467,7 +505,7 @@ mod tests {
));

assert_matches!(
is_suitable_for_latest_event(&event),
is_suitable_for_latest_event(&event, None),
PossibleLatestEvent::YesRoomMessage(SyncMessageLikeEvent::Redacted(_))
);
}
Expand All @@ -489,7 +527,10 @@ mod tests {
}),
));

assert_matches!(is_suitable_for_latest_event(&event), PossibleLatestEvent::NoEncrypted);
assert_matches!(
is_suitable_for_latest_event(&event, None),
PossibleLatestEvent::NoEncrypted
);
}

#[test]
Expand All @@ -506,7 +547,7 @@ mod tests {
));

assert_matches!(
is_suitable_for_latest_event(&event),
is_suitable_for_latest_event(&event, None),
PossibleLatestEvent::NoUnsupportedEventType
);
}
Expand All @@ -530,7 +571,7 @@ mod tests {
));

assert_matches!(
is_suitable_for_latest_event(&event),
is_suitable_for_latest_event(&event, None),
PossibleLatestEvent::NoUnsupportedMessageLikeType
);
}
Expand Down
14 changes: 13 additions & 1 deletion crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ use ruma::{
join_rules::JoinRule,
member::{MembershipState, RoomMemberEventContent},
pinned_events::RoomPinnedEventsEventContent,
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
redaction::SyncRoomRedactionEvent,
tombstone::RoomTombstoneEventContent,
},
Expand Down Expand Up @@ -71,7 +72,7 @@ use crate::{
read_receipts::RoomReadReceipts,
store::{DynStateStore, Result as StoreResult, StateStoreExt},
sync::UnreadNotificationsCount,
MinimalStateEvent, OriginalMinimalStateEvent, RoomMemberships,
Error, MinimalStateEvent, OriginalMinimalStateEvent, RoomMemberships,
};

/// Indicates that a notable update of `RoomInfo` has been applied, and why.
Expand Down Expand Up @@ -508,6 +509,17 @@ impl Room {
self.inner.read().base_info.max_power_level
}

/// Get the current power levels of this room.
pub async fn power_levels(&self) -> Result<RoomPowerLevels, Error> {
Ok(self
.store
.get_state_event_static::<RoomPowerLevelsEventContent>(self.room_id())
.await?
.ok_or(Error::InsufficientData)?
.deserialize()?
.power_levels())
}

/// Get the `m.room.name` of this room.
///
/// The returned string may be empty if the event has been redacted, or it's
Expand Down
Loading
Loading