Skip to content

Commit

Permalink
read receipts: move the unread messages and mentions counts to separa…
Browse files Browse the repository at this point in the history
…te fields of `RoomInfo`
  • Loading branch information
bnjbvr committed Dec 19, 2023
1 parent d6733e2 commit 68ee9d6
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 35 deletions.
4 changes: 4 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>,
num_unread_messages: u64,
num_unread_mentions: u64,
}

impl RoomInfo {
Expand Down Expand Up @@ -75,6 +77,8 @@ impl RoomInfo {
.iter()
.map(|u| u.to_string())
.collect(),
num_unread_messages: room.num_unread_messages(),
num_unread_mentions: room.num_unread_mentions(),
})
}
}
51 changes: 24 additions & 27 deletions crates/matrix-sdk-base/src/read_receipts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,21 @@ use ruma::{
serde::Raw,
EventId, OwnedEventId, UserId,
};
use tracing::{field::display, instrument, trace};
use tracing::{instrument, trace};

use super::BaseClient;
use crate::{error::Result, store::StateChanges, PreviousEventsProvider, RoomInfo};

#[instrument(skip_all, fields(room_id))]
#[instrument(skip_all, fields(room_id = %room_info.room_id))]
pub(crate) async fn compute_notifications(
client: &BaseClient,
changes: &StateChanges,
previous_events_provider: &dyn PreviousEventsProvider,
new_events: &[SyncTimelineEvent],
room_info: &mut RoomInfo,
) -> Result<()> {
// Only apply the algorithm to encrypted rooms, since unencrypted rooms' unread
// notification counts ought to be properly computed by the server.
if !room_info.is_encrypted() {
return Ok(());
}

tracing::Span::current().record("room_id", display(&room_info.room_id));

let user_id = &client.session_meta().unwrap().user_id;
let prev_latest_receipt_event_id = room_info.latest_read_receipt_event_id.clone();
let prev_latest_receipt_event_id = room_info.read_receipts.latest_read_receipt_event_id.clone();

if let Some(receipt_event) = changes.receipts.get(room_info.room_id()) {
trace!("Got a new receipt event!");
Expand Down Expand Up @@ -62,7 +54,7 @@ pub(crate) async fn compute_notifications(
// about.

// First, save the event id as the latest one that has a read receipt.
room_info.latest_read_receipt_event_id = Some(receipt_event_id.clone());
room_info.read_receipts.latest_read_receipt_event_id = Some(receipt_event_id.clone());

// Try to find if the read receipts refers to an event from the current sync, to
// avoid searching the cached timeline events.
Expand Down Expand Up @@ -110,17 +102,28 @@ pub(crate) async fn compute_notifications(
// for the next receipt.
trace!("All other ways failed, including all new events for the receipts count.");
for event in new_events {
if event.push_actions.iter().any(ruma::push::Action::is_highlight) {
room_info.notification_counts.highlight_count += 1;
}
if marks_as_unread(&event.event, user_id) {
room_info.notification_counts.notification_count += 1;
}
count_unread_and_mentions(event, user_id, room_info);
}

Ok(())
}

#[inline(always)]
fn count_unread_and_mentions(
event: &SyncTimelineEvent,
user_id: &UserId,
room_info: &mut RoomInfo,
) {
for action in &event.push_actions {
if action.should_notify() && marks_as_unread(&event.event, user_id) {
room_info.read_receipts.num_unread += 1;
}
if action.is_highlight() {
room_info.read_receipts.num_mentions += 1;
}
}
}

/// Try to find the event to which the receipt attaches to, and if found, will
/// update the notification count in the room.
///
Expand All @@ -134,20 +137,14 @@ fn find_and_count_events<'a>(
let mut counting_receipts = false;
for event in events {
if counting_receipts {
for action in &event.push_actions {
if action.is_highlight() {
room_info.notification_counts.highlight_count += 1;
}
if action.should_notify() && marks_as_unread(&event.event, user_id) {
room_info.notification_counts.notification_count += 1;
}
}
count_unread_and_mentions(event, user_id, room_info);
} else if let Ok(Some(event_id)) = event.event.get_field::<OwnedEventId>("event_id") {
if event_id == receipt_event_id {
// Bingo! Switch over to the counting state, after resetting the
// previous counts.
trace!("Found the event the receipt was referring to! Starting to count.");
room_info.notification_counts = Default::default();
room_info.read_receipts.num_unread = 0;
room_info.read_receipts.num_mentions = 0;
counting_receipts = true;
}
}
Expand Down
48 changes: 41 additions & 7 deletions crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,23 @@ impl Room {
self.inner.read().notification_counts
}

/// Get the number of unread messages (computed client-side).
///
/// This might be more precise than [`Self::unread_notification_counts`] for
/// encrypted rooms.
pub fn num_unread_messages(&self) -> u64 {
self.inner.read().read_receipts.num_unread
}

/// Get the number of unread mentions (computed client-side), that is,
/// messages causing a highlight in a room.
///
/// This might be more precise than [`Self::unread_notification_counts`] for
/// encrypted rooms.
pub fn num_unread_mentions(&self) -> u64 {
self.inner.read().read_receipts.num_mentions
}

/// Check if the room has its members fully synced.
///
/// Members might be missing if lazy member loading was enabled for the
Expand Down Expand Up @@ -710,6 +727,22 @@ impl Room {
}
}

/// Information abount read receipts collected during processing of that room.

Check warning on line 730 in crates/matrix-sdk-base/src/rooms/normal.rs

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"abount" should be "about".
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct RoomReadReceipts {
/// Does the room have unread messages?
pub(crate) num_unread: u64,

/// Does the room have messages causing highlights for the users? (aka
/// mentions)
pub(crate) num_mentions: u64,

/// The id of the event the last unthreaded (or main-threaded, for better
/// compatibility with clients that have thread support) read receipt is
/// attached to.
pub(crate) latest_read_receipt_event_id: Option<OwnedEventId>,
}

/// The underlying pure data structure for joined and left rooms.
///
/// Holds all the info needed to persist a room into the state store.
Expand All @@ -721,7 +754,10 @@ pub struct RoomInfo {
/// The state of the room.
pub(crate) room_state: RoomState,

/// The unread notifications counts.
/// The unread notifications counts, as returned by the server.
///
/// These might be incorrect for encrypted rooms, since the server doesn't
/// have access to the content of the encrypted events.
pub(crate) notification_counts: UnreadNotificationsCount,

/// The summary of this room.
Expand All @@ -743,11 +779,9 @@ pub struct RoomInfo {
#[cfg(feature = "experimental-sliding-sync")]
pub(crate) latest_event: Option<Box<LatestEvent>>,

/// The id of the event the last unthreaded (or main-threaded, for better
/// compatibility with clients that have thread support) read receipt is
/// attached to.
/// Information about read receipts for this room.
#[serde(default)]
pub(crate) latest_read_receipt_event_id: Option<OwnedEventId>,
pub(crate) read_receipts: RoomReadReceipts,

/// Base room info which holds some basic event contents important for the
/// room state.
Expand Down Expand Up @@ -785,7 +819,7 @@ impl RoomInfo {
encryption_state_synced: false,
#[cfg(feature = "experimental-sliding-sync")]
latest_event: None,
latest_read_receipt_event_id: None,
read_receipts: Default::default(),
base_info: Box::new(BaseRoomInfo::new()),
}
}
Expand Down Expand Up @@ -1267,7 +1301,7 @@ mod tests {
Raw::from_json_string(json!({"sender": "@u:i.uk"}).to_string()).unwrap().into(),
))),
base_info: Box::new(BaseRoomInfo::new()),
latest_read_receipt_event_id: None,
read_receipts: Default::default(),
};

let info_json = json!({
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/src/store/migration_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ impl RoomInfoV1 {
encryption_state_synced,
#[cfg(feature = "experimental-sliding-sync")]
latest_event: latest_event.map(|ev| Box::new(LatestEvent::new(ev))),
latest_read_receipt_event_id: None,
read_receipts: Default::default(),
base_info: base_info.migrate(create),
}
}
Expand Down

0 comments on commit 68ee9d6

Please sign in to comment.