atagen

macltok

public
Star0
Watch0
96fdf90aacd6dc2b8bd3d63773234245b389d28c
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: src/normalise/timeline.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
use std::sync::Arc;
96fdf90aacd6atagen2026-09-08 08:24 UTC2
96fdf90aacd6atagen2026-09-08 08:24 UTC3
use matrix_sdk_ui::{
96fdf90aacd6atagen2026-09-08 08:24 UTC4
    eyeball_im::VectorDiff,
96fdf90aacd6atagen2026-09-08 08:24 UTC5
    timeline::{
96fdf90aacd6atagen2026-09-08 08:24 UTC6
        EventSendState, MsgLikeKind, TimelineDetails, TimelineEventItemId,
96fdf90aacd6atagen2026-09-08 08:24 UTC7
        TimelineItem as SdkTimelineItem, TimelineItemContent, TimelineItemKind,
96fdf90aacd6atagen2026-09-08 08:24 UTC8
        VirtualTimelineItem,
96fdf90aacd6atagen2026-09-08 08:24 UTC9
    },
96fdf90aacd6atagen2026-09-08 08:24 UTC10
};
96fdf90aacd6atagen2026-09-08 08:24 UTC11
use ruma::{
96fdf90aacd6atagen2026-09-08 08:24 UTC12
    UserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC13
    events::room::message::{MessageFormat, MessageType},
96fdf90aacd6atagen2026-09-08 08:24 UTC14
};
96fdf90aacd6atagen2026-09-08 08:24 UTC15
96fdf90aacd6atagen2026-09-08 08:24 UTC16
use crate::{
96fdf90aacd6atagen2026-09-08 08:24 UTC17
    MessageMentions, Reaction, ReplyReference, SendState, ThreadSummary, TimelineDiff,
96fdf90aacd6atagen2026-09-08 08:24 UTC18
    TimelineItem, TimelineItemId, TimelineMessage,
96fdf90aacd6atagen2026-09-08 08:24 UTC19
};
96fdf90aacd6atagen2026-09-08 08:24 UTC20
96fdf90aacd6atagen2026-09-08 08:24 UTC21
use super::media::{
96fdf90aacd6atagen2026-09-08 08:24 UTC22
    MediaRegistration, MediaScope, media_descriptor_for_sticker, media_from_message,
96fdf90aacd6atagen2026-09-08 08:24 UTC23
    media_identity, media_registration_for_item,
96fdf90aacd6atagen2026-09-08 08:24 UTC24
};
96fdf90aacd6atagen2026-09-08 08:24 UTC25
96fdf90aacd6atagen2026-09-08 08:24 UTC26
pub(crate) struct NormalisedTimeline {
96fdf90aacd6atagen2026-09-08 08:24 UTC27
    pub(crate) items: Vec<TimelineItem>,
96fdf90aacd6atagen2026-09-08 08:24 UTC28
    pub(crate) media: Vec<MediaRegistration>,
96fdf90aacd6atagen2026-09-08 08:24 UTC29
}
96fdf90aacd6atagen2026-09-08 08:24 UTC30
96fdf90aacd6atagen2026-09-08 08:24 UTC31
pub(crate) fn normalise_timeline<'a>(
96fdf90aacd6atagen2026-09-08 08:24 UTC32
    items: impl Iterator<Item = &'a Arc<SdkTimelineItem>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC33
    current_user_id: Option<&UserId>,
96fdf90aacd6atagen2026-09-08 08:24 UTC34
    scope: &MediaScope,
