yew_hooks/hooks/
use_geolocation.rs1use gloo::utils::window;
2use wasm_bindgen::{prelude::*, JsCast};
3use yew::prelude::*;
4
5use super::use_effect_once;
6
7pub use web_sys::PositionOptions as UseGeolocationOptions;
8
9#[cfg(web_sys_unstable_apis)]
10type PositionErrorType = web_sys::GeolocationPositionError;
11#[cfg(not(web_sys_unstable_apis))]
12type PositionErrorType = web_sys::PositionError;
13
14#[derive(PartialEq, Default, Clone)]
15pub struct UseGeolocationState {
16 pub loading: bool,
17 pub accuracy: f64,
18 pub altitude: Option<f64>,
19 pub altitude_accuracy: Option<f64>,
20 pub heading: Option<f64>,
21 pub latitude: f64,
22 pub longitude: f64,
23 pub speed: Option<f64>,
24 pub timestamp: f64,
25 pub error: Option<PositionErrorType>,
26}
27
28#[hook]
64pub fn use_geolocation() -> UseGeolocationState {
65 use_geolocation_with_options(UseGeolocationOptions::default())
66}
67
68#[hook]
71pub fn use_geolocation_with_options(options: UseGeolocationOptions) -> UseGeolocationState {
72 let state = use_state(|| UseGeolocationState {
73 loading: true,
74 ..Default::default()
75 });
76
77 {
78 let state = state.clone();
79 use_effect_once(move || {
80 #[cfg(web_sys_unstable_apis)]
81 let (closure, error_closure, watch_id) = {
82 use web_sys::GeolocationPosition;
83
84 let closure = {
85 let state = state.clone();
86 Closure::wrap(Box::new(move |position: GeolocationPosition| {
87 state.set(UseGeolocationState {
88 loading: false,
89 accuracy: position.coords().accuracy(),
90 altitude: position.coords().altitude(),
91 altitude_accuracy: position.coords().altitude_accuracy(),
92 heading: position.coords().heading(),
93 latitude: position.coords().latitude(),
94 longitude: position.coords().longitude(),
95 speed: position.coords().speed(),
96 timestamp: position.timestamp(),
97 error: None,
98 });
99 }) as Box<dyn Fn(GeolocationPosition)>)
100 };
101
102 let error_closure = {
103 let state = state.clone();
104 Closure::wrap(Box::new(move |error: PositionErrorType| {
105 state.set(UseGeolocationState {
106 loading: false,
107 error: Some(error),
108 ..*state
109 });
110 }) as Box<dyn Fn(PositionErrorType)>)
111 };
112
113 window()
114 .navigator()
115 .geolocation()
116 .unwrap_throw()
117 .get_current_position_with_error_callback_and_options(
118 closure.as_ref().unchecked_ref(),
119 Some(error_closure.as_ref().unchecked_ref()),
120 &options,
121 );
122
123 let watch_id = window()
124 .navigator()
125 .geolocation()
126 .unwrap_throw()
127 .watch_position_with_error_callback_and_options(
128 closure.as_ref().unchecked_ref(),
129 Some(error_closure.as_ref().unchecked_ref()),
130 &options,
131 );
132
133 (closure, error_closure, watch_id)
134 };
135
136 #[cfg(not(web_sys_unstable_apis))]
137 let (closure, error_closure, watch_id) = {
138 use web_sys::Position;
139
140 let closure = {
141 let state = state.clone();
142 Closure::wrap(Box::new(move |position: Position| {
143 state.set(UseGeolocationState {
144 loading: false,
145 accuracy: position.coords().accuracy(),
146 altitude: position.coords().altitude(),
147 altitude_accuracy: position.coords().altitude_accuracy(),
148 heading: position.coords().heading(),
149 latitude: position.coords().latitude(),
150 longitude: position.coords().longitude(),
151 speed: position.coords().speed(),
152 timestamp: position.timestamp(),
153 error: None,
154 });
155 }) as Box<dyn Fn(Position)>)
156 };
157
158 let error_closure = {
159 let state = state.clone();
160 Closure::wrap(Box::new(move |error: PositionErrorType| {
161 state.set(UseGeolocationState {
162 loading: false,
163 error: Some(error),
164 ..*state
165 });
166 }) as Box<dyn Fn(PositionErrorType)>)
167 };
168
169 window()
170 .navigator()
171 .geolocation()
172 .unwrap_throw()
173 .get_current_position_with_error_callback_and_options(
174 closure.as_ref().unchecked_ref(),
175 Some(error_closure.as_ref().unchecked_ref()),
176 &options,
177 )
178 .unwrap_throw();
179
180 let watch_id = window()
181 .navigator()
182 .geolocation()
183 .unwrap_throw()
184 .watch_position_with_error_callback_and_options(
185 closure.as_ref().unchecked_ref(),
186 Some(error_closure.as_ref().unchecked_ref()),
187 &options,
188 )
189 .unwrap_throw();
190
191 (closure, error_closure, watch_id)
192 };
193
194 closure.forget();
196 error_closure.forget();
197
198 move || {
199 window()
200 .navigator()
201 .geolocation()
202 .unwrap_throw()
203 .clear_watch(watch_id);
204 }
205 });
206 }
207
208 (*state).clone()
209}