matrix_sdk/room/
knock_requests.rs1use js_int::UInt;
16use ruma::{EventId, OwnedEventId, OwnedMxcUri, OwnedUserId, RoomId};
17
18use crate::{Error, Room, room::RoomMember};
19
20#[derive(Debug, Clone)]
22pub struct KnockRequest {
23 room: Room,
24 pub event_id: OwnedEventId,
26 pub timestamp: Option<UInt>,
28 pub member_info: KnockRequestMemberInfo,
30 pub is_seen: bool,
32}
33
34impl KnockRequest {
35 pub(crate) fn new(
36 room: &Room,
37 event_id: &EventId,
38 timestamp: Option<UInt>,
39 member: KnockRequestMemberInfo,
40 is_seen: bool,
41 ) -> Self {
42 Self {
43 room: room.clone(),
44 event_id: event_id.to_owned(),
45 timestamp,
46 member_info: member,
47 is_seen,
48 }
49 }
50
51 pub fn room_id(&self) -> &RoomId {
53 self.room.room_id()
54 }
55
56 pub async fn mark_as_seen(&self) -> Result<(), Error> {
59 self.room.mark_knock_requests_as_seen(&[self.member_info.user_id.to_owned()]).await?;
60 Ok(())
61 }
62
63 pub async fn accept(&self) -> Result<(), Error> {
65 self.room.invite_user_by_id(&self.member_info.user_id).await
66 }
67
68 pub async fn decline(&self, reason: Option<&str>) -> Result<(), Error> {
71 self.room.kick_user(&self.member_info.user_id, reason).await
72 }
73
74 pub async fn decline_and_ban(&self, reason: Option<&str>) -> Result<(), Error> {
77 self.room.ban_user(&self.member_info.user_id, reason).await
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct KnockRequestMemberInfo {
84 pub user_id: OwnedUserId,
86 pub display_name: Option<String>,
88 pub avatar_url: Option<OwnedMxcUri>,
90 pub reason: Option<String>,
92}
93
94impl KnockRequestMemberInfo {
95 pub(crate) fn from_member(member: &RoomMember) -> Self {
96 Self {
97 user_id: member.user_id().to_owned(),
98 display_name: member.display_name().map(ToOwned::to_owned),
99 avatar_url: member.avatar_url().map(ToOwned::to_owned),
100 reason: member.event().reason().map(ToOwned::to_owned),
101 }
102 }
103}
104
105#[cfg(all(test, not(target_family = "wasm")))]
107mod tests {
108 use matrix_sdk_test::{JoinedRoomBuilder, async_test, event_factory::EventFactory};
109 use ruma::{
110 EventId, event_id, events::room::member::MembershipState, owned_user_id, room_id, user_id,
111 };
112
113 use crate::{
114 Room,
115 room::knock_requests::{KnockRequest, KnockRequestMemberInfo},
116 test_utils::mocks::MatrixMockServer,
117 };
118
119 #[async_test]
120 async fn test_mark_as_seen() {
121 let server = MatrixMockServer::new().await;
122 let client = server.client_builder().build().await;
123 let room_id = room_id!("!a:b.c");
124 let event_id = event_id!("$a:b.c");
125 let user_id = user_id!("@alice:b.c");
126
127 let f = EventFactory::new().room(room_id);
128 let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_bulk(vec![
129 f.member(user_id).membership(MembershipState::Knock).event_id(event_id).into_raw(),
130 ]);
131 let room = server.sync_room(&client, joined_room_builder).await;
132
133 let knock_request = make_knock_request(&room, Some(event_id));
134
135 knock_request.mark_as_seen().await.expect("Failed to mark as seen");
137
138 let seen_ids =
140 room.get_seen_knock_request_ids().await.expect("Failed to get seen join request ids");
141 assert_eq!(seen_ids.len(), 1);
142 assert_eq!(
143 seen_ids.into_iter().next().expect("Couldn't load next item"),
144 (event_id.to_owned(), user_id.to_owned())
145 );
146 }
147
148 #[async_test]
149 async fn test_accept() {
150 let server = MatrixMockServer::new().await;
151 let client = server.client_builder().build().await;
152 let room_id = room_id!("!a:b.c");
153
154 let room = server.sync_joined_room(&client, room_id).await;
155
156 let knock_request = make_knock_request(&room, None);
157
158 server.mock_invite_user_by_id().ok().mock_once().mount().await;
160
161 knock_request.accept().await.expect("Failed to accept the request");
163 }
164
165 #[async_test]
166 async fn test_decline() {
167 let server = MatrixMockServer::new().await;
168 let client = server.client_builder().build().await;
169 let room_id = room_id!("!a:b.c");
170
171 let room = server.sync_joined_room(&client, room_id).await;
172
173 let knock_request = make_knock_request(&room, None);
174
175 server.mock_kick_user().ok().mock_once().mount().await;
177
178 knock_request.decline(None).await.expect("Failed to decline the request");
180 }
181
182 #[async_test]
183 async fn test_decline_and_ban() {
184 let server = MatrixMockServer::new().await;
185 let client = server.client_builder().build().await;
186 let room_id = room_id!("!a:b.c");
187
188 let room = server.sync_joined_room(&client, room_id).await;
189
190 let knock_request = make_knock_request(&room, None);
191
192 server.mock_ban_user().ok().mock_once().mount().await;
194
195 knock_request
197 .decline_and_ban(None)
198 .await
199 .expect("Failed to decline the request and ban the user");
200 }
201
202 fn make_knock_request(room: &Room, event_id: Option<&EventId>) -> KnockRequest {
203 KnockRequest::new(
204 room,
205 event_id.unwrap_or(event_id!("$a:b.c")),
206 None,
207 KnockRequestMemberInfo {
208 user_id: owned_user_id!("@alice:b.c"),
209 display_name: None,
210 avatar_url: None,
211 reason: None,
212 },
213 false,
214 )
215 }
216}