1use bitflags::bitflags;
16use ruma::events::{AnyRoomAccountDataEvent, RoomAccountDataEventType, tag::Tags};
17use serde::{Deserialize, Serialize};
18
19use super::Room;
20use crate::{StateStore, store::Result as StoreResult};
21
22impl Room {
23 pub async fn tags(&self) -> StoreResult<Option<Tags>> {
25 if let Some(AnyRoomAccountDataEvent::Tag(event)) = self
26 .store
27 .get_room_account_data_event(self.room_id(), RoomAccountDataEventType::Tag)
28 .await?
29 .and_then(|raw| raw.deserialize().ok())
30 {
31 Ok(Some(event.content.tags))
32 } else {
33 Ok(None)
34 }
35 }
36
37 pub fn is_favourite(&self) -> bool {
41 self.info.read().base_info.notable_tags.contains(RoomNotableTags::FAVOURITE)
42 }
43
44 pub fn is_low_priority(&self) -> bool {
49 self.info.read().base_info.notable_tags.contains(RoomNotableTags::LOW_PRIORITY)
50 }
51}
52
53bitflags! {
54 #[repr(transparent)]
59 #[derive(Debug, Default, Clone, Copy, Deserialize, Serialize)]
60 pub(crate) struct RoomNotableTags: u8 {
61 const FAVOURITE = 0b0000_0001;
63
64 const LOW_PRIORITY = 0b0000_0010;
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use std::ops::Not;
72
73 use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
74 use matrix_sdk_test::async_test;
75 use ruma::{
76 events::tag::{TagInfo, TagName, Tags},
77 room_id,
78 serde::Raw,
79 user_id,
80 };
81 use serde_json::json;
82 use stream_assert::{assert_pending, assert_ready};
83
84 use super::{super::BaseRoomInfo, RoomNotableTags};
85 use crate::{
86 BaseClient, DmRoomDefinition, RoomState, SessionMeta,
87 client::ThreadingSupport,
88 response_processors as processors,
89 store::{RoomLoadSettings, StoreConfig},
90 };
91
92 #[async_test]
93 async fn test_is_favourite() {
94 let client = BaseClient::new(
96 StoreConfig::new(CrossProcessLockConfig::SingleProcess),
97 ThreadingSupport::Disabled,
98 DmRoomDefinition::default(),
99 );
100
101 client
102 .activate(
103 SessionMeta {
104 user_id: user_id!("@alice:example.org").into(),
105 device_id: ruma::device_id!("AYEAYEAYE").into(),
106 },
107 RoomLoadSettings::default(),
108 #[cfg(feature = "e2e-encryption")]
109 None,
110 )
111 .await
112 .unwrap();
113
114 let room_id = room_id!("!test:localhost");
115 let room = client.get_or_create_room(room_id, RoomState::Joined);
116
117 assert!(room.is_favourite().not());
119
120 let mut room_info_subscriber = room.subscribe_info();
122
123 assert_pending!(room_info_subscriber);
124
125 let tag_raw = Raw::new(&json!({
127 "content": {
128 "tags": {
129 "m.favourite": {
130 "order": 0.0
131 },
132 },
133 },
134 "type": "m.tag",
135 }))
136 .unwrap()
137 .cast_unchecked();
138
139 let mut context = processors::Context::default();
141
142 processors::account_data::for_room(&mut context, room_id, &[tag_raw], &client.state_store);
143
144 processors::changes::save_and_apply(
145 context.clone(),
146 &client.state_store,
147 &client.state_store_lock().lock().await,
148 &client.ignore_user_list_changes,
149 None,
150 )
151 .await
152 .unwrap();
153
154 assert_ready!(room_info_subscriber);
156 assert_pending!(room_info_subscriber);
157
158 assert!(room.is_favourite());
160
161 let tag_raw = Raw::new(&json!({
163 "content": {
164 "tags": {},
165 },
166 "type": "m.tag"
167 }))
168 .unwrap()
169 .cast_unchecked();
170
171 processors::account_data::for_room(&mut context, room_id, &[tag_raw], &client.state_store);
172
173 processors::changes::save_and_apply(
174 context,
175 &client.state_store,
176 &client.state_store_lock().lock().await,
177 &client.ignore_user_list_changes,
178 None,
179 )
180 .await
181 .unwrap();
182
183 assert_ready!(room_info_subscriber);
185 assert_pending!(room_info_subscriber);
186
187 assert!(room.is_favourite().not());
189 }
190
191 #[async_test]
192 async fn test_is_low_priority() {
193 let client = BaseClient::new(
195 StoreConfig::new(CrossProcessLockConfig::SingleProcess),
196 ThreadingSupport::Disabled,
197 DmRoomDefinition::default(),
198 );
199
200 client
201 .activate(
202 SessionMeta {
203 user_id: user_id!("@alice:example.org").into(),
204 device_id: ruma::device_id!("AYEAYEAYE").into(),
205 },
206 RoomLoadSettings::default(),
207 #[cfg(feature = "e2e-encryption")]
208 None,
209 )
210 .await
211 .unwrap();
212
213 let room_id = room_id!("!test:localhost");
214 let room = client.get_or_create_room(room_id, RoomState::Joined);
215
216 assert!(!room.is_low_priority());
218
219 let mut room_info_subscriber = room.subscribe_info();
221
222 assert_pending!(room_info_subscriber);
223
224 let tag_raw = Raw::new(&json!({
226 "content": {
227 "tags": {
228 "m.lowpriority": {
229 "order": 0.0
230 },
231 }
232 },
233 "type": "m.tag"
234 }))
235 .unwrap()
236 .cast_unchecked();
237
238 let mut context = processors::Context::default();
240
241 processors::account_data::for_room(&mut context, room_id, &[tag_raw], &client.state_store);
242
243 processors::changes::save_and_apply(
244 context.clone(),
245 &client.state_store,
246 &client.state_store_lock().lock().await,
247 &client.ignore_user_list_changes,
248 None,
249 )
250 .await
251 .unwrap();
252
253 assert_ready!(room_info_subscriber);
255 assert_pending!(room_info_subscriber);
256
257 assert!(room.is_low_priority());
259
260 let tag_raw = Raw::new(&json!({
262 "content": {
263 "tags": {},
264 },
265 "type": "m.tag"
266 }))
267 .unwrap()
268 .cast_unchecked();
269
270 processors::account_data::for_room(&mut context, room_id, &[tag_raw], &client.state_store);
271
272 processors::changes::save_and_apply(
273 context,
274 &client.state_store,
275 &client.state_store_lock().lock().await,
276 &client.ignore_user_list_changes,
277 None,
278 )
279 .await
280 .unwrap();
281
282 assert_ready!(room_info_subscriber);
284 assert_pending!(room_info_subscriber);
285
286 assert!(room.is_low_priority().not());
288 }
289
290 #[test]
291 fn test_handle_notable_tags_favourite() {
292 let mut base_room_info = BaseRoomInfo::default();
293
294 let mut tags = Tags::new();
295 tags.insert(TagName::Favorite, TagInfo::default());
296
297 assert!(base_room_info.notable_tags.contains(RoomNotableTags::FAVOURITE).not());
298 base_room_info.handle_notable_tags(&tags);
299 assert!(base_room_info.notable_tags.contains(RoomNotableTags::FAVOURITE));
300 tags.clear();
301 base_room_info.handle_notable_tags(&tags);
302 assert!(base_room_info.notable_tags.contains(RoomNotableTags::FAVOURITE).not());
303 }
304
305 #[test]
306 fn test_handle_notable_tags_low_priority() {
307 let mut base_room_info = BaseRoomInfo::default();
308
309 let mut tags = Tags::new();
310 tags.insert(TagName::LowPriority, TagInfo::default());
311
312 assert!(base_room_info.notable_tags.contains(RoomNotableTags::LOW_PRIORITY).not());
313 base_room_info.handle_notable_tags(&tags);
314 assert!(base_room_info.notable_tags.contains(RoomNotableTags::LOW_PRIORITY));
315 tags.clear();
316 base_room_info.handle_notable_tags(&tags);
317 assert!(base_room_info.notable_tags.contains(RoomNotableTags::LOW_PRIORITY).not());
318 }
319}