1use std::{collections::HashMap, sync::Arc};
2
3use matrix_sdk_ui::notification_client::{
4 NotificationClient as SdkNotificationClient, NotificationEvent as SdkNotificationEvent,
5 NotificationItem as SdkNotificationItem, NotificationStatus as SdkNotificationStatus,
6};
7use ruma::{EventId, OwnedEventId, OwnedRoomId, RoomId};
8
9use crate::{
10 client::{Client, JoinRule},
11 error::ClientError,
12 event::TimelineEvent,
13 room::Room,
14};
15
16#[derive(uniffi::Enum)]
17pub enum NotificationEvent {
18 Timeline { event: Arc<TimelineEvent> },
19 Invite { sender: String },
20}
21
22#[derive(uniffi::Record)]
23pub struct NotificationSenderInfo {
24 pub display_name: Option<String>,
25 pub avatar_url: Option<String>,
26 pub is_name_ambiguous: bool,
27}
28
29#[derive(uniffi::Record)]
30pub struct NotificationRoomInfo {
31 pub display_name: String,
32 pub avatar_url: Option<String>,
33 pub canonical_alias: Option<String>,
34 pub topic: Option<String>,
35 pub join_rule: Option<JoinRule>,
36 pub joined_members_count: u64,
37 pub is_encrypted: Option<bool>,
38 pub is_direct: bool,
39 pub is_space: bool,
40}
41
42#[derive(uniffi::Record)]
43pub struct NotificationItem {
44 pub event: NotificationEvent,
45
46 pub sender_info: NotificationSenderInfo,
47 pub room_info: NotificationRoomInfo,
48
49 pub is_noisy: Option<bool>,
53 pub has_mention: Option<bool>,
54 pub thread_id: Option<String>,
55
56 pub actions: Option<Vec<crate::notification_settings::Action>>,
58}
59
60impl NotificationItem {
61 fn from_inner(item: SdkNotificationItem) -> Self {
62 let event = match item.event {
63 SdkNotificationEvent::Timeline(event) => {
64 NotificationEvent::Timeline { event: Arc::new(TimelineEvent(event)) }
65 }
66 SdkNotificationEvent::Invite(event) => {
67 NotificationEvent::Invite { sender: event.sender.to_string() }
68 }
69 };
70 Self {
71 event,
72 sender_info: NotificationSenderInfo {
73 display_name: item.sender_display_name,
74 avatar_url: item.sender_avatar_url,
75 is_name_ambiguous: item.is_sender_name_ambiguous,
76 },
77 room_info: NotificationRoomInfo {
78 display_name: item.room_computed_display_name,
79 avatar_url: item.room_avatar_url,
80 canonical_alias: item.room_canonical_alias,
81 topic: item.room_topic,
82 join_rule: item.room_join_rule.map(TryInto::try_into).transpose().ok().flatten(),
83 joined_members_count: item.joined_members_count,
84 is_encrypted: item.is_room_encrypted,
85 is_direct: item.is_direct_message_room,
86 is_space: item.is_space,
87 },
88 is_noisy: item.is_noisy,
89 has_mention: item.has_mention,
90 thread_id: item.thread_id.map(|t| t.to_string()),
91 actions: item
92 .actions
93 .map(|a| a.into_iter().filter_map(|action| action.try_into().ok()).collect()),
94 }
95 }
96}
97
98#[allow(clippy::large_enum_variant)]
99#[derive(uniffi::Enum)]
100pub enum NotificationStatus {
101 Event { item: NotificationItem },
103 EventNotFound,
105 EventFilteredOut,
109}
110
111impl From<SdkNotificationStatus> for NotificationStatus {
112 fn from(item: SdkNotificationStatus) -> Self {
113 match item {
114 SdkNotificationStatus::Event(item) => {
115 NotificationStatus::Event { item: NotificationItem::from_inner(*item) }
116 }
117 SdkNotificationStatus::EventNotFound => NotificationStatus::EventNotFound,
118 SdkNotificationStatus::EventFilteredOut => NotificationStatus::EventFilteredOut,
119 }
120 }
121}
122
123#[allow(clippy::large_enum_variant)]
124#[derive(uniffi::Enum)]
125pub enum BatchNotificationResult {
126 Ok { status: NotificationStatus },
128 Error {
130 message: String,
132 },
133}
134
135#[derive(uniffi::Object)]
136pub struct NotificationClient {
137 pub(crate) inner: SdkNotificationClient,
138
139 pub(crate) client: Arc<Client>,
145}
146
147#[matrix_sdk_ffi_macros::export]
148impl NotificationClient {
149 pub fn get_room(&self, room_id: String) -> Result<Option<Arc<Room>>, ClientError> {
154 let room_id = RoomId::parse(room_id)?;
155 let sdk_room = self.inner.get_room(&room_id);
156 let room = sdk_room
157 .map(|room| Arc::new(Room::new(room, self.client.utd_hook_manager.get().cloned())));
158 Ok(room)
159 }
160
161 pub async fn get_notification(
170 &self,
171 room_id: String,
172 event_id: String,
173 ) -> Result<NotificationStatus, ClientError> {
174 let room_id = RoomId::parse(room_id)?;
175 let event_id = EventId::parse(event_id)?;
176
177 let item =
178 self.inner.get_notification(&room_id, &event_id).await.map_err(ClientError::from)?;
179
180 Ok(item.into())
181 }
182
183 pub async fn get_notifications(
191 &self,
192 requests: Vec<NotificationItemsRequest>,
193 ) -> Result<HashMap<String, BatchNotificationResult>, ClientError> {
194 let requests =
195 requests.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?;
196
197 let items = self.inner.get_notifications(&requests).await?;
198
199 let mut batch_result = HashMap::new();
200 for (key, value) in items.into_iter() {
201 let result = match value {
202 Ok(status) => BatchNotificationResult::Ok { status: status.into() },
203 Err(error) => BatchNotificationResult::Error { message: error.to_string() },
204 };
205 batch_result.insert(key.to_string(), result);
206 }
207
208 Ok(batch_result)
209 }
210}
211
212#[derive(uniffi::Record)]
214pub struct NotificationItemsRequest {
215 room_id: String,
216 event_ids: Vec<String>,
217}
218
219impl NotificationItemsRequest {
220 pub fn room_id(&self) -> Result<OwnedRoomId, ClientError> {
222 RoomId::parse(&self.room_id).map_err(ClientError::from)
223 }
224
225 pub fn event_ids(&self) -> Result<Vec<OwnedEventId>, ClientError> {
227 self.event_ids
228 .iter()
229 .map(|id| EventId::parse(id).map_err(ClientError::from))
230 .collect::<Result<Vec<_>, _>>()
231 }
232}
233
234impl TryFrom<NotificationItemsRequest>
235 for matrix_sdk_ui::notification_client::NotificationItemsRequest
236{
237 type Error = ClientError;
238 fn try_from(value: NotificationItemsRequest) -> Result<Self, Self::Error> {
239 Ok(Self { room_id: value.room_id()?, event_ids: value.event_ids()? })
240 }
241}