1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::*;
/// Reactive [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API).
/// It allows the user to provide their location to web applications if they so desire. For privacy reasons,
/// the user is asked for permission to report location information.
///
/// ## Demo
///
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_geolocation)
///
/// ## Usage
///
/// ```
/// # use leptos::*;
/// # use leptos_use::{use_geolocation, UseGeolocationReturn};
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
/// let UseGeolocationReturn {
/// coords,
/// located_at,
/// error,
/// resume,
/// pause,
/// } = use_geolocation();
/// #
/// # view! { }
/// # }
/// ```
///
/// ## Server-Side Rendering
///
/// On the server all signals returns will always contain `None` and the functions do nothing.
pub fn use_geolocation() -> UseGeolocationReturn<impl Fn() + Clone, impl Fn() + Clone> {
use_geolocation_with_options(UseGeolocationOptions::default())
}
/// Version of [`use_geolocation`] that takes a `UseGeolocationOptions`. See [`use_geolocation`] for how to use.
pub fn use_geolocation_with_options(
options: UseGeolocationOptions,
) -> UseGeolocationReturn<impl Fn() + Clone, impl Fn() + Clone> {
let (located_at, set_located_at) = create_signal(None::<f64>);
let (error, set_error) = create_signal(None::<web_sys::PositionError>);
let (coords, set_coords) = create_signal(None::<web_sys::Coordinates>);
cfg_if! { if #[cfg(feature = "ssr")] {
let resume = || ();
let pause = || ();
let _ = options;
let _ = set_located_at;
let _ = set_error;
let _ = set_coords;
} else {
use crate::use_window;
use std::cell::Cell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
let update_position = move |position: web_sys::Position| {
set_located_at.set(Some(position.timestamp()));
set_coords.set(Some(position.coords()));
set_error.set(None);
};
let on_error = move |err: web_sys::PositionError| {
set_error.set(Some(err));
};
let watch_handle = Rc::new(Cell::new(None::<i32>));
let resume = {
let watch_handle = Rc::clone(&watch_handle);
let position_options = options.as_position_options();
move || {
let navigator = use_window().navigator();
if let Some(navigator) = navigator {
if let Ok(geolocation) = navigator.geolocation() {
let update_position =
Closure::wrap(Box::new(update_position) as Box<dyn Fn(web_sys::Position)>);
let on_error =
Closure::wrap(Box::new(on_error) as Box<dyn Fn(web_sys::PositionError)>);
watch_handle.replace(
geolocation
.watch_position_with_error_callback_and_options(
update_position.as_ref().unchecked_ref(),
Some(on_error.as_ref().unchecked_ref()),
&position_options,
)
.ok(),
);
update_position.forget();
on_error.forget();
}
}
}
};
if options.immediate {
resume();
}
let pause = {
let watch_handle = Rc::clone(&watch_handle);
move || {
let navigator = use_window().navigator();
if let Some(navigator) = navigator {
if let Some(handle) = watch_handle.take() {
if let Ok(geolocation) = navigator.geolocation() {
geolocation.clear_watch(handle);
}
}
}
}
};
on_cleanup({
let pause = pause.clone();
move || {
pause();
}
});
}}
UseGeolocationReturn {
coords: coords.into(),
located_at: located_at.into(),
error: error.into(),
resume,
pause,
}
}
/// Options for [`use_geolocation_with_options`].
#[derive(DefaultBuilder, Clone)]
#[allow(dead_code)]
pub struct UseGeolocationOptions {
/// If `true` the geolocation watch is started when this function is called.
/// If `false` you have to call `resume` manually to start it. Defaults to `true`.
immediate: bool,
/// A boolean value that indicates the application would like to receive the best
/// possible results. If `true` and if the device is able to provide a more accurate
/// position, it will do so. Note that this can result in slower response times or
/// increased power consumption (with a GPS chip on a mobile device for example).
/// On the other hand, if `false`, the device can take the liberty to save
/// resources by responding more quickly and/or using less power. Default: `false`.
enable_high_accuracy: bool,
/// A positive value indicating the maximum age in milliseconds of a possible cached position that is acceptable to return.
/// If set to `0`, it means that the device cannot use a cached position and must attempt to retrieve the real current position.
/// Default: 30000.
maximum_age: u32,
/// A positive value representing the maximum length of time (in milliseconds)
/// the device is allowed to take in order to return a position.
/// The default value is 27000.
timeout: u32,
}
impl Default for UseGeolocationOptions {
fn default() -> Self {
Self {
enable_high_accuracy: false,
maximum_age: 30000,
timeout: 27000,
immediate: true,
}
}
}
#[cfg(not(feature = "ssr"))]
impl UseGeolocationOptions {
fn as_position_options(&self) -> web_sys::PositionOptions {
let UseGeolocationOptions {
enable_high_accuracy,
maximum_age,
timeout,
..
} = self;
let mut options = web_sys::PositionOptions::new();
options.enable_high_accuracy(*enable_high_accuracy);
options.maximum_age(*maximum_age);
options.timeout(*timeout);
options
}
}
/// Return type of [`use_geolocation`].
pub struct UseGeolocationReturn<ResumeFn, PauseFn>
where
ResumeFn: Fn() + Clone,
PauseFn: Fn() + Clone,
{
/// The coordinates of the current device like latitude and longitude.
/// See [`GeolocationCoordinates`](https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates)..
pub coords: Signal<Option<web_sys::Coordinates>>,
/// The timestamp of the current coordinates.
pub located_at: Signal<Option<f64>>,
/// The last error received from `navigator.geolocation`.
pub error: Signal<Option<web_sys::PositionError>>,
/// Resume the geolocation watch.
pub resume: ResumeFn,
/// Pause the geolocation watch.
pub pause: PauseFn,
}