rust and TS apis

pull/340/head
Lucas Nogueira 2 years ago
parent d8013e6ba4
commit 038f6d32fc
No known key found for this signature in database
GPG Key ID: 7C32FCA95C8C95D7

1
Cargo.lock generated

@ -4930,6 +4930,7 @@ dependencies = [
"rand 0.8.5",
"serde",
"serde_json",
"serde_repr",
"tauri",
"tauri-build",
"thiserror",

@ -19,6 +19,7 @@ thiserror.workspace = true
rand = "0.8"
time = { version = "0.3", features = ["serde", "parsing", "formatting"] }
url = { version = "2", features = ["serde"] }
serde_repr = "0.1"
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
notify-rust = "4.5"

@ -111,24 +111,31 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
}
@Command
fun cancel(invoke: Invoke) {
void cancel(invoke: Invoke) {
manager.cancel(invoke)
}
@Command
fun removeActive(invoke: Invoke) {
val notifications = invoke.getArray("notifications")
if (notifications == null) {
manager.cancel(invoke)
notificationManager.cancelAll()
invoke.resolve()
} else {
try {
for (o in notifications.toList<Any>()) {
if (o is JSONObject) {
val notification = JSObject.fromJSONObject((o))
val tag = notification.getString("tag")
val id = notification.getInteger("id")
if (tag.isEmpty()) {
notificationManager.cancel(id!!)
val tag = notification.getString("tag", null)
val id = notification.getInteger("id", 0)
if (tag == null) {
notificationManager.cancel(id)
} else {
notificationManager.cancel(tag, id!!)
notificationManager.cancel(tag, id)
}
} else {
invoke.reject("Unexpected notification type")
return
}
}
} catch (e: JSONException) {
@ -155,7 +162,7 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
@SuppressLint("ObsoleteSdkInt")
@Command
fun getDeliveredNotifications(invoke: Invoke) {
fun getActive(invoke: Invoke) {
val notifications = JSArray()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val activeNotifications = notificationManager.activeNotifications
@ -186,12 +193,6 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
invoke.resolve(result)
}
@Command
fun cancelAll(invoke: Invoke) {
notificationManager.cancelAll()
invoke.resolve()
}
@Command
fun createChannel(invoke: Invoke) {
channelManager.createChannel(invoke)

@ -210,6 +210,81 @@ interface Attachment {
url: string
}
interface Action {
id: string
title: string
requiresAuthentication?: boolean
foreground?: boolean
destructive?: boolean
input?: boolean
inputButtonTitle?: string
inputPlaceholder?: string
}
interface ActionType {
/**
* The identifier of this action type
*/
id: string
/**
* The list of associated actions
*/
actions: Action[]
hiddenPreviewsBodyPlaceholder?: string,
customDismissAction?: boolean,
allowInCarPlay?: boolean,
hiddenPreviewsShowTitle?: boolean,
hiddenPreviewsShowSubtitle?: boolean,
}
interface PendingNotification {
id: number
title?: string
body?: string
schedule: Schedule
}
interface ActiveNotification {
id: number
tag?: string
title?: string
body?: string
group?: string
groupSummary: boolean
data: Record<string, string>
extra: Record<string, unknown>
attachments: Attachment[]
actionTypeId?: string
schedule?: Schedule
sound?: string
}
enum Importance {
None = 0,
Min,
Low,
Default,
High
}
enum Visibility {
Secret = -1,
Private,
Public
}
interface Channel {
id: string
name: string
description?: string
sound?: string
lights?: boolean
lightColor?: string
vibration?: boolean
importance?: Importance
visibility?: Visibility
}
/** Possible permission values. */
type Permission = 'granted' | 'denied' | 'default'
@ -278,6 +353,205 @@ function sendNotification(options: Options | string): void {
}
}
export type { Attachment, Options, Permission }
/**
* Register actions that are performed when the user clicks on the notification.
*
* @example
* ```typescript
* import { registerActionTypes } from '@tauri-apps/api/notification';
* await registerActionTypes([{
* id: 'tauri',
* actions: [{
* id: 'my-action',
* title: 'Settings'
* }]
* }])
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function registerActionTypes(types: ActionType[]): Promise<void> {
return invoke('plugin:notification|register_action_types', { types })
}
/**
* Retrieves the list of pending notifications.
*
* @example
* ```typescript
* import { pending } from '@tauri-apps/api/notification';
* const pendingNotifications = await pending();
* ```
*
* @returns A promise resolving to the list of pending notifications.
*
* @since 2.0.0
*/
async function pending(): Promise<PendingNotification[]> {
return invoke('plugin:notification|get_pending')
}
/**
* Cancels the pending notifications with the given list of identifiers.
*
* @example
* ```typescript
* import { cancel } from '@tauri-apps/api/notification';
* await cancel([-34234, 23432, 4311]);
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function cancel(notifications: number[]): Promise<void> {
return invoke('plugin:notification|cancel', { notifications })
}
/**
* Cancels all pending notifications.
*
* @example
* ```typescript
* import { cancelAll } from '@tauri-apps/api/notification';
* await cancelAll();
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function cancelAll(): Promise<void> {
return invoke('plugin:notification|cancel')
}
/**
* Retrieves the list of active notifications.
*
* @example
* ```typescript
* import { active } from '@tauri-apps/api/notification';
* const activeNotifications = await active();
* ```
*
* @returns A promise resolving to the list of active notifications.
*
* @since 2.0.0
*/
async function active(): Promise<ActiveNotification[]> {
return invoke('plugin:notification|get_active')
}
/**
* Removes the active notifications with the given list of identifiers.
*
* @example
* ```typescript
* import { cancel } from '@tauri-apps/api/notification';
* await cancel([-34234, 23432, 4311])
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function removeActive(notifications: number[]): Promise<void> {
return invoke('plugin:notification|remove_active', { notifications })
}
/**
* Removes all active notifications.
*
* @example
* ```typescript
* import { removeAllActive } from '@tauri-apps/api/notification';
* await removeAllActive()
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function removeAllActive(): Promise<void> {
return invoke('plugin:notification|remove_active')
}
export { sendNotification, requestPermission, isPermissionGranted }
/**
* Removes all active notifications.
*
* @example
* ```typescript
* import { createChannel, Importance, Visibility } from '@tauri-apps/api/notification';
* await createChannel({
* id: 'new-messages',
* name: 'New Messages',
* lights: true,
* vibration: true,
* importance: Importance.Default,
* visibility: Visibility.Private
* });
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function createChannel(channel: Channel): Promise<void> {
return invoke('plugin:notification|create_channel', { ...channel })
}
/**
* Removes the channel with the given identifier.
*
* @example
* ```typescript
* import { removeChannel } from '@tauri-apps/api/notification';
* await removeChannel();
* ```
*
* @returns A promise indicating the success or failure of the operation.
*
* @since 2.0.0
*/
async function removeChannel(id: string): Promise<void> {
return invoke('plugin:notification|delete_channel', { id })
}
/**
* Retrieves the list of notification channels.
*
* @example
* ```typescript
* import { channels } from '@tauri-apps/api/notification';
* const notificationChannels = await channels();
* ```
*
* @returns A promise resolving to the list of notification channels.
*
* @since 2.0.0
*/
async function channels(): Promise<Channel[]> {
return invoke('plugin:notification|getActive')
}
export type { Attachment, Options, Permission, Action, ActionType, PendingNotification, ActiveNotification, Channel }
export {
Importance,
Visibility,
sendNotification,
requestPermission,
isPermissionGranted,
registerActionTypes,
pending,
cancel,
cancelAll,
active,
removeActive,
removeAllActive,
createChannel,
removeChannel,
channels
}

@ -16,7 +16,7 @@ public func makeCategories(_ actionTypes: [JSObject]) {
Logger.error("Action type must have an id field")
continue
}
let hiddenBodyPlaceholder = type["iosHiddenPreviewsBodyPlaceholder"] as? String ?? ""
let hiddenBodyPlaceholder = type["hiddenPreviewsBodyPlaceholder"] as? String ?? ""
let actions = type["actions"] as? [JSObject] ?? []
let newActions = makeActions(actions)
@ -96,10 +96,10 @@ func makeActionOptions(_ action: JSObject) -> UNNotificationActionOptions {
}
func makeCategoryOptions(_ type: JSObject) -> UNNotificationCategoryOptions {
let customDismiss = type["iosCustomDismissAction"] as? Bool ?? false
let carPlay = type["iosAllowInCarPlay"] as? Bool ?? false
let hiddenPreviewsShowTitle = type["iosHiddenPreviewsShowTitle"] as? Bool ?? false
let hiddenPreviewsShowSubtitle = type["iosHiddenPreviewsShowSubtitle"] as? Bool ?? false
let customDismiss = type["customDismissAction"] as? Bool ?? false
let carPlay = type["allowInCarPlay"] as? Bool ?? false
let hiddenPreviewsShowTitle = type["hiddenPreviewsShowTitle"] as? Bool ?? false
let hiddenPreviewsShowSubtitle = type["hiddenPreviewsShowSubtitle"] as? Bool ?? false
if customDismiss {
return .customDismissAction

@ -172,26 +172,21 @@ class NotificationPlugin: Plugin {
invoke.resolve()
}
@objc func removeDeliveredNotifications(_ invoke: Invoke) {
guard let notifications = invoke.getArray("notifications", JSObject.self) else {
invoke.reject("`notifications` input is required")
return
@objc func removeActive(_ invoke: Invoke) {
if let notifications = invoke.getArray("notifications", JSObject.self) {
let ids = notifications.map { "\($0["id"] ?? "")" }
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ids)
invoke.resolve()
} else {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
DispatchQueue.main.async(execute: {
UIApplication.shared.applicationIconBadgeNumber = 0
})
invoke.resolve()
}
let ids = notifications.map { "\($0["id"] ?? "")" }
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ids)
invoke.resolve()
}
@objc func removeAllDeliveredNotifications(_ invoke: Invoke) {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
DispatchQueue.main.async(execute: {
UIApplication.shared.applicationIconBadgeNumber = 0
})
invoke.resolve()
}
@objc func getDeliveredNotifications(_ invoke: Invoke) {
@objc func getActive(_ invoke: Invoke) {
UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: {
(notifications) in
let ret = notifications.map({ (notification) -> [String: Any] in

@ -2,9 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{collections::HashMap, fmt::Display};
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
use serde::Serialize;
#[cfg(mobile)]
use tauri::plugin::PluginHandle;
#[cfg(desktop)]
@ -31,193 +29,6 @@ pub use error::{Error, Result};
use desktop::Notification;
#[cfg(mobile)]
use mobile::Notification;
use url::Url;
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Attachment {
id: String,
url: Url,
}
impl Attachment {
pub fn new(id: impl Into<String>, url: Url) -> Self {
Self { id: id.into(), url }
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ScheduleInterval {
pub year: Option<u8>,
pub month: Option<u8>,
pub day: Option<u8>,
pub weekday: Option<u8>,
pub hour: Option<u8>,
pub minute: Option<u8>,
pub second: Option<u8>,
}
#[derive(Debug)]
pub enum ScheduleEvery {
Year,
Month,
TwoWeeks,
Week,
Day,
Hour,
Minute,
Second,
}
impl Display for ScheduleEvery {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Year => "Year",
Self::Month => "Month",
Self::TwoWeeks => "TwoWeeks",
Self::Week => "Week",
Self::Day => "Day",
Self::Hour => "Hour",
Self::Minute => "Minute",
Self::Second => "Second",
}
)
}
}
impl Serialize for ScheduleEvery {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
impl<'de> Deserialize<'de> for ScheduleEvery {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
"year" => Ok(Self::Year),
"month" => Ok(Self::Month),
"twoweeks" => Ok(Self::TwoWeeks),
"week" => Ok(Self::Week),
"day" => Ok(Self::Day),
"hour" => Ok(Self::Hour),
"minute" => Ok(Self::Minute),
"second" => Ok(Self::Second),
_ => Err(DeError::custom(format!("unknown every kind '{s}'"))),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "kind", content = "data")]
pub enum Schedule {
At {
#[serde(
serialize_with = "iso8601::serialize",
deserialize_with = "time::serde::iso8601::deserialize"
)]
date: time::OffsetDateTime,
#[serde(default)]
repeating: bool,
},
Interval(ScheduleInterval),
Every {
interval: ScheduleEvery,
},
}
// custom ISO-8601 serialization that does not use 6 digits for years.
mod iso8601 {
use serde::{ser::Error as _, Serialize, Serializer};
use time::{
format_description::well_known::iso8601::{Config, EncodedConfig},
format_description::well_known::Iso8601,
OffsetDateTime,
};
const SERDE_CONFIG: EncodedConfig = Config::DEFAULT.encode();
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
datetime
.format(&Iso8601::<SERDE_CONFIG>)
.map_err(S::Error::custom)?
.serialize(serializer)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct NotificationData {
/// Notification id.
#[serde(default = "default_id")]
id: i32,
channel_id: Option<String>,
title: Option<String>,
body: Option<String>,
schedule: Option<Schedule>,
large_body: Option<String>,
summary: Option<String>,
action_type_id: Option<String>,
group: Option<String>,
#[serde(default)]
group_summary: bool,
sound: Option<String>,
#[serde(default)]
inbox_lines: Vec<String>,
icon: Option<String>,
large_icon: Option<String>,
icon_color: Option<String>,
#[serde(default)]
attachments: Vec<Attachment>,
#[serde(default)]
extra: HashMap<String, serde_json::Value>,
#[serde(default)]
ongoing: bool,
#[serde(default)]
auto_cancel: bool,
}
fn default_id() -> i32 {
rand::random()
}
impl Default for NotificationData {
fn default() -> Self {
Self {
id: default_id(),
channel_id: None,
title: None,
body: None,
schedule: None,
large_body: None,
summary: None,
action_type_id: None,
group: None,
group_summary: false,
sound: None,
inbox_lines: Vec::new(),
icon: None,
large_icon: None,
icon_color: None,
attachments: Vec::new(),
extra: Default::default(),
ongoing: false,
auto_cancel: false,
}
}
}
/// The notification builder.
#[derive(Debug)]

@ -10,6 +10,8 @@ use tauri::{
use crate::models::*;
use std::collections::HashMap;
#[cfg(target_os = "android")]
const PLUGIN_IDENTIFIER: &str = "app.tauri.notification";
@ -58,6 +60,114 @@ impl<R: Runtime> Notification<R> {
.map(|r| r.permission_state)
.map_err(Into::into)
}
pub fn register_action_types(&self, types: Vec<ActionType>) -> crate::Result<()> {
let mut args = HashMap::new();
args.insert("types", types);
self.0
.run_mobile_plugin("registerActionTypes", args)
.map_err(Into::into)
}
pub fn remove_active(&self, notifications: Vec<i32>) -> crate::Result<()> {
let mut args = HashMap::new();
args.insert(
"notifications",
notifications
.into_iter()
.map(|id| {
let mut notification = HashMap::new();
notification.insert("id", id);
notification
})
.collect::<Vec<HashMap<&str, i32>>>(),
);
self.0
.run_mobile_plugin("removeActive", args)
.map_err(Into::into)
}
pub fn active(&self) -> crate::Result<Vec<ActiveNotification>> {
self.0
.run_mobile_plugin::<ActiveResponse>("getActive", ())
.map(|r| r.notifications)
.map_err(Into::into)
}
pub fn remove_all_active(&self) -> crate::Result<()> {
self.0
.run_mobile_plugin("removeActive", ())
.map_err(Into::into)
}
pub fn pending(&self) -> crate::Result<Vec<PendingNotification>> {
self.0
.run_mobile_plugin::<PendingResponse>("getPending", ())
.map(|r| r.notifications)
.map_err(Into::into)
}
/// Cancel pending notifications.
pub fn cancel(&self, notifications: Vec<i32>) -> crate::Result<()> {
let mut args = HashMap::new();
args.insert(
"notifications",
notifications
.into_iter()
.map(|id| {
let mut notification = HashMap::new();
notification.insert("id", id);
notification
})
.collect::<Vec<HashMap<&str, i32>>>(),
);
self.0.run_mobile_plugin("cancel", args).map_err(Into::into)
}
/// Cancel all pending notifications.
pub fn cancel_all(&self) -> crate::Result<()> {
self.0.run_mobile_plugin("cancel", ()).map_err(Into::into)
}
#[cfg(target_os = "android")]
pub fn create_channel(&self, channel: Channel) -> crate::Result<()> {
self.0
.run_mobile_plugin("createChannel", channel)
.map_err(Into::into)
}
#[cfg(target_os = "android")]
pub fn delete_channel(&self, id: impl Into<String>) -> crate::Result<()> {
let mut args = HashMap::new();
args.insert("id", id.into());
self.0
.run_mobile_plugin("deleteChannel", args)
.map_err(Into::into)
}
#[cfg(target_os = "android")]
pub fn list_channels(&self) -> crate::Result<Vec<Channel>> {
self.0
.run_mobile_plugin::<ListChannelsResult>("listChannels", ())
.map(|r| r.channels)
.map_err(Into::into)
}
}
#[cfg(target_os = "android")]
#[derive(Deserialize)]
struct ListChannelsResult {
channels: Vec<Channel>,
}
#[derive(Deserialize)]
struct PendingResponse {
notifications: Vec<PendingNotification>,
}
#[derive(Deserialize)]
struct ActiveResponse {
notifications: Vec<ActiveNotification>,
}
#[derive(Deserialize)]

@ -2,10 +2,198 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::fmt::Display;
use std::{collections::HashMap, fmt::Display};
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer};
use url::Url;
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Attachment {
id: String,
url: Url,
}
impl Attachment {
pub fn new(id: impl Into<String>, url: Url) -> Self {
Self { id: id.into(), url }
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScheduleInterval {
pub year: Option<u8>,
pub month: Option<u8>,
pub day: Option<u8>,
pub weekday: Option<u8>,
pub hour: Option<u8>,
pub minute: Option<u8>,
pub second: Option<u8>,
}
#[derive(Debug)]
pub enum ScheduleEvery {
Year,
Month,
TwoWeeks,
Week,
Day,
Hour,
Minute,
Second,
}
impl Display for ScheduleEvery {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Year => "Year",
Self::Month => "Month",
Self::TwoWeeks => "TwoWeeks",
Self::Week => "Week",
Self::Day => "Day",
Self::Hour => "Hour",
Self::Minute => "Minute",
Self::Second => "Second",
}
)
}
}
impl Serialize for ScheduleEvery {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
impl<'de> Deserialize<'de> for ScheduleEvery {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
"year" => Ok(Self::Year),
"month" => Ok(Self::Month),
"twoweeks" => Ok(Self::TwoWeeks),
"week" => Ok(Self::Week),
"day" => Ok(Self::Day),
"hour" => Ok(Self::Hour),
"minute" => Ok(Self::Minute),
"second" => Ok(Self::Second),
_ => Err(DeError::custom(format!("unknown every kind '{s}'"))),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "kind", content = "data")]
pub enum Schedule {
At {
#[serde(
serialize_with = "iso8601::serialize",
deserialize_with = "time::serde::iso8601::deserialize"
)]
date: time::OffsetDateTime,
#[serde(default)]
repeating: bool,
},
Interval(ScheduleInterval),
Every {
interval: ScheduleEvery,
},
}
// custom ISO-8601 serialization that does not use 6 digits for years.
mod iso8601 {
use serde::{ser::Error as _, Serialize, Serializer};
use time::{
format_description::well_known::iso8601::{Config, EncodedConfig},
format_description::well_known::Iso8601,
OffsetDateTime,
};
const SERDE_CONFIG: EncodedConfig = Config::DEFAULT.encode();
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
datetime
.format(&Iso8601::<SERDE_CONFIG>)
.map_err(S::Error::custom)?
.serialize(serializer)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NotificationData {
#[serde(default = "default_id")]
pub(crate) id: i32,
pub(crate) channel_id: Option<String>,
pub(crate) title: Option<String>,
pub(crate) body: Option<String>,
pub(crate) schedule: Option<Schedule>,
pub(crate) large_body: Option<String>,
pub(crate) summary: Option<String>,
pub(crate) action_type_id: Option<String>,
pub(crate) group: Option<String>,
#[serde(default)]
pub(crate) group_summary: bool,
pub(crate) sound: Option<String>,
#[serde(default)]
pub(crate) inbox_lines: Vec<String>,
pub(crate) icon: Option<String>,
pub(crate) large_icon: Option<String>,
pub(crate) icon_color: Option<String>,
#[serde(default)]
pub(crate) attachments: Vec<Attachment>,
#[serde(default)]
pub(crate) extra: HashMap<String, serde_json::Value>,
#[serde(default)]
pub(crate) ongoing: bool,
#[serde(default)]
pub(crate) auto_cancel: bool,
}
fn default_id() -> i32 {
rand::random()
}
impl Default for NotificationData {
fn default() -> Self {
Self {
id: default_id(),
channel_id: None,
title: None,
body: None,
schedule: None,
large_body: None,
summary: None,
action_type_id: None,
group: None,
group_summary: false,
sound: None,
inbox_lines: Vec::new(),
icon: None,
large_icon: None,
icon_color: None,
attachments: Vec::new(),
extra: Default::default(),
ongoing: false,
auto_cancel: false,
}
}
}
/// Permission state.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PermissionState {
@ -50,3 +238,269 @@ impl<'de> Deserialize<'de> for PermissionState {
}
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PendingNotification {
id: i32,
title: Option<String>,
body: Option<String>,
schedule: Schedule,
}
impl PendingNotification {
pub fn id(&self) -> i32 {
self.id
}
pub fn title(&self) -> Option<&str> {
self.title.as_deref()
}
pub fn body(&self) -> Option<&str> {
self.body.as_deref()
}
pub fn schedule(&self) -> &Schedule {
&self.schedule
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActiveNotification {
id: i32,
tag: Option<String>,
title: Option<String>,
body: Option<String>,
group: Option<String>,
#[serde(default)]
group_summary: bool,
#[serde(default)]
data: HashMap<String, String>,
#[serde(default)]
extra: HashMap<String, serde_json::Value>,
#[serde(default)]
attachments: Vec<Attachment>,
action_type_id: Option<String>,
schedule: Option<Schedule>,
sound: Option<String>,
}
impl ActiveNotification {
pub fn id(&self) -> i32 {
self.id
}
pub fn tag(&self) -> Option<&str> {
self.tag.as_deref()
}
pub fn title(&self) -> Option<&str> {
self.title.as_deref()
}
pub fn body(&self) -> Option<&str> {
self.body.as_deref()
}
pub fn group(&self) -> Option<&str> {
self.group.as_deref()
}
pub fn group_summary(&self) -> bool {
self.group_summary
}
pub fn data(&self) -> &HashMap<String, String> {
&self.data
}
pub fn extra(&self) -> &HashMap<String, serde_json::Value> {
&self.extra
}
pub fn attachments(&self) -> &[Attachment] {
&self.attachments
}
pub fn action_type_id(&self) -> Option<&str> {
self.action_type_id.as_deref()
}
pub fn schedule(&self) -> Option<&Schedule> {
self.schedule.as_ref()
}
pub fn sound(&self) -> Option<&str> {
self.sound.as_deref()
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionType {
id: String,
actions: Vec<Action>,
hidden_previews_body_placeholder: Option<String>,
custom_dismiss_action: bool,
allow_in_car_play: bool,
hidden_previews_show_title: bool,
hidden_previews_show_subtitle: bool,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Action {
id: String,
title: String,
requires_authentication: bool,
foreground: bool,
destructive: bool,
input: bool,
input_button_title: Option<String>,
input_placeholder: Option<String>,
}
#[cfg(target_os = "android")]
pub use android::*;
#[cfg(target_os = "android")]
mod android {
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum Importance {
None = 0,
Min = 1,
Low = 2,
Default = 3,
High = 4,
}
impl Default for Importance {
fn default() -> Self {
Self::Default
}
}
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(i8)]
pub enum Visibility {
Secret = -1,
Private = 0,
Public = 1,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Channel {
id: String,
name: String,
description: Option<String>,
sound: Option<String>,
lights: bool,
light_color: Option<String>,
vibration: bool,
importance: Importance,
visibility: Option<Visibility>,
}
#[derive(Debug)]
pub struct ChannelBuilder(Channel);
impl Channel {
pub fn builder(id: impl Into<String>, name: impl Into<String>) -> ChannelBuilder {
ChannelBuilder(Self {
id: id.into(),
name: name.into(),
description: None,
sound: None,
lights: false,
light_color: None,
vibration: false,
importance: Default::default(),
visibility: None,
})
}
pub fn id(&self) -> &str {
&self.id
}
pub fn name(&self) -> &str {
&self.name
}
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn sound(&self) -> Option<&str> {
self.sound.as_deref()
}
pub fn lights(&self) -> bool {
self.lights
}
pub fn light_color(&self) -> Option<&str> {
self.light_color.as_deref()
}
pub fn vibration(&self) -> bool {
self.vibration
}
pub fn importance(&self) -> Importance {
self.importance
}
pub fn visibility(&self) -> Option<Visibility> {
self.visibility
}
}
impl ChannelBuilder {
pub fn description(mut self, description: impl Into<String>) -> Self {
self.0.description.replace(description.into());
self
}
pub fn sound(mut self, sound: impl Into<String>) -> Self {
self.0.sound.replace(sound.into());
self
}
pub fn lights(mut self, lights: bool) -> Self {
self.0.lights = lights;
self
}
pub fn light_color(mut self, color: impl Into<String>) -> Self {
self.0.light_color.replace(color.into());
self
}
pub fn vibration(mut self, vibration: bool) -> Self {
self.0.vibration = vibration;
self
}
pub fn importance(mut self, importance: Importance) -> Self {
self.0.importance = importance;
self
}
pub fn visibility(mut self, visibility: Visibility) -> Self {
self.0.visibility.replace(visibility);
self
}
pub fn build(self) -> Channel {
self.0
}
}
}

Loading…
Cancel
Save