@@ -117,7 +117,7 @@ use ruma::{
117117use serde:: { Deserialize , Serialize } ;
118118use serde_json:: { Value , json} ;
119119use tokio:: sync:: broadcast:: error:: RecvError ;
120- use tracing:: { debug, error} ;
120+ use tracing:: { debug, error, warn } ;
121121use url:: Url ;
122122
123123use 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