Skip to content
Open
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
5 changes: 5 additions & 0 deletions bindings/matrix-sdk-ffi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ All notable changes to this project will be documented in this file.

### Features

- [**breaking**] `LazyTimelineItemProvider::get_shields` no longer returns an
an `Option`: the `ShieldState` type contains a `None` variant, so the
`Option` was redundant. The `message` field has also been removed: since there
was no way to localise the returned string, applications should not be using it.
([#5959](https://github.com/matrix-org/matrix-rust-sdk/pull/5959))
- Add `SpaceService::get_space_room` to get a space given its id from the space graph if available.
[#5944](https://github.com/matrix-org/matrix-rust-sdk/pull/5944)
- Add `QrCodeData::to_bytes()` to allow generation of a QR code.
Expand Down
26 changes: 11 additions & 15 deletions bindings/matrix-sdk-ffi/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use matrix_sdk::{
attachment::{
AttachmentInfo, BaseAudioInfo, BaseFileInfo, BaseImageInfo, BaseVideoInfo, Thumbnail,
},
deserialized_responses::{ShieldState as SdkShieldState, ShieldStateCode},
event_cache::RoomPaginationStatus,
room::edit::EditedContent as SdkEditedContent,
};
Expand All @@ -33,6 +32,7 @@ use matrix_sdk_ui::timeline::{
self, AttachmentConfig, AttachmentSource, EventItemOrigin,
LatestEventValue as UiLatestEventValue, LatestEventValueLocalState,
MediaUploadProgress as SdkMediaUploadProgress, Profile, TimelineDetails,
TimelineEventShieldState as SdkShieldState, TimelineEventShieldStateCode,
TimelineUniqueId as SdkTimelineUniqueId,
};
use mime::Mime;
Expand Down Expand Up @@ -980,25 +980,21 @@ impl From<&matrix_sdk_ui::timeline::EventSendState> for EventSendState {
/// authenticity properties.
#[derive(uniffi::Enum, Clone)]
pub enum ShieldState {
/// A red shield with a tooltip containing the associated message should be
/// presented.
Red { code: ShieldStateCode, message: String },
/// A grey shield with a tooltip containing the associated message should be
/// presented.
Grey { code: ShieldStateCode, message: String },
/// A red shield with a tooltip containing a message appropriate to the
/// associated code should be presented.
Red { code: TimelineEventShieldStateCode },
/// A grey shield with a tooltip containing a message appropriate to the
/// associated code should be presented.
Grey { code: TimelineEventShieldStateCode },
/// No shield should be presented.
None,
}

impl From<SdkShieldState> for ShieldState {
fn from(value: SdkShieldState) -> Self {
match value {
SdkShieldState::Red { code, message } => {
Self::Red { code, message: message.to_owned() }
}
SdkShieldState::Grey { code, message } => {
Self::Grey { code, message: message.to_owned() }
}
SdkShieldState::Red { code } => Self::Red { code },
SdkShieldState::Grey { code } => Self::Grey { code },
SdkShieldState::None => Self::None,
}
}
Expand Down Expand Up @@ -1277,8 +1273,8 @@ pub struct LazyTimelineItemProvider(Arc<matrix_sdk_ui::timeline::EventTimelineIt
#[matrix_sdk_ffi_macros::export]
impl LazyTimelineItemProvider {
/// Returns the shields for this event timeline item.
fn get_shields(&self, strict: bool) -> Option<ShieldState> {
self.0.get_shield(strict).map(Into::into)
fn get_shields(&self, strict: bool) -> ShieldState {
self.0.get_shield(strict).into()
}

/// Returns some debug information for this event timeline item.
Expand Down
7 changes: 7 additions & 0 deletions crates/matrix-sdk-common/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file.

## [Unreleased] - ReleaseDate

### Features

- [**breaking**] `ShieldStateCode` no longer includes
`SentInClear`. `VeificationState::to_shield_state_{lax,strict}` never
returned that code, ans so having it in the enum was somewhat misleading.
([#5959](https://github.com/matrix-org/matrix-rust-sdk/pull/5959))

### Bug Fixes

- Fix `TimelineEvent::from_bundled_latest_event` sometimes removing the `session_id` of UTDs. This broken event could later be saved to the event cache and become an unresolvable UTD. ([#5970](https://github.com/matrix-org/matrix-rust-sdk/pull/5970)).
Expand Down
4 changes: 0 additions & 4 deletions crates/matrix-sdk-common/src/deserialized_responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
const MISMATCHED_SENDER: &str = "\
The sender of the event does not match the owner of the device \
that created the Megolm session.";
pub const SENT_IN_CLEAR: &str = "Not encrypted.";

/// Represents the state of verification for a decrypted message sent by a
/// device.
Expand Down Expand Up @@ -283,8 +282,6 @@ pub enum ShieldStateCode {
UnsignedDevice,
/// The sender hasn't been verified by the Client's user.
UnverifiedIdentity,
/// An unencrypted event in an encrypted room.
SentInClear,
/// The sender was previously verified but changed their identity.
#[serde(alias = "PreviouslyVerified")]
VerificationViolation,
Expand Down Expand Up @@ -1984,7 +1981,6 @@ mod tests {
assert_json_snapshot!(ShieldStateCode::UnknownDevice);
assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
assert_json_snapshot!(ShieldStateCode::SentInClear);
assert_json_snapshot!(ShieldStateCode::VerificationViolation);
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/matrix-sdk-common/src/deserialized_responses.rs
expression: "ShieldStateCode::SentInClear"
expression: "ShieldStateCode::VerificationViolation"
---
"SentInClear"
"VerificationViolation"
4 changes: 4 additions & 0 deletions crates/matrix-sdk-ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ All notable changes to this project will be documented in this file.

### Features

- [**breaking**] `EventTimelineItem::get_shield` now returns a new type,
`TimelineEventShieldState`, which extends the old `ShieldState` with a code
for `SentInClear`, now that the latter has been removed from `ShieldState`.
([#5959](https://github.com/matrix-org/matrix-rust-sdk/pull/5959))
- Add `SpaceService::get_space_room` to get a space
given its id from the space graph if available.
([#5944](https://github.com/matrix-org/matrix-rust-sdk/pull/5944))
Expand Down
91 changes: 79 additions & 12 deletions crates/matrix-sdk-ui/src/timeline/event_item/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use matrix_sdk::{
deserialized_responses::{EncryptionInfo, ShieldState},
send_queue::{SendHandle, SendReactionHandle},
};
use matrix_sdk_base::deserialized_responses::{SENT_IN_CLEAR, ShieldStateCode};
use matrix_sdk_base::deserialized_responses::ShieldStateCode;
use once_cell::sync::Lazy;
use ruma::{
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedTransactionId,
Expand Down Expand Up @@ -309,30 +309,29 @@ impl EventTimelineItem {
}
}

/// Gets the [`ShieldState`] which can be used to decorate messages in the
/// recommended way.
pub fn get_shield(&self, strict: bool) -> Option<ShieldState> {
/// Gets the [`TimelineEventShieldState`] which can be used to decorate
/// messages in the recommended way.
pub fn get_shield(&self, strict: bool) -> TimelineEventShieldState {
if !self.is_room_encrypted || self.is_local_echo() {
return None;
return TimelineEventShieldState::None;
}

// An unable-to-decrypt message has no authenticity shield.
if self.content().is_unable_to_decrypt() {
return None;
return TimelineEventShieldState::None;
}

match self.encryption_info() {
Some(info) => {
if strict {
Some(info.verification_state.to_shield_state_strict())
info.verification_state.to_shield_state_strict().into()
} else {
Some(info.verification_state.to_shield_state_lax())
info.verification_state.to_shield_state_lax().into()
}
}
None => Some(ShieldState::Red {
code: ShieldStateCode::SentInClear,
message: SENT_IN_CLEAR,
}),
None => {
TimelineEventShieldState::Red { code: TimelineEventShieldStateCode::SentInClear }
}
}
}

Expand Down Expand Up @@ -693,3 +692,71 @@ impl ReactionsByKeyBySender {
None
}
}

/// Extends [`ShieldState`] to allow for a `SentInClear` code.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TimelineEventShieldState {
/// A red shield with a tooltip containing a message appropriate to the
/// associated code should be presented.
Red {
/// A machine-readable representation.
code: TimelineEventShieldStateCode,
},
/// A grey shield with a tooltip containing a message appropriate to the
/// associated code should be presented.
Grey {
/// A machine-readable representation.
code: TimelineEventShieldStateCode,
},
/// No shield should be presented.
None,
}

impl From<ShieldState> for TimelineEventShieldState {
fn from(value: ShieldState) -> Self {
match value {
ShieldState::Red { code, message: _ } => {
TimelineEventShieldState::Red { code: code.into() }
}
ShieldState::Grey { code, message: _ } => {
TimelineEventShieldState::Grey { code: code.into() }
}
ShieldState::None => TimelineEventShieldState::None,
}
}
}

/// Extends [`ShieldStateCode`] to allow for a `SentInClear` code.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum TimelineEventShieldStateCode {
Copy link
Member

Choose a reason for hiding this comment

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

I quite like how errors derive their display implementation, I wonder if there's anything like that for normal enums. Then again, any reason why TimelineEventShieldStateCode can't be an actual thiserror::Error?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not entirely following you. Do we need to implement display here? What don't you like about what's there today?

Copy link
Member

Choose a reason for hiding this comment

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

Ah sorry, this was coming as an addition to removing the msg. We have very well definied messages in the documentatioe here and also a good way of exposing them to the final user through that macro. I figured it fits better.

/// Not enough information available to check the authenticity.
AuthenticityNotGuaranteed,
/// The sending device isn't yet known by the Client.
UnknownDevice,
/// The sending device hasn't been verified by the sender.
UnsignedDevice,
/// The sender hasn't been verified by the Client's user.
UnverifiedIdentity,
/// The sender was previously verified but changed their identity.
VerificationViolation,
/// The `sender` field on the event does not match the owner of the device
/// that established the Megolm session.
MismatchedSender,
/// An unencrypted event in an encrypted room.
SentInClear,
}

impl From<ShieldStateCode> for TimelineEventShieldStateCode {
fn from(value: ShieldStateCode) -> Self {
use TimelineEventShieldStateCode::*;
match value {
ShieldStateCode::AuthenticityNotGuaranteed => AuthenticityNotGuaranteed,
ShieldStateCode::UnknownDevice => UnknownDevice,
ShieldStateCode::UnsignedDevice => UnsignedDevice,
ShieldStateCode::UnverifiedIdentity => UnverifiedIdentity,
ShieldStateCode::VerificationViolation => VerificationViolation,
ShieldStateCode::MismatchedSender => MismatchedSender,
}
}
}
3 changes: 2 additions & 1 deletion crates/matrix-sdk-ui/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ pub use self::{
MemberProfileChange, MembershipChange, Message, MsgLikeContent, MsgLikeKind,
OtherMessageLike, OtherState, PollResult, PollState, Profile, ReactionInfo, ReactionStatus,
ReactionsByKeyBySender, RoomMembershipChange, RoomPinnedEventsChange, Sticker,
ThreadSummary, TimelineDetails, TimelineEventItemId, TimelineItemContent,
ThreadSummary, TimelineDetails, TimelineEventItemId, TimelineEventShieldState,
TimelineEventShieldStateCode, TimelineItemContent,
},
event_type_filter::TimelineEventTypeFilter,
item::{TimelineItem, TimelineItemKind, TimelineUniqueId},
Expand Down
15 changes: 7 additions & 8 deletions crates/matrix-sdk-ui/src/timeline/tests/shields.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use assert_matches::assert_matches;
use eyeball_im::VectorDiff;
use matrix_sdk_base::deserialized_responses::{ShieldState, ShieldStateCode};
use matrix_sdk_test::{ALICE, async_test, event_factory::EventFactory};
use ruma::{
event_id,
Expand All @@ -17,7 +16,7 @@ use ruma::{
use stream_assert::{assert_next_matches, assert_pending};

use crate::timeline::{
EventSendState,
EventSendState, TimelineEventShieldState, TimelineEventShieldStateCode,
tests::{TestTimeline, TestTimelineBuilder},
};

Expand All @@ -31,7 +30,7 @@ async fn test_no_shield_in_unencrypted_room() {

let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
let shield = item.as_event().unwrap().get_shield(false);
assert!(shield.is_none());
assert_eq!(shield, TimelineEventShieldState::None);
}

#[async_test]
Expand All @@ -46,7 +45,7 @@ async fn test_sent_in_clear_shield() {
let shield = item.as_event().unwrap().get_shield(false);
assert_eq!(
shield,
Some(ShieldState::Red { code: ShieldStateCode::SentInClear, message: "Not encrypted." })
TimelineEventShieldState::Red { code: TimelineEventShieldStateCode::SentInClear }
);
}

Expand Down Expand Up @@ -75,7 +74,7 @@ async fn test_local_sent_in_clear_shield() {
// available).
assert!(event_item.is_local_echo());
let shield = event_item.get_shield(false);
assert_eq!(shield, None);
assert_eq!(shield, TimelineEventShieldState::None);

{
// The date divider comes in late.
Expand All @@ -96,7 +95,7 @@ async fn test_local_sent_in_clear_shield() {
// Then the local echo still should not have a shield.
assert!(event_item.is_local_echo());
let shield = event_item.get_shield(false);
assert_eq!(shield, None);
assert_eq!(shield, TimelineEventShieldState::None);

// When the remote echo comes in.
timeline
Expand All @@ -118,7 +117,7 @@ async fn test_local_sent_in_clear_shield() {
let shield = event_item.get_shield(false);
assert_eq!(
shield,
Some(ShieldState::Red { code: ShieldStateCode::SentInClear, message: "Not encrypted." })
TimelineEventShieldState::Red { code: TimelineEventShieldStateCode::SentInClear }
);

// Date divider is adjusted.
Expand Down Expand Up @@ -168,5 +167,5 @@ async fn test_utd_shield() {
// Then the message is displayed with no shield
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
let shield = item.as_event().unwrap().get_shield(false);
assert!(shield.is_none());
assert_eq!(shield, TimelineEventShieldState::None);
}
14 changes: 7 additions & 7 deletions crates/matrix-sdk-ui/tests/integration/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ use matrix_sdk_ui::{
Timeline,
timeline::{
AnyOtherFullStateEventContent, Error, EventSendState, MsgLikeKind, OtherMessageLike,
RedactError, RoomExt, TimelineBuilder, TimelineEventItemId, TimelineFocus,
TimelineItemContent, VirtualTimelineItem, default_event_filter,
RedactError, RoomExt, TimelineBuilder, TimelineEventItemId, TimelineEventShieldState,
TimelineFocus, TimelineItemContent, VirtualTimelineItem, default_event_filter,
},
};
use ruma::{
Expand Down Expand Up @@ -757,7 +757,7 @@ async fn test_timeline_without_encryption_info() {
assert_eq!(items.len(), 2);
assert!(items[0].as_virtual().is_some());
// No encryption, no shields.
assert!(items[1].as_event().unwrap().get_shield(false).is_none());
assert_eq!(items[1].as_event().unwrap().get_shield(false), TimelineEventShieldState::None);
}

#[async_test]
Expand Down Expand Up @@ -787,7 +787,7 @@ async fn test_timeline_without_encryption_can_update() {
assert_eq!(items.len(), 2);
assert!(items[0].as_virtual().is_some());
// No encryption, no shields
assert!(items[1].as_event().unwrap().get_shield(false).is_none());
assert_eq!(items[1].as_event().unwrap().get_shield(false), TimelineEventShieldState::None);

let encryption_event_content = RoomEncryptionEventContent::with_recommended_defaults();
server
Expand All @@ -805,17 +805,17 @@ async fn test_timeline_without_encryption_can_update() {
// Previous timeline event now has a shield.
assert_let!(VectorDiff::Set { index, value } = &timeline_updates[0]);
assert_eq!(*index, 1);
assert!(value.as_event().unwrap().get_shield(false).is_some());
assert_ne!(value.as_event().unwrap().get_shield(false), TimelineEventShieldState::None);

// Room encryption event is received.
assert_let!(VectorDiff::PushBack { value } = &timeline_updates[1]);
assert_let!(TimelineItemContent::OtherState(other_state) = value.as_event().unwrap().content());
assert_let!(AnyOtherFullStateEventContent::RoomEncryption(_) = other_state.content());
assert!(value.as_event().unwrap().get_shield(false).is_some());
assert_ne!(value.as_event().unwrap().get_shield(false), TimelineEventShieldState::None);

// New message event is received and has a shield.
assert_let!(VectorDiff::PushBack { value } = &timeline_updates[2]);
assert!(value.as_event().unwrap().get_shield(false).is_some());
assert_ne!(value.as_event().unwrap().get_shield(false), TimelineEventShieldState::None);

assert_pending!(stream);
}
Expand Down
Loading