1use fission_core::{
2 CancelNotificationRequest, NotificationError, NotificationPermission,
3 NotificationPermissionRequest, NotificationReceipt, NotificationRequest, NotificationSchedule,
4 NotificationSettings, PushPlatform, PushRegistration, PushRegistrationRequest,
5 SetBadgeCountRequest, CANCEL_ALL_NOTIFICATIONS, CANCEL_NOTIFICATION, GET_NOTIFICATION_SETTINGS,
6 REGISTER_PUSH_NOTIFICATIONS, REQUEST_NOTIFICATION_PERMISSION, SCHEDULE_NOTIFICATION,
7 SET_BADGE_COUNT, SHOW_NOTIFICATION, UNREGISTER_PUSH_NOTIFICATIONS,
8};
9use fission_shell::async_host::AsyncRegistry;
10use std::sync::Arc;
11
12pub trait NotificationHost: Send + Sync + 'static {
14 fn request_permission(
19 &self,
20 request: NotificationPermissionRequest,
21 ) -> Result<NotificationSettings, NotificationError>;
22
23 fn settings(&self) -> Result<NotificationSettings, NotificationError>;
28
29 fn show(&self, request: NotificationRequest) -> Result<NotificationReceipt, NotificationError>;
35
36 fn schedule(
42 &self,
43 request: NotificationRequest,
44 ) -> Result<NotificationReceipt, NotificationError>;
45
46 fn cancel(&self, request: CancelNotificationRequest) -> Result<(), NotificationError>;
51
52 fn cancel_all(&self) -> Result<(), NotificationError>;
54
55 fn set_badge_count(&self, request: SetBadgeCountRequest) -> Result<(), NotificationError>;
60
61 fn register_push(
66 &self,
67 request: PushRegistrationRequest,
68 ) -> Result<PushRegistration, NotificationError>;
69
70 fn unregister_push(&self) -> Result<(), NotificationError>;
72}
73
74#[derive(Debug, Default)]
76pub struct UnsupportedNotificationHost;
77
78impl NotificationHost for UnsupportedNotificationHost {
79 fn request_permission(
80 &self,
81 _request: NotificationPermissionRequest,
82 ) -> Result<NotificationSettings, NotificationError> {
83 Ok(NotificationSettings {
84 permission: NotificationPermission::Unsupported,
85 ..Default::default()
86 })
87 }
88
89 fn settings(&self) -> Result<NotificationSettings, NotificationError> {
90 Ok(NotificationSettings {
91 permission: NotificationPermission::Unsupported,
92 ..Default::default()
93 })
94 }
95
96 fn show(
97 &self,
98 _request: NotificationRequest,
99 ) -> Result<NotificationReceipt, NotificationError> {
100 Err(NotificationError::unsupported("show"))
101 }
102
103 fn schedule(
104 &self,
105 _request: NotificationRequest,
106 ) -> Result<NotificationReceipt, NotificationError> {
107 Err(NotificationError::unsupported("schedule"))
108 }
109
110 fn cancel(&self, _request: CancelNotificationRequest) -> Result<(), NotificationError> {
111 Err(NotificationError::unsupported("cancel"))
112 }
113
114 fn cancel_all(&self) -> Result<(), NotificationError> {
115 Err(NotificationError::unsupported("cancel_all"))
116 }
117
118 fn set_badge_count(&self, _request: SetBadgeCountRequest) -> Result<(), NotificationError> {
119 Err(NotificationError::unsupported("set_badge_count"))
120 }
121
122 fn register_push(
123 &self,
124 _request: PushRegistrationRequest,
125 ) -> Result<PushRegistration, NotificationError> {
126 Err(NotificationError::unsupported("register_push"))
127 }
128
129 fn unregister_push(&self) -> Result<(), NotificationError> {
130 Err(NotificationError::unsupported("unregister_push"))
131 }
132}
133
134#[derive(Debug, Default)]
136pub struct MemoryNotificationHost;
137
138impl NotificationHost for MemoryNotificationHost {
139 fn request_permission(
140 &self,
141 request: NotificationPermissionRequest,
142 ) -> Result<NotificationSettings, NotificationError> {
143 Ok(NotificationSettings {
144 permission: NotificationPermission::Granted,
145 alerts: request.alerts,
146 badge: request.badge,
147 sound: request.sound,
148 scheduling: true,
149 push: false,
150 })
151 }
152
153 fn settings(&self) -> Result<NotificationSettings, NotificationError> {
154 Ok(NotificationSettings {
155 permission: NotificationPermission::Granted,
156 alerts: true,
157 badge: true,
158 sound: true,
159 scheduling: true,
160 push: false,
161 })
162 }
163
164 fn show(&self, request: NotificationRequest) -> Result<NotificationReceipt, NotificationError> {
165 Ok(NotificationReceipt {
166 id: request.id,
167 scheduled: false,
168 delivered: true,
169 })
170 }
171
172 fn schedule(
173 &self,
174 request: NotificationRequest,
175 ) -> Result<NotificationReceipt, NotificationError> {
176 Ok(NotificationReceipt {
177 id: request.id,
178 scheduled: !matches!(request.schedule, NotificationSchedule::Immediate),
179 delivered: matches!(request.schedule, NotificationSchedule::Immediate),
180 })
181 }
182
183 fn cancel(&self, _request: CancelNotificationRequest) -> Result<(), NotificationError> {
184 Ok(())
185 }
186
187 fn cancel_all(&self) -> Result<(), NotificationError> {
188 Ok(())
189 }
190
191 fn set_badge_count(&self, _request: SetBadgeCountRequest) -> Result<(), NotificationError> {
192 Ok(())
193 }
194
195 fn register_push(
196 &self,
197 _request: PushRegistrationRequest,
198 ) -> Result<PushRegistration, NotificationError> {
199 Ok(PushRegistration {
200 platform: PushPlatform::Other("memory".into()),
201 token: "memory-push-token".into(),
202 endpoint: None,
203 p256dh_key: None,
204 auth_secret: None,
205 })
206 }
207
208 fn unregister_push(&self) -> Result<(), NotificationError> {
209 Ok(())
210 }
211}
212
213pub(crate) fn register_notification_capabilities(
214 async_registry: &mut AsyncRegistry,
215 host: Arc<dyn NotificationHost>,
216) {
217 let request_host = host.clone();
218 async_registry.register_operation_capability(
219 REQUEST_NOTIFICATION_PERMISSION,
220 move |request, _| {
221 let host = request_host.clone();
222 async move { host.request_permission(request) }
223 },
224 );
225
226 let settings_host = host.clone();
227 async_registry.register_operation_capability(GET_NOTIFICATION_SETTINGS, move |(), _| {
228 let host = settings_host.clone();
229 async move { host.settings() }
230 });
231
232 let show_host = host.clone();
233 async_registry.register_operation_capability(SHOW_NOTIFICATION, move |request, _| {
234 let host = show_host.clone();
235 async move { host.show(request) }
236 });
237
238 let schedule_host = host.clone();
239 async_registry.register_operation_capability(SCHEDULE_NOTIFICATION, move |request, _| {
240 let host = schedule_host.clone();
241 async move { host.schedule(request) }
242 });
243
244 let cancel_host = host.clone();
245 async_registry.register_operation_capability(CANCEL_NOTIFICATION, move |request, _| {
246 let host = cancel_host.clone();
247 async move { host.cancel(request) }
248 });
249
250 let cancel_all_host = host.clone();
251 async_registry.register_operation_capability(CANCEL_ALL_NOTIFICATIONS, move |(), _| {
252 let host = cancel_all_host.clone();
253 async move { host.cancel_all() }
254 });
255
256 let badge_host = host.clone();
257 async_registry.register_operation_capability(SET_BADGE_COUNT, move |request, _| {
258 let host = badge_host.clone();
259 async move { host.set_badge_count(request) }
260 });
261
262 let push_host = host.clone();
263 async_registry.register_operation_capability(REGISTER_PUSH_NOTIFICATIONS, move |request, _| {
264 let host = push_host.clone();
265 async move { host.register_push(request) }
266 });
267
268 async_registry.register_operation_capability(UNREGISTER_PUSH_NOTIFICATIONS, move |(), _| {
269 let host = host.clone();
270 async move { host.unregister_push() }
271 });
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use fission_core::NotificationId;
278
279 #[test]
280 fn unsupported_host_reports_permission_without_panicking() {
281 let host = UnsupportedNotificationHost;
282 let settings = host
283 .request_permission(NotificationPermissionRequest::default())
284 .unwrap();
285 assert_eq!(settings.permission, NotificationPermission::Unsupported);
286 assert_eq!(
287 host.show(NotificationRequest::default()).unwrap_err().code,
288 "unsupported"
289 );
290 }
291
292 #[test]
293 fn memory_host_returns_receipts() {
294 let host = MemoryNotificationHost;
295 let receipt = host
296 .show(NotificationRequest {
297 id: NotificationId::new("n1"),
298 title: "Title".into(),
299 body: "Body".into(),
300 ..Default::default()
301 })
302 .unwrap();
303 assert_eq!(receipt.id, NotificationId::new("n1"));
304 assert!(receipt.delivered);
305 }
306}