xmtp_mls/groups/
members.rs1use crate::{context::XmtpSharedContext, identity_updates::IdentityUpdates};
2
3use super::{GroupError, MlsGroup};
4use xmtp_db::prelude::*;
5use xmtp_db::{
6 StorageError,
7 consent_record::{ConsentState, ConsentType},
8};
9use xmtp_id::{
10 InboxId,
11 associations::{AssociationState, Identifier, handle::Handle},
12};
13use xmtp_mls_validation::commit::extract_group_membership;
14
15#[derive(Debug, Clone)]
16pub struct GroupMember {
17 pub inbox_id: InboxId,
18 pub account_identifiers: Vec<Identifier>,
19 pub installation_ids: Vec<Vec<u8>>,
20 pub permission_level: PermissionLevel,
21 pub consent_state: ConsentState,
22 pub handle: Option<Handle>,
25 pub other_handles: Vec<Handle>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub enum PermissionLevel {
31 Member,
32 Admin,
33 SuperAdmin,
34}
35
36impl<Context> MlsGroup<Context>
37where
38 Context: XmtpSharedContext,
39{
40 pub async fn members(&self) -> Result<Vec<GroupMember>, GroupError> {
43 let db = self.context.db();
44 let group_membership = self.with_group_snapshot(|mls_group| {
45 Ok(extract_group_membership(mls_group.extensions())?)
46 })?;
47 let requests = group_membership
48 .members
49 .into_iter()
50 .map(|(inbox_id, sequence_id)| (inbox_id, sequence_id as i64))
51 .filter(|(_, sequence_id)| *sequence_id != 0) .collect::<Vec<_>>();
53
54 let association_states = db.batch_read_from_cache(requests.clone())?;
55 let mut association_states: Vec<AssociationState> = association_states
56 .into_iter()
57 .map(|a| a.try_into())
58 .collect::<Result<_, _>>()
59 .map_err(StorageError::from)?;
60 if association_states.len() != requests.len() {
61 let missing_requests: Vec<_> = requests
63 .iter()
64 .filter_map(|(id, sequence)| {
65 if association_states
67 .iter()
68 .any(|state| state.inbox_id() == id)
69 {
70 return None;
71 }
72 Some((id.as_str(), Some(*sequence)))
73 })
74 .collect();
75 let identity_updates = IdentityUpdates::new(&self.context);
76 let mut new_states = identity_updates
77 .batch_get_association_state(&db, &missing_requests)
78 .await?;
79 association_states.append(&mut new_states);
80
81 if association_states.len() != requests.len() {
82 tracing::error!(
86 "Failed to load all members for group - metadata: {:?}, computed members: {:?}",
87 requests,
88 association_states
89 );
90 return Err(GroupError::InvalidGroupMembership);
91 }
92 }
93 let inbox_ids: Vec<&str> = requests.iter().map(|(id, _)| id.as_str()).collect();
96 let mut newest_states = db.newest_cached_states(&inbox_ids)?;
97 let mutable_metadata = self.live_mutable_metadata()?;
98 let members = association_states
99 .into_iter()
100 .map(|association_state| {
101 let inbox_id_str = association_state.inbox_id().to_string();
102 let newest_state: Option<AssociationState> = newest_states
105 .remove(&inbox_id_str)
106 .map(|(_, state)| state.try_into())
107 .transpose()
108 .map_err(StorageError::from)?;
109 let handle_state = newest_state.as_ref().unwrap_or(&association_state);
110 let is_admin = mutable_metadata.is_admin(&inbox_id_str);
111 let is_super_admin = mutable_metadata.is_super_admin(&inbox_id_str);
112 let permission_level = if is_super_admin {
113 PermissionLevel::SuperAdmin
114 } else if is_admin {
115 PermissionLevel::Admin
116 } else {
117 PermissionLevel::Member
118 };
119
120 let consent = db.get_consent_record(inbox_id_str.clone(), ConsentType::InboxId)?;
121
122 Ok(GroupMember {
123 inbox_id: inbox_id_str.clone(),
124 account_identifiers: association_state.identifiers(),
125 installation_ids: association_state.installation_ids(),
126 permission_level,
127 consent_state: consent.map_or(ConsentState::Unknown, |c| c.state),
128 handle: handle_state.display_handle().cloned(),
129 other_handles: handle_state.other_handles().cloned().collect(),
130 })
131 })
132 .collect::<Result<Vec<GroupMember>, GroupError>>()?;
133
134 Ok(members)
135 }
136}