96fdf90aacd6atagen2026-09-08 08:24 UTC35
) -> NormalisedTimeline {
96fdf90aacd6atagen2026-09-08 08:24 UTC36
    let mut normalised = NormalisedTimeline {
96fdf90aacd6atagen2026-09-08 08:24 UTC37
        items: Vec::new(),
96fdf90aacd6atagen2026-09-08 08:24 UTC38
        media: Vec::new(),
96fdf90aacd6atagen2026-09-08 08:24 UTC39
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC40
    for item in items {
96fdf90aacd6atagen2026-09-08 08:24 UTC41
        if let Some(media) = media_registration_for_item(item, scope) {
96fdf90aacd6atagen2026-09-08 08:24 UTC42
            normalised.media.push(media);
96fdf90aacd6atagen2026-09-08 08:24 UTC43
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC44
        normalised
96fdf90aacd6atagen2026-09-08 08:24 UTC45
            .items
96fdf90aacd6atagen2026-09-08 08:24 UTC46
            .push(normalise_timeline_item(item, current_user_id, scope));
96fdf90aacd6atagen2026-09-08 08:24 UTC47
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC48
    normalised
96fdf90aacd6atagen2026-09-08 08:24 UTC49
}
96fdf90aacd6atagen2026-09-08 08:24 UTC50
96fdf90aacd6atagen2026-09-08 08:24 UTC51
pub(crate) fn normalise_timeline_diffs(
96fdf90aacd6atagen2026-09-08 08:24 UTC52
    diffs: Vec<VectorDiff<Arc<SdkTimelineItem>>>,
96fdf90aacd6atagen2026-09-08 08:24 UTC53
    current_user_id: Option<&UserId>,
96fdf90aacd6atagen2026-09-08 08:24 UTC54
    scope: &MediaScope,
96fdf90aacd6atagen2026-09-08 08:24 UTC55
) -> NormalisedTimelineDiffs {
96fdf90aacd6atagen2026-09-08 08:24 UTC56
    let mut media = Vec::new();
96fdf90aacd6atagen2026-09-08 08:24 UTC57
    let diffs = diffs
96fdf90aacd6atagen2026-09-08 08:24 UTC58
        .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC59
        .map(|diff| {
96fdf90aacd6atagen2026-09-08 08:24 UTC60
            let diff = diff.map(|item| {
96fdf90aacd6atagen2026-09-08 08:24 UTC61
                if let Some(registration) = media_registration_for_item(&item, scope) {
96fdf90aacd6atagen2026-09-08 08:24 UTC62
                    media.push(registration);
96fdf90aacd6atagen2026-09-08 08:24 UTC63
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC64
                normalise_timeline_item(&item, current_user_id, scope)
96fdf90aacd6atagen2026-09-08 08:24 UTC65
            });
96fdf90aacd6atagen2026-09-08 08:24 UTC66
            match diff {
96fdf90aacd6atagen2026-09-08 08:24 UTC67
                VectorDiff::Append { values } => TimelineDiff::Append {
96fdf90aacd6atagen2026-09-08 08:24 UTC68
                    values: values.into_iter().collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC69
                },
96fdf90aacd6atagen2026-09-08 08:24 UTC70
                VectorDiff::Clear => TimelineDiff::Clear,
96fdf90aacd6atagen2026-09-08 08:24 UTC71
                VectorDiff::PushFront { value } => TimelineDiff::PushFront { value },
96fdf90aacd6atagen2026-09-08 08:24 UTC72
                VectorDiff::PushBack { value } => TimelineDiff::PushBack { value },
96fdf90aacd6atagen2026-09-08 08:24 UTC73
                VectorDiff::PopFront => TimelineDiff::PopFront,
96fdf90aacd6atagen2026-09-08 08:24 UTC74
                VectorDiff::PopBack => TimelineDiff::PopBack,
96fdf90aacd6atagen2026-09-08 08:24 UTC75
                VectorDiff::Insert { index, value } => TimelineDiff::Insert { index, value },
96fdf90aacd6atagen2026-09-08 08:24 UTC76
                VectorDiff::Set { index, value } => TimelineDiff::Set { index, value },
96fdf90aacd6atagen2026-09-08 08:24 UTC77
                VectorDiff::Remove { index } => TimelineDiff::Remove { index },
96fdf90aacd6atagen2026-09-08 08:24 UTC78
                VectorDiff::Truncate { length } => TimelineDiff::Truncate { length },
96fdf90aacd6atagen2026-09-08 08:24 UTC79
                VectorDiff::Reset { values } => TimelineDiff::Reset {
96fdf90aacd6atagen2026-09-08 08:24 UTC80
                    values: values.into_iter().collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC81
                },
96fdf90aacd6atagen2026-09-08 08:24 UTC82
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC83
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC84
        .collect();
96fdf90aacd6atagen2026-09-08 08:24 UTC85
    NormalisedTimelineDiffs { diffs, media }
96fdf90aacd6atagen2026-09-08 08:24 UTC86
}
96fdf90aacd6atagen2026-09-08 08:24 UTC87
96fdf90aacd6atagen2026-09-08 08:24 UTC88
pub(crate) struct NormalisedTimelineDiffs {
96fdf90aacd6atagen2026-09-08 08:24 UTC89
    pub(crate) diffs: Vec<TimelineDiff>,
96fdf90aacd6atagen2026-09-08 08:24 UTC90
    pub(crate) media: Vec<MediaRegistration>,
96fdf90aacd6atagen2026-09-08 08:24 UTC91
}
96fdf90aacd6atagen2026-09-08 08:24 UTC92
96fdf90aacd6atagen2026-09-08 08:24 UTC93
fn normalise_timeline_item(
96fdf90aacd6atagen2026-09-08 08:24 UTC94
    item: &SdkTimelineItem,
96fdf90aacd6atagen2026-09-08 08:24 UTC95
    current_user_id: Option<&UserId>,
96fdf90aacd6atagen2026-09-08 08:24 UTC96
    scope: &MediaScope,
96fdf90aacd6atagen2026-09-08 08:24 UTC97
) -> TimelineItem {
96fdf90aacd6atagen2026-09-08 08:24 UTC98
    match item.kind() {
96fdf90aacd6atagen2026-09-08 08:24 UTC99
        TimelineItemKind::Event(event) => TimelineItem::Message(Box::new(timeline_message(
96fdf90aacd6atagen2026-09-08 08:24 UTC100
            item,
96fdf90aacd6atagen2026-09-08 08:24 UTC101
            event,
96fdf90aacd6atagen2026-09-08 08:24 UTC102
            current_user_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC103
            scope,
96fdf90aacd6atagen2026-09-08 08:24 UTC104
        ))),
96fdf90aacd6atagen2026-09-08 08:24 UTC105
        TimelineItemKind::Virtual(VirtualTimelineItem::DateDivider(timestamp)) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC106
            TimelineItem::DayDivider {
96fdf90aacd6atagen2026-09-08 08:24 UTC107
                timestamp_ms: timestamp.get().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC108
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC109
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC110
        TimelineItemKind::Virtual(VirtualTimelineItem::ReadMarker) => TimelineItem::ReadMarker,
96fdf90aacd6atagen2026-09-08 08:24 UTC111
        TimelineItemKind::Virtual(VirtualTimelineItem::TimelineStart) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC112
            TimelineItem::TimelineStart
96fdf90aacd6atagen2026-09-08 08:24 UTC113
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC114
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC115
}
96fdf90aacd6atagen2026-09-08 08:24 UTC116
96fdf90aacd6atagen2026-09-08 08:24 UTC117
fn timeline_message(
96fdf90aacd6atagen2026-09-08 08:24 UTC118
    item: &SdkTimelineItem,
96fdf90aacd6atagen2026-09-08 08:24 UTC119
    event: &matrix_sdk_ui::timeline::EventTimelineItem,
96fdf90aacd6atagen2026-09-08 08:24 UTC120
    current_user_id: Option<&UserId>,
96fdf90aacd6atagen2026-09-08 08:24 UTC121
    scope: &MediaScope,
96fdf90aacd6atagen2026-09-08 08:24 UTC122
) -> TimelineMessage {
96fdf90aacd6atagen2026-09-08 08:24 UTC123
    let (body, formatted_body, mentions, edited, unable_to_decrypt, media) = match event.content() {
96fdf90aacd6atagen2026-09-08 08:24 UTC124
        TimelineItemContent::MsgLike(content) => match &content.kind {
96fdf90aacd6atagen2026-09-08 08:24 UTC125
            MsgLikeKind::Message(message) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC126
                message.body().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC127
                formatted_body(message.msgtype()),
96fdf90aacd6atagen2026-09-08 08:24 UTC128
                message.mentions().map(message_mentions),
96fdf90aacd6atagen2026-09-08 08:24 UTC129
                message.is_edited(),
96fdf90aacd6atagen2026-09-08 08:24 UTC130
                false,
96fdf90aacd6atagen2026-09-08 08:24 UTC131
                media_from_message(&media_identity(item, scope), message.msgtype()),
96fdf90aacd6atagen2026-09-08 08:24 UTC132
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC133
            MsgLikeKind::Sticker(sticker) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC134
                let content = sticker.content();
96fdf90aacd6atagen2026-09-08 08:24 UTC135
                (
96fdf90aacd6atagen2026-09-08 08:24 UTC136
                    content.body.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC137
                    None,
96fdf90aacd6atagen2026-09-08 08:24 UTC138
                    None,
96fdf90aacd6atagen2026-09-08 08:24 UTC139
                    false,
96fdf90aacd6atagen2026-09-08 08:24 UTC140
                    false,
96fdf90aacd6atagen2026-09-08 08:24 UTC141
                    Some(media_descriptor_for_sticker(
96fdf90aacd6atagen2026-09-08 08:24 UTC142
                        &media_identity(item, scope),
96fdf90aacd6atagen2026-09-08 08:24 UTC143
                        content,
96fdf90aacd6atagen2026-09-08 08:24 UTC144
                    )),
96fdf90aacd6atagen2026-09-08 08:24 UTC145
                )
96fdf90aacd6atagen2026-09-08 08:24 UTC146
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC147
            MsgLikeKind::Redacted => ("<message redacted>".into(), None, None, false, false, None),
96fdf90aacd6atagen2026-09-08 08:24 UTC148
            MsgLikeKind::UnableToDecrypt(_) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC149
                "<unable to decrypt message>".into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC150
                None,
96fdf90aacd6atagen2026-09-08 08:24 UTC151
                None,
96fdf90aacd6atagen2026-09-08 08:24 UTC152
                false,
96fdf90aacd6atagen2026-09-08 08:24 UTC153
                true,
96fdf90aacd6atagen2026-09-08 08:24 UTC154
                None,
96fdf90aacd6atagen2026-09-08 08:24 UTC155
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC156
            MsgLikeKind::Poll(_) => ("[poll]".into(), None, None, false, false, None),
96fdf90aacd6atagen2026-09-08 08:24 UTC157
            MsgLikeKind::LiveLocation(_) => {
96fdf90aacd6atagen2026-09-08 08:24 UTC158
                ("[live location]".into(), None, None, false, false, None)
96fdf90aacd6atagen2026-09-08 08:24 UTC159
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC160
            MsgLikeKind::Other(other) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC161
                format!("[{}]", other.event_type()),
96fdf90aacd6atagen2026-09-08 08:24 UTC162
                None,
96fdf90aacd6atagen2026-09-08 08:24 UTC163
                None,
96fdf90aacd6atagen2026-09-08 08:24 UTC164
                false,
96fdf90aacd6atagen2026-09-08 08:24 UTC165
                false,
96fdf90aacd6atagen2026-09-08 08:24 UTC166
                None,
96fdf90aacd6atagen2026-09-08 08:24 UTC167
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC168
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC169
        other => (
96fdf90aacd6atagen2026-09-08 08:24 UTC170
            format!(
96fdf90aacd6atagen2026-09-08 08:24 UTC171
                "[{}]",
96fdf90aacd6atagen2026-09-08 08:24 UTC172
                other.event_type_str().unwrap_or_else(|| "event".into())
96fdf90aacd6atagen2026-09-08 08:24 UTC173
            ),
96fdf90aacd6atagen2026-09-08 08:24 UTC174
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC175
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC176
            false,
96fdf90aacd6atagen2026-09-08 08:24 UTC177
            false,
96fdf90aacd6atagen2026-09-08 08:24 UTC178
            None,
96fdf90aacd6atagen2026-09-08 08:24 UTC179
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC180
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC181
    let reply_to = match event.content() {
96fdf90aacd6atagen2026-09-08 08:24 UTC182
        TimelineItemContent::MsgLike(content) => content.in_reply_to.as_ref().map(|reply| {
96fdf90aacd6atagen2026-09-08 08:24 UTC183
            let event_id = reply.event_id.to_owned();
96fdf90aacd6atagen2026-09-08 08:24 UTC184
            let embedded_sender = match &reply.event {
96fdf90aacd6atagen2026-09-08 08:24 UTC185
                TimelineDetails::Ready(event) => Some((
96fdf90aacd6atagen2026-09-08 08:24 UTC186
                    event.sender.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC187
                    sender_name(&event.sender, &event.sender_profile),
96fdf90aacd6atagen2026-09-08 08:24 UTC188
                )),
96fdf90aacd6atagen2026-09-08 08:24 UTC189
                _ => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC190
            };
96fdf90aacd6atagen2026-09-08 08:24 UTC191
            ReplyReference {
96fdf90aacd6atagen2026-09-08 08:24 UTC192
                event_id,
96fdf90aacd6atagen2026-09-08 08:24 UTC193
                sender_id: embedded_sender.as_ref().map(|(id, _)| id.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC194
                sender_name: embedded_sender.map(|(_, name)| name),
96fdf90aacd6atagen2026-09-08 08:24 UTC195
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC196
        }),
96fdf90aacd6atagen2026-09-08 08:24 UTC197
        _ => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC198
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC199
    let (thread_root, thread_summary) = match event.content() {
96fdf90aacd6atagen2026-09-08 08:24 UTC200
        TimelineItemContent::MsgLike(content) => (
96fdf90aacd6atagen2026-09-08 08:24 UTC201
            content.thread_root.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC202
            content.thread_summary.as_ref().map(|summary| {
96fdf90aacd6atagen2026-09-08 08:24 UTC203
                let latest_event = match &summary.latest_event {
96fdf90aacd6atagen2026-09-08 08:24 UTC204
                    TimelineDetails::Ready(event) => Some(event.as_ref()),
96fdf90aacd6atagen2026-09-08 08:24 UTC205
                    _ => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC206
                };
96fdf90aacd6atagen2026-09-08 08:24 UTC207
                ThreadSummary {
96fdf90aacd6atagen2026-09-08 08:24 UTC208
                    reply_count: summary.num_replies,
96fdf90aacd6atagen2026-09-08 08:24 UTC209
                    latest_event_id: latest_event.and_then(|event| match &event.identifier {
96fdf90aacd6atagen2026-09-08 08:24 UTC210
                        TimelineEventItemId::EventId(event_id) => Some(event_id.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC211
                        TimelineEventItemId::TransactionId(_) => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC212
                    }),
96fdf90aacd6atagen2026-09-08 08:24 UTC213
                    latest_event_timestamp_ms: latest_event
96fdf90aacd6atagen2026-09-08 08:24 UTC214
                        .map(|event| event.timestamp.get().into()),
96fdf90aacd6atagen2026-09-08 08:24 UTC215
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC216
            }),
96fdf90aacd6atagen2026-09-08 08:24 UTC217
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC218
        _ => (None, None),
96fdf90aacd6atagen2026-09-08 08:24 UTC219
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC220
    let reactions = event
96fdf90aacd6atagen2026-09-08 08:24 UTC221
        .content()
96fdf90aacd6atagen2026-09-08 08:24 UTC222
        .reactions()
96fdf90aacd6atagen2026-09-08 08:24 UTC223
        .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC224
        .flat_map(|reactions| reactions.iter())
96fdf90aacd6atagen2026-09-08 08:24 UTC225
        .map(|(key, senders)| Reaction {
96fdf90aacd6atagen2026-09-08 08:24 UTC226
            key: key.clone(),
96fdf90aacd6atagen2026-09-08 08:24 UTC227
            count: senders.len(),
96fdf90aacd6atagen2026-09-08 08:24 UTC228
            own: current_user_id.is_some_and(|user_id| senders.contains_key(user_id)),
96fdf90aacd6atagen2026-09-08 08:24 UTC229
            senders: senders.keys().cloned().collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC230
        })
96fdf90aacd6atagen2026-09-08 08:24 UTC231
        .collect();
96fdf90aacd6atagen2026-09-08 08:24 UTC232
    let mut read_by: Vec<_> = event
96fdf90aacd6atagen2026-09-08 08:24 UTC233
        .read_receipts()
96fdf90aacd6atagen2026-09-08 08:24 UTC234
        .keys()
96fdf90aacd6atagen2026-09-08 08:24 UTC235
        .filter(|user_id| *user_id != event.sender())
96fdf90aacd6atagen2026-09-08 08:24 UTC236
        .cloned()
96fdf90aacd6atagen2026-09-08 08:24 UTC237
        .collect();
96fdf90aacd6atagen2026-09-08 08:24 UTC238
    read_by.sort();
96fdf90aacd6atagen2026-09-08 08:24 UTC239
96fdf90aacd6atagen2026-09-08 08:24 UTC240
    TimelineMessage {
96fdf90aacd6atagen2026-09-08 08:24 UTC241
        item_id: TimelineItemId::new(item.unique_id().0.clone()),
96fdf90aacd6atagen2026-09-08 08:24 UTC242
        event_id: event.event_id().map(ToOwned::to_owned),
96fdf90aacd6atagen2026-09-08 08:24 UTC243
        transaction_id: event.transaction_id().map(ToOwned::to_owned),
96fdf90aacd6atagen2026-09-08 08:24 UTC244
        sender_id: event.sender().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC245
        sender_name: sender_name(event.sender(), event.sender_profile()),
96fdf90aacd6atagen2026-09-08 08:24 UTC246
        body,
96fdf90aacd6atagen2026-09-08 08:24 UTC247
        formatted_body,
96fdf90aacd6atagen2026-09-08 08:24 UTC248
        mentions,
96fdf90aacd6atagen2026-09-08 08:24 UTC249
        thread_root,
96fdf90aacd6atagen2026-09-08 08:24 UTC250
        thread_summary,
96fdf90aacd6atagen2026-09-08 08:24 UTC251
        timestamp_ms: event.timestamp().get().into(),
96fdf90aacd6atagen2026-09-08 08:24 UTC252
        own: event.is_own(),
96fdf90aacd6atagen2026-09-08 08:24 UTC253
        send_state: match event.send_state() {
96fdf90aacd6atagen2026-09-08 08:24 UTC254
            None | Some(EventSendState::Sent { .. }) => SendState::Sent,
96fdf90aacd6atagen2026-09-08 08:24 UTC255
            Some(EventSendState::NotSentYet { progress: Some(_) }) => SendState::Sending,
96fdf90aacd6atagen2026-09-08 08:24 UTC256
            Some(EventSendState::NotSentYet { progress: None }) => SendState::Queued,
96fdf90aacd6atagen2026-09-08 08:24 UTC257
            Some(EventSendState::SendingFailed { is_recoverable, .. }) => SendState::Failed {
96fdf90aacd6atagen2026-09-08 08:24 UTC258
                retriable: *is_recoverable,
96fdf90aacd6atagen2026-09-08 08:24 UTC259
            },
96fdf90aacd6atagen2026-09-08 08:24 UTC260
        },
96fdf90aacd6atagen2026-09-08 08:24 UTC261
        edited,
96fdf90aacd6atagen2026-09-08 08:24 UTC262
        unable_to_decrypt,
96fdf90aacd6atagen2026-09-08 08:24 UTC263
        media,
96fdf90aacd6atagen2026-09-08 08:24 UTC264
        reply_to,
96fdf90aacd6atagen2026-09-08 08:24 UTC265
        reactions,
96fdf90aacd6atagen2026-09-08 08:24 UTC266
        read_by,
96fdf90aacd6atagen2026-09-08 08:24 UTC267
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC268
}
96fdf90aacd6atagen2026-09-08 08:24 UTC269
96fdf90aacd6atagen2026-09-08 08:24 UTC270
fn sender_name(
96fdf90aacd6atagen2026-09-08 08:24 UTC271
    sender: &UserId,
96fdf90aacd6atagen2026-09-08 08:24 UTC272
    sender_profile: &TimelineDetails<matrix_sdk_ui::timeline::Profile>,
96fdf90aacd6atagen2026-09-08 08:24 UTC273
) -> String {
96fdf90aacd6atagen2026-09-08 08:24 UTC274
    match sender_profile {
96fdf90aacd6atagen2026-09-08 08:24 UTC275
        TimelineDetails::Ready(profile) => profile
96fdf90aacd6atagen2026-09-08 08:24 UTC276
            .display_name
96fdf90aacd6atagen2026-09-08 08:24 UTC277
            .clone()
96fdf90aacd6atagen2026-09-08 08:24 UTC278
            .unwrap_or_else(|| sender.to_string()),
96fdf90aacd6atagen2026-09-08 08:24 UTC279
        _ => sender.to_string(),
96fdf90aacd6atagen2026-09-08 08:24 UTC280
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC281
}
96fdf90aacd6atagen2026-09-08 08:24 UTC282
96fdf90aacd6atagen2026-09-08 08:24 UTC283
fn formatted_body(message: &MessageType) -> Option<String> {
96fdf90aacd6atagen2026-09-08 08:24 UTC284
    let formatted = match message {
96fdf90aacd6atagen2026-09-08 08:24 UTC285
        MessageType::Text(content) => content.formatted.as_ref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC286
        MessageType::Emote(content) => content.formatted.as_ref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC287
        MessageType::Notice(content) => content.formatted.as_ref(),
96fdf90aacd6atagen2026-09-08 08:24 UTC288
        MessageType::Audio(content) => content.formatted_caption(),
96fdf90aacd6atagen2026-09-08 08:24 UTC289
        MessageType::File(content) => content.formatted_caption(),
96fdf90aacd6atagen2026-09-08 08:24 UTC290
        MessageType::Image(content) => content.formatted_caption(),
96fdf90aacd6atagen2026-09-08 08:24 UTC291
        MessageType::Video(content) => content.formatted_caption(),
96fdf90aacd6atagen2026-09-08 08:24 UTC292
        _ => None,
96fdf90aacd6atagen2026-09-08 08:24 UTC293
    }?;
96fdf90aacd6atagen2026-09-08 08:24 UTC294
    (formatted.format == MessageFormat::Html).then(|| formatted.body.clone())
96fdf90aacd6atagen2026-09-08 08:24 UTC295
}
96fdf90aacd6atagen2026-09-08 08:24 UTC296
96fdf90aacd6atagen2026-09-08 08:24 UTC297
fn message_mentions(mentions: &ruma::events::Mentions) -> MessageMentions {
96fdf90aacd6atagen2026-09-08 08:24 UTC298
    MessageMentions {
96fdf90aacd6atagen2026-09-08 08:24 UTC299
        user_ids: mentions.user_ids.iter().cloned().collect(),
96fdf90aacd6atagen2026-09-08 08:24 UTC300
        room: mentions.room,
96fdf90aacd6atagen2026-09-08 08:24 UTC301
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC302
}