atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

Blame: bindings/matrix-sdk-ffi/src/notification.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2025 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// See the License for that specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
use std::{collections::HashMap, sync::Arc};
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
use matrix_sdk_ui::notification_client::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
    NotificationClient as SdkNotificationClient, NotificationEvent as SdkNotificationEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
    NotificationItem as SdkNotificationItem, NotificationStatus as SdkNotificationStatus,
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
    RawNotificationEvent as SdkRawNotificationEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId};
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
    client::{Client, JoinRule},
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
    error::ClientError,
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
    event::TimelineEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
    room::Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
pub enum NotificationEvent {
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
    Timeline { event: Arc<TimelineEvent> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
    Invite { sender: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
pub struct NotificationSenderInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
    pub display_name: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
    pub avatar_url: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
    pub is_name_ambiguous: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
pub struct NotificationRoomInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
    pub display_name: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
    pub avatar_url: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
    pub canonical_alias: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
    pub topic: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
    pub join_rule: Option<JoinRule>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
    pub joined_members_count: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
    pub active_service_members_count: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
    pub service_members: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
    pub is_encrypted: Option<bool>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
    pub is_direct: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
    pub is_space: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
    pub is_dm: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
pub struct NotificationItem {
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
    pub event: NotificationEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
    /// The raw JSON of the underlying event.
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
    pub raw_event: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
    pub sender_info: NotificationSenderInfo,
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
    pub room_info: NotificationRoomInfo,
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
    /// Is the notification supposed to be at the "noisy" level?
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
    /// Can be `None` if we couldn't determine this, because we lacked
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
    /// information to create a push context.
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
    pub is_noisy: Option<bool>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
    pub has_mention: Option<bool>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
    pub thread_id: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
    /// The push actions for this notification (notify, sound, highlight, etc.).
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
    pub actions: Option<Vec<crate::notification_settings::Action>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
impl NotificationItem {
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
    fn from_inner(item: SdkNotificationItem) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
        let event = match item.event {
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
            SdkNotificationEvent::Timeline(event) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
                NotificationEvent::Timeline { event: Arc::new(TimelineEvent(event)) }
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
            SdkNotificationEvent::Invite(event) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
                NotificationEvent::Invite { sender: event.sender.to_string() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
        let raw_event = match &item.raw_event {
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
            SdkRawNotificationEvent::Timeline(raw) => raw.json().get().to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
            SdkRawNotificationEvent::Invite(raw) => raw.json().get().to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
            event,
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
            raw_event,
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
            sender_info: NotificationSenderInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
                display_name: item.sender_display_name,
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
                avatar_url: item.sender_avatar_url,
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
                is_name_ambiguous: item.is_sender_name_ambiguous,
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
            room_info: NotificationRoomInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
                display_name: item.room_computed_display_name,
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
                avatar_url: item.room_avatar_url,
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
                canonical_alias: item.room_canonical_alias,
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
                topic: item.room_topic,
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
                join_rule: item.room_join_rule.map(TryInto::try_into).transpose().ok().flatten(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
                joined_members_count: item.joined_members_count,
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
                service_members: item.service_members,
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
                active_service_members_count: item.active_service_members_count,
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
                is_encrypted: item.is_room_encrypted,
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
                is_direct: item.is_direct_message_room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
                is_space: item.is_space,
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
                is_dm: item.room_is_dm,
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
            is_noisy: item.is_noisy,
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
            has_mention: item.has_mention,
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
            thread_id: item.thread_id.map(|t| t.to_string()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
            actions: item
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
                .actions
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
                .map(|a| a.into_iter().filter_map(|action| action.try_into().ok()).collect()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
#[allow(clippy::large_enum_variant)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
pub enum NotificationStatus {
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
    /// The event has been found and was not filtered out.
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
    Event { item: NotificationItem },
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
    /// The event couldn't be found in the network queries used to find it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
    EventNotFound,
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
    /// The event has been filtered out, either because of the user's push
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
    /// rules, or because the user which triggered it is ignored by the
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
    /// current user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
    EventFilteredOut,
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
    /// The event has been redacted.
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
    EventRedacted,
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
impl From<SdkNotificationStatus> for NotificationStatus {
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
    fn from(item: SdkNotificationStatus) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
        match item {
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
            SdkNotificationStatus::Event(item) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
                NotificationStatus::Event { item: NotificationItem::from_inner(*item) }
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
            SdkNotificationStatus::EventNotFound => NotificationStatus::EventNotFound,
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
            SdkNotificationStatus::EventFilteredOut => NotificationStatus::EventFilteredOut,
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
            SdkNotificationStatus::EventRedacted => NotificationStatus::EventRedacted,
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
#[allow(clippy::large_enum_variant)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
pub enum BatchNotificationResult {
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
    /// We have more detailed information about the notification.
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
    Ok { status: NotificationStatus },
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
    /// An error occurred while trying to fetch the notification.
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
    Error {
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
        /// The error message observed while handling a specific notification.
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
        message: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
pub struct NotificationClient {
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
    pub(crate) inner: SdkNotificationClient,
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    /// A reference to the FFI client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
    /// Note: we do this to make it so that the FFI `NotificationClient` keeps
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
    /// the FFI `Client` and thus the SDK `Client` alive. Otherwise, we
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
    /// would need to repeat the hack done in the FFI `Client::drop` method.
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
    pub(crate) client: Arc<Client>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
impl NotificationClient {
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
    /// Fetches a room by its ID using the in-memory state store backed client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
    /// Useful to retrieve room information after running the limited
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
    /// notification client sliding sync loop.
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
    pub fn get_room(&self, room_id: String) -> Result<Option<Arc<Room>>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
        let room_id = RoomId::parse(room_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
        let sdk_room = self.inner.get_room(&room_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
        let room = sdk_room
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
            .map(|room| Arc::new(Room::new(room, self.client.utd_hook_manager.get().cloned())));
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
        Ok(room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
    /// Fetches the content of a notification.
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
    /// This will first try to get the notification using a short-lived sliding
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
    /// sync, and if the sliding-sync can't find the event, then it'll use a
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
    /// `/context` query to find the event with associated member information.
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
    /// An error result means that we couldn't resolve the notification; in that
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
    /// case, a dummy notification may be displayed instead.
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
    pub async fn get_notification(
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
        room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
        event_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
    ) -> Result<NotificationStatus, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
        let room_id = RoomId::parse(room_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
        let event_id = EventId::parse(event_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
        let item =
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
            self.inner.get_notification(&room_id, &event_id).await.map_err(ClientError::from)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
        Ok(item.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
    /// Get several notification items in a single batch.
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
    /// Returns an error if the flow failed when preparing to fetch the
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
    /// notifications, and a [`HashMap`] containing either a
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
    /// [`BatchNotificationResult`], that indicates if the notification was
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
    /// successfully fetched (in which case, it's a [`NotificationStatus`]), or
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
    /// an error message if it couldn't be fetched.
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
    pub async fn get_notifications(
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
        requests: Vec<NotificationItemsRequest>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
    ) -> Result<HashMap<String, BatchNotificationResult>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
        let requests =
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
            requests.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
        let items = self.inner.get_notifications(&requests).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
        let mut batch_result = HashMap::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
        for (key, value) in items.into_iter() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
            let result = match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
                Ok(status) => BatchNotificationResult::Ok { status: status.into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
                Err(error) => BatchNotificationResult::Error { message: error.to_string() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
            batch_result.insert(key.to_string(), result);
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
        Ok(batch_result)
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
/// A request for notification items grouped by their room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
pub struct NotificationItemsRequest {
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
    room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
    event_ids: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
impl NotificationItemsRequest {
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
    /// The parsed [`OwnedRoomId`] to use with the SDK crates.
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
    pub fn room_id(&self) -> Result<OwnedRoomId, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
        RoomId::parse(&self.room_id).map_err(ClientError::from)
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
    /// The parsed [`OwnedEventId`] list to use with the SDK crates.
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
    pub fn event_ids(&self) -> Result<Vec<OwnedEventId>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
        self.event_ids
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
            .map(|id| EventId::parse(id).map_err(ClientError::from))
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
            .collect::<Result<Vec<_>, _>>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
impl TryFrom<NotificationItemsRequest>
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
    for matrix_sdk_ui::notification_client::NotificationItemsRequest
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
{
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
    type Error = ClientError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
    fn try_from(value: NotificationItemsRequest) -> Result<Self, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
        Ok(Self { room_id: value.room_id()?, event_ids: value.event_ids()? })
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
}