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 let update_position =
101 Closure::wrap(Box::new(update_position) as Box<dyn Fn(web_sys::Position)>);
102 let on_error =
103 Closure::wrap(Box::new(on_error) as Box<dyn Fn(web_sys::PositionError)>);
104
105 let handle = geolocation.watch_position_with_error_callback_and_options(
106 update_position.as_ref().unchecked_ref(),
107 Some(on_error.as_ref().unchecked_ref()),
108 &position_options,
109 );
110
111 #[cfg(web_sys_unstable_apis)]
112 let handle = Some(handle);
113
114 #[cfg(not(web_sys_unstable_apis))]
115 let handle = handle.ok();
116
117 *watch_handle.lock().unwrap() = handle;
118
119 update_position.forget();
120 on_error.forget();
121 }
122 })
123 };
124
125 if options.immediate {
126 resume();
127 }
128
129 pause = {
130 let watch_handle = Arc::clone(&watch_handle);
131
132 sendwrap_fn!(move || {
133 let navigator = use_window().navigator();
134 if let Some(navigator) = navigator
135 && let Some(handle) = *watch_handle.lock().unwrap()
136 && let Ok(geolocation) = navigator.geolocation()
137 {
138 geolocation.clear_watch(handle);
139 }
140 })
141 };
142
143 on_cleanup({
144 let pause = pause.clone();
145
146 move || {
147 pause();
148 }
149 });
150 }
151
152 UseGeolocationReturn {
153 coords: coords.read_only(),
154 located_at: located_at.into(),
155 error: error.read_only(),
156 resume,
157 pause,
158 }
159}
160
161#[derive(DefaultBuilder, Clone)]
163#[allow(dead_code)]
164pub struct UseGeolocationOptions {
165 immediate: bool,
168
169 enable_high_accuracy: bool,
176
177 maximum_age: u32,
181
182 timeout: u32,
186}
187
188impl Default for UseGeolocationOptions {
189 fn default() -> Self {
190 Self {
191 enable_high_accuracy: false,
192 maximum_age: 30000,
193 timeout: 27000,
194 immediate: true,
195 }
196 }
197}
198
199#[cfg(not(feature = "ssr"))]
200impl UseGeolocationOptions {
201 fn as_position_options(&self) -> web_sys::PositionOptions {
202 let UseGeolocationOptions {
203 enable_high_accuracy,
204 maximum_age,
205 timeout,
206 ..
207 } = self;
208
209 let options = web_sys::PositionOptions::new();
210 options.set_enable_high_accuracy(*enable_high_accuracy);
211 options.set_maximum_age(*maximum_age);
212 options.set_timeout(*timeout);
213
214 options
215 }
216}
217
218pub struct UseGeolocationReturn<ResumeFn, PauseFn>
220where
221 ResumeFn: Fn() + Clone + Send + Sync,
222 PauseFn: Fn() + Clone + Send + Sync,
223{
224 pub coords: OptionLocalSignal<web_sys::Coordinates>,
227
228 pub located_at: Signal<Option<f64>>,
230
231 pub error: OptionLocalSignal<web_sys::PositionError>,
233
234 pub resume: ResumeFn,
236
237 pub pause: PauseFn,
239}