From 78a7d46be050842f5cf671b22dbad20093e7d84a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 28 Aug 2024 23:22:18 -0300 Subject: [PATCH 1/4] feat: use PermissionState type from tauri, closes #979 PermissionState now also includes a `prompt-with-rationale` variant, which is the Android way of saying "you should explain why your app needs this permission before prompting" (see https://developer.android.com/training/permissions/requesting#explain for more information) --- .changes/consolidate-permission-state.md | 10 +++++ .changes/{tauri-rc-7.md => tauri-rc-8.md} | 2 +- Cargo.lock | 4 +- Cargo.toml | 2 +- plugins/barcode-scanner/Cargo.toml | 3 ++ plugins/barcode-scanner/api-iife.js | 2 +- plugins/barcode-scanner/guest-js/index.ts | 16 +++++--- plugins/geolocation/guest-js/bindings.ts | 14 +++---- plugins/geolocation/src/models.rs | 14 +------ plugins/notification/src/commands.rs | 6 +-- plugins/notification/src/desktop.rs | 7 +++- plugins/notification/src/lib.rs | 1 + plugins/notification/src/mobile.rs | 2 +- plugins/notification/src/models.rs | 45 ----------------------- 14 files changed, 46 insertions(+), 82 deletions(-) create mode 100644 .changes/consolidate-permission-state.md rename .changes/{tauri-rc-7.md => tauri-rc-8.md} (97%) diff --git a/.changes/consolidate-permission-state.md b/.changes/consolidate-permission-state.md new file mode 100644 index 000000000..93f8c9ae6 --- /dev/null +++ b/.changes/consolidate-permission-state.md @@ -0,0 +1,10 @@ +--- +"barcode-scanner": patch +"barcode-scanner-js": patch +"geolocation": patch +"geolocation-js": patch +"notification": patch +"notification-js": patch +--- + +Use `PermissionState` from the `tauri` crate, which now also includes a "prompt with rationale" variant for Android (returned when your app must explain to the user why it needs the permission). diff --git a/.changes/tauri-rc-7.md b/.changes/tauri-rc-8.md similarity index 97% rename from .changes/tauri-rc-7.md rename to .changes/tauri-rc-8.md index 0d1e5e715..9a7083010 100644 --- a/.changes/tauri-rc-7.md +++ b/.changes/tauri-rc-8.md @@ -58,4 +58,4 @@ "geolocation-js": patch --- -Update to tauri 2.0.0-rc.7 +Update to tauri 2.0.0-rc.8 diff --git a/Cargo.lock b/Cargo.lock index 94040a0ae..c17e9f118 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6360,9 +6360,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.0.0-rc.7" +version = "2.0.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd19af7f228219ba1c18b8058594f444d9758f94f9e2643283985059f056d5bd" +checksum = "e8345ccc676ef16e26b61fc0f5340b4e770678b1e1f53f08c69ebdac5e56b422" dependencies = [ "anyhow", "bytes", diff --git a/Cargo.toml b/Cargo.toml index f4bf02827..9b9763b49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ resolver = "2" [workspace.dependencies] serde = { version = "1", features = ["derive"] } log = "0.4" -tauri = { version = "2.0.0-rc.7", default-features = false } +tauri = { version = "2.0.0-rc.8", default-features = false } tauri-build = "2.0.0-rc.7" tauri-plugin = "2.0.0-rc.7" tauri-utils = "2.0.0-rc.7" diff --git a/plugins/barcode-scanner/Cargo.toml b/plugins/barcode-scanner/Cargo.toml index 369b3999d..031cbd0eb 100644 --- a/plugins/barcode-scanner/Cargo.toml +++ b/plugins/barcode-scanner/Cargo.toml @@ -23,3 +23,6 @@ serde_json = { workspace = true } tauri = { workspace = true } log = { workspace = true } thiserror = { workspace = true } + +[target.'cfg(target_os = "ios")'.dependencies] +tauri = { workspace = true, features = ["wry"] } diff --git a/plugins/barcode-scanner/api-iife.js b/plugins/barcode-scanner/api-iife.js index 12841dfe0..cd6f1193c 100644 --- a/plugins/barcode-scanner/api-iife.js +++ b/plugins/barcode-scanner/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function e(n,e={},a){return window.__TAURI_INTERNALS__.invoke(n,e,a)}var a;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(a=n.Format||(n.Format={})).QRCode="QR_CODE",a.UPC_A="UPC_A",a.UPC_E="UPC_E",a.EAN8="EAN_8",a.EAN13="EAN_13",a.Code39="CODE_39",a.Code93="CODE_93",a.Code128="CODE_128",a.Codabar="CODABAR",a.ITF="ITF",a.Aztec="AZTEC",a.DataMatrix="DATA_MATRIX",a.PDF417="PDF_417",n.cancel=async function(){await e("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await e("plugin:barcode-scanner|check_permissions").then((n=>n.camera))},n.openAppSettings=async function(){await e("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await e("plugin:barcode-scanner|request_permissions").then((n=>n.camera))},n.scan=async function(n){return await e("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_BARCODE_SCANNER__=function(n){"use strict";async function e(n,e={},r){return window.__TAURI_INTERNALS__.invoke(n,e,r)}var r;return"function"==typeof SuppressedError&&SuppressedError,n.Format=void 0,(r=n.Format||(n.Format={})).QRCode="QR_CODE",r.UPC_A="UPC_A",r.UPC_E="UPC_E",r.EAN8="EAN_8",r.EAN13="EAN_13",r.Code39="CODE_39",r.Code93="CODE_93",r.Code128="CODE_128",r.Codabar="CODABAR",r.ITF="ITF",r.Aztec="AZTEC",r.DataMatrix="DATA_MATRIX",r.PDF417="PDF_417",n.cancel=async function(){await e("plugin:barcode-scanner|cancel")},n.checkPermissions=async function(){return await async function(n){return e(`plugin:${n}|request_permissions`)}("barcode-scanner").then((n=>n.camera))},n.openAppSettings=async function(){await e("plugin:barcode-scanner|open_app_settings")},n.requestPermissions=async function(){return await async function(n){return e(`plugin:${n}|check_permissions`)}("barcode-scanner").then((n=>n.camera))},n.scan=async function(n){return await e("plugin:barcode-scanner|scan",{...n})},n}({});Object.defineProperty(window.__TAURI__,"barcodeScanner",{value:__TAURI_PLUGIN_BARCODE_SCANNER__})} diff --git a/plugins/barcode-scanner/guest-js/index.ts b/plugins/barcode-scanner/guest-js/index.ts index d766e0dc1..233095c5a 100644 --- a/plugins/barcode-scanner/guest-js/index.ts +++ b/plugins/barcode-scanner/guest-js/index.ts @@ -2,9 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -import { invoke } from "@tauri-apps/api/core"; +import { + invoke, + requestPermissions as checkPermissions_, + checkPermissions as requestPermissions_, +} from "@tauri-apps/api/core"; -export type PermissionState = "granted" | "denied" | "prompt"; +export type { PermissionState } from "@tauri-apps/api/core"; export enum Format { QRCode = "QR_CODE", @@ -53,8 +57,8 @@ export async function cancel(): Promise { * Get permission state. */ export async function checkPermissions(): Promise { - return await invoke<{ camera: PermissionState }>( - "plugin:barcode-scanner|check_permissions", + return await checkPermissions_<{ camera: PermissionState }>( + "barcode-scanner" ).then((r) => r.camera); } @@ -62,8 +66,8 @@ export async function checkPermissions(): Promise { * Request permissions to use the camera. */ export async function requestPermissions(): Promise { - return await invoke<{ camera: PermissionState }>( - "plugin:barcode-scanner|request_permissions", + return await requestPermissions_<{ camera: PermissionState }>( + "barcode-scanner" ).then((r) => r.camera); } diff --git a/plugins/geolocation/guest-js/bindings.ts b/plugins/geolocation/guest-js/bindings.ts index 6bff7b548..ab82643b6 100644 --- a/plugins/geolocation/guest-js/bindings.ts +++ b/plugins/geolocation/guest-js/bindings.ts @@ -9,7 +9,7 @@ export const commands = { async getCurrentPosition( - options: PositionOptions | null, + options: PositionOptions | null ): Promise> { try { return { @@ -25,7 +25,7 @@ export const commands = { }, async watchPosition( options: PositionOptions, - channel: any, + channel: any ): Promise> { try { return { @@ -65,7 +65,7 @@ export const commands = { } }, async requestPermissions( - permissions: PermissionType[] | null, + permissions: PermissionType[] | null ): Promise> { try { return { @@ -198,10 +198,10 @@ import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webview type __EventObj__ = { listen: ( - cb: TAURI_API_EVENT.EventCallback, + cb: TAURI_API_EVENT.EventCallback ) => ReturnType>; once: ( - cb: TAURI_API_EVENT.EventCallback, + cb: TAURI_API_EVENT.EventCallback ) => ReturnType>; emit: T extends null ? (payload?: T) => ReturnType @@ -213,7 +213,7 @@ export type Result = | { status: "error"; error: E }; function __makeEvents__>( - mappings: Record, + mappings: Record ) { return new Proxy( {} as unknown as { @@ -243,6 +243,6 @@ function __makeEvents__>( }, }); }, - }, + } ); } diff --git a/plugins/geolocation/src/models.rs b/plugins/geolocation/src/models.rs index b2e6e5fbe..165c036fc 100644 --- a/plugins/geolocation/src/models.rs +++ b/plugins/geolocation/src/models.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use specta::Type; +use tauri::plugin::PermissionState; #[derive(Debug, Clone, Default, Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] @@ -24,19 +25,6 @@ pub struct PermissionStatus { pub coarse_location: PermissionState, } -/// Permission state. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, Type)] -#[serde(rename_all = "camelCase")] -pub enum PermissionState { - /// Permission access has been granted. - Granted, - /// Permission access has been denied. - Denied, - /// The end user should be prompted for permission. - #[default] - Prompt, -} - #[derive(Debug, Clone, Default, Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] pub struct PositionOptions { diff --git a/plugins/notification/src/commands.rs b/plugins/notification/src/commands.rs index 4af855857..58dc5d843 100644 --- a/plugins/notification/src/commands.rs +++ b/plugins/notification/src/commands.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use tauri::{command, AppHandle, Runtime, State}; +use tauri::{command, plugin::PermissionState, AppHandle, Runtime, State}; -use crate::{Notification, NotificationData, PermissionState, Result}; +use crate::{Notification, NotificationData, Result}; #[command] pub(crate) async fn is_permission_granted( @@ -15,7 +15,7 @@ pub(crate) async fn is_permission_granted( match state { PermissionState::Granted => Ok(Some(true)), PermissionState::Denied => Ok(Some(false)), - PermissionState::Unknown => Ok(None), + PermissionState::Unknown | PermissionState::PromptWithRationale => Ok(None), } } diff --git a/plugins/notification/src/desktop.rs b/plugins/notification/src/desktop.rs index 05a72a47a..092b88924 100644 --- a/plugins/notification/src/desktop.rs +++ b/plugins/notification/src/desktop.rs @@ -3,9 +3,12 @@ // SPDX-License-Identifier: MIT use serde::de::DeserializeOwned; -use tauri::{plugin::PluginApi, AppHandle, Runtime}; +use tauri::{ + plugin::{PermissionState, PluginApi}, + AppHandle, Runtime, +}; -use crate::{models::*, NotificationBuilder}; +use crate::NotificationBuilder; pub fn init( app: &AppHandle, diff --git a/plugins/notification/src/lib.rs b/plugins/notification/src/lib.rs index 35304a6b9..aa15cc933 100644 --- a/plugins/notification/src/lib.rs +++ b/plugins/notification/src/lib.rs @@ -22,6 +22,7 @@ use tauri::{ }; pub use models::*; +pub use tauri::plugin::PermissionState; #[cfg(desktop)] mod desktop; diff --git a/plugins/notification/src/mobile.rs b/plugins/notification/src/mobile.rs index b786ed5c8..9167dcc1e 100644 --- a/plugins/notification/src/mobile.rs +++ b/plugins/notification/src/mobile.rs @@ -4,7 +4,7 @@ use serde::{de::DeserializeOwned, Deserialize}; use tauri::{ - plugin::{PluginApi, PluginHandle}, + plugin::{PermissionState, PluginApi, PluginHandle}, AppHandle, Runtime, }; diff --git a/plugins/notification/src/models.rs b/plugins/notification/src/models.rs index 4c260597f..02134e4db 100644 --- a/plugins/notification/src/models.rs +++ b/plugins/notification/src/models.rs @@ -209,51 +209,6 @@ impl Default for NotificationData { } } -/// Permission state. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PermissionState { - /// Permission access has been granted. - Granted, - /// Permission access has been denied. - Denied, - /// Unknown state. Must request permission. - Unknown, -} - -impl Display for PermissionState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Granted => write!(f, "granted"), - Self::Denied => write!(f, "denied"), - Self::Unknown => write!(f, "Unknown"), - } - } -} - -impl Serialize for PermissionState { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, - { - serializer.serialize_str(self.to_string().as_ref()) - } -} - -impl<'de> Deserialize<'de> for PermissionState { - fn deserialize(deserializer: D) -> std::result::Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - match s.to_lowercase().as_str() { - "granted" => Ok(Self::Granted), - "denied" => Ok(Self::Denied), - "prompt" => Ok(Self::Unknown), - _ => Err(DeError::custom(format!("unknown permission state '{s}'"))), - } - } -} - #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PendingNotification { From e3bee03616ce8be977b6e7189e316e8477205af3 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 28 Aug 2024 23:24:06 -0300 Subject: [PATCH 2/4] fmt --- plugins/barcode-scanner/guest-js/index.ts | 4 ++-- plugins/geolocation/guest-js/bindings.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/barcode-scanner/guest-js/index.ts b/plugins/barcode-scanner/guest-js/index.ts index 233095c5a..bf0edf8a5 100644 --- a/plugins/barcode-scanner/guest-js/index.ts +++ b/plugins/barcode-scanner/guest-js/index.ts @@ -58,7 +58,7 @@ export async function cancel(): Promise { */ export async function checkPermissions(): Promise { return await checkPermissions_<{ camera: PermissionState }>( - "barcode-scanner" + "barcode-scanner", ).then((r) => r.camera); } @@ -67,7 +67,7 @@ export async function checkPermissions(): Promise { */ export async function requestPermissions(): Promise { return await requestPermissions_<{ camera: PermissionState }>( - "barcode-scanner" + "barcode-scanner", ).then((r) => r.camera); } diff --git a/plugins/geolocation/guest-js/bindings.ts b/plugins/geolocation/guest-js/bindings.ts index ab82643b6..6bff7b548 100644 --- a/plugins/geolocation/guest-js/bindings.ts +++ b/plugins/geolocation/guest-js/bindings.ts @@ -9,7 +9,7 @@ export const commands = { async getCurrentPosition( - options: PositionOptions | null + options: PositionOptions | null, ): Promise> { try { return { @@ -25,7 +25,7 @@ export const commands = { }, async watchPosition( options: PositionOptions, - channel: any + channel: any, ): Promise> { try { return { @@ -65,7 +65,7 @@ export const commands = { } }, async requestPermissions( - permissions: PermissionType[] | null + permissions: PermissionType[] | null, ): Promise> { try { return { @@ -198,10 +198,10 @@ import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webview type __EventObj__ = { listen: ( - cb: TAURI_API_EVENT.EventCallback + cb: TAURI_API_EVENT.EventCallback, ) => ReturnType>; once: ( - cb: TAURI_API_EVENT.EventCallback + cb: TAURI_API_EVENT.EventCallback, ) => ReturnType>; emit: T extends null ? (payload?: T) => ReturnType @@ -213,7 +213,7 @@ export type Result = | { status: "error"; error: E }; function __makeEvents__>( - mappings: Record + mappings: Record, ) { return new Proxy( {} as unknown as { @@ -243,6 +243,6 @@ function __makeEvents__>( }, }); }, - } + }, ); } From 8fb52f277b7a37e53da4762779f4af58427799d8 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Wed, 28 Aug 2024 23:36:32 -0300 Subject: [PATCH 3/4] tweak notification permission type --- .changes/notification-permission-type-change.md | 5 +++++ plugins/notification/guest-js/index.ts | 8 +++----- plugins/notification/guest-js/init.ts | 15 +++++++-------- plugins/notification/src/init-iife.js | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 .changes/notification-permission-type-change.md diff --git a/.changes/notification-permission-type-change.md b/.changes/notification-permission-type-change.md new file mode 100644 index 000000000..3616e5b04 --- /dev/null +++ b/.changes/notification-permission-type-change.md @@ -0,0 +1,5 @@ +--- +"notification-js": patch +--- + +**Breaking change**: The permission type when using the API is now `'granted' | 'denied' | 'prompt' | 'prompt-with-rationale'` instead of `'granted' | 'denied' | 'default'` to better support mobile. When using the `window.Notification` API the type is unchanged to match the Web API type. diff --git a/plugins/notification/guest-js/index.ts b/plugins/notification/guest-js/index.ts index dc89890f3..efc466a22 100644 --- a/plugins/notification/guest-js/index.ts +++ b/plugins/notification/guest-js/index.ts @@ -15,6 +15,8 @@ import { addPluginListener, } from "@tauri-apps/api/core"; +export type { PermissionState } from "@tauri-apps/api/core"; + /** * Options to send a notification. * @@ -304,9 +306,6 @@ interface Channel { visibility?: Visibility; } -/** Possible permission values. */ -type Permission = "granted" | "denied" | "default"; - /** * Checks if the permission to send notifications is granted. * @example @@ -340,7 +339,7 @@ async function isPermissionGranted(): Promise { * * @since 2.0.0 */ -async function requestPermission(): Promise { +async function requestPermission(): Promise { return await window.Notification.requestPermission(); } @@ -570,7 +569,6 @@ async function onAction( export type { Attachment, Options, - Permission, Action, ActionType, PendingNotification, diff --git a/plugins/notification/guest-js/init.ts b/plugins/notification/guest-js/init.ts index 73d25facd..ec98403d4 100644 --- a/plugins/notification/guest-js/init.ts +++ b/plugins/notification/guest-js/init.ts @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT import { invoke } from "@tauri-apps/api/core"; +import type { PermissionState } from "@tauri-apps/api/core"; import type { Options } from "./index"; (function () { @@ -19,23 +20,21 @@ import type { Options } from "./index"; return await invoke("plugin:notification|is_permission_granted"); } - function setNotificationPermission( - value: "granted" | "denied" | "default", - ): void { + function setNotificationPermission(value: NotificationPermission): void { permissionSettable = true; // @ts-expect-error we can actually set this value on the webview window.Notification.permission = value; permissionSettable = false; } - async function requestPermission(): Promise< - "default" | "denied" | "granted" | "prompt" - > { - return await invoke<"prompt" | "default" | "granted" | "denied">( + async function requestPermission(): Promise { + return await invoke( "plugin:notification|request_permission", ).then((permission) => { setNotificationPermission( - permission === "prompt" ? "default" : permission, + permission === "prompt" || permission === "prompt-with-rationale" + ? "default" + : permission, ); return permission; }); diff --git a/plugins/notification/src/init-iife.js b/plugins/notification/src/init-iife.js index eb3b0cce1..30bff97dc 100644 --- a/plugins/notification/src/init-iife.js +++ b/plugins/notification/src/init-iife.js @@ -1 +1 @@ -!function(){"use strict";async function i(i,n={},t){return window.__TAURI_INTERNALS__.invoke(i,n,t)}"function"==typeof SuppressedError&&SuppressedError,function(){let n=!1,t="default";function o(i){n=!0,window.Notification.permission=i,n=!1}window.Notification=function(n,t){const o=t||{};!async function(n){"object"==typeof n&&Object.freeze(n),await i("plugin:notification|notify",{options:"string"==typeof n?{title:n}:n})}(Object.assign(o,{title:n}))},window.Notification.requestPermission=async function(){return await i("plugin:notification|request_permission").then((i=>(o("prompt"===i?"default":i),i)))},Object.defineProperty(window.Notification,"permission",{enumerable:!0,get:()=>t,set:i=>{if(!n)throw new Error("Readonly property");t=i}}),async function(){return"default"!==window.Notification.permission||__TEMPLATE_windows__?await Promise.resolve("granted"===window.Notification.permission):await i("plugin:notification|is_permission_granted")}().then((function(i){o(null===i?"default":i?"granted":"denied")}))}()}(); +!function(){"use strict";async function i(i,n={},t){return window.__TAURI_INTERNALS__.invoke(i,n,t)}"function"==typeof SuppressedError&&SuppressedError,function(){let n=!1,t="default";function o(i){n=!0,window.Notification.permission=i,n=!1}window.Notification=function(n,t){const o=t||{};!async function(n){"object"==typeof n&&Object.freeze(n),await i("plugin:notification|notify",{options:"string"==typeof n?{title:n}:n})}(Object.assign(o,{title:n}))},window.Notification.requestPermission=async function(){return await i("plugin:notification|request_permission").then((i=>(o("prompt"===i||"prompt-with-rationale"===i?"default":i),i)))},Object.defineProperty(window.Notification,"permission",{enumerable:!0,get:()=>t,set:i=>{if(!n)throw new Error("Readonly property");t=i}}),async function(){return"default"!==window.Notification.permission||__TEMPLATE_windows__?await Promise.resolve("granted"===window.Notification.permission):await i("plugin:notification|is_permission_granted")}().then((function(i){o(null===i?"default":i?"granted":"denied")}))}()}(); From f51909f7247aa473c607a2d9acc708f5df7b99c7 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Thu, 29 Aug 2024 00:01:09 -0300 Subject: [PATCH 4/4] update change file --- .changes/notification-permission-type-change.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/notification-permission-type-change.md b/.changes/notification-permission-type-change.md index 3616e5b04..451c8b779 100644 --- a/.changes/notification-permission-type-change.md +++ b/.changes/notification-permission-type-change.md @@ -2,4 +2,4 @@ "notification-js": patch --- -**Breaking change**: The permission type when using the API is now `'granted' | 'denied' | 'prompt' | 'prompt-with-rationale'` instead of `'granted' | 'denied' | 'default'` to better support mobile. When using the `window.Notification` API the type is unchanged to match the Web API type. +**Breaking change**: The permission type when using the API is now `'granted' | 'denied' | 'prompt' | 'prompt-with-rationale'` instead of `'granted' | 'denied' | 'default'` for consistency with Rust types. When using the `window.Notification` API the type is unchanged to match the Web API type.