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