Skip to content

Commit

Permalink
feat: add implementation to support safari web push (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
hortopan committed Nov 5, 2021
1 parent 66186ca commit da596b6
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Expand Up @@ -127,7 +127,7 @@ mod signer;

pub use crate::request::notification::{
CollapseId, LocalizedNotificationBuilder, NotificationBuilder, NotificationOptions, PlainNotificationBuilder,
Priority, SilentNotificationBuilder,
Priority, SilentNotificationBuilder, WebNotificationBuilder, WebPushAlert,
};

pub use crate::response::{ErrorBody, ErrorReason, Response};
Expand Down
2 changes: 2 additions & 0 deletions src/request/notification.rs
Expand Up @@ -4,11 +4,13 @@ mod localized;
mod options;
mod plain;
mod silent;
mod web;

pub use self::localized::{LocalizedAlert, LocalizedNotificationBuilder};
pub use self::options::{CollapseId, NotificationOptions, Priority};
pub use self::plain::PlainNotificationBuilder;
pub use self::silent::SilentNotificationBuilder;
pub use self::web::{WebNotificationBuilder, WebPushAlert};

use crate::request::payload::Payload;

Expand Down
1 change: 1 addition & 0 deletions src/request/notification/localized.rs
Expand Up @@ -315,6 +315,7 @@ impl<'a> NotificationBuilder<'a> for LocalizedNotificationBuilder<'a> {
content_available: None,
category: self.category,
mutable_content: Some(self.mutable_content),
url_args: None,
},
device_token,
options,
Expand Down
1 change: 1 addition & 0 deletions src/request/notification/plain.rs
Expand Up @@ -120,6 +120,7 @@ impl<'a> NotificationBuilder<'a> for PlainNotificationBuilder<'a> {
content_available: None,
category: self.category,
mutable_content: None,
url_args: None,
},
device_token,
options,
Expand Down
1 change: 1 addition & 0 deletions src/request/notification/silent.rs
Expand Up @@ -66,6 +66,7 @@ impl<'a> NotificationBuilder<'a> for SilentNotificationBuilder {
content_available: Some(self.content_available),
category: None,
mutable_content: None,
url_args: None,
},
device_token,
options,
Expand Down
127 changes: 127 additions & 0 deletions src/request/notification/web.rs
@@ -0,0 +1,127 @@
use crate::request::notification::{NotificationBuilder, NotificationOptions};
use crate::request::payload::{APSAlert, Payload, APS};
use std::collections::BTreeMap;

#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct WebPushAlert<'a> {
pub title: &'a str,
pub body: &'a str,
pub action: &'a str,
}

/// A builder to create a simple APNs notification payload.
///
/// # Example
///
/// ```rust
/// # use a2::request::notification::{NotificationBuilder, WebNotificationBuilder, WebPushAlert};
/// # fn main() {
/// let mut builder = WebNotificationBuilder::new(WebPushAlert {title: "Hello", body: "World", action: "View"}, &["arg1"]);
/// builder.set_sound("prööt");
/// let payload = builder.build("device_id", Default::default())
/// .to_json_string().unwrap();
/// # }
/// ```
pub struct WebNotificationBuilder<'a> {
alert: WebPushAlert<'a>,
sound: Option<&'a str>,
url_args: &'a [&'a str],
}

impl<'a> WebNotificationBuilder<'a> {
/// Creates a new builder with the minimum amount of content.
///
/// ```rust
/// # use a2::request::notification::{WebNotificationBuilder, NotificationBuilder, WebPushAlert};
/// # fn main() {
/// let mut builder = WebNotificationBuilder::new(WebPushAlert {title: "Hello", body: "World", action: "View"}, &["arg1"]);
/// let payload = builder.build("token", Default::default());
///
/// assert_eq!(
/// "{\"aps\":{\"alert\":{\"action\":\"View\",\"body\":\"World\",\"title\":\"Hello\"},\"url-args\":[\"arg1\"]}}",
/// &payload.to_json_string().unwrap()
/// );
/// # }
/// ```
pub fn new(alert: WebPushAlert<'a>, url_args: &'a [&'a str]) -> WebNotificationBuilder<'a> {
WebNotificationBuilder {
alert,
sound: None,
url_args: url_args,
}
}

/// File name of the custom sound to play when receiving the notification.
///
/// ```rust
/// # use a2::request::notification::{WebNotificationBuilder, NotificationBuilder, WebPushAlert};
/// # fn main() {
/// let mut builder = WebNotificationBuilder::new(WebPushAlert {title: "Hello", body: "World", action: "View"}, &["arg1"]);
/// builder.set_sound("meow");
/// let payload = builder.build("token", Default::default());
///
/// assert_eq!(
/// "{\"aps\":{\"alert\":{\"action\":\"View\",\"body\":\"World\",\"title\":\"Hello\"},\"sound\":\"meow\",\"url-args\":[\"arg1\"]}}",
/// &payload.to_json_string().unwrap()
/// );
/// # }
/// ```
pub fn set_sound(&mut self, sound: &'a str) -> &mut Self {
self.sound = Some(sound);
self
}
}

impl<'a> NotificationBuilder<'a> for WebNotificationBuilder<'a> {
fn build(self, device_token: &'a str, options: NotificationOptions<'a>) -> Payload<'a> {
Payload {
aps: APS {
alert: Some(APSAlert::WebPush(self.alert)),
badge: None,
sound: self.sound,
content_available: None,
category: None,
mutable_content: None,
url_args: Some(self.url_args),
},
device_token,
options,
data: BTreeMap::new(),
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_webpush_notification() {
let payload = WebNotificationBuilder::new(
WebPushAlert {
action: "View",
title: "Hello",
body: "world",
},
&vec!["arg1"],
)
.build("device-token", Default::default())
.to_json_string()
.unwrap();

let expected_payload = json!({
"aps": {
"alert": {
"body": "world",
"action": "View",
"title": "Hello"
},
"url-args": ["arg1"]
}
})
.to_string();

assert_eq!(expected_payload, payload);
}
}
7 changes: 6 additions & 1 deletion src/request/payload.rs
@@ -1,7 +1,7 @@
//! Payload with `aps` and custom data

use crate::error::Error;
use crate::request::notification::{LocalizedAlert, NotificationOptions};
use crate::request::notification::{LocalizedAlert, NotificationOptions, WebPushAlert};
use erased_serde::Serialize;
use serde_json::{self, Value};
use std::collections::BTreeMap;
Expand Down Expand Up @@ -117,6 +117,9 @@ pub struct APS<'a> {
/// displaying it to the user.
#[serde(skip_serializing_if = "Option::is_none")]
pub mutable_content: Option<u8>,

#[serde(skip_serializing_if = "Option::is_none")]
pub url_args: Option<&'a [&'a str]>,
}

/// Different notification content types.
Expand All @@ -127,4 +130,6 @@ pub enum APSAlert<'a> {
Plain(&'a str),
/// A rich localized notification.
Localized(LocalizedAlert<'a>),
/// Safari web push notification
WebPush(WebPushAlert<'a>),
}

0 comments on commit da596b6

Please sign in to comment.