96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 1 | use std::sync::Arc; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 2 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 3 | use matrix_sdk_crypto::types::events::UtdCause as SdkUtdCause; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 4 | use matrix_sdk_ui::{ |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 5 | eyeball_im::VectorDiff, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 6 | timeline::{ |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 7 | AnyOtherStateEventContentChange, EncryptedMessage, EventSendState, EventTimelineItem, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 8 | MembershipChange as SdkMembershipChange, MsgLikeContent, MsgLikeKind, Profile, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 9 | TimelineDetails, TimelineEventItemId, TimelineItem as SdkTimelineItem, TimelineItemContent, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 10 | TimelineItemKind, VirtualTimelineItem, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 11 | }, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 12 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 13 | use ruma::{ |
8fa8a7cde89c | atagen | 2026-10-08 12:39 UTC | 14 | MxcUri, OwnedEventId, UserId, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 15 | events::{ |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 16 | StateEventContentChange, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 17 | room::message::{MessageFormat, MessageType}, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 18 | }, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 19 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 20 | |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 21 | use crate::{ |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 22 | Change, HistoryGap, HistoryGapLifecycle, MediaId, MembershipChange, Message, MessageKind, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 23 | MessageMentions, Reaction, ReplyDetails, ReplyReference, SendState, Sender, ThreadSummary, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 24 | TimelineContent, TimelineDiff, TimelineEvent, TimelineItem, TimelineItemId, UtdCause, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 25 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 26 | |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 27 | use super::link_preview::sender_previews; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 28 | use super::media::{ |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 29 | MediaRegistration, MediaScope, media_descriptor_for_sticker, media_from_message, |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 30 | media_identity, media_registrations_for_item, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 31 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 32 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 33 | #[derive(Default)] |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 34 | pub(crate) struct NormalisedTimeline { |
d5d8097c3afd | atagen | 2026-09-26 09:33 UTC | 35 | pub(crate) items: imbl::Vector<TimelineItem>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 36 | pub(crate) media: Vec<MediaRegistration>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 37 | /// replies whose target isn't loaded, so somebody should go and ask for it |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 38 | pub(crate) missing_replies: Vec<OwnedEventId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 39 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 40 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 41 | pub(crate) struct NormalisedTimelineDiffs { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 42 | pub(crate) diffs: Vec<TimelineDiff>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 43 | pub(crate) media: Vec<MediaRegistration>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 44 | pub(crate) missing_replies: Vec<OwnedEventId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 45 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 46 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 47 | struct Normaliser<'a> { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 48 | current_user_id: Option<&'a UserId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 49 | scope: &'a MediaScope, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 50 | media: Vec<MediaRegistration>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 51 | missing_replies: Vec<OwnedEventId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 52 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 53 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 54 | impl Normaliser<'_> { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 55 | /// `None` for rows macltok leaves out |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 56 | fn item(&mut self, item: &SdkTimelineItem) -> Option<TimelineItem> { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 57 | if let TimelineItemKind::Virtual(VirtualTimelineItem::HistoryGap(gap)) = item.kind() |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 58 | && gap.status == matrix_sdk::event_cache::HistoryGapStatus::Joined |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 59 | { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 60 | return None; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 61 | } |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 62 | self.media |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 63 | .extend(media_registrations_for_item(item, self.scope)); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 64 | Some(match item.kind() { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 65 | TimelineItemKind::Event(event) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 66 | TimelineItem::Event(Box::new(self.event(item, event))) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 67 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 68 | TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(timestamp)) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 69 | TimelineItem::DayDivider { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 70 | timestamp_ms: timestamp.get().into(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 71 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 72 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 73 | TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker) => TimelineItem::ReadMarker, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 74 | TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 75 | TimelineItem::TimelineStart |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 76 | } |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 77 | TimelineItemKind::Virtual(VirtualTimelineItem::HistoryGap(gap)) => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 78 | use matrix_sdk::event_cache::HistoryGapStatus; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 79 | let (lifecycle, failure) = match &gap.status { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 80 | HistoryGapStatus::Idle | HistoryGapStatus::Joined => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 81 | (HistoryGapLifecycle::Idle, None) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 82 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 83 | HistoryGapStatus::Paused => (HistoryGapLifecycle::Paused, None), |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 84 | HistoryGapStatus::Failed { message } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 85 | (HistoryGapLifecycle::Failed, Some(message.clone())) |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 86 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 87 | HistoryGapStatus::Unavailable => (HistoryGapLifecycle::Unavailable, None), |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 88 | }; |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 89 | TimelineItem::HistoryGap(HistoryGap { |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 90 | id: gap.id.clone(), |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 91 | older_boundary: gap.older_boundary.clone(), |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 92 | newer_boundary: gap.newer_boundary.clone(), |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 93 | lifecycle, |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 94 | failure, |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 95 | }) |
c9f0fd0ee1cc | atagen | 2026-10-25 06:16 UTC | 96 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 97 | }) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 98 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 99 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 100 | fn event(&mut self, item: &SdkTimelineItem, event: &EventTimelineItem) -> TimelineEvent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 101 | let msg_like = match event.content() { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 102 | TimelineItemContent::MsgLike(content) => Some(content), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 103 | _ => None, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 104 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 105 | let reply_to = msg_like |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 106 | .and_then(|content| content.in_reply_to.as_ref()) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 107 | .map(|reply| { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 108 | let details = match &reply.event { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 109 | TimelineDetails::Ready(embedded) => ReplyDetails::Ready { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 110 | sender: sender(&embedded.sender, &embedded.sender_profile), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 111 | content: Box::new(content(&embedded.content, None)), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 112 | timestamp_ms: embedded.timestamp.get().into(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 113 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 114 | TimelineDetails::Unavailable => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 115 | self.missing_replies.push(reply.event_id.clone()); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 116 | ReplyDetails::Pending |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 117 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 118 | TimelineDetails::Pending => ReplyDetails::Pending, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 119 | TimelineDetails::Error(_) => ReplyDetails::Unavailable, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 120 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 121 | ReplyReference { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 122 | event_id: reply.event_id.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 123 | details, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 124 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 125 | }); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 126 | let mut read_by: Vec<_> = event |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 127 | .read_receipts() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 128 | .keys() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 129 | .filter(|user_id| *user_id != event.sender()) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 130 | .cloned() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 131 | .collect(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 132 | read_by.sort(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 133 | TimelineEvent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 134 | item_id: TimelineItemId::new(item.unique_id().0.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 135 | event_id: event.event_id().map(ToOwned::to_owned), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 136 | transaction_id: event.transaction_id().map(ToOwned::to_owned), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 137 | sender: sender(event.sender(), event.sender_profile()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 138 | timestamp_ms: event.timestamp().get().into(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 139 | own: event.is_own(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 140 | send_state: send_state(event.send_state()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 141 | content: content(event.content(), Some(&media_identity(item, self.scope))), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 142 | reply_to, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 143 | thread_root: msg_like.and_then(|content| content.thread_root.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 144 | thread_summary: msg_like.and_then(thread_summary), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 145 | reactions: reactions(event, self.current_user_id), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 146 | read_by, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 147 | editable: event.is_editable(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 148 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 149 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 150 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 151 | |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 152 | pub(crate) fn normalise_timeline<'a>( |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 153 | items: impl Iterator<Item = &'a Arc<SdkTimelineItem>>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 154 | current_user_id: Option<&UserId>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 155 | scope: &MediaScope, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 156 | ) -> (NormalisedTimeline, HiddenRows) { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 157 | let mut normaliser = Normaliser { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 158 | current_user_id, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 159 | scope, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 160 | media: Vec::new(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 161 | missing_replies: Vec::new(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 162 | }; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 163 | let mut hidden = HiddenRows::default(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 164 | let items = hidden.reset(items.map(|item| normaliser.item(item))); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 165 | let normalised = NormalisedTimeline { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 166 | items: items.into_iter().collect(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 167 | media: normaliser.media, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 168 | missing_replies: normaliser.missing_replies, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 169 | }; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 170 | (normalised, hidden) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 171 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 172 | |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 173 | pub(crate) fn normalise_timeline_diffs( |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 174 | diffs: Vec<VectorDiff<Arc<SdkTimelineItem>>>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 175 | current_user_id: Option<&UserId>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 176 | scope: &MediaScope, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 177 | hidden: &mut HiddenRows, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 178 | ) -> NormalisedTimelineDiffs { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 179 | let mut normaliser = Normaliser { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 180 | current_user_id, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 181 | scope, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 182 | media: Vec::new(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 183 | missing_replies: Vec::new(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 184 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 185 | let diffs = diffs |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 186 | .into_iter() |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 187 | .filter_map(|diff| hidden.translate(diff.map(|item| normaliser.item(&item)))) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 188 | .collect(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 189 | NormalisedTimelineDiffs { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 190 | diffs, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 191 | media: normaliser.media, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 192 | missing_replies: normaliser.missing_replies, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 193 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 194 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 195 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 196 | /// which sdk rows were left out, so diff indices can step over them |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 197 | #[derive(Default)] |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 198 | pub(crate) struct HiddenRows { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 199 | rows: imbl::Vector<bool>, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 200 | hidden: usize, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 201 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 202 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 203 | impl HiddenRows { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 204 | fn visible_index(&self, index: usize) -> usize { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 205 | if self.hidden == 0 { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 206 | return index; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 207 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 208 | self.rows |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 209 | .iter() |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 210 | .take(index) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 211 | .filter(|hidden| !**hidden) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 212 | .count() |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 213 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 214 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 215 | fn push(&mut self, front: bool, item: &Option<TimelineItem>) { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 216 | self.hidden += usize::from(item.is_none()); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 217 | if front { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 218 | self.rows.push_front(item.is_none()); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 219 | } else { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 220 | self.rows.push_back(item.is_none()); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 221 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 222 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 223 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 224 | fn forget(&mut self, hidden: bool) -> bool { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 225 | self.hidden -= usize::from(hidden); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 226 | !hidden |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 227 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 228 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 229 | fn reset(&mut self, items: impl Iterator<Item = Option<TimelineItem>>) -> Vec<TimelineItem> { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 230 | *self = Self::default(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 231 | items |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 232 | .filter_map(|item| { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 233 | self.push(false, &item); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 234 | item |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 235 | }) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 236 | .collect() |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 237 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 238 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 239 | fn translate(&mut self, diff: VectorDiff<Option<TimelineItem>>) -> Option<TimelineDiff> { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 240 | Some(match diff { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 241 | VectorDiff::Append { values } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 242 | let values: Vec<_> = values |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 243 | .into_iter() |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 244 | .filter_map(|item| { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 245 | self.push(false, &item); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 246 | item |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 247 | }) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 248 | .collect(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 249 | if values.is_empty() { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 250 | return None; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 251 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 252 | TimelineDiff::Append { values } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 253 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 254 | VectorDiff::Clear => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 255 | *self = Self::default(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 256 | TimelineDiff::Clear |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 257 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 258 | VectorDiff::PushFront { value } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 259 | self.push(true, &value); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 260 | TimelineDiff::PushFront { value: value? } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 261 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 262 | VectorDiff::PushBack { value } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 263 | self.push(false, &value); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 264 | TimelineDiff::PushBack { value: value? } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 265 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 266 | VectorDiff::PopFront => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 267 | let hidden = self.rows.pop_front()?; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 268 | self.forget(hidden).then_some(TimelineDiff::PopFront)? |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 269 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 270 | VectorDiff::PopBack => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 271 | let hidden = self.rows.pop_back()?; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 272 | self.forget(hidden).then_some(TimelineDiff::PopBack)? |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 273 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 274 | VectorDiff::Insert { index, value } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 275 | let visible = self.visible_index(index); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 276 | self.hidden += usize::from(value.is_none()); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 277 | self.rows.insert(index, value.is_none()); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 278 | TimelineDiff::Insert { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 279 | index: visible, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 280 | value: value?, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 281 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 282 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 283 | VectorDiff::Set { index, value } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 284 | let visible = self.visible_index(index); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 285 | let was_hidden = self.rows.set(index, value.is_none()); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 286 | self.hidden = self.hidden + usize::from(value.is_none()) - usize::from(was_hidden); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 287 | match (was_hidden, value) { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 288 | (false, Some(value)) => TimelineDiff::Set { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 289 | index: visible, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 290 | value, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 291 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 292 | (false, None) => TimelineDiff::Remove { index: visible }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 293 | (true, Some(value)) => TimelineDiff::Insert { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 294 | index: visible, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 295 | value, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 296 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 297 | (true, None) => return None, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 298 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 299 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 300 | VectorDiff::Remove { index } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 301 | let visible = self.visible_index(index); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 302 | let hidden = self.rows.remove(index); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 303 | self.forget(hidden) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 304 | .then_some(TimelineDiff::Remove { index: visible })? |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 305 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 306 | VectorDiff::Truncate { length } => { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 307 | let visible = self.visible_index(length); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 308 | let dropped = self |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 309 | .rows |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 310 | .iter() |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 311 | .skip(length) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 312 | .filter(|hidden| **hidden) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 313 | .count(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 314 | self.hidden -= dropped; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 315 | self.rows.truncate(length); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 316 | TimelineDiff::Truncate { length: visible } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 317 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 318 | VectorDiff::Reset { values } => TimelineDiff::Reset { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 319 | values: self.reset(values.into_iter()), |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 320 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 321 | }) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 322 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 323 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 324 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 325 | /// `media_identity` is `None` for embedded reply content, which never gets fetchable media of its own |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 326 | fn content(content: &TimelineItemContent, media_identity: Option<&str>) -> TimelineContent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 327 | match content { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 328 | TimelineItemContent::MsgLike(content) => msg_like(content, media_identity), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 329 | TimelineItemContent::MembershipChange(change) => TimelineContent::Membership { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 330 | user_id: change.user_id().to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 331 | display_name: change.display_name(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 332 | change: change |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 333 | .change() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 334 | .map_or(MembershipChange::Other, membership_change), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 335 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 336 | TimelineItemContent::ProfileChange(change) => TimelineContent::Profile { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 337 | user_id: change.user_id().to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 338 | display_name: change.displayname_change().map(|change| Change { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 339 | old: change.old.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 340 | new: change.new.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 341 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 342 | avatar: change.avatar_url_change().map(|change| Change { |
5d2974de5826 | atagen | 2026-10-08 11:29 UTC | 343 | old: change.old.as_deref().map(MediaId::plain), |
5d2974de5826 | atagen | 2026-10-08 11:29 UTC | 344 | new: change.new.as_deref().map(MediaId::plain), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 345 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 346 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 347 | TimelineItemContent::OtherState(state) => match state.content() { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 348 | AnyOtherStateEventContentChange::RoomName(change) => TimelineContent::RoomName( |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 349 | original(change).and_then(|content| non_empty(&content.name)), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 350 | ), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 351 | AnyOtherStateEventContentChange::RoomTopic(change) => TimelineContent::RoomTopic( |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 352 | original(change).and_then(|content| non_empty(&content.topic)), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 353 | ), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 354 | AnyOtherStateEventContentChange::RoomAvatar(change) => TimelineContent::RoomAvatar( |
5d2974de5826 | atagen | 2026-10-08 11:29 UTC | 355 | original(change).and_then(|content| content.url.as_deref().map(MediaId::plain)), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 356 | ), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 357 | AnyOtherStateEventContentChange::RoomEncryption(_) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 358 | TimelineContent::EncryptionEnabled |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 359 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 360 | AnyOtherStateEventContentChange::RoomCreate(_) => TimelineContent::RoomCreated, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 361 | AnyOtherStateEventContentChange::RoomTombstone(change) => match original(change) { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 362 | Some(content) => TimelineContent::RoomUpgraded { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 363 | successor: content.replacement_room.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 364 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 365 | None => other_state(state.content().event_type().to_string(), state.state_key()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 366 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 367 | other => other_state(other.event_type().to_string(), state.state_key()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 368 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 369 | TimelineItemContent::FailedToParseMessageLike { event_type, .. } => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 370 | TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 371 | event_type: event_type.to_string(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 372 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 373 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 374 | TimelineItemContent::FailedToParseState { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 375 | event_type, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 376 | state_key, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 377 | .. |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 378 | } => other_state(event_type.to_string(), state_key), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 379 | TimelineItemContent::CallInvite => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 380 | event_type: "m.call.invite".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 381 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 382 | TimelineItemContent::RtcNotification { .. } => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 383 | event_type: "org.matrix.msc4075.rtc.notification".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 384 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 385 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 386 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 387 | |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 388 | /// a cleared name or topic arrives as an empty string |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 389 | fn non_empty(value: &str) -> Option<String> { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 390 | (!value.is_empty()).then(|| value.to_owned()) |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 391 | } |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 392 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 393 | fn other_state(event_type: String, state_key: &str) -> TimelineContent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 394 | TimelineContent::OtherState { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 395 | event_type, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 396 | state_key: state_key.to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 397 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 398 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 399 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 400 | fn original<C>(change: &StateEventContentChange<C>) -> Option<&C> |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 401 | where |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 402 | C: ruma::events::StaticStateEventContent + ruma::events::RedactContent, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 403 | { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 404 | match change { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 405 | StateEventContentChange::Original { content, .. } => Some(content), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 406 | StateEventContentChange::Redacted(_) => None, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 407 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 408 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 409 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 410 | fn msg_like(content: &MsgLikeContent, media_identity: Option<&str>) -> TimelineContent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 411 | match &content.kind { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 412 | MsgLikeKind::Message(message) => TimelineContent::Message(Message { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 413 | kind: message_kind(message.msgtype()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 414 | body: message.body().to_owned(), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 415 | geo_uri: match message.msgtype() { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 416 | MessageType::Location(location) => Some(location.geo_uri.clone()), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 417 | _ => None, |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 418 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 419 | formatted_body: formatted_body(message.msgtype()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 420 | mentions: message.mentions().map(|mentions| MessageMentions { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 421 | user_ids: mentions.user_ids.iter().cloned().collect(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 422 | room: mentions.room, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 423 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 424 | edited: message.is_edited(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 425 | media: media_identity |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 426 | .and_then(|identity| media_from_message(identity, message.msgtype())), |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 427 | link_previews: sender_previews(media_identity, message.msgtype()) |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 428 | .into_iter() |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 429 | .map(|(preview, _)| preview) |
0291516d40b5 | atagen | 2026-10-22 08:31 UTC | 430 | .collect(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 431 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 432 | MsgLikeKind::Sticker(sticker) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 433 | let sticker = sticker.content(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 434 | match media_identity { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 435 | Some(identity) => TimelineContent::Sticker { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 436 | body: sticker.body.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 437 | media: media_descriptor_for_sticker(identity, sticker), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 438 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 439 | None => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 440 | event_type: "m.sticker".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 441 | }, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 442 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 443 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 444 | MsgLikeKind::Poll(poll) => TimelineContent::Poll { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 445 | question: poll.results().question, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 446 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 447 | MsgLikeKind::Redacted => TimelineContent::Redacted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 448 | MsgLikeKind::UnableToDecrypt(encrypted) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 449 | TimelineContent::UnableToDecrypt(match encrypted { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 450 | EncryptedMessage::MegolmV1AesSha2 { cause, .. } => utd_cause(*cause), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 451 | _ => UtdCause::Unknown, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 452 | }) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 453 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 454 | MsgLikeKind::Other(other) => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 455 | event_type: other.event_type().to_string(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 456 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 457 | MsgLikeKind::LiveLocation(_) => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 458 | event_type: "m.beacon_info".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 459 | }, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 460 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 461 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 462 | |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 463 | pub(crate) fn message_kind(message: &MessageType) -> MessageKind { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 464 | match message { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 465 | MessageType::Text(_) => MessageKind::Text, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 466 | MessageType::Emote(_) => MessageKind::Emote, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 467 | MessageType::Notice(_) => MessageKind::Notice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 468 | MessageType::ServerNotice(_) => MessageKind::ServerNotice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 469 | MessageType::Image(_) => MessageKind::Image, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 470 | MessageType::Video(_) => MessageKind::Video, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 471 | MessageType::Audio(_) => MessageKind::Audio, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 472 | MessageType::File(_) => MessageKind::File, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 473 | MessageType::Location(_) => MessageKind::Location, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 474 | MessageType::VerificationRequest(_) => MessageKind::VerificationRequest, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 475 | other => MessageKind::Other(other.msgtype().to_owned()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 476 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 477 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 478 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 479 | const fn membership_change(change: SdkMembershipChange) -> MembershipChange { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 480 | match change { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 481 | SdkMembershipChange::Joined => MembershipChange::Joined, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 482 | SdkMembershipChange::Left => MembershipChange::Left, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 483 | SdkMembershipChange::Banned => MembershipChange::Banned, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 484 | SdkMembershipChange::Unbanned => MembershipChange::Unbanned, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 485 | SdkMembershipChange::Kicked => MembershipChange::Kicked, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 486 | SdkMembershipChange::KickedAndBanned => MembershipChange::KickedAndBanned, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 487 | SdkMembershipChange::Invited => MembershipChange::Invited, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 488 | SdkMembershipChange::InvitationAccepted => MembershipChange::InvitationAccepted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 489 | SdkMembershipChange::InvitationRejected => MembershipChange::InvitationRejected, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 490 | SdkMembershipChange::InvitationRevoked => MembershipChange::InvitationRevoked, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 491 | SdkMembershipChange::Knocked => MembershipChange::Knocked, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 492 | SdkMembershipChange::KnockAccepted => MembershipChange::KnockAccepted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 493 | SdkMembershipChange::KnockRetracted => MembershipChange::KnockRetracted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 494 | SdkMembershipChange::KnockDenied => MembershipChange::KnockDenied, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 495 | SdkMembershipChange::None |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 496 | | SdkMembershipChange::Error |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 497 | | SdkMembershipChange::NotImplemented => MembershipChange::Other, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 498 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 499 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 500 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 501 | const fn utd_cause(cause: SdkUtdCause) -> UtdCause { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 502 | match cause { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 503 | SdkUtdCause::Unknown => UtdCause::Unknown, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 504 | SdkUtdCause::SentBeforeWeJoined => UtdCause::SentBeforeWeJoined, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 505 | SdkUtdCause::VerificationViolation => UtdCause::VerificationViolation, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 506 | SdkUtdCause::UnsignedDevice => UtdCause::UnsignedDevice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 507 | SdkUtdCause::UnknownDevice => UtdCause::UnknownDevice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 508 | SdkUtdCause::HistoricalMessageAndBackupIsDisabled => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 509 | UtdCause::HistoricalMessageAndBackupIsDisabled |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 510 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 511 | SdkUtdCause::WithheldForUnverifiedOrInsecureDevice => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 512 | UtdCause::WithheldForUnverifiedOrInsecureDevice |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 513 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 514 | SdkUtdCause::WithheldBySender => UtdCause::WithheldBySender, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 515 | SdkUtdCause::HistoricalMessageAndDeviceIsUnverified => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 516 | UtdCause::HistoricalMessageAndDeviceIsUnverified |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 517 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 518 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 519 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 520 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 521 | fn sender(user_id: &UserId, profile: &TimelineDetails<Profile>) -> Sender { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 522 | let profile = match profile { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 523 | TimelineDetails::Ready(profile) => Some(profile), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 524 | _ => None, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 525 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 526 | Sender { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 527 | user_id: user_id.to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 528 | display_name: profile.and_then(|profile| profile.display_name.clone()), |
5d2974de5826 | atagen | 2026-10-08 11:29 UTC | 529 | avatar: profile.and_then(|profile| profile.avatar_url.as_deref().map(MediaId::plain)), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 530 | name_ambiguous: profile.is_some_and(|profile| profile.display_name_ambiguous), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 531 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 532 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 533 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 534 | fn send_state(state: Option<&EventSendState>) -> SendState { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 535 | match state { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 536 | None | Some(EventSendState::Sent { .. }) => SendState::Sent, |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 537 | Some(EventSendState::NotSentYet { .. }) => SendState::Sending, |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 538 | // recoverable means the sdk parks the queue and we unpark it, nothing for the user to do |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 539 | Some(EventSendState::SendingFailed { is_recoverable, .. }) => SendState::Failed { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 540 | will_retry: *is_recoverable, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 541 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 542 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 543 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 544 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 545 | fn thread_summary(content: &MsgLikeContent) -> Option<ThreadSummary> { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 546 | content.thread_summary.as_ref().map(|summary| { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 547 | let latest_event = match &summary.latest_event { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 548 | TimelineDetails::Ready(event) => Some(event.as_ref()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 549 | _ => None, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 550 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 551 | ThreadSummary { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 552 | reply_count: summary.num_replies, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 553 | latest_event_id: latest_event.and_then(|event| match &event.identifier { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 554 | TimelineEventItemId::EventId(event_id) => Some(event_id.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 555 | TimelineEventItemId::TransactionId(_) => None, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 556 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 557 | latest_event_timestamp_ms: latest_event.map(|event| event.timestamp.get().into()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 558 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 559 | }) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 560 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 561 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 562 | fn reactions(event: &EventTimelineItem, current_user_id: Option<&UserId>) -> Vec<Reaction> { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 563 | event |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 564 | .content() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 565 | .reactions() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 566 | .into_iter() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 567 | .flat_map(|reactions| reactions.iter()) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 568 | .map(|(key, senders)| Reaction { |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 569 | key: key.clone(), |
8fa8a7cde89c | atagen | 2026-10-08 12:39 UTC | 570 | shortcode: senders.values().find_map(|info| info.shortcode.clone()), |
8fa8a7cde89c | atagen | 2026-10-08 12:39 UTC | 571 | image: <&MxcUri>::from(key.as_str()) |
8fa8a7cde89c | atagen | 2026-10-08 12:39 UTC | 572 | .is_valid() |
8fa8a7cde89c | atagen | 2026-10-08 12:39 UTC | 573 | .then(|| MediaId::plain(key)), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 574 | count: senders.len(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 575 | own: current_user_id.is_some_and(|user_id| senders.contains_key(user_id)), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 576 | senders: senders.keys().cloned().collect(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 577 | }) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 578 | .collect() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 579 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 580 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 581 | pub(super) fn formatted_body(message: &MessageType) -> Option<String> { |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 582 | let formatted = match message { |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 583 | MessageType::Text(content) => content.formatted.as_ref(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 584 | MessageType::Emote(content) => content.formatted.as_ref(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 585 | MessageType::Notice(content) => content.formatted.as_ref(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 586 | MessageType::Audio(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 587 | MessageType::File(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 588 | MessageType::Image(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 589 | MessageType::Video(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 590 | _ => None, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 591 | }?; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 592 | (formatted.format == MessageFormat::Html).then(|| formatted.body.clone()) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 593 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 594 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 595 | #[cfg(test)] |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 596 | mod tests { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 597 | use super::*; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 598 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 599 | fn row(n: u64) -> Option<TimelineItem> { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 600 | Some(TimelineItem::DayDivider { timestamp_ms: n }) |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 601 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 602 | |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 603 | #[test] |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 604 | fn hidden_rows_keep_diff_indices_on_the_visible_timeline() { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 605 | let mut sdk: imbl::Vector<Option<TimelineItem>> = imbl::Vector::new(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 606 | let mut hidden = HiddenRows::default(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 607 | let mut visible: imbl::Vector<TimelineItem> = imbl::Vector::new(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 608 | let diffs = [ |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 609 | VectorDiff::Reset { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 610 | values: [row(0), None, row(1)].into_iter().collect(), |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 611 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 612 | VectorDiff::Insert { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 613 | index: 1, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 614 | value: row(2), |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 615 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 616 | VectorDiff::Set { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 617 | index: 2, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 618 | value: row(3), |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 619 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 620 | VectorDiff::Set { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 621 | index: 0, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 622 | value: None, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 623 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 624 | VectorDiff::PushFront { value: None }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 625 | VectorDiff::Remove { index: 3 }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 626 | VectorDiff::Append { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 627 | values: [None, row(4)].into_iter().collect(), |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 628 | }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 629 | VectorDiff::PopFront, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 630 | VectorDiff::Truncate { length: 2 }, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 631 | VectorDiff::PopBack, |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 632 | ]; |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 633 | for diff in diffs { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 634 | diff.clone().apply(&mut sdk); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 635 | if let Some(diff) = hidden.translate(diff) { |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 636 | diff.try_apply(&mut visible).unwrap(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 637 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 638 | let expected: imbl::Vector<_> = sdk.iter().flatten().cloned().collect(); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 639 | assert_eq!(visible, expected); |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 640 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 641 | } |
d9b96952ec60 | atagen | 2026-10-25 09:07 UTC | 642 | } |