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(
44 &'a self,
45 alias: &RoomAliasId,
46 ) -> Result<bool> {
47 if self.client.is_room_alias_available(alias).await? {
48 self.client.create_room_alias(alias, self.room.room_id()).await?;
49 return Ok(true);
50 }
51
52 Ok(false)
53 }
54
55 pub async fn remove_room_alias_from_room_directory(
63 &'a self,
64 alias: &RoomAliasId,
65 ) -> Result<bool> {
66 if self.client.resolve_room_alias(alias).await.is_ok() {
67 self.client.remove_room_alias(alias).await?;
68 return Ok(true);
69 }
70
71 Ok(false)
72 }
73
74 pub async fn update_canonical_alias(
90 &'a self,
91 alias: Option<OwnedRoomAliasId>,
92 alt_aliases: Vec<OwnedRoomAliasId>,
93 ) -> Result<()> {
94 let content = assign!(
96 RoomCanonicalAliasEventContent::new(),
97 { alias, alt_aliases }
98 );
99
100 let request = send_state_event::v3::Request::new(
102 self.room.room_id().to_owned(),
103 &EmptyStateKey,
104 &content,
105 )?;
106 self.client.send(request).await?;
107
108 Ok(())
109 }
110
111 pub async fn update_room_history_visibility(
120 &'a self,
121 new_value: HistoryVisibility,
122 ) -> Result<()> {
123 let request = send_state_event::v3::Request::new(
124 self.room.room_id().to_owned(),
125 &EmptyStateKey,
126 &RoomHistoryVisibilityEventContent::new(new_value),
127 )?;
128 self.client.send(request).await?;
129 Ok(())
130 }
131
132 pub async fn update_join_rule(&'a self, new_rule: JoinRule) -> Result<()> {
141 let request = send_state_event::v3::Request::new(
142 self.room.room_id().to_owned(),
143 &EmptyStateKey,
144 &RoomJoinRulesEventContent::new(new_rule),
145 )?;
146 self.client.send(request).await?;
147 Ok(())
148 }
149
150 pub async fn update_room_retention(&'a self, content: RoomRetentionEventContent) -> Result<()> {
164 let request = send_state_event::v3::Request::new(
165 self.room.room_id().to_owned(),
166 &EmptyStateKey,
167 &content,
168 )?;
169 self.client.send(request).await?;
170 Ok(())
171 }
172
173 pub async fn get_room_visibility(&'a self) -> Result<Visibility> {
178 let request = get_room_visibility::v3::Request::new(self.room.room_id().to_owned());
179 let response = self.client.send(request).await?;
180 Ok(response.visibility)
181 }
182
183 pub async fn update_room_visibility(&'a self, visibility: Visibility) -> Result<()> {
188 let request =
189 set_room_visibility::v3::Request::new(self.room.room_id().to_owned(), visibility);
190
191 self.client.send(request).await?;
192
193 Ok(())
194 }
195}
196
197#[cfg(all(test, not(target_family = "wasm")))]
198mod tests {
199 use std::{ops::Not, time::Duration};
200
201 use matrix_sdk_test::{JoinedRoomBuilder, async_test, event_factory::EventFactory};
202 use ruma::{
203 api::client::room::Visibility,
204 event_id,
205 events::{
206 StateEventType,
207 room::{
208 history_visibility::HistoryVisibility, join_rules::JoinRule,
209 retention::RoomRetentionEventContent,
210 },
211 },
212 owned_room_alias_id, room_id, user_id,
213 };
214
215 use crate::test_utils::mocks::MatrixMockServer;
216
217 #[async_test]
218 async fn test_publish_room_alias_to_room_directory() {
219 let server = MatrixMockServer::new().await;
220 let client = server.client_builder().build().await;
221
222 let room_id = room_id!("!a:b.c");
223 let room = server.sync_joined_room(&client, room_id).await;
224
225 let room_alias = owned_room_alias_id!("#a:b.c");
226
227 server
229 .mock_room_directory_resolve_alias()
230 .for_alias(room_alias.to_string())
231 .not_found()
232 .mock_once()
233 .mount()
234 .await;
235
236 server.mock_room_directory_create_room_alias().ok().mock_once().mount().await;
239
240 let published = room
241 .privacy_settings()
242 .publish_room_alias_in_room_directory(&room_alias)
243 .await
244 .expect("we should get a result value, not an error");
245 assert!(published);
246 }
247
248 #[async_test]
249 async fn test_publish_room_alias_to_room_directory_when_alias_exists() {
250 let server = MatrixMockServer::new().await;
251 let client = server.client_builder().build().await;
252
253 let room_id = room_id!("!a:b.c");
254 let room = server.sync_joined_room(&client, room_id).await;
255
256 let room_alias = owned_room_alias_id!("#a:b.c");
257
258 server
260 .mock_room_directory_resolve_alias()
261 .for_alias(room_alias.to_string())
262 .ok(room_id.as_ref(), Vec::new())
263 .mock_once()
264 .mount()
265 .await;
266
267 server.mock_room_directory_create_room_alias().ok().never().mount().await;
269
270 let published = room
271 .privacy_settings()
272 .publish_room_alias_in_room_directory(&room_alias)
273 .await
274 .expect("we should get a result value, not an error");
275 assert!(published.not());
276 }
277
278 #[async_test]
279 async fn test_remove_room_alias() {
280 let server = MatrixMockServer::new().await;
281 let client = server.client_builder().build().await;
282
283 let room_id = room_id!("!a:b.c");
284 let f = EventFactory::new().sender(user_id!("@example:localhost"));
285 let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_event(
286 f.canonical_alias(Some(owned_room_alias_id!("#tutorial:localhost")), vec![]),
287 );
288 let room = server.sync_room(&client, joined_room_builder).await;
289
290 let room_alias = owned_room_alias_id!("#a:b.c");
291
292 server
294 .mock_room_directory_resolve_alias()
295 .for_alias(room_alias.to_string())
296 .ok(room_id.as_ref(), Vec::new())
297 .mock_once()
298 .mount()
299 .await;
300
301 server.mock_room_directory_remove_room_alias().ok().mock_once().mount().await;
303
304 let removed = room
305 .privacy_settings()
306 .remove_room_alias_from_room_directory(&room_alias)
307 .await
308 .expect("we should get a result value, not an error");
309 assert!(removed);
310 }
311
312 #[async_test]
313 async fn test_remove_room_alias_if_it_does_not_exist() {
314 let server = MatrixMockServer::new().await;
315 let client = server.client_builder().build().await;
316
317 let room_id = room_id!("!a:b.c");
318 let f = EventFactory::new().sender(user_id!("@example:localhost"));
319 let joined_room_builder = JoinedRoomBuilder::new(room_id).add_state_event(
320 f.canonical_alias(Some(owned_room_alias_id!("#tutorial:localhost")), vec![]),
321 );
322 let room = server.sync_room(&client, joined_room_builder).await;
323
324 let room_alias = owned_room_alias_id!("#a:b.c");
325
326 server
328 .mock_room_directory_resolve_alias()
329 .for_alias(room_alias.to_string())
330 .not_found()
331 .mock_once()
332 .mount()
333 .await;
334
335 server.mock_room_directory_remove_room_alias().ok().never().mount().await;
337
338 let removed = room
339 .privacy_settings()
340 .remove_room_alias_from_room_directory(&room_alias)
341 .await
342 .expect("we should get a result value, not an error");
343 assert!(removed.not());
344 }
345
346 #[async_test]
347 async fn test_update_canonical_alias_with_some_value() {
348 let server = MatrixMockServer::new().await;
349 let client = server.client_builder().build().await;
350
351 let room_id = room_id!("!a:b.c");
352 let room = server.sync_joined_room(&client, room_id).await;
353
354 server
355 .mock_room_send_state()
356 .for_type(StateEventType::RoomCanonicalAlias)
357 .ok(event_id!("$a:b.c"))
358 .mock_once()
359 .mount()
360 .await;
361
362 let room_alias = owned_room_alias_id!("#a:b.c");
363 let ret = room
364 .privacy_settings()
365 .update_canonical_alias(Some(room_alias.clone()), Vec::new())
366 .await;
367 assert!(ret.is_ok());
368 }
369
370 #[async_test]
371 async fn test_update_canonical_alias_with_no_value() {
372 let server = MatrixMockServer::new().await;
373 let client = server.client_builder().build().await;
374
375 let room_id = room_id!("!a:b.c");
376 let room = server.sync_joined_room(&client, room_id).await;
377
378 server
379 .mock_room_send_state()
380 .for_type(StateEventType::RoomCanonicalAlias)
381 .ok(event_id!("$a:b.c"))
382 .mock_once()
383 .mount()
384 .await;
385
386 let ret = room.privacy_settings().update_canonical_alias(None, Vec::new()).await;
387 assert!(ret.is_ok());
388 }
389
390 #[async_test]
391 async fn test_update_room_history_visibility() {
392 let server = MatrixMockServer::new().await;
393 let client = server.client_builder().build().await;
394
395 let room_id = room_id!("!a:b.c");
396 let room = server.sync_joined_room(&client, room_id).await;
397
398 server
399 .mock_room_send_state()
400 .for_type(StateEventType::RoomHistoryVisibility)
401 .ok(event_id!("$a:b.c"))
402 .mock_once()
403 .mount()
404 .await;
405
406 let ret =
407 room.privacy_settings().update_room_history_visibility(HistoryVisibility::Joined).await;
408 assert!(ret.is_ok());
409 }
410
411 #[async_test]
412 async fn test_update_join_rule() {
413 let server = MatrixMockServer::new().await;
414 let client = server.client_builder().build().await;
415
416 let room_id = room_id!("!a:b.c");
417 let room = server.sync_joined_room(&client, room_id).await;
418
419 server
420 .mock_room_send_state()
421 .for_type(StateEventType::RoomJoinRules)
422 .ok(event_id!("$a:b.c"))
423 .mock_once()
424 .mount()
425 .await;
426
427 let ret = room.privacy_settings().update_join_rule(JoinRule::Public).await;
428 assert!(ret.is_ok());
429 }
430
431 #[async_test]
432 async fn test_update_room_retention() {
433 let server = MatrixMockServer::new().await;
434 let client = server.client_builder().build().await;
435
436 let room_id = room_id!("!a:b.c");
437 let room = server.sync_joined_room(&client, room_id).await;
438
439 server
440 .mock_room_send_state()
441 .for_type(StateEventType::RoomRetention)
442 .body_matches_partial_json(serde_json::json!({
443 "max_lifetime": Duration::from_secs(86_400).as_millis() as u64,
444 }))
445 .ok(event_id!("$a:b.c"))
446 .mock_once()
447 .mount()
448 .await;
449
450 let ret = room
451 .privacy_settings()
452 .update_room_retention(
453 RoomRetentionEventContent::new().at_most(Duration::from_secs(86_400)).unwrap(),
454 )
455 .await;
456 assert!(ret.is_ok());
457 }
458
459 #[async_test]
460 async fn test_get_room_visibility() {
461 let server = MatrixMockServer::new().await;
462 let client = server.client_builder().build().await;
463
464 let room_id = room_id!("!a:b.c");
465 let room = server.sync_joined_room(&client, room_id).await;
466
467 server
468 .mock_room_send_state()
469 .for_type(StateEventType::RoomJoinRules)
470 .ok(event_id!("$a:b.c"))
471 .mock_once()
472 .mount()
473 .await;
474
475 let ret = room.privacy_settings().update_join_rule(JoinRule::Public).await;
476 assert!(ret.is_ok());
477 }
478
479 #[async_test]
480 async fn test_update_room_visibility() {
481 let server = MatrixMockServer::new().await;
482 let client = server.client_builder().build().await;
483
484 let room_id = room_id!("!a:b.c");
485 let room = server.sync_joined_room(&client, room_id).await;
486
487 server.mock_room_directory_set_room_visibility().ok().mock_once().mount().await;
488
489 let ret = room.privacy_settings().update_room_visibility(Visibility::Private).await;
490 assert!(ret.is_ok());
491 }
492}