From f685dd1d9ef23efb678ee51d38d742efebc14688 Mon Sep 17 00:00:00 2001 From: Daniel Dickison Date: Thu, 17 Apr 2025 01:52:03 -0700 Subject: [PATCH] Add declaratiive Notification payload builder --- src/lib.rs | 2 + src/notification.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/notification.rs diff --git a/src/lib.rs b/src/lib.rs index 91499f42..5a183de5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ pub use crate::{ error::WebPushError, http_ece::ContentEncoding, message::{SubscriptionInfo, SubscriptionKeys, Urgency, WebPushMessage, WebPushMessageBuilder, WebPushPayload}, + notification::{Notification, NotificationAction}, vapid::{builder::PartialVapidSignatureBuilder, VapidSignature, VapidSignatureBuilder}, }; @@ -64,4 +65,5 @@ mod clients; mod error; mod http_ece; mod message; +mod notification; mod vapid; diff --git a/src/notification.rs b/src/notification.rs new file mode 100644 index 00000000..246fda74 --- /dev/null +++ b/src/notification.rs @@ -0,0 +1,104 @@ +use serde::Serialize; + +/// Declarative notification that can be used to populate the payload of a web push. +/// +/// See https://webkit.org/blog/16535/meet-declarative-web-push +#[derive(Debug, Serialize)] +pub struct Notification { + pub title: String, + pub navigate: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub body: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub lang: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub dir: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub tag: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub image: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub icon: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub badge: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub vibrate: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub timestamp: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub renotify: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub silent: Option, + + #[serde(skip_serializing_if = "Option::is_none", rename = "requireInteraction")] + pub require_interaction: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub actions: Option>, +} + +#[derive(Debug, Serialize)] +pub struct NotificationAction { + pub title: String, + pub action: String, + pub navigate: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub icon: Option, +} + +impl Notification { + pub fn new(title: String, navigate: String) -> Self { + Notification { + title, + navigate, + lang: None, + dir: None, + tag: None, + body: None, + icon: None, + image: None, + badge: None, + vibrate: None, + timestamp: None, + renotify: None, + silent: None, + require_interaction: None, + data: None, + actions: None, + } + } + + pub fn to_payload(&self) -> serde_json::Result> { + serde_json::to_vec(&DeclarativePushPayload::new(self)) + } +} + +#[derive(Debug, Serialize)] +struct DeclarativePushPayload<'a, D: Serialize> { + web_push: u16, + pub notification: &'a Notification, +} + +impl<'a, D: Serialize> DeclarativePushPayload<'a, D> { + pub fn new(notification: &'a Notification) -> Self { + DeclarativePushPayload { + web_push: 8030, + notification, + } + } +}