matrix_sdk_ui/timeline/event_item/content/
pinned_events.rs1use std::collections::HashSet;
16
17use ruma::{
18 events::{room::pinned_events::RoomPinnedEventsEventContent, FullStateEventContent},
19 OwnedEventId,
20};
21
22#[derive(Clone, Debug)]
23#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
24pub enum RoomPinnedEventsChange {
26 Added,
28 Removed,
30 Changed,
32}
33
34impl From<&FullStateEventContent<RoomPinnedEventsEventContent>> for RoomPinnedEventsChange {
35 fn from(value: &FullStateEventContent<RoomPinnedEventsEventContent>) -> Self {
36 match value {
37 FullStateEventContent::Original { content, prev_content } => {
38 if let Some(prev_content) = prev_content {
39 let mut new_pinned: HashSet<&OwnedEventId> =
40 HashSet::from_iter(&content.pinned);
41 if let Some(old_pinned) = &prev_content.pinned {
42 let mut still_pinned: HashSet<&OwnedEventId> =
43 HashSet::from_iter(old_pinned);
44
45 still_pinned.retain(|item| new_pinned.remove(item));
48
49 let added = !new_pinned.is_empty();
50 let removed = still_pinned.len() < old_pinned.len();
51 if added && removed {
52 RoomPinnedEventsChange::Changed
53 } else if added {
54 RoomPinnedEventsChange::Added
55 } else if removed {
56 RoomPinnedEventsChange::Removed
57 } else {
58 RoomPinnedEventsChange::Changed
60 }
61 } else {
62 RoomPinnedEventsChange::Changed
64 }
65 } else {
66 RoomPinnedEventsChange::Added
69 }
70 }
71 FullStateEventContent::Redacted(_) => RoomPinnedEventsChange::Changed,
72 }
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use assert_matches::assert_matches;
79 use ruma::{
80 events::{
81 room::pinned_events::{
82 PossiblyRedactedRoomPinnedEventsEventContent, RedactedRoomPinnedEventsEventContent,
83 RoomPinnedEventsEventContent,
84 },
85 FullStateEventContent,
86 },
87 owned_event_id,
88 serde::Raw,
89 };
90 use serde_json::json;
91
92 use crate::timeline::event_item::content::pinned_events::RoomPinnedEventsChange;
93
94 #[test]
95 fn redacted_pinned_events_content_has_generic_changes() {
96 let content = FullStateEventContent::Redacted(RedactedRoomPinnedEventsEventContent::new());
97 let ret: RoomPinnedEventsChange = (&content).into();
98 assert_matches!(ret, RoomPinnedEventsChange::Changed);
99 }
100
101 #[test]
102 fn pinned_events_content_with_no_prev_content_returns_added() {
103 let content = FullStateEventContent::Original {
104 content: RoomPinnedEventsEventContent::new(vec![owned_event_id!("$1")]),
105 prev_content: None,
106 };
107 let ret: RoomPinnedEventsChange = (&content).into();
108 assert_matches!(ret, RoomPinnedEventsChange::Added);
109 }
110
111 #[test]
112 fn pinned_events_content_with_added_ids_returns_added() {
113 let prev_content = possibly_redacted_content(Vec::new());
115 let content = FullStateEventContent::Original {
116 content: RoomPinnedEventsEventContent::new(vec![owned_event_id!("$1")]),
117 prev_content,
118 };
119 let ret: RoomPinnedEventsChange = (&content).into();
120 assert_matches!(ret, RoomPinnedEventsChange::Added);
121 }
122
123 #[test]
124 fn pinned_events_content_with_removed_ids_returns_removed() {
125 let prev_content = possibly_redacted_content(vec!["$1"]);
127 let content = FullStateEventContent::Original {
128 content: RoomPinnedEventsEventContent::new(Vec::new()),
129 prev_content,
130 };
131 let ret: RoomPinnedEventsChange = (&content).into();
132 assert_matches!(ret, RoomPinnedEventsChange::Removed);
133 }
134
135 #[test]
136 fn pinned_events_content_with_added_and_removed_ids_returns_changed() {
137 let prev_content = possibly_redacted_content(vec!["$1"]);
139 let content = FullStateEventContent::Original {
140 content: RoomPinnedEventsEventContent::new(vec![owned_event_id!("$2")]),
141 prev_content,
142 };
143 let ret: RoomPinnedEventsChange = (&content).into();
144 assert_matches!(ret, RoomPinnedEventsChange::Changed);
145 }
146
147 #[test]
148 fn pinned_events_content_with_changed_order_returns_changed() {
149 let prev_content = possibly_redacted_content(vec!["$1", "$2"]);
151 let content = FullStateEventContent::Original {
152 content: RoomPinnedEventsEventContent::new(vec![
153 owned_event_id!("$2"),
154 owned_event_id!("$1"),
155 ]),
156 prev_content,
157 };
158 let ret: RoomPinnedEventsChange = (&content).into();
159 assert_matches!(ret, RoomPinnedEventsChange::Changed);
160 }
161
162 #[test]
163 fn pinned_events_content_with_no_changes_returns_changed() {
164 let prev_content = possibly_redacted_content(vec!["$1", "$2"]);
169 let content = FullStateEventContent::Original {
170 content: RoomPinnedEventsEventContent::new(vec![
171 owned_event_id!("$1"),
172 owned_event_id!("$2"),
173 ]),
174 prev_content,
175 };
176 let ret: RoomPinnedEventsChange = (&content).into();
177 assert_matches!(ret, RoomPinnedEventsChange::Changed);
178 }
179
180 fn possibly_redacted_content(
181 ids: Vec<&str>,
182 ) -> Option<PossiblyRedactedRoomPinnedEventsEventContent> {
183 Raw::new(&json!({
185 "pinned": ids,
186 }))
187 .unwrap()
188 .cast::<PossiblyRedactedRoomPinnedEventsEventContent>()
189 .deserialize()
190 .ok()
191 }
192}