leptos_use/
use_permission.rs

1use leptos::prelude::*;
2use leptos::reactive::wrappers::read::Signal;
3use std::fmt::Display;
4
5/// Reactive [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API).
6///
7/// ## Demo
8///
9/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_permission)
10///
11/// ## Usage
12///
13/// ```
14/// # use leptos::prelude::*;
15/// # use leptos_use::use_permission;
16/// #
17/// # #[component]
18/// # fn Demo() -> impl IntoView {
19/// let microphone_access = use_permission("microphone");
20/// #
21/// # view! { }
22/// # }
23/// ```
24///
25/// ## Server-Side Rendering
26///
27/// On the server the returned signal will always be `PermissionState::Unknown`.
28pub fn use_permission(permission_name: &str) -> Signal<PermissionState> {
29    let (state, set_state) = signal(PermissionState::Unknown);
30
31    #[cfg(not(feature = "ssr"))]
32    {
33        use crate::use_event_listener;
34        use std::cell::RefCell;
35        use std::rc::Rc;
36
37        let permission_status = Rc::new(RefCell::new(None::<web_sys::PermissionStatus>));
38
39        let on_change = {
40            let permission_status = Rc::clone(&permission_status);
41
42            move || {
43                if let Some(permission_status) = permission_status.borrow().as_ref() {
44                    set_state.set(PermissionState::from(permission_status.state()));
45                }
46            }
47        };
48
49        leptos::task::spawn_local({
50            let permission_name = permission_name.to_owned();
51
52            async move {
53                if let Ok(status) = query_permission(permission_name).await {
54                    let _ = use_event_listener(status.clone(), leptos::ev::change, {
55                        let on_change = on_change.clone();
56                        move |_| on_change()
57                    });
58                    permission_status.replace(Some(status));
59                    on_change();
60                } else {
61                    set_state.set(PermissionState::Prompt);
62                }
63            }
64        });
65    }
66
67    #[cfg(feature = "ssr")]
68    {
69        let _ = set_state;
70        let _ = permission_name;
71    }
72
73    state.into()
74}
75
76/// Return type of [`use_permission`].
77#[derive(Copy, Clone, Default, Eq, PartialEq)]
78pub enum PermissionState {
79    /// State hasn't been requested yet. This is the initial value.
80    #[default]
81    Unknown,
82
83    /// The permission has been granted by the user.
84    Granted,
85
86    /// The user will automatically be prompted to give permission once the relevant API is called.
87    Prompt,
88
89    /// The user has denied permission.
90    Denied,
91}
92
93impl Display for PermissionState {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        match *self {
96            PermissionState::Unknown => write!(f, "unknown"),
97            PermissionState::Granted => write!(f, "granted"),
98            PermissionState::Prompt => write!(f, "prompt"),
99            PermissionState::Denied => write!(f, "denied"),
100        }
101    }
102}
103
104impl From<web_sys::PermissionState> for PermissionState {
105    fn from(permission_state: web_sys::PermissionState) -> Self {
106        match permission_state {
107            web_sys::PermissionState::Granted => PermissionState::Granted,
108            web_sys::PermissionState::Prompt => PermissionState::Prompt,
109            web_sys::PermissionState::Denied => PermissionState::Denied,
110            _ => PermissionState::Unknown,
111        }
112    }
113}
114
115#[cfg(not(feature = "ssr"))]
116async fn query_permission(
117    permission: String,
118) -> Result<web_sys::PermissionStatus, wasm_bindgen::JsValue> {
119    use crate::{js, js_fut};
120    use wasm_bindgen::JsCast;
121
122    let permission_object = js_sys::Object::new();
123    js!(permission_object["name"] = permission);
124
125    let permission_state: web_sys::PermissionStatus = js_fut!(window()
126        .navigator()
127        .permissions()?
128        .query(&permission_object)?)
129    .await?
130    .unchecked_into();
131
132    Ok(permission_state)
133}