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_family = "wasm")))]
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()]);
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}