1use matrix_sdk_base::Room as BaseRoom;
2use ruma::{
3 OwnedRoomAliasId, RoomAliasId,
4 api::client::{
5 directory::{get_room_visibility, set_room_visibility},
6 room::Visibility,
7 state::send_state_event,
8 },
9 assign,
10 events::{
11 EmptyStateKey,
12 room::{
13 canonical_alias::RoomCanonicalAliasEventContent,
14 history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
15 join_rules::{JoinRule, RoomJoinRulesEventContent},
16 retention::RoomRetentionEventContent,
17 },
18 },
19};
20
21use crate::{Client, Result};
22
23#[derive(Debug)]
26pub struct RoomPrivacySettings<'a> {
27 room: &'a BaseRoom,
28 client: &'a Client,
29}
30
31impl<'a> RoomPrivacySettings<'a> {
32 pub(crate) fn new(room: &'a BaseRoom, client: &'a Client) -> Self {
33 Self { room, client }
34 }
35
36 pub async fn publish_room_alias_in_room_directory(
43 &'a self,
44 alias: &RoomAliasId,
45 ) -> Result<bool> {
46 if self.client.is_room_alias_available(alias).await? {
47 self.client.create_room_alias(alias, self.room.room_id()).await?;
48 return Ok(true);
49 }
50
51 Ok(false)
52 }
53
54 pub async fn remove_room_alias_from_room_directory(
61 &'a self,
62 alias: &RoomAliasId,
63 ) -> Result<bool> {
64 if self.client.resolve_room_alias(alias).await.is_ok() {
65 self.client.remove_room_alias(alias).await?;
66 return Ok(true);
67 }
68
69 Ok(false)
70 }
71
72 pub async fn update_canonical_alias(
85 &'a self,
86 alias: Option<OwnedRoomAliasId>,
87 alt_aliases: Vec<OwnedRoomAliasId>,
88 ) -> Result<()> {
89 let content = assign!(
91 RoomCanonicalAliasEventContent::new(),
92 { alias, alt_aliases }
93 );
94
95 let request = send_state_event::v3::Request::new(
97 self.room.room_id().to_owned(),
98 &EmptyStateKey,
99 &content,
100 )?;
101 self.client.send(request).await?;
102
103 Ok(())
104 }
105
106 pub async fn update_room_history_visibility(
113 &'a self,
114 new_value: HistoryVisibility,
115 ) -> Result<()> {
116 let request = send_state_event::v3::Request::new(
117 self.room.room_id().to_owned(),
118 &EmptyStateKey,
119 &RoomHistoryVisibilityEventContent::new(new_value),
120 )?;
121 self.client.send(request).await?;
122 Ok(())
123 }
124
125 pub async fn update_join_rule(&'a self, new_rule: JoinRule) -> Result<()> {
132 let request = send_state_event::v3::Request::new(
133 self.room.room_id().to_owned(),
134 &EmptyStateKey,
135 &RoomJoinRulesEventContent::new(new_rule),
136 )?;
137 self.client.send(request).await?;
138 Ok(())
139 }
140
141 pub async fn update_room_retention(&'a self, content: RoomRetentionEventContent) -> Result<()> {
153 let request = send_state_event::v3::Request::new(
154 self.room.room_id().to_owned(),
155 &EmptyStateKey,
156 &content,
157 )?;
158 self.client.send(request).await?;
159 Ok(())
160 }
161
162 pub async fn get_room_visibility(&'a self) -> Result<Visibility> {
167 let request = get_room_visibility::v3::Request::new(self.room.room_id().to_owned());
168 let response = self.client.send(request).await?;
169 Ok(response.visibility)
170 }
171
172 pub async fn update_room_visibility(&'a self, visibility: Visibility) -> Result<()> {
177 let request =
178 set_room_visibility::v3::Request::new(self.room.room_id().to_owned(), visibility);
179
180 self.client.send(request).await?;
181
182 Ok(())
183 }
184}
185
186#[cfg(all(test, not(target_family = "wasm")))]
187mod tests {
188 use std::{ops::Not, time::Duration};
189
190 use matrix_sdk_test::{JoinedRoomBuilder, async_test, event_factory::EventFactory};
191 use ruma::{
192 api::client::room::Visibility,
193 event_id,
194 events::{
195 StateEventType,
196 room::{
197 history_visibility::HistoryVisibility, join_rules::JoinRule,
198 retention::RoomRetentionEventContent,
199 },
200 },
201 owned_room_alias_id, room_id, user_id,
202 };
203
204 use crate::test_utils::mocks::MatrixMockServer;
205
206 #[async_test]
207 async fn test_publish_room_alias_to_room_directory() {
208 let server = MatrixMockServer::new().await;
209 let client = server.client_builder().build().await;
210
211 let room_id = room_id!("!a:b.c");
212 let room = server.sync_joined_room(&client, room_id).await;
213
214 let room_alias = owned_room_alias_id!("#a:b.c");
215
216 server
218 .mock_room_directory_resolve_alias()
219 .for_alias(room_alias.to_string())
220 .not_found()
221 .mock_once()
222 .mount()
223 .await;
224
225 server.mock_room_directory_create_room_alias().ok().mock_once().mount().await;
227
228 let published = room
229 .privacy_settings()
230 .publish_room_alias_in_room_directory(&room_alias)
231 .await
232 .expect("we should get a result value, not an error");
233 assert!(published);
234 }
235
236 #[async_test]
237 async fn test_publish_room_alias_to_room_directory_when_alias_exists() {
238 let server = MatrixMockServer::new().await;
239 let client = server.client_builder().build().await;
240
241 let room_id = room_id!("!a:b.c");
242 let room = server.sync_joined_room(&client, room_id).await;
243
244 let room_alias = owned_room_alias_id!("#a:b.c");
245
246 server
248 .mock_room_directory_resolve_alias()
249 .for_alias(room_alias.to_string())
250 .ok(room_id.as_ref(), Vec::new())
251 .mock_once()
252 .mount()
253 .await;
254
255 server.mock_room_directory_create_room_alias().ok().never().mount().await;
257
258 let published = room
259 .privacy_settings()
260 .publish_room_alias_in_room_directory(&room_alias)
261 .await
262 .expect("we should get a result value, not an error");
263 assert!(published.not());
264 }
265
266 #[async_test]
267 async fn test_remove_room_alias() {
268 let server = MatrixMockServer::new().await;
269 let client = server.client_builder().build().await;
270
271 let room_id = room_id!("!a:b.c");
272 let f = EventFactory::new().sender(user_id!("@example:localhost"));
273 let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_event(
274 f.canonical_alias(Some(owned_room_alias_id!("#tutorial:localhost")), vec![]),
275 );
276 let room = server.sync_room(&client, joined_room_builder).await;
277
278 let room_alias = owned_room_alias_id!("#a:b.c");
279
280 server
282 .mock_room_directory_resolve_alias()
283 .for_alias(room_alias.to_string())
284 .ok(room_id.as_ref(), Vec::new())
285 .mock_once()
286 .mount()
287 .await;
288
289 server.mock_room_directory_remove_room_alias().ok().mock_once().mount().await;
291
292 let removed = room
293 .privacy_settings()
294 .remove_room_alias_from_room_directory(&room_alias)
295 .await
296 .expect("we should get a result value, not an error");
297 assert!(removed);
298 }
299
300 #[async_test]
301 async fn test_remove_room_alias_if_it_does_not_exist() {
302 let server = MatrixMockServer::new().await;
303 let client = server.client_builder().build().await;
304
305 let room_id = room_id!("!a:b.c");
306 let f = EventFactory::new().sender(user_id!("@example:localhost"));
307 let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_event(
308 f.canonical_alias(Some(owned_room_alias_id!("#tutorial:localhost")), vec![]),
309 );
310 let room = server.sync_room(&client, joined_room_builder).await;
311
312 let room_alias = owned_room_alias_id!("#a:b.c");
313
314 server
316 .mock_room_directory_resolve_alias()
317 .for_alias(room_alias.to_string())
318 .not_found()
319 .mock_once()
320 .mount()
321 .await;
322
323 server.mock_room_directory_remove_room_alias().ok().never().mount().await;
325
326 let removed = room
327 .privacy_settings()
328 .remove_room_alias_from_room_directory(&room_alias)
329 .await
330 .expect("we should get a result value, not an error");
331 assert!(removed.not());
332 }
333
334 #[async_test]
335 async fn test_update_canonical_alias_with_some_value() {
336 let server = MatrixMockServer::new().await;
337 let client = server.client_builder().build().await;
338
339 let room_id = room_id!("!a:b.c");
340 let room = server.sync_joined_room(&client, room_id).await;
341
342 server
343 .mock_room_send_state()
344 .for_type(StateEventType::RoomCanonicalAlias)
345 .ok(event_id!("$a:b.c"))
346 .mock_once()
347 .mount()
348 .await;
349
350 let room_alias = owned_room_alias_id!("#a:b.c");
351 let ret = room
352 .privacy_settings()
353 .update_canonical_alias(Some(room_alias.clone()), Vec::new())
354 .await;
355 assert!(ret.is_ok());
356 }
357
358 #[async_test]
359 async fn test_update_canonical_alias_with_no_value() {
360 let server = MatrixMockServer::new().await;
361 let client = server.client_builder().build().await;
362
363 let room_id = room_id!("!a:b.c");
364 let room = server.sync_joined_room(&client, room_id).await;
365
366 server
367 .mock_room_send_state()
368 .for_type(StateEventType::RoomCanonicalAlias)
369 .ok(event_id!("$a:b.c"))
370 .mock_once()
371 .mount()
372 .await;
373
374 let ret = room.privacy_settings().update_canonical_alias(None, Vec::new()).await;
375 assert!(ret.is_ok());
376 }
377
378 #[async_test]
379 async fn test_update_room_history_visibility() {
380 let server = MatrixMockServer::new().await;
381 let client = server.client_builder().build().await;
382
383 let room_id = room_id!("!a:b.c");
384 let room = server.sync_joined_room(&client, room_id).await;
385
386 server
387 .mock_room_send_state()
388 .for_type(StateEventType::RoomHistoryVisibility)
389 .ok(event_id!("$a:b.c"))
390 .mock_once()
391 .mount()
392 .await;
393
394 let ret =
395 room.privacy_settings().update_room_history_visibility(HistoryVisibility::Joined).await;
396 assert!(ret.is_ok());
397 }
398
399 #[async_test]
400 async fn test_update_join_rule() {
401 let server = MatrixMockServer::new().await;
402 let client = server.client_builder().build().await;
403
404 let room_id = room_id!("!a:b.c");
405 let room = server.sync_joined_room(&client, room_id).await;
406
407 server
408 .mock_room_send_state()
409 .for_type(StateEventType::RoomJoinRules)
410 .ok(event_id!("$a:b.c"))
411 .mock_once()
412 .mount()
413 .await;
414
415 let ret = room.privacy_settings().update_join_rule(JoinRule::Public).await;
416 assert!(ret.is_ok());
417 }
418
419 #[async_test]
420 async fn test_update_room_retention() {
421 let server = MatrixMockServer::new().await;
422 let client = server.client_builder().build().await;
423
424 let room_id = room_id!("!a:b.c");
425 let room = server.sync_joined_room(&client, room_id).await;
426
427 server
428 .mock_room_send_state()
429 .for_type(StateEventType::RoomRetention)
430 .body_matches_partial_json(serde_json::json!({
431 "max_lifetime": Duration::from_secs(86_400).as_millis() as u64,
432 }))
433 .ok(event_id!("$a:b.c"))
434 .mock_once()
435 .mount()
436 .await;
437
438 let ret = room
439 .privacy_settings()
440 .update_room_retention(
441 RoomRetentionEventContent::new().at_most(Duration::from_secs(86_400)).unwrap(),
442 )
443 .await;
444 assert!(ret.is_ok());
445 }
446
447 #[async_test]
448 async fn test_get_room_visibility() {
449 let server = MatrixMockServer::new().await;
450 let client = server.client_builder().build().await;
451
452 let room_id = room_id!("!a:b.c");
453 let room = server.sync_joined_room(&client, room_id).await;
454
455 server
456 .mock_room_send_state()
457 .for_type(StateEventType::RoomJoinRules)
458 .ok(event_id!("$a:b.c"))
459 .mock_once()
460 .mount()
461 .await;
462
463 let ret = room.privacy_settings().update_join_rule(JoinRule::Public).await;
464 assert!(ret.is_ok());
465 }
466
467 #[async_test]
468 async fn test_update_room_visibility() {
469 let server = MatrixMockServer::new().await;
470 let client = server.client_builder().build().await;
471
472 let room_id = room_id!("!a:b.c");
473 let room = server.sync_joined_room(&client, room_id).await;
474
475 server.mock_room_directory_set_room_visibility().ok().mock_once().mount().await;
476
477 let ret = room.privacy_settings().update_room_visibility(Visibility::Private).await;
478 assert!(ret.is_ok());
479 }
480}