use std::collections::HashSet;
use ruma::{
events::{room::pinned_events::RoomPinnedEventsEventContent, FullStateEventContent},
OwnedEventId,
};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum RoomPinnedEventsChange {
Added,
Removed,
Changed,
}
impl From<&FullStateEventContent<RoomPinnedEventsEventContent>> for RoomPinnedEventsChange {
fn from(value: &FullStateEventContent<RoomPinnedEventsEventContent>) -> Self {
match value {
FullStateEventContent::Original { content, prev_content } => {
if let Some(prev_content) = prev_content {
let mut new_pinned: HashSet<&OwnedEventId> =
HashSet::from_iter(&content.pinned);
if let Some(old_pinned) = &prev_content.pinned {
let mut still_pinned: HashSet<&OwnedEventId> =
HashSet::from_iter(old_pinned);
still_pinned.retain(|item| new_pinned.remove(item));
let added = !new_pinned.is_empty();
let removed = still_pinned.len() < old_pinned.len();
if added && removed {
RoomPinnedEventsChange::Changed
} else if added {
RoomPinnedEventsChange::Added
} else if removed {
RoomPinnedEventsChange::Removed
} else {
RoomPinnedEventsChange::Changed
}
} else {
RoomPinnedEventsChange::Changed
}
} else {
RoomPinnedEventsChange::Added
}
}
FullStateEventContent::Redacted(_) => RoomPinnedEventsChange::Changed,
}
}
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use ruma::{
events::{
room::pinned_events::{
PossiblyRedactedRoomPinnedEventsEventContent, RedactedRoomPinnedEventsEventContent,
RoomPinnedEventsEventContent,
},
FullStateEventContent,
},
owned_event_id,
serde::Raw,
};
use serde_json::json;
use crate::timeline::event_item::content::pinned_events::RoomPinnedEventsChange;
#[test]
fn redacted_pinned_events_content_has_generic_changes() {
let content = FullStateEventContent::Redacted(RedactedRoomPinnedEventsEventContent::new());
let ret: RoomPinnedEventsChange = (&content).into();
assert_matches!(ret, RoomPinnedEventsChange::Changed);
}
#[test]
fn pinned_events_content_with_no_prev_content_returns_added() {
let content = FullStateEventContent::Original {
content: RoomPinnedEventsEventContent::new(vec![owned_event_id!("$1")]),
prev_content: None,
};
let ret: RoomPinnedEventsChange = (&content).into();
assert_matches!(ret, RoomPinnedEventsChange::Added);
}
#[test]
fn pinned_events_content_with_added_ids_returns_added() {
let prev_content = possibly_redacted_content(Vec::new());
let content = FullStateEventContent::Original {
content: RoomPinnedEventsEventContent::new(vec![owned_event_id!("$1")]),
prev_content,
};
let ret: RoomPinnedEventsChange = (&content).into();
assert_matches!(ret, RoomPinnedEventsChange::Added);
}
#[test]
fn pinned_events_content_with_removed_ids_returns_removed() {
let prev_content = possibly_redacted_content(vec!["$1"]);
let content = FullStateEventContent::Original {
content: RoomPinnedEventsEventContent::new(Vec::new()),
prev_content,
};
let ret: RoomPinnedEventsChange = (&content).into();
assert_matches!(ret, RoomPinnedEventsChange::Removed);
}
#[test]
fn pinned_events_content_with_added_and_removed_ids_returns_changed() {
let prev_content = possibly_redacted_content(vec!["$1"]);
let content = FullStateEventContent::Original {
content: RoomPinnedEventsEventContent::new(vec![owned_event_id!("$2")]),
prev_content,
};
let ret: RoomPinnedEventsChange = (&content).into();
assert_matches!(ret, RoomPinnedEventsChange::Changed);
}
#[test]
fn pinned_events_content_with_changed_order_returns_changed() {
let prev_content = possibly_redacted_content(vec!["$1", "$2"]);
let content = FullStateEventContent::Original {
content: RoomPinnedEventsEventContent::new(vec![
owned_event_id!("$2"),
owned_event_id!("$1"),
]),
prev_content,
};
let ret: RoomPinnedEventsChange = (&content).into();
assert_matches!(ret, RoomPinnedEventsChange::Changed);
}
#[test]
fn pinned_events_content_with_no_changes_returns_changed() {
let prev_content = possibly_redacted_content(vec!["$1", "$2"]);
let content = FullStateEventContent::Original {
content: RoomPinnedEventsEventContent::new(vec![
owned_event_id!("$1"),
owned_event_id!("$2"),
]),
prev_content,
};
let ret: RoomPinnedEventsChange = (&content).into();
assert_matches!(ret, RoomPinnedEventsChange::Changed);
}
fn possibly_redacted_content(
ids: Vec<&str>,
) -> Option<PossiblyRedactedRoomPinnedEventsEventContent> {
Raw::new(&json!({
"pinned": ids,
}))
.unwrap()
.cast::<PossiblyRedactedRoomPinnedEventsEventContent>()
.deserialize()
.ok()
}
}