leptos_use/
use_geolocation.rs1use crate::core::{OptionLocalRwSignal, OptionLocalSignal};
2use default_struct_builder::DefaultBuilder;
3use leptos::prelude::*;
4use leptos::reactive::wrappers::read::Signal;
5
6pub fn use_geolocation()
47-> UseGeolocationReturn<impl Fn() + Clone + Send + Sync, impl Fn() + Clone + Send + Sync> {
48 use_geolocation_with_options(UseGeolocationOptions::default())
49}
50
51pub fn use_geolocation_with_options(
53 options: UseGeolocationOptions,
54) -> UseGeolocationReturn<impl Fn() + Clone + Send + Sync, impl Fn() + Clone + Send + Sync> {
55 let (located_at, set_located_at) = signal(None::<f64>);
56 let error = OptionLocalRwSignal::<web_sys::PositionError>::new();
57 let coords = OptionLocalRwSignal::<web_sys::Coordinates>::new();
58
59 let resume;
60 let pause;
61
62 #[cfg(feature = "ssr")]
63 {
64 resume = || ();
65 pause = || ();
66
67 let _ = options;
68 let _ = set_located_at;
69 let _ = error;
70 let _ = coords;
71 }
72
73 #[cfg(not(feature = "ssr"))]
74 {
75 use crate::{sendwrap_fn, use_window};
76 use std::sync::{Arc, Mutex};
77 use wasm_bindgen::prelude::*;
78
79 let update_position = move |position: web_sys::Position| {
80 set_located_at.set(Some(position.timestamp()));
81 coords.set(Some(position.coords()));
82 error.set(None);
83 };
84
85 let on_error = move |err: web_sys::PositionError| {
86 error.set(Some(err));
87 };
88
89 let watch_handle = Arc::new(Mutex::new(None::<i32>));
90
91 resume = {
92 let watch_handle = Arc::clone(&watch_handle);
93 let position_options = options.as_position_options();
94
95 sendwrap_fn!(move || {
96 let navigator = use_window().navigator();
97 if let Some(navigator) = navigator
98 && let Ok(geolocation) = navigator.geolocation()
99 {
100 if let Some(handle) = watch_handle.lock().unwrap().take() {
101 geolocation.clear_watch(handle);
102 }
103
104 let update_position =
105 Closure::wrap(Box::new(update_position) as Box<dyn Fn(web_sys::Position)>);
106 let on_error =
107 Closure::wrap(Box::new(on_error) as Box<dyn Fn(web_sys::PositionError)>);
108
109 let handle = geolocation.watch_position_with_error_callback_and_options(
110 update_position.as_ref().unchecked_ref(),
111 Some(on_error.as_ref().unchecked_ref()),
112 &position_options,
113 );
114
115 #[cfg(web_sys_unstable_apis)]
116 let handle = Some(handle);
117
118 #[cfg(not(web_sys_unstable_apis))]
119 let handle = handle.ok();
120
121 *watch_handle.lock().unwrap() = handle;
122
123 update_position.forget();
124 on_error.forget();
125 }
126 })
127 };
128
129 if options.immediate {
130 resume();
131 }
132
133 pause = {
134 let watch_handle = Arc::clone(&watch_handle);
135
136 sendwrap_fn!(move || {
137 let navigator = use_window().navigator();
138 if let Some(navigator) = navigator
139 && let Some(handle) = *watch_handle.lock().unwrap()
140 && let Ok(geolocation) = navigator.geolocation()
141 {
142 geolocation.clear_watch(handle);
143 }
144 })
145 };
146
147 on_cleanup({
148 let pause = pause.clone();
149
150 move || {
151 pause();
152 }
153 });
154 }
155
156 UseGeolocationReturn {
157 coords: coords.read_only(),
158 located_at: located_at.into(),
159 error: error.read_only(),
160 resume,
161 pause,
162 }
163}
164
165#[derive(DefaultBuilder, Clone)]
167#[allow(dead_code)]
168pub struct UseGeolocationOptions {
169 immediate: bool,
172
173 enable_high_accuracy: bool,
180
181 maximum_age: u32,
185
186 timeout: u32,
190}
191
192impl Default for UseGeolocationOptions {
193 fn default() -> Self {
194 Self {
195 enable_high_accuracy: false,
196 maximum_age: 30000,
197 timeout: 27000,
198 immediate: true,
199 }
200 }
201}
202
203#[cfg(not(feature = "ssr"))]
204impl UseGeolocationOptions {
205 fn as_position_options(&self) -> web_sys::PositionOptions {
206 let UseGeolocationOptions {
207 enable_high_accuracy,
208 maximum_age,
209 timeout,
210 ..
211 } = self;
212
213 let options = web_sys::PositionOptions::new();
214 options.set_enable_high_accuracy(*enable_high_accuracy);
215 options.set_maximum_age(*maximum_age);
216 options.set_timeout(*timeout);
217
218 options
219 }
220}
221
222pub struct UseGeolocationReturn<ResumeFn, PauseFn>
224where
225 ResumeFn: Fn() + Clone + Send + Sync,
226 PauseFn: Fn() + Clone + Send + Sync,
227{
228 pub coords: OptionLocalSignal<web_sys::Coordinates>,
231
232 pub located_at: Signal<Option<f64>>,
234
235 pub error: OptionLocalSignal<web_sys::PositionError>,
237
238 pub resume: ResumeFn,
240
241 pub pause: PauseFn,
243}