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::{ |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 14 | 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::{ |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 22 | Change, MembershipChange, Message, MessageKind, MessageMentions, Reaction, ReplyDetails, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 23 | ReplyReference, SendState, Sender, ThreadSummary, TimelineContent, TimelineDiff, TimelineEvent, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 24 | TimelineItem, TimelineItemId, UtdCause, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 25 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 26 | |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 27 | use super::media::{ |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 28 | MediaRegistration, MediaScope, media_descriptor_for_sticker, media_from_message, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 29 | media_identity, media_registration_for_item, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 30 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 31 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 32 | #[derive(Default)] |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 33 | pub(crate) struct NormalisedTimeline { |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 34 | pub(crate) items: Vec<TimelineItem>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 35 | pub(crate) media: Vec<MediaRegistration>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 36 | /// replies whose target isn't loaded, so somebody should go and ask for it |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 37 | pub(crate) missing_replies: Vec<OwnedEventId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 38 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 39 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 40 | pub(crate) struct NormalisedTimelineDiffs { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 41 | pub(crate) diffs: Vec<TimelineDiff>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 42 | pub(crate) media: Vec<MediaRegistration>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 43 | pub(crate) missing_replies: Vec<OwnedEventId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 44 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 45 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 46 | struct Normaliser<'a> { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 47 | current_user_id: Option<&'a UserId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 48 | scope: &'a MediaScope, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 49 | media: Vec<MediaRegistration>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 50 | missing_replies: Vec<OwnedEventId>, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 51 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 52 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 53 | impl Normaliser<'_> { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 54 | fn item(&mut self, item: &SdkTimelineItem) -> TimelineItem { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 55 | if let Some(media) = media_registration_for_item(item, self.scope) { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 56 | self.media.push(media); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 57 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 58 | match item.kind() { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 59 | TimelineItemKind::Event(event) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 60 | TimelineItem::Event(Box::new(self.event(item, event))) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 61 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 62 | TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(timestamp)) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 63 | TimelineItem::DayDivider { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 64 | timestamp_ms: timestamp.get().into(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 65 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 66 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 67 | TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker) => TimelineItem::ReadMarker, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 68 | TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 69 | TimelineItem::TimelineStart |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 70 | } |
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 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 74 | fn event(&mut self, item: &SdkTimelineItem, event: &EventTimelineItem) -> TimelineEvent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 75 | let msg_like = match event.content() { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 76 | TimelineItemContent::MsgLike(content) => Some(content), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 77 | _ => None, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 78 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 79 | let reply_to = msg_like |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 80 | .and_then(|content| content.in_reply_to.as_ref()) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 81 | .map(|reply| { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 82 | let details = match &reply.event { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 83 | TimelineDetails::Ready(embedded) => ReplyDetails::Ready { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 84 | sender: sender(&embedded.sender, &embedded.sender_profile), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 85 | content: Box::new(content(&embedded.content, None)), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 86 | timestamp_ms: embedded.timestamp.get().into(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 87 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 88 | TimelineDetails::Unavailable => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 89 | self.missing_replies.push(reply.event_id.clone()); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 90 | ReplyDetails::Pending |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 91 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 92 | TimelineDetails::Pending => ReplyDetails::Pending, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 93 | TimelineDetails::Error(_) => ReplyDetails::Unavailable, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 94 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 95 | ReplyReference { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 96 | event_id: reply.event_id.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 97 | details, |
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 | let mut read_by: Vec<_> = event |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 101 | .read_receipts() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 102 | .keys() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 103 | .filter(|user_id| *user_id != event.sender()) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 104 | .cloned() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 105 | .collect(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 106 | read_by.sort(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 107 | TimelineEvent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 108 | item_id: TimelineItemId::new(item.unique_id().0.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 109 | event_id: event.event_id().map(ToOwned::to_owned), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 110 | transaction_id: event.transaction_id().map(ToOwned::to_owned), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 111 | sender: sender(event.sender(), event.sender_profile()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 112 | timestamp_ms: event.timestamp().get().into(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 113 | own: event.is_own(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 114 | send_state: send_state(event.send_state()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 115 | content: content(event.content(), Some(&media_identity(item, self.scope))), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 116 | reply_to, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 117 | thread_root: msg_like.and_then(|content| content.thread_root.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 118 | thread_summary: msg_like.and_then(thread_summary), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 119 | reactions: reactions(event, self.current_user_id), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 120 | read_by, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 121 | editable: event.is_editable(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 122 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 123 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 124 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 125 | |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 126 | pub(crate) fn normalise_timeline<'a>( |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 127 | items: impl Iterator<Item = &'a Arc<SdkTimelineItem>>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 128 | current_user_id: Option<&UserId>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 129 | scope: &MediaScope, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 130 | ) -> NormalisedTimeline { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 131 | let mut normaliser = Normaliser { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 132 | current_user_id, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 133 | scope, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 134 | media: Vec::new(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 135 | missing_replies: Vec::new(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 136 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 137 | let items = items.map(|item| normaliser.item(item)).collect(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 138 | NormalisedTimeline { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 139 | items, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 140 | media: normaliser.media, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 141 | missing_replies: normaliser.missing_replies, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 142 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 143 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 144 | |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 145 | pub(crate) fn normalise_timeline_diffs( |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 146 | diffs: Vec<VectorDiff<Arc<SdkTimelineItem>>>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 147 | current_user_id: Option<&UserId>, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 148 | scope: &MediaScope, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 149 | ) -> NormalisedTimelineDiffs { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 150 | let mut normaliser = Normaliser { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 151 | current_user_id, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 152 | scope, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 153 | media: Vec::new(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 154 | missing_replies: Vec::new(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 155 | }; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 156 | let diffs = diffs |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 157 | .into_iter() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 158 | .map(|diff| match diff.map(|item| normaliser.item(&item)) { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 159 | VectorDiff::Append { values } => TimelineDiff::Append { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 160 | values: values.into_iter().collect(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 161 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 162 | VectorDiff::Clear => TimelineDiff::Clear, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 163 | VectorDiff::PushFront { value } => TimelineDiff::PushFront { value }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 164 | VectorDiff::PushBack { value } => TimelineDiff::PushBack { value }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 165 | VectorDiff::PopFront => TimelineDiff::PopFront, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 166 | VectorDiff::PopBack => TimelineDiff::PopBack, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 167 | VectorDiff::Insert { index, value } => TimelineDiff::Insert { index, value }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 168 | VectorDiff::Set { index, value } => TimelineDiff::Set { index, value }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 169 | VectorDiff::Remove { index } => TimelineDiff::Remove { index }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 170 | VectorDiff::Truncate { length } => TimelineDiff::Truncate { length }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 171 | VectorDiff::Reset { values } => TimelineDiff::Reset { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 172 | values: values.into_iter().collect(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 173 | }, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 174 | }) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 175 | .collect(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 176 | NormalisedTimelineDiffs { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 177 | diffs, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 178 | media: normaliser.media, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 179 | missing_replies: normaliser.missing_replies, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 180 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 181 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 182 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 183 | /// `media_identity` is `None` for embedded reply content, which never gets fetchable media of its own |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 184 | fn content(content: &TimelineItemContent, media_identity: Option<&str>) -> TimelineContent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 185 | match content { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 186 | TimelineItemContent::MsgLike(content) => msg_like(content, media_identity), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 187 | TimelineItemContent::MembershipChange(change) => TimelineContent::Membership { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 188 | user_id: change.user_id().to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 189 | display_name: change.display_name(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 190 | change: change |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 191 | .change() |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 192 | .map_or(MembershipChange::Other, membership_change), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 193 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 194 | TimelineItemContent::ProfileChange(change) => TimelineContent::Profile { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 195 | user_id: change.user_id().to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 196 | display_name: change.displayname_change().map(|change| Change { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 197 | old: change.old.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 198 | new: change.new.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 199 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 200 | avatar: change.avatar_url_change().map(|change| Change { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 201 | old: change.old.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 202 | new: change.new.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 203 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 204 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 205 | TimelineItemContent::OtherState(state) => match state.content() { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 206 | AnyOtherStateEventContentChange::RoomName(change) => TimelineContent::RoomName( |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 207 | original(change).and_then(|content| non_empty(&content.name)), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 208 | ), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 209 | AnyOtherStateEventContentChange::RoomTopic(change) => TimelineContent::RoomTopic( |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 210 | original(change).and_then(|content| non_empty(&content.topic)), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 211 | ), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 212 | AnyOtherStateEventContentChange::RoomAvatar(change) => TimelineContent::RoomAvatar( |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 213 | original(change).and_then(|content| content.url.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 214 | ), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 215 | AnyOtherStateEventContentChange::RoomEncryption(_) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 216 | TimelineContent::EncryptionEnabled |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 217 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 218 | AnyOtherStateEventContentChange::RoomCreate(_) => TimelineContent::RoomCreated, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 219 | AnyOtherStateEventContentChange::RoomTombstone(change) => match original(change) { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 220 | Some(content) => TimelineContent::RoomUpgraded { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 221 | successor: content.replacement_room.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 222 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 223 | None => other_state(state.content().event_type().to_string(), state.state_key()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 224 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 225 | other => other_state(other.event_type().to_string(), state.state_key()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 226 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 227 | TimelineItemContent::FailedToParseMessageLike { event_type, .. } => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 228 | TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 229 | event_type: event_type.to_string(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 230 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 231 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 232 | TimelineItemContent::FailedToParseState { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 233 | event_type, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 234 | state_key, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 235 | .. |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 236 | } => other_state(event_type.to_string(), state_key), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 237 | TimelineItemContent::CallInvite => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 238 | event_type: "m.call.invite".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 239 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 240 | TimelineItemContent::RtcNotification { .. } => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 241 | event_type: "org.matrix.msc4075.rtc.notification".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 242 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 243 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 244 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 245 | |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 246 | /// a cleared name or topic arrives as an empty string |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 247 | fn non_empty(value: &str) -> Option<String> { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 248 | (!value.is_empty()).then(|| value.to_owned()) |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 249 | } |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 250 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 251 | fn other_state(event_type: String, state_key: &str) -> TimelineContent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 252 | TimelineContent::OtherState { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 253 | event_type, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 254 | state_key: state_key.to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 255 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 256 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 257 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 258 | fn original<C>(change: &StateEventContentChange<C>) -> Option<&C> |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 259 | where |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 260 | C: ruma::events::StaticStateEventContent + ruma::events::RedactContent, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 261 | { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 262 | match change { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 263 | StateEventContentChange::Original { content, .. } => Some(content), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 264 | StateEventContentChange::Redacted(_) => None, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 265 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 266 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 267 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 268 | fn msg_like(content: &MsgLikeContent, media_identity: Option<&str>) -> TimelineContent { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 269 | match &content.kind { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 270 | MsgLikeKind::Message(message) => TimelineContent::Message(Message { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 271 | kind: message_kind(message.msgtype()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 272 | body: message.body().to_owned(), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 273 | geo_uri: match message.msgtype() { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 274 | MessageType::Location(location) => Some(location.geo_uri.clone()), |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 275 | _ => None, |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 276 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 277 | formatted_body: formatted_body(message.msgtype()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 278 | mentions: message.mentions().map(|mentions| MessageMentions { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 279 | user_ids: mentions.user_ids.iter().cloned().collect(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 280 | room: mentions.room, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 281 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 282 | edited: message.is_edited(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 283 | media: media_identity |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 284 | .and_then(|identity| media_from_message(identity, message.msgtype())), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 285 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 286 | MsgLikeKind::Sticker(sticker) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 287 | let sticker = sticker.content(); |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 288 | match media_identity { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 289 | Some(identity) => TimelineContent::Sticker { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 290 | body: sticker.body.clone(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 291 | media: media_descriptor_for_sticker(identity, sticker), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 292 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 293 | None => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 294 | event_type: "m.sticker".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 295 | }, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 296 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 297 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 298 | MsgLikeKind::Poll(poll) => TimelineContent::Poll { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 299 | question: poll.results().question, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 300 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 301 | MsgLikeKind::Redacted => TimelineContent::Redacted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 302 | MsgLikeKind::UnableToDecrypt(encrypted) => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 303 | TimelineContent::UnableToDecrypt(match encrypted { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 304 | EncryptedMessage::MegolmV1AesSha2 { cause, .. } => utd_cause(*cause), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 305 | _ => UtdCause::Unknown, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 306 | }) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 307 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 308 | MsgLikeKind::Other(other) => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 309 | event_type: other.event_type().to_string(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 310 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 311 | MsgLikeKind::LiveLocation(_) => TimelineContent::Other { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 312 | event_type: "m.beacon_info".to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 313 | }, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 314 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 315 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 316 | |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 317 | pub(crate) fn message_kind(message: &MessageType) -> MessageKind { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 318 | match message { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 319 | MessageType::Text(_) => MessageKind::Text, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 320 | MessageType::Emote(_) => MessageKind::Emote, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 321 | MessageType::Notice(_) => MessageKind::Notice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 322 | MessageType::ServerNotice(_) => MessageKind::ServerNotice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 323 | MessageType::Image(_) => MessageKind::Image, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 324 | MessageType::Video(_) => MessageKind::Video, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 325 | MessageType::Audio(_) => MessageKind::Audio, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 326 | MessageType::File(_) => MessageKind::File, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 327 | MessageType::Location(_) => MessageKind::Location, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 328 | MessageType::VerificationRequest(_) => MessageKind::VerificationRequest, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 329 | other => MessageKind::Other(other.msgtype().to_owned()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 330 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 331 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 332 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 333 | const fn membership_change(change: SdkMembershipChange) -> MembershipChange { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 334 | match change { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 335 | SdkMembershipChange::Joined => MembershipChange::Joined, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 336 | SdkMembershipChange::Left => MembershipChange::Left, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 337 | SdkMembershipChange::Banned => MembershipChange::Banned, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 338 | SdkMembershipChange::Unbanned => MembershipChange::Unbanned, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 339 | SdkMembershipChange::Kicked => MembershipChange::Kicked, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 340 | SdkMembershipChange::KickedAndBanned => MembershipChange::KickedAndBanned, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 341 | SdkMembershipChange::Invited => MembershipChange::Invited, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 342 | SdkMembershipChange::InvitationAccepted => MembershipChange::InvitationAccepted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 343 | SdkMembershipChange::InvitationRejected => MembershipChange::InvitationRejected, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 344 | SdkMembershipChange::InvitationRevoked => MembershipChange::InvitationRevoked, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 345 | SdkMembershipChange::Knocked => MembershipChange::Knocked, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 346 | SdkMembershipChange::KnockAccepted => MembershipChange::KnockAccepted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 347 | SdkMembershipChange::KnockRetracted => MembershipChange::KnockRetracted, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 348 | SdkMembershipChange::KnockDenied => MembershipChange::KnockDenied, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 349 | SdkMembershipChange::None |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 350 | | SdkMembershipChange::Error |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 351 | | SdkMembershipChange::NotImplemented => MembershipChange::Other, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 352 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 353 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 354 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 355 | const fn utd_cause(cause: SdkUtdCause) -> UtdCause { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 356 | match cause { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 357 | SdkUtdCause::Unknown => UtdCause::Unknown, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 358 | SdkUtdCause::SentBeforeWeJoined => UtdCause::SentBeforeWeJoined, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 359 | SdkUtdCause::VerificationViolation => UtdCause::VerificationViolation, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 360 | SdkUtdCause::UnsignedDevice => UtdCause::UnsignedDevice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 361 | SdkUtdCause::UnknownDevice => UtdCause::UnknownDevice, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 362 | SdkUtdCause::HistoricalMessageAndBackupIsDisabled => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 363 | UtdCause::HistoricalMessageAndBackupIsDisabled |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 364 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 365 | SdkUtdCause::WithheldForUnverifiedOrInsecureDevice => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 366 | UtdCause::WithheldForUnverifiedOrInsecureDevice |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 367 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 368 | SdkUtdCause::WithheldBySender => UtdCause::WithheldBySender, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 369 | SdkUtdCause::HistoricalMessageAndDeviceIsUnverified => { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 370 | UtdCause::HistoricalMessageAndDeviceIsUnverified |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 371 | } |
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 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 375 | fn sender(user_id: &UserId, profile: &TimelineDetails<Profile>) -> Sender { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 376 | let profile = match profile { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 377 | TimelineDetails::Ready(profile) => Some(profile), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 378 | _ => None, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 379 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 380 | Sender { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 381 | user_id: user_id.to_owned(), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 382 | display_name: profile.and_then(|profile| profile.display_name.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 383 | avatar: profile.and_then(|profile| profile.avatar_url.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 384 | name_ambiguous: profile.is_some_and(|profile| profile.display_name_ambiguous), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 385 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 386 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 387 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 388 | fn send_state(state: Option<&EventSendState>) -> SendState { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 389 | match state { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 390 | None | Some(EventSendState::Sent { .. }) => SendState::Sent, |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 391 | Some(EventSendState::NotSentYet { .. }) => SendState::Sending, |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 392 | // 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 | 393 | Some(EventSendState::SendingFailed { is_recoverable, .. }) => SendState::Failed { |
5a52012c9b1d | atagen | 2026-09-24 23:45 UTC | 394 | will_retry: *is_recoverable, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 395 | }, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 396 | } |
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 | fn thread_summary(content: &MsgLikeContent) -> Option<ThreadSummary> { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 400 | content.thread_summary.as_ref().map(|summary| { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 401 | let latest_event = match &summary.latest_event { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 402 | TimelineDetails::Ready(event) => Some(event.as_ref()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 403 | _ => None, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 404 | }; |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 405 | ThreadSummary { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 406 | reply_count: summary.num_replies, |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 407 | latest_event_id: latest_event.and_then(|event| match &event.identifier { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 408 | TimelineEventItemId::EventId(event_id) => Some(event_id.clone()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 409 | TimelineEventItemId::TransactionId(_) => None, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 410 | }), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 411 | latest_event_timestamp_ms: latest_event.map(|event| event.timestamp.get().into()), |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 412 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 413 | }) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 414 | } |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 415 | |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 416 | fn reactions(event: &EventTimelineItem, current_user_id: Option<&UserId>) -> Vec<Reaction> { |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 417 | event |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 418 | .content() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 419 | .reactions() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 420 | .into_iter() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 421 | .flat_map(|reactions| reactions.iter()) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 422 | .map(|(key, senders)| Reaction { |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 423 | key: key.clone(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 424 | count: senders.len(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 425 | own: current_user_id.is_some_and(|user_id| senders.contains_key(user_id)), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 426 | senders: senders.keys().cloned().collect(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 427 | }) |
960ac0721079 | atagen | 2026-09-25 00:24 UTC | 428 | .collect() |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 429 | } |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 430 | |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 431 | fn formatted_body(message: &MessageType) -> Option<String> { |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 432 | let formatted = match message { |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 433 | MessageType::Text(content) => content.formatted.as_ref(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 434 | MessageType::Emote(content) => content.formatted.as_ref(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 435 | MessageType::Notice(content) => content.formatted.as_ref(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 436 | MessageType::Audio(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 437 | MessageType::File(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 438 | MessageType::Image(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 439 | MessageType::Video(content) => content.formatted_caption(), |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 440 | _ => None, |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 441 | }?; |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 442 | (formatted.format == MessageFormat::Html).then(|| formatted.body.clone()) |
96fdf90aacd6 | atagen | 2026-09-08 08:24 UTC | 443 | } |