yew_hooks/hooks/
use_geolocation.rs1use gloo::utils::window;
2use wasm_bindgen::{prelude::*, JsCast};
3use web_sys::{Position, PositionError};
4use yew::prelude::*;
5
6use super::use_effect_once;
7
8pub use web_sys::PositionOptions as UseGeolocationOptions;
9
10#[derive(PartialEq, Default, Clone)]
11pub struct UseGeolocationState {
12 pub loading: bool,
13 pub accuracy: f64,
14 pub altitude: Option<f64>,
15 pub altitude_accuracy: Option<f64>,
16 pub heading: Option<f64>,
17 pub latitude: f64,
18 pub longitude: f64,
19 pub speed: Option<f64>,
20 pub timestamp: f64,
21 pub error: Option<PositionError>,
22}
23
24#[hook]
60pub fn use_geolocation() -> UseGeolocationState {
61 use_geolocation_with_options(UseGeolocationOptions::default())
62}
63
64#[hook]
67pub fn use_geolocation_with_options(options: UseGeolocationOptions) -> UseGeolocationState {
68 let state = use_state(|| UseGeolocationState {
69 loading: true,
70 ..Default::default()
71 });
72
73 {
74 let state = state.clone();
75 use_effect_once(move || {
76 let closure = {
77 let state = state.clone();
78 Closure::wrap(Box::new(move |position: Position| {
79 state.set(UseGeolocationState {
80 loading: false,
81 accuracy: position.coords().accuracy(),
82 altitude: position.coords().altitude(),
83 altitude_accuracy: position.coords().altitude_accuracy(),
84 heading: position.coords().heading(),
85 latitude: position.coords().latitude(),
86 longitude: position.coords().longitude(),
87 speed: position.coords().speed(),
88 timestamp: position.timestamp(),
89 error: None,
90 });
91 }) as Box<dyn Fn(Position)>)
92 };
93
94 let error_closure = {
95 let state = state.clone();
96 Closure::wrap(Box::new(move |error: PositionError| {
97 state.set(UseGeolocationState {
98 loading: false,
99 error: Some(error),
100 ..*state
101 });
102 }) as Box<dyn Fn(PositionError)>)
103 };
104
105 window()
106 .navigator()
107 .geolocation()
108 .unwrap_throw()
109 .get_current_position_with_error_callback_and_options(
110 closure.as_ref().unchecked_ref(),
111 Some(error_closure.as_ref().unchecked_ref()),
112 &options,
113 )
114 .unwrap_throw();
115
116 let watch_id = window()
117 .navigator()
118 .geolocation()
119 .unwrap_throw()
120 .watch_position_with_error_callback_and_options(
121 closure.as_ref().unchecked_ref(),
122 Some(error_closure.as_ref().unchecked_ref()),
123 &options,
124 )
125 .unwrap_throw();
126
127 closure.forget();
129 error_closure.forget();
130
131 move || {
132 window()
133 .navigator()
134 .geolocation()
135 .unwrap_throw()
136 .clear_watch(watch_id);
137 }
138 });
139 }
140
141 (*state).clone()
142}