atagen

macltok

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

Blame: src/normalise/room.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
use futures_util::{StreamExt, stream};
31ce2453c82fatagen2026-09-24 13:07 UTC2
use matrix_sdk::{
31ce2453c82fatagen2026-09-24 13:07 UTC3
    Client, Room, RoomState,
31ce2453c82fatagen2026-09-24 13:07 UTC4
    deserialized_responses::SyncOrStrippedState,
31ce2453c82fatagen2026-09-24 13:07 UTC5
    latest_events::LatestEventValue,
3e5a785a7273atagen2026-10-28 07:57 UTC6
    notification_settings::{IsEncrypted, IsOneToOne, NotificationSettings},
31ce2453c82fatagen2026-09-24 13:07 UTC7
    ruma::events::{
fa5b4e09943aatagen2026-10-24 09:33 UTC8
        AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncTimelineEvent, StateEventType,
fa5b4e09943aatagen2026-10-24 09:33 UTC9
        SyncStateEvent,
5a52012c9b1datagen2026-09-24 23:45 UTC10
        room::message::{Relation, RoomMessageEventContent},
31ce2453c82fatagen2026-09-24 13:07 UTC11
        space::child::SpaceChildEventContent,
31ce2453c82fatagen2026-09-24 13:07 UTC12
    },
31ce2453c82fatagen2026-09-24 13:07 UTC13
};
31ce2453c82fatagen2026-09-24 13:07 UTC14
use matrix_sdk_ui::spaces::{SpaceRoom, SpaceService};
9bd34e25bb86atagen2026-10-24 07:23 UTC15
use ruma::{
9bd34e25bb86atagen2026-10-24 07:23 UTC16
    OwnedRoomId,
9bd34e25bb86atagen2026-10-24 07:23 UTC17
    serde::{JsonObject, Raw},
9bd34e25bb86atagen2026-10-24 07:23 UTC18
};
96fdf90aacd6atagen2026-09-08 08:24 UTC19
5a52012c9b1datagen2026-09-24 23:45 UTC20
use super::message_kind;
31ce2453c82fatagen2026-09-24 13:07 UTC21
use crate::{
5d2974de5826atagen2026-10-08 11:29 UTC22
    LatestEvent, MediaId, MessageKind, RoomHero, RoomMembership, RoomNotificationMode, RoomSummary,
9bd34e25bb86atagen2026-10-24 07:23 UTC23
    SendState, SpaceChild, space_order,
31ce2453c82fatagen2026-09-24 13:07 UTC24
};
96fdf90aacd6atagen2026-09-08 08:24 UTC25
31ce2453c82fatagen2026-09-24 13:07 UTC26
pub(crate) async fn room_summaries(client: &Client, spaces: &SpaceService) -> Vec<RoomSummary> {
3e5a785a7273atagen2026-10-28 07:57 UTC27
    let settings = client.notification_settings().await;
3e5a785a7273atagen2026-10-28 07:57 UTC28
    let settings = &settings;
96fdf90aacd6atagen2026-09-08 08:24 UTC29
    let mut summaries = stream::iter(client.rooms())
3e5a785a7273atagen2026-10-28 07:57 UTC30
        .map(|room| async move { room_summary(&room, spaces, settings).await })
96fdf90aacd6atagen2026-09-08 08:24 UTC31
        .buffer_unordered(16)
96fdf90aacd6atagen2026-09-08 08:24 UTC32
        .collect::<Vec<_>>()
96fdf90aacd6atagen2026-09-08 08:24 UTC33
        .await;
96fdf90aacd6atagen2026-09-08 08:24 UTC34
    summaries.sort_by(|left, right| {
96fdf90aacd6atagen2026-09-08 08:24 UTC35
        right
96fdf90aacd6atagen2026-09-08 08:24 UTC36
            .latest_event_timestamp_ms
96fdf90aacd6atagen2026-09-08 08:24 UTC37
            .cmp(&left.latest_event_timestamp_ms)
96fdf90aacd6atagen2026-09-08 08:24 UTC38
            .then_with(|| left.name.to_lowercase().cmp(&right.name.to_lowercase()))
96fdf90aacd6atagen2026-09-08 08:24 UTC39
            .then_with(|| left.room_id.cmp(&right.room_id))
96fdf90aacd6atagen2026-09-08 08:24 UTC40
    });
96fdf90aacd6atagen2026-09-08 08:24 UTC41
    summaries
96fdf90aacd6atagen2026-09-08 08:24 UTC42
}
96fdf90aacd6atagen2026-09-08 08:24 UTC43
3e5a785a7273atagen2026-10-28 07:57 UTC44
async fn room_summary(
3e5a785a7273atagen2026-10-28 07:57 UTC45
    room: &Room,
3e5a785a7273atagen2026-10-28 07:57 UTC46
    spaces: &SpaceService,
3e5a785a7273atagen2026-10-28 07:57 UTC47
    settings: &NotificationSettings,
3e5a785a7273atagen2026-10-28 07:57 UTC48
) -> RoomSummary {
96fdf90aacd6atagen2026-09-08 08:24 UTC49
    let name = room
96fdf90aacd6atagen2026-09-08 08:24 UTC50
        .display_name()
96fdf90aacd6atagen2026-09-08 08:24 UTC51
        .await
96fdf90aacd6atagen2026-09-08 08:24 UTC52
        .map(|name| name.to_string())
96fdf90aacd6atagen2026-09-08 08:24 UTC53
        .unwrap_or_else(|_| room.room_id().to_string());
3e5a785a7273atagen2026-10-28 07:57 UTC54
    let joined = room.state() == RoomState::Joined;
3e5a785a7273atagen2026-10-28 07:57 UTC55
    let custom_notification_mode = if joined {
3e5a785a7273atagen2026-10-28 07:57 UTC56
        settings
3e5a785a7273atagen2026-10-28 07:57 UTC57
            .get_user_defined_room_notification_mode(room.room_id())
3e5a785a7273atagen2026-10-28 07:57 UTC58
            .await
3e5a785a7273atagen2026-10-28 07:57 UTC59
    } else {
3e5a785a7273atagen2026-10-28 07:57 UTC60
        None
3e5a785a7273atagen2026-10-28 07:57 UTC61
    };
3e5a785a7273atagen2026-10-28 07:57 UTC62
    if let Some(mode) = custom_notification_mode {
3e5a785a7273atagen2026-10-28 07:57 UTC63
        room.update_cached_user_defined_notification_mode(mode);
3e5a785a7273atagen2026-10-28 07:57 UTC64
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC65
    let effective_notification_mode = match custom_notification_mode {
96fdf90aacd6atagen2026-09-08 08:24 UTC66
        Some(mode) => Some(mode),
3e5a785a7273atagen2026-10-28 07:57 UTC67
        None if joined => match room.latest_encryption_state().await {
3e5a785a7273atagen2026-10-28 07:57 UTC68
            Ok(state) => Some(
3e5a785a7273atagen2026-10-28 07:57 UTC69
                settings
3e5a785a7273atagen2026-10-28 07:57 UTC70
                    .get_default_room_notification_mode(
3e5a785a7273atagen2026-10-28 07:57 UTC71
                        IsEncrypted::from(state.is_encrypted()),
3e5a785a7273atagen2026-10-28 07:57 UTC72
                        IsOneToOne::from(room.active_members_count() == 2),
3e5a785a7273atagen2026-10-28 07:57 UTC73
                    )
3e5a785a7273atagen2026-10-28 07:57 UTC74
                    .await,
3e5a785a7273atagen2026-10-28 07:57 UTC75
            ),
3e5a785a7273atagen2026-10-28 07:57 UTC76
            Err(_) => None,
3e5a785a7273atagen2026-10-28 07:57 UTC77
        },
3e5a785a7273atagen2026-10-28 07:57 UTC78
        None => None,
3e5a785a7273atagen2026-10-28 07:57 UTC79
    };
3e5a785a7273atagen2026-10-28 07:57 UTC80
    let power_levels = if joined {
3e5a785a7273atagen2026-10-28 07:57 UTC81
        room.power_levels().await.ok()
3e5a785a7273atagen2026-10-28 07:57 UTC82
    } else {
3e5a785a7273atagen2026-10-28 07:57 UTC83
        None
96fdf90aacd6atagen2026-09-08 08:24 UTC84
    };
31ce2453c82fatagen2026-09-24 13:07 UTC85
    let inviter = match room.state() {
31ce2453c82fatagen2026-09-24 13:07 UTC86
        RoomState::Invited => room
31ce2453c82fatagen2026-09-24 13:07 UTC87
            .invite_details()
31ce2453c82fatagen2026-09-24 13:07 UTC88
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC89
            .ok()
31ce2453c82fatagen2026-09-24 13:07 UTC90
            .map(|invite| invite.inviter_id),
31ce2453c82fatagen2026-09-24 13:07 UTC91
        _ => None,
31ce2453c82fatagen2026-09-24 13:07 UTC92
    };
31ce2453c82fatagen2026-09-24 13:07 UTC93
    let children = if room.is_space() {
31ce2453c82fatagen2026-09-24 13:07 UTC94
        space_children(room).await
31ce2453c82fatagen2026-09-24 13:07 UTC95
    } else {
31ce2453c82fatagen2026-09-24 13:07 UTC96
        Vec::new()
31ce2453c82fatagen2026-09-24 13:07 UTC97
    };
21b9227729a4atagen2026-10-25 11:03 UTC98
    let thread_unreads = super::stored_thread_unreads(room).await;
96fdf90aacd6atagen2026-09-08 08:24 UTC99
    RoomSummary {
96fdf90aacd6atagen2026-09-08 08:24 UTC100
        room_id: room.room_id().to_owned(),
96fdf90aacd6atagen2026-09-08 08:24 UTC101
        name,
31ce2453c82fatagen2026-09-24 13:07 UTC102
        membership: membership(room.state()),
96fdf90aacd6atagen2026-09-08 08:24 UTC103
        unread_messages: room.num_unread_messages(),
e5c6427adb35atagen2026-10-25 10:23 UTC104
        unread_mentions: room.num_unread_mentions()
e5c6427adb35atagen2026-10-25 10:23 UTC105
            + thread_unreads
e5c6427adb35atagen2026-10-25 10:23 UTC106
                .values()
e5c6427adb35atagen2026-10-25 10:23 UTC107
                .map(|unread| unread.mentions)
e5c6427adb35atagen2026-10-25 10:23 UTC108
                .sum::<u64>(),
5a52012c9b1datagen2026-09-24 23:45 UTC109
        unread_notifications: room.num_unread_notifications(),
e5c6427adb35atagen2026-10-25 10:23 UTC110
        thread_unreads,
96fdf90aacd6atagen2026-09-08 08:24 UTC111
        encrypted: room.encryption_state().is_encrypted(),
96fdf90aacd6atagen2026-09-08 08:24 UTC112
        space: room.is_space(),
96fdf90aacd6atagen2026-09-08 08:24 UTC113
        direct: room.is_direct().await.unwrap_or(false),
96fdf90aacd6atagen2026-09-08 08:24 UTC114
        favourite: room.is_favourite(),
96fdf90aacd6atagen2026-09-08 08:24 UTC115
        low_priority: room.is_low_priority(),
5d2974de5826atagen2026-10-08 11:29 UTC116
        avatar: room.avatar_url().as_deref().map(MediaId::plain),
96fdf90aacd6atagen2026-09-08 08:24 UTC117
        latest_event_timestamp_ms: room
96fdf90aacd6atagen2026-09-08 08:24 UTC118
            .latest_event_timestamp()
96fdf90aacd6atagen2026-09-08 08:24 UTC119
            .map(|value| value.get().into()),
31ce2453c82fatagen2026-09-24 13:07 UTC120
        latest_event: latest_event(room),
96fdf90aacd6atagen2026-09-08 08:24 UTC121
        notification_mode: effective_notification_mode.map(notification_mode),
96fdf90aacd6atagen2026-09-08 08:24 UTC122
        notification_mode_custom: custom_notification_mode.is_some(),
31ce2453c82fatagen2026-09-24 13:07 UTC123
        topic: room.topic(),
31ce2453c82fatagen2026-09-24 13:07 UTC124
        canonical_alias: room.canonical_alias(),
31ce2453c82fatagen2026-09-24 13:07 UTC125
        joined_members: room.joined_members_count(),
31ce2453c82fatagen2026-09-24 13:07 UTC126
        invited_members: room.invited_members_count(),
31ce2453c82fatagen2026-09-24 13:07 UTC127
        heroes: room
31ce2453c82fatagen2026-09-24 13:07 UTC128
            .heroes()
31ce2453c82fatagen2026-09-24 13:07 UTC129
            .into_iter()
31ce2453c82fatagen2026-09-24 13:07 UTC130
            .map(|hero| RoomHero {
31ce2453c82fatagen2026-09-24 13:07 UTC131
                user_id: hero.user_id,
31ce2453c82fatagen2026-09-24 13:07 UTC132
                display_name: hero.display_name,
5d2974de5826atagen2026-10-08 11:29 UTC133
                avatar: hero.avatar_url.as_deref().map(MediaId::plain),
31ce2453c82fatagen2026-09-24 13:07 UTC134
            })
31ce2453c82fatagen2026-09-24 13:07 UTC135
            .collect(),
31ce2453c82fatagen2026-09-24 13:07 UTC136
        successor: room.successor_room().map(|successor| successor.room_id),
31ce2453c82fatagen2026-09-24 13:07 UTC137
        inviter,
31ce2453c82fatagen2026-09-24 13:07 UTC138
        parent_spaces: spaces
31ce2453c82fatagen2026-09-24 13:07 UTC139
            .joined_parents_of_child(room.room_id())
31ce2453c82fatagen2026-09-24 13:07 UTC140
            .await
31ce2453c82fatagen2026-09-24 13:07 UTC141
            .into_iter()
31ce2453c82fatagen2026-09-24 13:07 UTC142
            .map(|parent| parent.room_id)
31ce2453c82fatagen2026-09-24 13:07 UTC143
            .collect(),
31ce2453c82fatagen2026-09-24 13:07 UTC144
        children,
9bd34e25bb86atagen2026-10-24 07:23 UTC145
        space_order: if room.is_space() {
9bd34e25bb86atagen2026-10-24 07:23 UTC146
            stored_space_order(room).await
9bd34e25bb86atagen2026-10-24 07:23 UTC147
        } else {
9bd34e25bb86atagen2026-10-24 07:23 UTC148
            None
9bd34e25bb86atagen2026-10-24 07:23 UTC149
        },
3e5a785a7273atagen2026-10-28 07:57 UTC150
        can_edit_packs: power_levels
3e5a785a7273atagen2026-10-28 07:57 UTC151
            .as_ref()
3e5a785a7273atagen2026-10-28 07:57 UTC152
            .is_some_and(|levels| super::can_edit_packs_with(room, levels)),
fa5b4e09943aatagen2026-10-24 09:33 UTC153
        pinned_events: room.pinned_event_ids().unwrap_or_default(),
3e5a785a7273atagen2026-10-28 07:57 UTC154
        can_pin: power_levels.as_ref().is_some_and(|levels| {
3e5a785a7273atagen2026-10-28 07:57 UTC155
            levels.user_can_send_state(room.own_user_id(), StateEventType::RoomPinnedEvents)
3e5a785a7273atagen2026-10-28 07:57 UTC156
        }),
31ce2453c82fatagen2026-09-24 13:07 UTC157
    }
31ce2453c82fatagen2026-09-24 13:07 UTC158
}
31ce2453c82fatagen2026-09-24 13:07 UTC159
9bd34e25bb86atagen2026-10-24 07:23 UTC160
/// None when unset or when another client wrote something msc3230 doesn't allow
9bd34e25bb86atagen2026-10-24 07:23 UTC161
pub(crate) async fn stored_space_order(room: &Room) -> Option<String> {
9bd34e25bb86atagen2026-10-24 07:23 UTC162
    room.account_data(space_order::EVENT_TYPE.into())
9bd34e25bb86atagen2026-10-24 07:23 UTC163
        .await
9bd34e25bb86atagen2026-10-24 07:23 UTC164
        .ok()??
9bd34e25bb86atagen2026-10-24 07:23 UTC165
        .get_field::<Raw<JsonObject>>("content")
9bd34e25bb86atagen2026-10-24 07:23 UTC166
        .ok()??
9bd34e25bb86atagen2026-10-24 07:23 UTC167
        .get_field::<String>("order")
9bd34e25bb86atagen2026-10-24 07:23 UTC168
        .ok()?
9bd34e25bb86atagen2026-10-24 07:23 UTC169
        .filter(|order| space_order::valid(order))
9bd34e25bb86atagen2026-10-24 07:23 UTC170
}
9bd34e25bb86atagen2026-10-24 07:23 UTC171
31ce2453c82fatagen2026-09-24 13:07 UTC172
const fn membership(state: RoomState) -> RoomMembership {
31ce2453c82fatagen2026-09-24 13:07 UTC173
    match state {
31ce2453c82fatagen2026-09-24 13:07 UTC174
        RoomState::Joined => RoomMembership::Joined,
31ce2453c82fatagen2026-09-24 13:07 UTC175
        RoomState::Invited => RoomMembership::Invited,
31ce2453c82fatagen2026-09-24 13:07 UTC176
        RoomState::Left => RoomMembership::Left,
31ce2453c82fatagen2026-09-24 13:07 UTC177
        RoomState::Knocked => RoomMembership::Knocked,
31ce2453c82fatagen2026-09-24 13:07 UTC178
        RoomState::Banned => RoomMembership::Banned,
31ce2453c82fatagen2026-09-24 13:07 UTC179
    }
31ce2453c82fatagen2026-09-24 13:07 UTC180
}
31ce2453c82fatagen2026-09-24 13:07 UTC181
31ce2453c82fatagen2026-09-24 13:07 UTC182
/// what the space itself lists, straight from local state. redacted children don't count
31ce2453c82fatagen2026-09-24 13:07 UTC183
async fn space_children(room: &Room) -> Vec<OwnedRoomId> {
31ce2453c82fatagen2026-09-24 13:07 UTC184
    let Ok(events) = room
31ce2453c82fatagen2026-09-24 13:07 UTC185
        .get_state_events_static::<SpaceChildEventContent>()
31ce2453c82fatagen2026-09-24 13:07 UTC186
        .await
31ce2453c82fatagen2026-09-24 13:07 UTC187
    else {
31ce2453c82fatagen2026-09-24 13:07 UTC188
        return Vec::new();
31ce2453c82fatagen2026-09-24 13:07 UTC189
    };
31ce2453c82fatagen2026-09-24 13:07 UTC190
    events
31ce2453c82fatagen2026-09-24 13:07 UTC191
        .into_iter()
31ce2453c82fatagen2026-09-24 13:07 UTC192
        .filter_map(|event| match event.deserialize().ok()? {
31ce2453c82fatagen2026-09-24 13:07 UTC193
            SyncOrStrippedState::Sync(SyncStateEvent::Original(event)) => Some(event.state_key),
31ce2453c82fatagen2026-09-24 13:07 UTC194
            SyncOrStrippedState::Stripped(event) => Some(event.state_key),
31ce2453c82fatagen2026-09-24 13:07 UTC195
            SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_)) => None,
31ce2453c82fatagen2026-09-24 13:07 UTC196
        })
