Skip to content

Commit a5bd0d7

Browse files
committed
feat(ffi): introduce a mark_all_rooms_as_read method for mitigating backend badge count issues
1 parent 777ce05 commit a5bd0d7

1 file changed

Lines changed: 55 additions & 1 deletion

File tree

bindings/matrix-sdk-ffi/src/client.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ use ruma::{
117117
use serde::{Deserialize, Serialize};
118118
use serde_json::{Value, json};
119119
use tokio::sync::broadcast::error::RecvError;
120-
use tracing::{debug, error};
120+
use tracing::{debug, error, warn};
121121
use url::Url;
122122

123123
use super::{
@@ -1494,6 +1494,60 @@ impl Client {
14941494
.collect()
14951495
}
14961496

1497+
/// Mark all joined rooms as read by sending public, private and fully-read
1498+
/// receipts on each room's latest event.
1499+
///
1500+
/// This is a best-effort operation — per-room errors are logged and
1501+
/// skipped. Receipts are sent unthreaded, which per the Matrix spec
1502+
/// covers all events in a room including those inside threads.
1503+
///
1504+
/// This is useful to mitigate backend led wrong iOS app badges and work
1505+
/// around https://github.com/element-hq/element-x-ios/issues/3151
1506+
pub async fn mark_all_rooms_as_read(&self) -> Result<(), ClientError> {
1507+
use matrix_sdk::room::Receipts;
1508+
use matrix_sdk_base::RoomStateFilter;
1509+
use matrix_sdk_ui::timeline::{TimelineBuilder, TimelineFocus};
1510+
1511+
for sdk_room in self.inner.rooms_filtered(RoomStateFilter::JOINED) {
1512+
let timeline = match TimelineBuilder::new(&sdk_room)
1513+
.with_focus(TimelineFocus::Live { hide_threaded_events: false })
1514+
.build()
1515+
.await
1516+
{
1517+
Ok(tl) => tl,
1518+
Err(err) => {
1519+
warn!(
1520+
"mark_all_rooms_as_read: failed to build timeline for {}: {err}",
1521+
sdk_room.room_id()
1522+
);
1523+
continue;
1524+
}
1525+
};
1526+
1527+
if let Some(event_id) = timeline.latest_event_id().await {
1528+
let receipts = Receipts::new()
1529+
.fully_read_marker(event_id.clone())
1530+
.private_read_receipt(event_id);
1531+
if let Err(err) = sdk_room.send_multiple_receipts(receipts).await {
1532+
warn!(
1533+
"mark_all_rooms_as_read: failed to send receipts for {}: {err}",
1534+
sdk_room.room_id()
1535+
);
1536+
}
1537+
} else {
1538+
// Room has no events; just clear any stale explicit unread flag.
1539+
if let Err(err) = sdk_room.set_unread_flag(false).await {
1540+
warn!(
1541+
"mark_all_rooms_as_read: failed to clear unread flag for {}: {err}",
1542+
sdk_room.room_id()
1543+
);
1544+
}
1545+
}
1546+
}
1547+
1548+
Ok(())
1549+
}
1550+
14971551
/// Get a room by its ID.
14981552
///
14991553
/// # Arguments

0 commit comments

Comments
 (0)