Skip to content

Commit 6c408b7

Browse files
feat: add notification sound (#7269)
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio> Co-authored-by: Lucas Nogueira <lucas@tauri.app>
1 parent c272e4a commit 6c408b7

File tree

17 files changed

+866
-768
lines changed

17 files changed

+866
-768
lines changed

.changes/notification-sound.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri': 'minor:feat'
3+
'@tauri-apps/api': 'minor:feat'
4+
---
5+
6+
Add option to specify notification sound.

.github/workflows/check-generated-files.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ jobs:
3232
filters: |
3333
api:
3434
- 'tooling/api/src/**'
35-
- 'tooling/api/docs/js-api.json'
3635
- 'core/tauri/scripts/bundle.global.js'
3736
schema:
3837
- 'core/tauri-utils/src/config.rs'
@@ -50,9 +49,7 @@ jobs:
5049
working-directory: tooling/api
5150
run: yarn && yarn build
5251
- name: check api
53-
run: |
54-
git restore tooling/api/docs/js-api.json
55-
./.scripts/ci/has-diff.sh
52+
run: ./.scripts/ci/has-diff.sh
5653

5754
schema:
5855
runs-on: ubuntu-latest

.github/workflows/test-core.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ jobs:
9999
cargo update -p is-terminal --precise 0.4.7
100100
cargo update -p colored --precise 2.0.2
101101
cargo update -p tempfile --precise 3.6.0
102-
cargo update -p serde_with:3.1.0 --precise 3.0.0
102+
cargo update -p serde_with:3.2.0 --precise 3.0.0
103103
104104
- name: test
105105
run: cargo test --target ${{ matrix.platform.target }} ${{ matrix.features.args }}

.prettierignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,4 @@ dist
99
/tooling/cli/templates
1010
/tooling/cli/node
1111
/tooling/cli/schema.json
12-
/tooling/api/docs/js-api.json
1312
/core/tauri-config-schema/schema.json

core/tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ objc = "0.2"
103103

104104
[target."cfg(windows)".dependencies]
105105
webview2-com = "0.19.1"
106-
win7-notifications = { version = "0.3.1", optional = true }
106+
win7-notifications = { version = "0.4", optional = true }
107107

108108
[target."cfg(windows)".dependencies.windows]
109109
version = "0.39.0"

core/tauri/src/api/notification.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,50 @@ pub struct Notification {
4040
icon: Option<String>,
4141
/// The notification identifier
4242
identifier: String,
43+
/// The notification sound
44+
sound: Option<Sound>,
45+
}
46+
47+
/// Notification sound.
48+
#[derive(Debug)]
49+
pub enum Sound {
50+
/// The default notification sound.
51+
Default,
52+
/// A custom notification sound.
53+
///
54+
/// ## Platform-specific
55+
///
56+
/// Each OS has a different sound name so you will need to conditionally specify an appropriate sound
57+
/// based on the OS in use, for a list of sounds see:
58+
/// - **Linux**: can be one of the sounds listed in <https://0pointer.de/public/sound-naming-spec.html>
59+
/// - **Windows**: can be one of the sounds listed in <https://learn.microsoft.com/en-us/uwp/schemas/tiles/toastschema/element-audio>
60+
/// but without the prefix, for example, if `ms-winsoundevent:Notification.Default` you would use `Default` and
61+
/// if `ms-winsoundevent:Notification.Looping.Alarm2`, you would use `Alarm2`.
62+
/// Windows 7 is not supported, if a sound is provided, it will play the default sound, otherwise it will be silent.
63+
/// - **macOS**: you can specify the name of the sound you'd like to play when the notification is shown.
64+
/// Any of the default sounds (under System Preferences > Sound) can be used, in addition to custom sound files.
65+
/// Be sure that the sound file is under one of the following locations:
66+
/// - `~/Library/Sounds`
67+
/// - `/Library/Sounds`
68+
/// - `/Network/Library/Sounds`
69+
/// - `/System/Library/Sounds`
70+
///
71+
/// See the [`NSSound`] docs for more information.
72+
///
73+
/// [`NSSound`]: https://developer.apple.com/documentation/appkit/nssound
74+
Custom(String),
75+
}
76+
77+
impl From<String> for Sound {
78+
fn from(value: String) -> Self {
79+
Self::Custom(value)
80+
}
81+
}
82+
83+
impl From<&str> for Sound {
84+
fn from(value: &str) -> Self {
85+
Self::Custom(value.into())
86+
}
4387
}
4488

4589
impl Notification {
@@ -72,6 +116,15 @@ impl Notification {
72116
self
73117
}
74118

119+
/// Sets the notification sound. By default the notification has no sound.
120+
///
121+
/// See [`Sound`] for more information.
122+
#[must_use]
123+
pub fn sound(mut self, sound: impl Into<Sound>) -> Self {
124+
self.sound.replace(sound.into());
125+
self
126+
}
127+
75128
/// Shows the notification.
76129
///
77130
/// # Examples
@@ -108,6 +161,17 @@ impl Notification {
108161
} else {
109162
notification.auto_icon();
110163
}
164+
if let Some(sound) = self.sound {
165+
notification.sound_name(&match sound {
166+
#[cfg(target_os = "macos")]
167+
Sound::Default => "NSUserNotificationDefaultSoundName".to_string(),
168+
#[cfg(windows)]
169+
Sound::Default => "Default".to_string(),
170+
#[cfg(all(unix, not(target_os = "macos")))]
171+
Sound::Default => "message-new-instant".to_string(),
172+
Sound::Custom(c) => c,
173+
});
174+
}
111175
#[cfg(windows)]
112176
{
113177
let exe = tauri_utils::platform::current_exe()?;
@@ -191,6 +255,7 @@ impl Notification {
191255
if let Some(title) = self.title {
192256
notification.summary(&title);
193257
}
258+
notification.silent(self.sound.is_none());
194259
if let Some(crate::Icon::Rgba {
195260
rgba,
196261
width,

core/tauri/src/endpoints/notification.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use super::InvokeContext;
88
use crate::Runtime;
9-
use serde::Deserialize;
9+
use serde::{Deserialize, Deserializer};
1010
use tauri_macros::{command_enum, module_command_handler, CommandModule};
1111

1212
#[cfg(notification_all)]
@@ -17,6 +17,36 @@ const PERMISSION_GRANTED: &str = "granted";
1717
// `Denied` response from `request_permission`. Matches the Web API return value.
1818
const PERMISSION_DENIED: &str = "denied";
1919

20+
#[derive(Debug, Clone)]
21+
pub enum SoundDto {
22+
Default,
23+
Custom(String),
24+
}
25+
26+
#[cfg(notification_all)]
27+
impl From<SoundDto> for crate::api::notification::Sound {
28+
fn from(sound: SoundDto) -> Self {
29+
match sound {
30+
SoundDto::Default => crate::api::notification::Sound::Default,
31+
SoundDto::Custom(s) => crate::api::notification::Sound::Custom(s),
32+
}
33+
}
34+
}
35+
36+
impl<'de> Deserialize<'de> for SoundDto {
37+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
38+
where
39+
D: Deserializer<'de>,
40+
{
41+
let s = String::deserialize(deserializer)?;
42+
if s.to_lowercase() == "default" {
43+
Ok(Self::Default)
44+
} else {
45+
Ok(Self::Custom(s))
46+
}
47+
}
48+
}
49+
2050
/// The options for the notification API.
2151
#[derive(Debug, Clone, Deserialize)]
2252
pub struct NotificationOptions {
@@ -26,6 +56,8 @@ pub struct NotificationOptions {
2656
pub body: Option<String>,
2757
/// The notification icon.
2858
pub icon: Option<String>,
59+
/// The notification sound.
60+
pub sound: Option<SoundDto>,
2961
}
3062

3163
/// The API descriptor.
@@ -56,6 +88,9 @@ impl Cmd {
5688
if let Some(icon) = options.icon {
5789
notification = notification.icon(icon);
5890
}
91+
if let Some(sound) = options.sound {
92+
notification = notification.sound(sound);
93+
}
5994
#[cfg(feature = "windows7-compat")]
6095
{
6196
notification.notify(&context.window.app_handle)?;
@@ -84,16 +119,27 @@ impl Cmd {
84119

85120
#[cfg(test)]
86121
mod tests {
87-
use super::NotificationOptions;
122+
use super::{NotificationOptions, SoundDto};
88123

89124
use quickcheck::{Arbitrary, Gen};
90125

126+
impl Arbitrary for SoundDto {
127+
fn arbitrary(g: &mut Gen) -> Self {
128+
if bool::arbitrary(g) {
129+
Self::Default
130+
} else {
131+
Self::Custom(String::arbitrary(g))
132+
}
133+
}
134+
}
135+
91136
impl Arbitrary for NotificationOptions {
92137
fn arbitrary(g: &mut Gen) -> Self {
93138
Self {
94139
title: String::arbitrary(g),
95140
body: Option::arbitrary(g),
96141
icon: Option::arbitrary(g),
142+
sound: Option::arbitrary(g),
97143
}
98144
}
99145
}

core/tests/app-updater/frameworks/test.framework/Headers

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Versions/Current/Headers

core/tests/app-updater/frameworks/test.framework/Resources

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)