31ce2453c82fatagen2026-09-24 13:07 UTC197
        .collect()
31ce2453c82fatagen2026-09-24 13:07 UTC198
}
31ce2453c82fatagen2026-09-24 13:07 UTC199
31ce2453c82fatagen2026-09-24 13:07 UTC200
fn latest_event(room: &Room) -> Option<LatestEvent> {
5a52012c9b1datagen2026-09-24 23:45 UTC201
    let own = || Some(room.own_user_id().to_owned());
5a52012c9b1datagen2026-09-24 23:45 UTC202
    let local = |local: &matrix_sdk::latest_events::LocalLatestEventValue| {
5a52012c9b1datagen2026-09-24 23:45 UTC203
        let preview = match local.content.deserialize() {
5a52012c9b1datagen2026-09-24 23:45 UTC204
            Ok(AnyMessageLikeEventContent::RoomMessage(content)) => preview(&content),
5a52012c9b1datagen2026-09-24 23:45 UTC205
            _ => (None, None),
5a52012c9b1datagen2026-09-24 23:45 UTC206
        };
5a52012c9b1datagen2026-09-24 23:45 UTC207
        (preview, Some(local.timestamp))
5a52012c9b1datagen2026-09-24 23:45 UTC208
    };
5a52012c9b1datagen2026-09-24 23:45 UTC209
    let (sender, (kind, body), timestamp, send_state) = match room.latest_event() {
31ce2453c82fatagen2026-09-24 13:07 UTC210
        LatestEventValue::None => return None,
31ce2453c82fatagen2026-09-24 13:07 UTC211
        LatestEventValue::Remote(event) => {
5a52012c9b1datagen2026-09-24 23:45 UTC212
            let preview = match event.raw().deserialize() {
31ce2453c82fatagen2026-09-24 13:07 UTC213
                Ok(AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(
31ce2453c82fatagen2026-09-24 13:07 UTC214
                    message,
31ce2453c82fatagen2026-09-24 13:07 UTC215
                ))) => message
31ce2453c82fatagen2026-09-24 13:07 UTC216
                    .as_original()
5a52012c9b1datagen2026-09-24 23:45 UTC217
                    .map_or((None, None), |message| preview(&message.content)),
5a52012c9b1datagen2026-09-24 23:45 UTC218
                _ => (None, None),
31ce2453c82fatagen2026-09-24 13:07 UTC219
            };
5a52012c9b1datagen2026-09-24 23:45 UTC220
            (event.sender(), preview, event.timestamp(), SendState::Sent)
31ce2453c82fatagen2026-09-24 13:07 UTC221
        }
31ce2453c82fatagen2026-09-24 13:07 UTC222
        LatestEventValue::RemoteInvite {
31ce2453c82fatagen2026-09-24 13:07 UTC223
            timestamp, inviter, ..
5a52012c9b1datagen2026-09-24 23:45 UTC224
        } => (inviter, (None, None), Some(timestamp), SendState::Sent),
5a52012c9b1datagen2026-09-24 23:45 UTC225
        LatestEventValue::LocalIsSending(value) => {
5a52012c9b1datagen2026-09-24 23:45 UTC226
            let (preview, timestamp) = local(&value);
5a52012c9b1datagen2026-09-24 23:45 UTC227
            (own(), preview, timestamp, SendState::Sending)
5a52012c9b1datagen2026-09-24 23:45 UTC228
        }
31ce2453c82fatagen2026-09-24 13:07 UTC229
        LatestEventValue::LocalHasBeenSent { value, .. } => {
5a52012c9b1datagen2026-09-24 23:45 UTC230
            let (preview, timestamp) = local(&value);
5a52012c9b1datagen2026-09-24 23:45 UTC231
            (own(), preview, timestamp, SendState::Sent)
31ce2453c82fatagen2026-09-24 13:07 UTC232
        }
5a52012c9b1datagen2026-09-24 23:45 UTC233
        // the sdk won't say whether it'll retry, so assume the user has to
5a52012c9b1datagen2026-09-24 23:45 UTC234
        LatestEventValue::LocalCannotBeSent(value) => {
5a52012c9b1datagen2026-09-24 23:45 UTC235
            let (preview, timestamp) = local(&value);
5a52012c9b1datagen2026-09-24 23:45 UTC236
            (
5a52012c9b1datagen2026-09-24 23:45 UTC237
                own(),
5a52012c9b1datagen2026-09-24 23:45 UTC238
                preview,
5a52012c9b1datagen2026-09-24 23:45 UTC239
                timestamp,
5a52012c9b1datagen2026-09-24 23:45 UTC240
                SendState::Failed { will_retry: false },
5a52012c9b1datagen2026-09-24 23:45 UTC241
            )
31ce2453c82fatagen2026-09-24 13:07 UTC242
        }
31ce2453c82fatagen2026-09-24 13:07 UTC243
    };
