use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::time::SystemTime;
use crate::IconImage;
pub trait Notifications {
fn post(&self, notification: &Notification) -> PostFuture;
fn remove(&self, id: &str);
fn permission(&self) -> Option<&dyn NotificationPermission>;
fn replies(&self) -> Option<&dyn NotificationReplies>;
fn scheduling(&self) -> Option<&dyn NotificationScheduling>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct Notification {
pub id: String,
pub title: String,
pub body: Option<String>,
pub image: Option<IconImage>,
pub actions: Vec<NotificationAction>,
pub silent: bool,
}
impl Notification {
pub fn new(id: impl Into<String>, title: impl Into<String>) -> Notification {
Notification {
id: id.into(),
title: title.into(),
body: None,
image: None,
actions: Vec::new(),
silent: false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NotificationAction {
pub id: String,
pub label: String,
reply: Option<String>,
}
impl NotificationAction {
pub fn button(id: impl Into<String>, label: impl Into<String>) -> NotificationAction {
NotificationAction {
id: id.into(),
label: label.into(),
reply: None,
}
}
pub(crate) fn with_reply(
id: impl Into<String>,
label: impl Into<String>,
placeholder: impl Into<String>,
) -> NotificationAction {
NotificationAction {
id: id.into(),
label: label.into(),
reply: Some(placeholder.into()),
}
}
pub fn reply_placeholder(&self) -> Option<&str> {
self.reply.as_deref()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NotificationActivation {
pub id: String,
pub action: Option<String>,
pub reply: Option<String>,
}
pub type PostFuture = Pin<Box<dyn Future<Output = Result<(), NotificationError>>>>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NotificationError {
Denied,
Failed(String),
}
impl fmt::Display for NotificationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NotificationError::Denied => f.write_str("the user does not let the app notify"),
NotificationError::Failed(reason) => write!(f, "the notification failed: {reason}"),
}
}
}
impl std::error::Error for NotificationError {}
pub trait NotificationPermission {
fn state(&self) -> PermissionFuture;
fn request(&self) -> PermissionFuture;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PermissionState {
Granted,
Denied,
NotAsked,
}
pub type PermissionFuture = Pin<Box<dyn Future<Output = PermissionState>>>;
pub trait NotificationReplies {
fn reply_action(&self, id: &str, label: &str, placeholder: &str) -> NotificationAction {
NotificationAction::with_reply(id, label, placeholder)
}
}
pub trait NotificationScheduling {
fn schedule(&self, notification: &Notification, at: SystemTime) -> PostFuture;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_reply_action_carries_its_placeholder_and_a_button_none() {
struct Replies;
impl NotificationReplies for Replies {}
let reply = Replies.reply_action("answer", "Reply", "Your answer");
assert_eq!(reply.reply_placeholder(), Some("Your answer"));
assert_eq!(
NotificationAction::button("later", "Later").reply_placeholder(),
None
);
let mut notification = Notification::new("message-7", "New message");
notification.actions.push(reply);
assert_eq!(notification.actions[0].id, "answer");
}
}