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/// > Make sure you follow the [instructions in Server-Side Rendering](https://leptos-use.rs/server_side_rendering.html).
28///
29/// On the server the returned signal will always be `PermissionState::Unknown`.
30pub fn use_permission(permission_name: &str) -> Signal<PermissionState> {
31    let (state, set_state) = signal(PermissionState::Unknown);
32
33    #[cfg(not(feature = "ssr"))]
34    {
35        use crate::use_event_listener;
36        use std::cell::RefCell;
37        use std::rc::Rc;
38
39        let permission_status = Rc::new(RefCell::new(None::<web_sys::PermissionStatus>));
40
41        let on_change = {
42            let permission_status = Rc::clone(&permission_status);
43
44            move || {
45                if let Some(permission_status) = permission_status.borrow().as_ref() {
46                    set_state.set(PermissionState::from(permission_status.state()));
47                }
48            }
49        };
50
51        leptos::task::spawn_local({
52            let permission_name = permission_name.to_owned();
53
54            async move {
55                if let Ok(status) = query_permission(permission_name).await {
56                    let _ = use_event_listener(status.clone(), leptos::ev::change, {
57                        let on_change = on_change.clone();
58                        move |_| on_change()
59                    });
60                    permission_status.replace(Some(status));
61                    on_change();
62                } else {
63                    set_state.set(PermissionState::Prompt);
64                }
65            }
66        });
67    }
68
69    #[cfg(feature = "ssr")]
70    {
71        let _ = set_state;
72        let _ = permission_name;
73    }
74
75    state.into()
76}
77
78/// Return type of [`use_permission`].
79#[derive(Copy, Clone, Default, Eq, PartialEq)]
80pub enum PermissionState {
81    /// State hasn't been requested yet. This is the initial value.
82    #[default]
83    Unknown,
84
85    /// The permission has been granted by the user.
86    Granted,
87
88    /// The user will automatically be prompted to give permission once the relevant API is called.
89    Prompt,
90
91    /// The user has denied permission.
92    Denied,
93}
94
95impl Display for PermissionState {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match *self {
98            PermissionState::Unknown => write!(f, "unknown"),
99            PermissionState::Granted => write!(f, "granted"),
100            PermissionState::Prompt => write!(f, "prompt"),
101            PermissionState::Denied => write!(f, "denied"),
102        }
103    }
104}
105
106impl From<web_sys::PermissionState> for PermissionState {
107    fn from(permission_state: web_sys::PermissionState) -> Self {
108        match permission_state {
109            web_sys::PermissionState::Granted => PermissionState::Granted,
110            web_sys::PermissionState::Prompt => PermissionState::Prompt,
111            web_sys::PermissionState::Denied => PermissionState::Denied,
112            _ => PermissionState::Unknown,
113        }
114    }
115}
116
117#[cfg(not(feature = "ssr"))]
118async fn query_permission(
119    permission: String,
120) -> Result<web_sys::PermissionStatus, wasm_bindgen::JsValue> {
121    use crate::{js, js_fut};
122    use wasm_bindgen::JsCast;
123
124    let permission_object = js_sys::Object::new();
125    js!(permission_object["name"] = permission);
126
127    let permission_state: web_sys::PermissionStatus = js_fut!(window()
128        .navigator()
129        .permissions()?
130        .query(&permission_object)?)
131    .await?
132    .unchecked_into();
133
134    Ok(permission_state)
135}