31ce2453c82fatagen2026-09-24 13:07 UTC244
    Some(LatestEvent {
31ce2453c82fatagen2026-09-24 13:07 UTC245
        sender,
5a52012c9b1datagen2026-09-24 23:45 UTC246
        kind,
31ce2453c82fatagen2026-09-24 13:07 UTC247
        body,
31ce2453c82fatagen2026-09-24 13:07 UTC248
        timestamp_ms: timestamp.map(|timestamp| timestamp.get().into()),
31ce2453c82fatagen2026-09-24 13:07 UTC249
        send_state,
31ce2453c82fatagen2026-09-24 13:07 UTC250
    })
31ce2453c82fatagen2026-09-24 13:07 UTC251
}
31ce2453c82fatagen2026-09-24 13:07 UTC252
5a52012c9b1datagen2026-09-24 23:45 UTC253
/// the latest event of an edited message is the edit, whose own body is the `* fallback`
5a52012c9b1datagen2026-09-24 23:45 UTC254
fn preview(content: &RoomMessageEventContent) -> (Option<MessageKind>, Option<String>) {
5a52012c9b1datagen2026-09-24 23:45 UTC255
    let msgtype = match &content.relates_to {
5a52012c9b1datagen2026-09-24 23:45 UTC256
        Some(Relation::Replacement(replacement)) => &replacement.new_content.msgtype,
5a52012c9b1datagen2026-09-24 23:45 UTC257
        _ => &content.msgtype,
31ce2453c82fatagen2026-09-24 13:07 UTC258
    };
5a52012c9b1datagen2026-09-24 23:45 UTC259
    (Some(message_kind(msgtype)), Some(msgtype.body().to_owned()))
31ce2453c82fatagen2026-09-24 13:07 UTC260
}
31ce2453c82fatagen2026-09-24 13:07 UTC261
31ce2453c82fatagen2026-09-24 13:07 UTC262
pub(crate) fn space_child(room: SpaceRoom) -> SpaceChild {
31ce2453c82fatagen2026-09-24 13:07 UTC263
    SpaceChild {
31ce2453c82fatagen2026-09-24 13:07 UTC264
        name: room.display_name,
31ce2453c82fatagen2026-09-24 13:07 UTC265
        room_id: room.room_id,
31ce2453c82fatagen2026-09-24 13:07 UTC266
        topic: room.topic,
5d2974de5826atagen2026-10-08 11:29 UTC267
        avatar: room.avatar_url.as_deref().map(MediaId::plain),
31ce2453c82fatagen2026-09-24 13:07 UTC268
        canonical_alias: room.canonical_alias,
31ce2453c82fatagen2026-09-24 13:07 UTC269
        joined_members: room.num_joined_members,
31ce2453c82fatagen2026-09-24 13:07 UTC270
        space: room.room_type == Some(ruma::room::RoomType::Space),
31ce2453c82fatagen2026-09-24 13:07 UTC271
        children_count: room.children_count,
31ce2453c82fatagen2026-09-24 13:07 UTC272
        membership: room.state.map(membership),
31ce2453c82fatagen2026-09-24 13:07 UTC273
        via: room.via,
31ce2453c82fatagen2026-09-24 13:07 UTC274
        suggested: room.suggested,
31ce2453c82fatagen2026-09-24 13:07 UTC275
        world_readable: room.world_readable,
31ce2453c82fatagen2026-09-24 13:07 UTC276
        guest_can_join: room.guest_can_join,
96fdf90aacd6atagen2026-09-08 08:24 UTC277
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC278
}
96fdf90aacd6atagen2026-09-08 08:24 UTC279
96fdf90aacd6atagen2026-09-08 08:24 UTC280
pub(crate) const fn notification_mode(
96fdf90aacd6atagen2026-09-08 08:24 UTC281
    mode: matrix_sdk::notification_settings::RoomNotificationMode,
96fdf90aacd6atagen2026-09-08 08:24 UTC282
) -> RoomNotificationMode {
96fdf90aacd6atagen2026-09-08 08:24 UTC283
    match mode {
96fdf90aacd6atagen2026-09-08 08:24 UTC284
        matrix_sdk::notification_settings::RoomNotificationMode::AllMessages => {
96fdf90aacd6atagen2026-09-08 08:24 UTC285
            RoomNotificationMode::AllMessages
96fdf90aacd6atagen2026-09-08 08:24 UTC286
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC287
        matrix_sdk::notification_settings::RoomNotificationMode::MentionsAndKeywordsOnly => {
96fdf90aacd6atagen2026-09-08 08:24 UTC288
            RoomNotificationMode::MentionsAndKeywordsOnly
96fdf90aacd6atagen2026-09-08 08:24 UTC289
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC290
        matrix_sdk::notification_settings::RoomNotificationMode::Mute => RoomNotificationMode::Mute,
96fdf90aacd6atagen2026-09-08 08:24 UTC291
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC292
}