atagen

macltok

public
Star0
Watch0

Commit 121a56ce119d

tarball

atagen <boss@atagen.co>2026-10-24 06:43 UTC

1 file changed+46-19
transport: refetch each room's roster once per engine and add refresh_members

A store was seen with RoomInfo::members_synced persisted over a partial roster, and Room::members never requests /members again once the flag is set, so the member list stayed short until a fresh login. The first ClientHandle::members call per room now marks members missing, which costs one deduplicated /members per room per session. refresh_members forces the refetch on demand.
Modified file: src/transport.rs+46 additions−19 deletions
@@ -1,8 +1,9 @@
11 use std::{
2+ collections::HashSet,
23 fmt,
34 future::Future,
45 pin::Pin,
5- sync::Arc,
6+ sync::{Arc, Mutex},
67 task::{Context, Poll},
78 };
89
@@ -49,6 +50,7 @@ pub struct ClientHandle {
4950 sync_service: Arc<SyncService>,
5051 media: Arc<MediaService>,
5152 spaces: Arc<OnceCell<SpaceService>>,
53+ members_checked: Arc<Mutex<HashSet<OwnedRoomId>>>,
5254 _store_lock: Option<Arc<StoreLock>>,
5355 }
5456
@@ -751,24 +753,25 @@ impl ClientHandle {
751753 room_id: &RoomId,
752754 filter: MemberFilter,
753755 ) -> Result<Vec<MemberSummary>, Error> {
754- let memberships = match filter {
755- MemberFilter::Active => RoomMemberships::ACTIVE,
756- MemberFilter::Joined => RoomMemberships::JOIN,
757- MemberFilter::Invited => RoomMemberships::INVITE,
758- MemberFilter::Banned => RoomMemberships::BAN,
759- MemberFilter::Left => RoomMemberships::LEAVE,
760- // An empty mask is the SDK's unfiltered wildcard: it matches every
761- // membership, including custom states that `all()` would exclude.
762- MemberFilter::All => RoomMemberships::empty(),
763- };
764- Ok(self
765- .room(room_id)?
766- .members(memberships)
767- .await
768- .map_err(|source| Error::from_sdk("listing members failed", source))?
769- .into_iter()
770- .map(member_summary)
771- .collect())
756+ let room = self.room(room_id)?;
757+ // stores have been seen with members_synced persisted over a partial
758+ // roster, and the sdk never asks /members again once it's set
759+ if self.members_checked.lock().unwrap().insert(room_id.to_owned()) {
760+ room.mark_members_missing();
761+ }
762+ list_members(&room, filter).await
763+ }
764+
765+ /// drop the cached roster for a room and fetch /members again
766+ pub async fn refresh_members(
767+ &self,
768+ room_id: &RoomId,
769+ filter: MemberFilter,
770+ ) -> Result<Vec<MemberSummary>, Error> {
771+ let room = self.room(room_id)?;
772+ room.mark_members_missing();
773+ self.members_checked.lock().unwrap().insert(room_id.to_owned());
774+ list_members(&room, filter).await
772775 }
773776
774777 pub async fn room_power_levels(&self, room_id: &RoomId) -> Result<RoomPowerLevels, Error> {
@@ -1060,6 +1063,7 @@ pub(crate) fn transport_pair(
10601063 sync_service,
10611064 media,
10621065 spaces: Arc::new(OnceCell::new()),
1066+ members_checked: Arc::default(),
10631067 _store_lock: store_lock,
10641068 },
10651069 DriverPort {
@@ -1071,6 +1075,29 @@ pub(crate) fn transport_pair(
10711075 )
10721076 }
10731077
1078+async fn list_members(
1079+ room: &matrix_sdk::Room,
1080+ filter: MemberFilter,
1081+) -> Result<Vec<MemberSummary>, Error> {
1082+ let memberships = match filter {
1083+ MemberFilter::Active => RoomMemberships::ACTIVE,
1084+ MemberFilter::Joined => RoomMemberships::JOIN,
1085+ MemberFilter::Invited => RoomMemberships::INVITE,
1086+ MemberFilter::Banned => RoomMemberships::BAN,
1087+ MemberFilter::Left => RoomMemberships::LEAVE,
1088+ // An empty mask is the SDK's unfiltered wildcard: it matches every
1089+ // membership, including custom states that `all()` would exclude.
1090+ MemberFilter::All => RoomMemberships::empty(),
1091+ };
1092+ Ok(room
1093+ .members(memberships)
1094+ .await
1095+ .map_err(|source| Error::from_sdk("listing members failed", source))?
1096+ .into_iter()
1097+ .map(member_summary)
1098+ .collect())
1099+}
1100+
10741101 fn normalise_push_notification(
10751102 room_id: &RoomId,
10761103 event_id: &EventId,