Skip to content

Commit 971bc7b

Browse files
committed
fix: Make sure Heroes are reactive to status changes.
1 parent 9104883 commit 971bc7b

3 files changed

Lines changed: 85 additions & 2 deletions

File tree

crates/matrix-sdk-base/src/room/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,13 @@ impl Room {
469469
self.store.get_user_ids(self.room_id(), RoomMemberships::JOIN).await
470470
}
471471

472+
/// The user IDs of this room's heroes, as stored, for cheaply checking
473+
/// hero membership without loading their global profiles.
474+
#[cfg(feature = "unstable-msc4426")]
475+
pub(crate) fn hero_user_ids(&self) -> Vec<OwnedUserId> {
476+
self.info.read().heroes().iter().map(|hero| hero.user_id.clone()).collect()
477+
}
478+
472479
/// Get the heroes for this room.
473480
///
474481
/// This also filters out possible service members from the list of heroes

crates/matrix-sdk-base/src/room/room_info.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,9 @@ bitflags! {
14581458

14591459
/// The user's `m.fully_read` marker has changed.
14601460
const FULLY_READ = 0b0000_0001_0000_0000;
1461+
1462+
/// A room hero's global profile changed (e.g. their status or call).
1463+
const HEROES = 0b0000_0010_0000_0000;
14611464
}
14621465
}
14631466

crates/matrix-sdk-base/src/sliding_sync.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,29 @@ impl BaseClient {
219219
)
220220
.await?;
221221

222-
// Notify subscribers (e.g. open timelines) about global profile updates, which
223-
// don't otherwise touch any room and so trigger no other broadcast.
222+
// Profile-only updates don't modify any rooms, so nothing else broadcasts
223+
// them. Surface the change so subscribers can react accordingly.
224224
if !extensions.profiles.is_empty() {
225225
let _ = self
226226
.global_profile_updates_sender
227227
.send(extensions.profiles.keys().cloned().collect());
228+
229+
// Nudge `RoomInfo` so hero status/call fields are re-read.
230+
#[cfg(feature = "unstable-msc4426")]
231+
for room in self.state_store.rooms() {
232+
if room
233+
.hero_user_ids()
234+
.iter()
235+
.any(|user_id| extensions.profiles.contains_key(user_id))
236+
{
237+
let _ = self.state_store.room_info_notable_update_sender.send(
238+
crate::RoomInfoNotableUpdate {
239+
room_id: room.room_id().to_owned(),
240+
reasons: crate::RoomInfoNotableUpdateReasons::HEROES,
241+
},
242+
);
243+
}
244+
}
228245
}
229246

230247
let mut context = processors::Context::default();
@@ -1643,6 +1660,62 @@ mod tests {
16431660
);
16441661
}
16451662

1663+
#[cfg(feature = "unstable-msc4426")]
1664+
#[async_test]
1665+
async fn test_hero_global_profile_update_triggers_notable_update() {
1666+
use ruma::profile::UserProfileUpdate;
1667+
1668+
let client = logged_in_base_client(None).await;
1669+
let room_id = room_id!("!r:e.uk");
1670+
let alice = owned_user_id!("@alice:e.uk");
1671+
1672+
// Given a room where Alice is a hero.
1673+
let mut room = http::response::Room::new();
1674+
room.heroes = Some(vec![assign!(http::response::Hero::new(alice.clone()), {
1675+
name: Some("Alice".to_owned()),
1676+
})]);
1677+
let response = response_with_room(room_id, room);
1678+
client
1679+
.process_sliding_sync(
1680+
&response,
1681+
&RequestedRequiredStates::default(),
1682+
&client.state_store_lock().lock().await,
1683+
)
1684+
.await
1685+
.expect("Failed to process sync");
1686+
1687+
let mut room_info_notable_update = client.room_info_notable_update_receiver();
1688+
1689+
// When a subsequent sync carries only a profiles-extension update for Alice.
1690+
let mut response = http::Response::new("1".to_owned());
1691+
response.extensions.profiles.insert(
1692+
alice.clone(),
1693+
UserProfileUpdate::from_iter([(
1694+
"org.matrix.msc4426.status".to_owned(),
1695+
json!({ "text": "Away", "emoji": "🌴" }),
1696+
)]),
1697+
);
1698+
client
1699+
.process_sliding_sync(
1700+
&response,
1701+
&RequestedRequiredStates::default(),
1702+
&client.state_store_lock().lock().await,
1703+
)
1704+
.await
1705+
.expect("Failed to process sync");
1706+
1707+
// Then a `HEROES` notable update is emitted for the room, so consumers
1708+
// re-read the hero profiles.
1709+
assert_matches!(
1710+
room_info_notable_update.recv().await,
1711+
Ok(RoomInfoNotableUpdate { room_id: received_room_id, reasons }) => {
1712+
assert_eq!(received_room_id, room_id);
1713+
assert!(reasons.contains(RoomInfoNotableUpdateReasons::HEROES));
1714+
}
1715+
);
1716+
assert!(room_info_notable_update.is_empty());
1717+
}
1718+
16461719
#[async_test]
16471720
async fn test_recency_stamp_is_found_when_processing_sliding_sync_response() {
16481721
// Given a logged-in client

0 commit comments

Comments
 (0)