feat: add notification sound (#7269)

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
Amr Bashir 2023-08-08 21:09:04 +03:00 committed by GitHub
parent c272e4afbf
commit 6c408b736c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 876 additions and 778 deletions

View File

@ -0,0 +1,6 @@
---
'tauri': 'minor:feat'
'@tauri-apps/api': 'minor:feat'
---
Add option to specify notification sound.

View File

@ -32,7 +32,6 @@ jobs:
filters: |
api:
- 'tooling/api/src/**'
- 'tooling/api/docs/js-api.json'
- 'core/tauri/scripts/bundle.global.js'
schema:
- 'core/tauri-utils/src/config.rs'
@ -50,9 +49,7 @@ jobs:
working-directory: tooling/api
run: yarn && yarn build
- name: check api
run: |
git restore tooling/api/docs/js-api.json
./.scripts/ci/has-diff.sh
run: ./.scripts/ci/has-diff.sh
schema:
runs-on: ubuntu-latest

View File

@ -99,7 +99,7 @@ jobs:
cargo update -p is-terminal --precise 0.4.7
cargo update -p colored --precise 2.0.2
cargo update -p tempfile --precise 3.6.0
cargo update -p serde_with:3.1.0 --precise 3.0.0
cargo update -p serde_with:3.2.0 --precise 3.0.0
- name: test
run: cargo test --target ${{ matrix.platform.target }} ${{ matrix.features.args }}

View File

@ -9,5 +9,4 @@ dist
/tooling/cli/templates
/tooling/cli/node
/tooling/cli/schema.json
/tooling/api/docs/js-api.json
/core/tauri-config-schema/schema.json

View File

@ -103,7 +103,7 @@ objc = "0.2"
[target."cfg(windows)".dependencies]
webview2-com = "0.19.1"
win7-notifications = { version = "0.3.1", optional = true }
win7-notifications = { version = "0.4", optional = true }
[target."cfg(windows)".dependencies.windows]
version = "0.39.0"

View File

@ -40,6 +40,50 @@ pub struct Notification {
icon: Option<String>,
/// The notification identifier
identifier: String,
/// The notification sound
sound: Option<Sound>,
}
/// Notification sound.
#[derive(Debug)]
pub enum Sound {
/// The default notification sound.
Default,
/// A custom notification sound.
///
/// ## Platform-specific
///
/// Each OS has a different sound name so you will need to conditionally specify an appropriate sound
/// based on the OS in use, for a list of sounds see:
/// - **Linux**: can be one of the sounds listed in <https://0pointer.de/public/sound-naming-spec.html>
/// - **Windows**: can be one of the sounds listed in <https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-audio>
/// but without the prefix, for example, if `ms-winsoundevent:Notification.Default` you would use `Default` and
/// if `ms-winsoundevent:Notification.Looping.Alarm2`, you would use `Alarm2`.
/// Windows 7 is not supported, if a sound is provided, it will play the default sound, otherwise it will be silent.
/// - **macOS**: you can specify the name of the sound you'd like to play when the notification is shown.
/// Any of the default sounds (under System Preferences > Sound) can be used, in addition to custom sound files.
/// Be sure that the sound file is under one of the following locations:
/// - `~/Library/Sounds`
/// - `/Library/Sounds`
/// - `/Network/Library/Sounds`
/// - `/System/Library/Sounds`
///
/// See the [`NSSound`] docs for more information.
///
/// [`NSSound`]: https://developer.apple.com/documentation/appkit/nssound
Custom(String),
}
impl From<String> for Sound {
fn from(value: String) -> Self {
Self::Custom(value)
}
}
impl From<&str> for Sound {
fn from(value: &str) -> Self {
Self::Custom(value.into())
}
}
impl Notification {
@ -72,6 +116,15 @@ impl Notification {
self
}
/// Sets the notification sound. By default the notification has no sound.
///
/// See [`Sound`] for more information.
#[must_use]
pub fn sound(mut self, sound: impl Into<Sound>) -> Self {
self.sound.replace(sound.into());
self
}
/// Shows the notification.
///
/// # Examples
@ -108,6 +161,17 @@ impl Notification {
} else {
notification.auto_icon();
}
if let Some(sound) = self.sound {
notification.sound_name(&match sound {
#[cfg(target_os = "macos")]
Sound::Default => "NSUserNotificationDefaultSoundName".to_string(),
#[cfg(windows)]
Sound::Default => "Default".to_string(),
#[cfg(all(unix, not(target_os = "macos")))]
Sound::Default => "message-new-instant".to_string(),
Sound::Custom(c) => c,
});
}
#[cfg(windows)]
{
let exe = tauri_utils::platform::current_exe()?;
@ -191,6 +255,7 @@ impl Notification {
if let Some(title) = self.title {
notification.summary(&title);
}
notification.silent(self.sound.is_none());
if let Some(crate::Icon::Rgba {
rgba,
width,

View File

@ -6,7 +6,7 @@
use super::InvokeContext;
use crate::Runtime;
use serde::Deserialize;
use serde::{Deserialize, Deserializer};
use tauri_macros::{command_enum, module_command_handler, CommandModule};
#[cfg(notification_all)]
@ -17,6 +17,36 @@ const PERMISSION_GRANTED: &str = "granted";
// `Denied` response from `request_permission`. Matches the Web API return value.
const PERMISSION_DENIED: &str = "denied";
#[derive(Debug, Clone)]
pub enum SoundDto {
Default,
Custom(String),
}
#[cfg(notification_all)]
impl From<SoundDto> for crate::api::notification::Sound {
fn from(sound: SoundDto) -> Self {
match sound {
SoundDto::Default => crate::api::notification::Sound::Default,
SoundDto::Custom(s) => crate::api::notification::Sound::Custom(s),
}
}
}
impl<'de> Deserialize<'de> for SoundDto {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.to_lowercase() == "default" {
Ok(Self::Default)
} else {
Ok(Self::Custom(s))
}
}
}
/// The options for the notification API.
#[derive(Debug, Clone, Deserialize)]
pub struct NotificationOptions {
@ -26,6 +56,8 @@ pub struct NotificationOptions {
pub body: Option<String>,
/// The notification icon.
pub icon: Option<String>,
/// The notification sound.
pub sound: Option<SoundDto>,
}
/// The API descriptor.
@ -56,6 +88,9 @@ impl Cmd {
if let Some(icon) = options.icon {
notification = notification.icon(icon);
}
if let Some(sound) = options.sound {
notification = notification.sound(sound);
}
#[cfg(feature = "windows7-compat")]
{
notification.notify(&context.window.app_handle)?;
@ -84,16 +119,27 @@ impl Cmd {
#[cfg(test)]
mod tests {
use super::NotificationOptions;
use super::{NotificationOptions, SoundDto};
use quickcheck::{Arbitrary, Gen};
impl Arbitrary for SoundDto {
fn arbitrary(g: &mut Gen) -> Self {
if bool::arbitrary(g) {
Self::Default
} else {
Self::Custom(String::arbitrary(g))
}
}
}
impl Arbitrary for NotificationOptions {
fn arbitrary(g: &mut Gen) -> Self {
Self {
title: String::arbitrary(g),
body: Option::arbitrary(g),
icon: Option::arbitrary(g),
sound: Option::arbitrary(g),
}
}
}

View File

@ -1 +0,0 @@
Versions/Current/Headers

View File

@ -0,0 +1 @@
Versions/Current/Headers

View File

@ -1 +0,0 @@
Versions/Current/Resources

View File

@ -0,0 +1 @@
Versions/Current/Resources

View File

View File

@ -1 +0,0 @@
Versions/Current/test

View File

@ -0,0 +1 @@
Versions/Current/test

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,15 @@
<script>
import { sendNotification as notify } from '@tauri-apps/api/notification'
export let onMessage
// send the notification directly
// the backend is responsible for checking the permission
function _sendNotification() {
new Notification('Notification title', {
body: 'This is the notification body'
async function _sendNotification() {
notify({
title: 'Notification title',
body: 'This is the notification body',
sound: 'default'
})
}
@ -29,6 +33,6 @@
}
</script>
<button class="btn" id="notification" on:click={_sendNotification}>
<button class="btn" id="notification" on:click={sendNotification}>
Send test notification
</button>

View File

@ -65,4 +65,3 @@ package-lock.json
# Documentation output
docs/*
!docs/js-api.json

View File

@ -38,6 +38,31 @@ interface Options {
body?: string
/** Optional notification icon. */
icon?: string
/**
* Optional notification sound.
*
* #### Platform-specific
*
* Each OS has a different sound name so you will need to conditionally specify an appropriate sound
* based on the OS in use, 'default' represents the default system sound. For a list of sounds see:
* - **Linux**: can be one of the sounds listed in {@link https://0pointer.de/public/sound-naming-spec.html}
* - **Windows**: can be one of the sounds listed in {@link https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-audio}
* but without the prefix, for example, if `ms-winsoundevent:Notification.Default` you would use `Default` and
* if `ms-winsoundevent:Notification.Looping.Alarm2`, you would use `Alarm2`.
* Windows 7 is not supported, if a sound is provided, it will play the default sound, otherwise it will be silent.
* - **macOS**: you can specify the name of the sound you'd like to play when the notification is shown.
* Any of the default sounds (under System Preferences > Sound) can be used, in addition to custom sound files.
* Be sure that the sound file is copied under the app bundle (e.g., `YourApp.app/Contents/Resources`), or one of the following locations:
* - `~/Library/Sounds`
* - `/Library/Sounds`
* - `/Network/Library/Sounds`
* - `/System/Library/Sounds`
*
* See the {@link https://developer.apple.com/documentation/appkit/nssound | NSSound} docs for more information.
*
* @since 1.5.0
*/
sound?: 'default' | string
}
/** Possible permission values. */