Skip to content

Commit

Permalink
Merge pull request #3076 from matrix-org/stefan/mark_unread
Browse files Browse the repository at this point in the history
Add support for MSC2867 - Manually marking rooms as unread
  • Loading branch information
Hywan authored Feb 5, 2024
2 parents 32aa784 + df61037 commit 8975e6a
Show file tree
Hide file tree
Showing 15 changed files with 253 additions and 25 deletions.
18 changes: 9 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ futures-executor = "0.3.21"
futures-util = { version = "0.3.26", default-features = false, features = ["alloc"] }
http = "0.2.6"
itertools = "0.12.0"
ruma = { git = "https://github.com/ruma/ruma", rev = "684ffc789877355bd25269451b2356817c17cc3f", features = ["client-api-c", "compat-upload-signatures", "compat-user-id", "compat-arbitrary-length-ids", "unstable-msc3401"] }
ruma-common = { git = "https://github.com/ruma/ruma", rev = "684ffc789877355bd25269451b2356817c17cc3f"}
ruma = { git = "https://github.com/ruma/ruma", rev = "68c9bb0930f2195fa8672fbef9633ef62737df5d", features = ["client-api-c", "compat-upload-signatures", "compat-user-id", "compat-arbitrary-length-ids", "unstable-msc3401"] }
ruma-common = { git = "https://github.com/ruma/ruma", rev = "68c9bb0930f2195fa8672fbef9633ef62737df5d"}
once_cell = "1.16.0"
rand = "0.8.5"
serde = "1.0.151"
Expand Down
34 changes: 33 additions & 1 deletion bindings/matrix-sdk-ffi/src/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
room_info::RoomInfo,
room_member::RoomMember,
ruma::ImageInfo,
timeline::{EventTimelineItem, Timeline},
timeline::{EventTimelineItem, ReceiptType, Timeline},
utils::u64_to_uint,
TaskHandle,
};
Expand Down Expand Up @@ -517,6 +517,38 @@ impl Room {
pub async fn typing_notice(&self, is_typing: bool) -> Result<(), ClientError> {
Ok(self.inner.typing_notice(is_typing).await?)
}

/// Sets a flag on the room to indicate that the user has explicitly marked
/// it as unread
pub async fn mark_as_unread(&self) -> Result<(), ClientError> {
Ok(self.inner.mark_unread(true).await?)
}

/// Reverts a previously set unread flag.
pub async fn mark_as_read(&self) -> Result<(), ClientError> {
Ok(self.inner.mark_unread(false).await?)
}

/// Reverts a previously set unread flag and sends a read receipt to the
/// latest event in the room. Sending read receipts is useful when
/// executing this from the room list but shouldn't be use when entering
/// the room, the timeline should be left to its own devices in that
/// case.
pub async fn mark_as_read_and_send_read_receipt(
&self,
receipt_type: ReceiptType,
) -> Result<(), ClientError> {
let timeline = self.timeline().await?;

if let Some(event) = timeline.latest_event().await {
if let Err(error) = timeline.send_read_receipt(receipt_type, event.event_id().unwrap())
{
error!("Failed to send read receipt: {error}");
}
}

self.mark_as_read().await
}
}

#[uniffi::export(callback_interface)]
Expand Down
3 changes: 3 additions & 0 deletions bindings/matrix-sdk-ffi/src/room_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub struct RoomInfo {
user_defined_notification_mode: Option<RoomNotificationMode>,
has_room_call: bool,
active_room_call_participants: Vec<String>,
/// Whether this room has been explicitly marked as unread
is_marked_unread: bool,
/// "Interesting" messages received in that room, independently of the
/// notification settings.
num_unread_messages: u64,
Expand Down Expand Up @@ -84,6 +86,7 @@ impl RoomInfo {
.iter()
.map(|u| u.to_string())
.collect(),
is_marked_unread: room.is_marked_unread(),
num_unread_messages: room.num_unread_messages(),
num_unread_notifications: room.num_unread_notifications(),
num_unread_mentions: room.num_unread_mentions(),
Expand Down
6 changes: 6 additions & 0 deletions bindings/matrix-sdk-ffi/src/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,12 @@ impl Timeline {
Ok(Arc::new(RoomMessageEventContentWithoutRelation::new(msgtype)))
})
}

pub async fn latest_event(&self) -> Option<Arc<EventTimelineItem>> {
let latest_event = self.inner.latest_event().await;

latest_event.map(|item| Arc::new(EventTimelineItem(item)))
}
}

