use async_trait::async_trait;
use origin_domain::{AppError, Result};
use origin_platform::{Notification, NotificationService, Urgency};
use tauri::{AppHandle, Runtime};
use tauri_plugin_notification::{NotificationExt, PermissionState};
#[derive(Debug, Clone)]
pub struct TauriNotificationService<R: Runtime> {
app: AppHandle<R>,
}
impl<R: Runtime> TauriNotificationService<R> {
pub fn new(app: AppHandle<R>) -> Self {
Self { app }
}
fn ensure_permission(&self) -> Result<bool> {
let state = self
.app
.notification()
.permission_state()
.map_err(|error| AppError::internal(format!("notification permission: {error}")))?;
let state = match state {
PermissionState::Prompt | PermissionState::PromptWithRationale => self
.app
.notification()
.request_permission()
.map_err(|error| {
AppError::internal(format!("notification permission request: {error}"))
})?,
decided => decided,
};
Ok(matches!(state, PermissionState::Granted))
}
}
#[async_trait]
impl<R: Runtime> NotificationService for TauriNotificationService<R> {
async fn notify(&self, notification: Notification) -> Result<()> {
if !self.ensure_permission()? {
tracing::debug!(
title = %notification.title,
"notification suppressed: permission not granted"
);
return Ok(());
}
let mut builder = self.app.notification().builder().title(¬ification.title);
if let Some(body) = ¬ification.body {
builder = builder.body(body);
}
if let Some(tag) = ¬ification.tag {
builder = builder.group(tag);
}
if notification.urgency == Urgency::Critical {
builder = builder.sound("default");
}
builder
.show()
.map_err(|error| AppError::internal(format!("cannot show notification: {error}")))
}
}