From 36d02fc146f410ddb453526c4601abeb6dea6f74 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Fri, 12 Jun 2020 14:19:24 -0300 Subject: [PATCH] feat(notifications) use confirm() instead of dialog crate --- cli/tauri.js/templates/tauri.js | 31 +- tauri-api/src/dialog.rs | 15 - tauri/examples/communication/dist/index.html | 549 +++++++++--------- .../communication/dist/index.tauri.html | 79 ++- .../communication/dist/notification.js | 21 +- .../examples/communication/src-tauri/tauri.js | 79 +++ tauri/src/endpoints.rs | 79 ++- tauri/src/endpoints/cmd.rs | 7 + tauri/src/settings.rs | 1 + 9 files changed, 550 insertions(+), 311 deletions(-) diff --git a/cli/tauri.js/templates/tauri.js b/cli/tauri.js/templates/tauri.js index e8eed25f2440..90451b7ac8ce 100644 --- a/cli/tauri.js/templates/tauri.js +++ b/cli/tauri.js/templates/tauri.js @@ -666,12 +666,17 @@ window.tauri = { Object.freeze(options); } - return this.promisified({ - cmd: 'notification', - options: typeof options === 'string' ? { - body: options - } : options - }); + return window.tauri.isNotificationPermissionGranted() + .then(function (permission) { + if (permission) { + return window.tauri.promisified({ + cmd: 'notification', + options: typeof options === 'string' ? { + body: options + } : options + }); + } + }) <% } else { %> <% if (ctx.dev) { %> return __whitelistWarning('notification') @@ -682,6 +687,9 @@ window.tauri = { isNotificationPermissionGranted: function isNotificationPermissionGranted() { <% if (tauri.whitelist.notification === true || tauri.whitelist.all === true) { %> + if (window.Notification.permission !== 'default' && window.Notification.permission !== 'loading') { + return Promise.resolve(window.Notification.permission === 'granted') + } return window.tauri.promisified({ cmd: 'isNotificationPermissionGranted' }) @@ -697,6 +705,14 @@ window.tauri = { <% if (tauri.whitelist.notification === true || tauri.whitelist.all === true) { %> return window.tauri.promisified({ cmd: 'requestNotificationPermission' + }).then(function (state) { + if (state === 'default') { + return window.tauri.isNotificationPermissionGranted() + .then(function (permission) { + return permission === 'granted' + }) + } + return state }) <% } else { %> <% if (ctx.dev) { %> @@ -729,8 +745,9 @@ window.tauri = { .then(function (response) { if (response === null) { window.Notification.permission = 'default' + } else { + window.Notification.permission = response ? 'granted' : 'denied' } - window.Notification.permission = response ? 'granted' : 'denied' }) <% } %> diff --git a/tauri-api/src/dialog.rs b/tauri-api/src/dialog.rs index 05453c0d28cf..e3ab4cf35f88 100644 --- a/tauri-api/src/dialog.rs +++ b/tauri-api/src/dialog.rs @@ -1,7 +1,5 @@ pub use nfd::Response; use nfd::{open_dialog, DialogType}; -use dialog::DialogBox; -pub use dialog::Choice as DialogAnswer; fn open_dialog_internal( dialog_type: DialogType, @@ -15,19 +13,6 @@ fn open_dialog_internal( } } -/// Displays a dialog with a message and an optional title with a "yes" and a "no" button. -pub fn ask( - message: String, - title: Option, -) -> crate::Result { - let mut question = dialog::Question::new(message); - if let Some(title) = title { - question.title(title); - } - question.show() - .map_err(|e| anyhow::anyhow!(e)) -} - /// Open single select file dialog pub fn select( filter_list: Option, diff --git a/tauri/examples/communication/dist/index.html b/tauri/examples/communication/dist/index.html index 6d32b541a201..1774b96da359 100644 --- a/tauri/examples/communication/dist/index.html +++ b/tauri/examples/communication/dist/index.html @@ -1,282 +1,305 @@ - - - - -
- -
-
-
-
- - -
- - - - - -
- - -
+ + + + + +
+ +
+ +
+
+
+ + +
+ + + + + +
+ +
-
- - -
-
- - - +
+
+ + +
+
+ + + +
+
+ + +
+ +
-
- - -
- - -
-
- - -
- - - +
+ +
+ + +
+
-
- - -
-
- - -
+
+ + +
+
+ + +
-
- - -
- -
- -
+
+ + +
+ +
+
-
- - - + + + + + + + + - var dirSelect = document.getElementById('dir') - for (var key in window.tauri.Dir) { - var value = window.tauri.Dir[key] - var opt = document.createElement("option") - opt.value = value - opt.innerHTML = key - dirSelect.appendChild(opt) - } - - - - - - - - diff --git a/tauri/examples/communication/dist/index.tauri.html b/tauri/examples/communication/dist/index.tauri.html index 52015531381f..b28c37c34bc0 100644 --- a/tauri/examples/communication/dist/index.tauri.html +++ b/tauri/examples/communication/dist/index.tauri.html @@ -562,6 +562,64 @@ options: options }); + }, + + /** + * @name notification + * @description Display a desktop notification + * @param {Object|String} options the notifications options if an object, otherwise its body + * @param {String} [options.summary] the notification's summary + * @param {String} options.body the notification's body + * @param {String} [options.icon] the notifications's icon + * @returns {*|Promise|Promise} + */ + + notification: function notification(options) { + + + if (_typeof(options) === 'object') { + Object.freeze(options); + } + + return window.tauri.isNotificationPermissionGranted() + .then(function (permission) { + if (permission) { + return window.tauri.promisified({ + cmd: 'notification', + options: typeof options === 'string' ? { + body: options + } : options + }); + } + }) + + }, + + isNotificationPermissionGranted: function isNotificationPermissionGranted() { + + if (window.Notification.permission !== 'default' && window.Notification.permission !== 'loading') { + return Promise.resolve(window.Notification.permission === 'granted') + } + return window.tauri.promisified({ + cmd: 'isNotificationPermissionGranted' + }) + + }, + + requestNotificationPermission: function requestNotificationPermission() { + + return window.tauri.promisified({ + cmd: 'requestNotificationPermission' + }).then(function (state) { + if (state === 'default') { + return window.tauri.isNotificationPermissionGranted() + .then(function (permission) { + return permission === 'granted' + }) + } + return state + }) + }, loadAsset: function loadAsset(assetName, assetType) { @@ -574,10 +632,23 @@ }; - window.Notification = function (options) { + window.Notification = function (title, options) { + if (options === void 0) { + options = {} + } + options.title = title window.tauri.notification(options) } - window.Notification.permission = 'granted' + window.Notification.requestPermission = window.tauri.requestNotificationPermission + window.Notification.permission = 'loading' + window.tauri.isNotificationPermissionGranted() + .then(function (response) { + if (response === null) { + window.Notification.permission = 'default' + } else { + window.Notification.permission = response ? 'granted' : 'denied' + } + }) @@ -666,7 +737,7 @@ subtree: true }) })() -


\ No newline at end of file +


\ No newline at end of file diff --git a/tauri/examples/communication/dist/notification.js b/tauri/examples/communication/dist/notification.js index 55b34c58b5b1..95ca957f8926 100644 --- a/tauri/examples/communication/dist/notification.js +++ b/tauri/examples/communication/dist/notification.js @@ -1,6 +1,21 @@ -document.getElementById('notification').addEventListener('click', function () { - new Notification({ - title: 'Notification title', +function sendNotification() { + new Notification('Notification title', { body: 'This is the notification body' }) +} + +document.getElementById('notification').addEventListener('click', function () { + if (Notification.permission === 'default') { + Notification.requestPermission().then(function (response) { + if (response === 'granted') { + sendNotification() + } else { + registerResponse('Permission is ' + response) + } + }).catch(registerResponse) + } else if (Notification.permission === 'granted') { + sendNotification() + } else { + registerResponse('Permission is denied') + } }) diff --git a/tauri/examples/communication/src-tauri/tauri.js b/tauri/examples/communication/src-tauri/tauri.js index 95e413962621..4467b03c48cf 100644 --- a/tauri/examples/communication/src-tauri/tauri.js +++ b/tauri/examples/communication/src-tauri/tauri.js @@ -562,6 +562,64 @@ window.tauri = { options: options }); + }, + + /** + * @name notification + * @description Display a desktop notification + * @param {Object|String} options the notifications options if an object, otherwise its body + * @param {String} [options.summary] the notification's summary + * @param {String} options.body the notification's body + * @param {String} [options.icon] the notifications's icon + * @returns {*|Promise|Promise} + */ + + notification: function notification(options) { + + + if (_typeof(options) === 'object') { + Object.freeze(options); + } + + return window.tauri.isNotificationPermissionGranted() + .then(function (permission) { + if (permission) { + return window.tauri.promisified({ + cmd: 'notification', + options: typeof options === 'string' ? { + body: options + } : options + }); + } + }) + + }, + + isNotificationPermissionGranted: function isNotificationPermissionGranted() { + + if (window.Notification.permission !== 'default' && window.Notification.permission !== 'loading') { + return Promise.resolve(window.Notification.permission === 'granted') + } + return window.tauri.promisified({ + cmd: 'isNotificationPermissionGranted' + }) + + }, + + requestNotificationPermission: function requestNotificationPermission() { + + return window.tauri.promisified({ + cmd: 'requestNotificationPermission' + }).then(function (state) { + if (state === 'default') { + return window.tauri.isNotificationPermissionGranted() + .then(function (permission) { + return permission === 'granted' + }) + } + return state + }) + }, loadAsset: function loadAsset(assetName, assetType) { @@ -573,6 +631,27 @@ window.tauri = { } }; + + window.Notification = function (title, options) { + if (options === void 0) { + options = {} + } + options.title = title + window.tauri.notification(options) + } + window.Notification.requestPermission = window.tauri.requestNotificationPermission + window.Notification.permission = 'loading' + window.tauri.isNotificationPermissionGranted() + .then(function (response) { + if (response === null) { + window.Notification.permission = 'default' + } else { + window.Notification.permission = response ? 'granted' : 'denied' + } + }) + + + // init tauri API try { window.tauri.invoke({ diff --git a/tauri/src/endpoints.rs b/tauri/src/endpoints.rs index 61acad81b33c..51820d284416 100644 --- a/tauri/src/endpoints.rs +++ b/tauri/src/endpoints.rs @@ -12,6 +12,15 @@ use web_view::WebView; #[cfg(windows)] use std::path::MAIN_SEPARATOR; +#[cfg(any(feature = "all-api", feature = "notification"))] +use once_cell::sync::Lazy; + +#[cfg(any(feature = "all-api", feature = "notification"))] +fn notification_permission_id() -> &'static String { + static ID: Lazy = Lazy::new(|| uuid::Uuid::new_v4().to_string()); + &ID +} + #[allow(unused_variables)] pub(crate) fn handle( webview: &mut WebView<'_, T>, @@ -202,6 +211,44 @@ pub(crate) fn handle( )?; } #[cfg(any(feature = "all-api", feature = "notification"))] + RequestNotificationPermission { callback, error } => { + let handle = webview.handle(); + crate::execute_promise( + webview, + move || { + let settings = crate::settings::read_settings()?; + let granted = r#""granted""#.to_string(); + let denied = r#""denied""#.to_string(); + if let Some(allow_notification) = settings.allow_notification { + return Ok(if allow_notification { granted } else { denied }); + } + handle.dispatch(|dwebview| { + dwebview.eval( + &format!( + " + var __tauriNotificationPermissionCheck = confirm('This app wants to show notifications. Do you allow?') + window.Notification.permission = __tauriNotificationPermissionCheck ? 'granted' : 'denied' + window.tauri.promisified({{ + cmd: 'saveNotificationPermission', + id: '{}', + permission: __tauriNotificationPermissionCheck + }}) + .catch(function (err) {{ + console.error(err) + alert('Failed to save permission state.') + }}) + ", + notification_permission_id() + ) + ) + }).expect("failed to eval request permission dialog"); + Ok(r#""default""#.to_string()) + }, + callback, + error, + ) + } + #[cfg(any(feature = "all-api", feature = "notification"))] IsNotificationPermissionGranted { callback, error } => { crate::execute_promise( webview, @@ -218,27 +265,21 @@ pub(crate) fn handle( ); } #[cfg(any(feature = "all-api", feature = "notification"))] - RequestNotificationPermission { callback, error } => crate::execute_promise( + SaveNotificationPermission { + id, + permission, + callback, + error, + } => crate::execute_promise( webview, move || { - let mut settings = crate::settings::read_settings()?; - let granted = r#""granted""#.to_string(); - let denied = r#""denied""#.to_string(); - if let Some(allow_notification) = settings.allow_notification { - return Ok(if allow_notification { granted } else { denied }); - } - let answer = tauri_api::dialog::ask( - "This app wants to show notifications. Do you allow?".to_string(), - Some("Permissions".to_string()), - )?; - match answer { - tauri_api::dialog::DialogAnswer::Yes => { - settings.allow_notification = Some(true); - crate::settings::write_settings(settings)?; - Ok(granted) - } - tauri_api::dialog::DialogAnswer::No => Ok(denied), - tauri_api::dialog::DialogAnswer::Cancel => Ok(r#""default""#.to_string()), + if id != notification_permission_id().to_string() { + Err(anyhow::anyhow!(r#""can't call this function directly""#)) + } else { + let mut settings = crate::settings::read_settings()?; + settings.allow_notification = Some(true); + crate::settings::write_settings(settings)?; + Ok("".to_string()) } }, callback, diff --git a/tauri/src/endpoints/cmd.rs b/tauri/src/endpoints/cmd.rs index 52f54431d50b..f97d31ba76f1 100644 --- a/tauri/src/endpoints/cmd.rs +++ b/tauri/src/endpoints/cmd.rs @@ -183,4 +183,11 @@ pub enum Cmd { callback: String, error: String, }, + #[cfg(any(feature = "all-api", feature = "notification"))] + SaveNotificationPermission { + id: String, + permission: bool, + callback: String, + error: String, + }, } diff --git a/tauri/src/settings.rs b/tauri/src/settings.rs index fb4e58ab0180..34eb59b6a654 100644 --- a/tauri/src/settings.rs +++ b/tauri/src/settings.rs @@ -18,6 +18,7 @@ fn get_settings_path() -> tauri_api::Result { pub(crate) fn write_settings(settings: Settings) -> crate::Result<()> { let settings_path = get_settings_path()?; + std::fs::create_dir(Path::new(&settings_path).parent().unwrap())?; File::create(settings_path) .map_err(|e| anyhow!(e)) .and_then(|mut f| {