#[derive(uniffi::Record)]
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ matrix-sdk-crypto = { workspace = true, optional = true }
matrix-sdk-store-encryption = { workspace = true }
matrix-sdk-test = { workspace = true, optional = true }
once_cell = { workspace = true }
ruma = { workspace = true, features = ["canonical-json", "unstable-msc3381"] }
ruma = { workspace = true, features = ["canonical-json", "unstable-msc3381", "unstable-msc2867"] }
serde = { workspace = true, features = ["rc"] }
serde_json = { workspace = true }
tokio = { workspace = true }
Expand Down
16 changes: 15 additions & 1 deletion crates/matrix-sdk-base/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,21 @@ impl BaseClient {
) {
for raw_event in events {
if let Ok(event) = raw_event.deserialize() {
changes.add_room_account_data(room_id, event, raw_event.clone());
changes.add_room_account_data(room_id, event.clone(), raw_event.clone());

// Rooms can either appear in the current request or already be
// known to the store. If neither of
// those are true then the room is `unknown` and we cannot
// process its account data
if let AnyRoomAccountDataEvent::MarkedUnread(e) = event {
if let Some(room) = changes.room_infos.get_mut(room_id) {
room.base_info.is_marked_unread = e.content.unread;
} else if let Some(room) = self.store.get_room(room_id) {
let mut info = room.clone_info();
info.base_info.is_marked_unread = e.content.unread;
changes.add_room(info);
}
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/matrix-sdk-base/src/rooms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ pub struct BaseRoomInfo {
/// memberships.
#[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
pub(crate) rtc_member: BTreeMap<OwnedUserId, MinimalStateEvent<CallMemberEventContent>>,
// Whether this room has been manually marked as unread
#[serde(default)]
pub(crate) is_marked_unread: bool,
}

impl BaseRoomInfo {
Expand Down Expand Up @@ -317,6 +320,7 @@ impl Default for BaseRoomInfo {
tombstone: None,
topic: None,
rtc_member: BTreeMap::new(),
is_marked_unread: false,
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,12 @@ impl Room {
.get_event_room_receipt_events(self.room_id(), receipt_type, thread, event_id)
.await
}

/// Returns a boolean indicating if this room has been manually marked as
/// unread
pub fn is_marked_unread(&self) -> bool {
self.inner.read().base_info.is_marked_unread
}
}

/// The underlying pure data structure for joined and left rooms.
Expand Down Expand Up @@ -1393,6 +1399,7 @@ mod tests {
"encryption": null,
"guest_access": null,
"history_visibility": null,
"is_marked_unread": false,
"join_rules": null,
"max_power_level": 100,
"name": null,
Expand Down
11 changes: 2 additions & 9 deletions crates/matrix-sdk-base/src/sliding_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,7 @@ impl BaseClient {
.push(raw.clone().cast());
}

// Handles the remaining rooms account data not handled by
// process_sliding_sync_room.
// Handle room account data
for (room_id, raw) in &rooms_account_data {
self.handle_room_account_data(room_id, raw, &mut changes).await;

Expand Down Expand Up @@ -363,13 +362,6 @@ impl BaseClient {
.await?;
}

let room_account_data = if let Some(events) = rooms_account_data.remove(room_id) {
self.handle_room_account_data(room_id, &events, changes).await;
Some(events)
} else {
None
};

process_room_properties(room_data, &mut room_info);

let timeline = self
Expand Down Expand Up @@ -414,6 +406,7 @@ impl BaseClient {
room_info.update_notification_count(notification_count);

let ambiguity_changes = ambiguity_cache.changes.remove(room_id).unwrap_or_default();
let room_account_data = rooms_account_data.get(room_id).cloned();

match room_info.state() {
RoomState::Joined => {
Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk-base/src/store/migration_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ impl BaseRoomInfoV1 {
tombstone,
topic,
rtc_member: BTreeMap::new(),
is_marked_unread: false,
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ matrix-sdk-sqlite = { workspace = true, optional = true }
mime = "0.3.16"
mime2ext = "0.1.52"
rand = { workspace = true , optional = true }
ruma = { workspace = true, features = ["rand", "unstable-msc2448", "unstable-msc2965", "unstable-msc3930", "unstable-msc3245-v1-compat"] }
ruma = { workspace = true, features = ["rand", "unstable-msc2448", "unstable-msc2965", "unstable-msc3930", "unstable-msc3245-v1-compat", "unstable-msc2867"] }
serde = { workspace = true }
serde_html_form = { workspace = true }
serde_json = { workspace = true }
Expand Down
21 changes: 20 additions & 1 deletion crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use ruma::events::{
};
use ruma::{
api::client::{
config::set_global_account_data,
config::{set_global_account_data, set_room_account_data},
context,
error::ErrorKind,
filter::LazyLoadOptions,
Expand All @@ -43,6 +43,7 @@ use ruma::{
assign,
events::{
direct::DirectEventContent,
marked_unread::MarkedUnreadEventContent,
receipt::{Receipt, ReceiptThread, ReceiptType},
room::{
avatar::{self, RoomAvatarEventContent},
Expand Down Expand Up @@ -2394,6 +2395,24 @@ impl Room {
);
Ok(self.client.send(request, None).await?)
}

/// Sets a flag on the room to indicate that the user has explicitly marked
/// it as (un)read
pub async fn mark_unread(&self, unread: bool) -> Result<()> {
let user_id =
self.client.user_id().ok_or_else(|| Error::from(HttpError::AuthenticationRequired))?;

let content = MarkedUnreadEventContent::new(unread);

let request = set_room_account_data::v3::Request::new(
user_id.to_owned(),
self.inner.room_id().to_owned(),
&content,
)?;

self.client.send(request, None).await?;
Ok(())
}
}

/// Details of the (latest) invite.
Expand Down
Loading

0 comments on commit 8975e6a

Please sign in to comment.