matrix_sdk/room/
knock_requests.rs1use js_int::UInt;
16use ruma::{EventId, OwnedEventId, OwnedMxcUri, OwnedUserId, RoomId};
17
18use crate::{room::RoomMember, Error, Room};
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_arch = "wasm32")))]
107mod tests {
108 use matrix_sdk_test::{async_test, event_factory::EventFactory, JoinedRoomBuilder};
109 use ruma::{
110 event_id, events::room::member::MembershipState, owned_user_id, room_id, user_id, EventId,
111 };
112
113 use crate::{
114 room::knock_requests::{KnockRequest, KnockRequestMemberInfo},
115 test_utils::mocks::MatrixMockServer,
116 Room,
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![f
129 .member(user_id)
130 .membership(MembershipState::Knock)
131 .event_id(event_id)
132 .into_raw_timeline()
133 .cast()]);
134 let room = server.sync_room(&client, joined_room_builder).await;
135
136 let knock_request = make_knock_request(&room, Some(event_id));
137
138 knock_request.mark_as_seen().await.expect("Failed to mark as seen");
140
141 let seen_ids =
143 room.get_seen_knock_request_ids().await.expect("Failed to get seen join request ids");
144 assert_eq!(seen_ids.len(), 1);
145 assert_eq!(
146 seen_ids.into_iter().next().expect("Couldn't load next item"),
147 (event_id.to_owned(), user_id.to_owned())
148 );
149 }
150
151 #[async_test]
152 async fn test_accept() {
153 let server = MatrixMockServer::new().await;
154 let client = server.client_builder().build().await;
155 let room_id = room_id!("!a:b.c");
156
157 let room = server.sync_joined_room(&client, room_id).await;
158
159 let knock_request = make_knock_request(&room, None);
160
161 server.mock_invite_user_by_id().ok().mock_once().mount().await;
163
164 knock_request.accept().await.expect("Failed to accept the request");
166 }
167
168 #[async_test]
169 async fn test_decline() {
170 let server = MatrixMockServer::new().await;
171 let client = server.client_builder().build().await;
172 let room_id = room_id!("!a:b.c");
173
174 let room = server.sync_joined_room(&client, room_id).await;
175
176 let knock_request = make_knock_request(&room, None);
177
178 server.mock_kick_user().ok().mock_once().mount().await;
180
181 knock_request.decline(None).await.expect("Failed to decline the request");
183 }
184
185 #[async_test]
186 async fn test_decline_and_ban() {
187 let server = MatrixMockServer::new().await;
188 let client = server.client_builder().build().await;
189 let room_id = room_id!("!a:b.c");
190
191 let room = server.sync_joined_room(&client, room_id).await;
192
193 let knock_request = make_knock_request(&room, None);
194
195 server.mock_ban_user().ok().mock_once().mount().await;
197
198 knock_request
200 .decline_and_ban(None)
201 .await
202 .expect("Failed to decline the request and ban the user");
203 }
204
205 fn make_knock_request(room: &Room, event_id: Option<&EventId>) -> KnockRequest {
206 KnockRequest::new(
207 room,
208 event_id.unwrap_or(event_id!("$a:b.c")),
209 None,
210 KnockRequestMemberInfo {
211 user_id: owned_user_id!("@alice:b.c"),
212 display_name: None,
213 avatar_url: None,
214 reason: None,
215 },
216 false,
217 )
218 }
219}