0

I'm trying to retrieve the user's lat/lon, once, in the create function of a Yew Component, so that I can do some math and pass useful decisions to child components. I've made the Yew hook "use_geolocation()" work just fine, but that only runs in a function_component, and there doesn't appear to be any straightforward way to use that location in other components.

Then I found this neat tutorial which uses wasm_bindgen and Seed::app::orders::Orders to make a "cheap clone" of the app, and to call the Javascript functions. The bindings look like:

#[wasm_bindgen]
extern "C" {
   type GeolocationCoordinates;
   #[wasm_bindgen(method, getter)]
   fn latitude(this: &GeolocationCoordinates) -> f64;
   #[wasm_bindgen(method, getter)]
   fn longitude(this: &GeolocationCoordinates) -> f64;
   type GeolocationPosition;
   #[wasm_bindgen(method, getter)]
   fn coords(this: &GeolocationPosition) ->    
     GeolocationCoordinates;
 }

And the function for fetching geolocation, splicing together the chunks of code from Tor Hovland's tutorial:

let (app, msg_mapper) = (orders.clone_app(), orders.msg_mapper());

let geo_callback = move |position: JsValue| {
   let pos: GeolocationPosition = position.into();
   let coords = pos.coords();
   app.update(msg_mapper(Msg::Position(
     coords.latitude(),
     coords.longitude(),
   )));
};    
let geolocation = web_sys::window()
  .expect("Unable to get browser window.")
  .navigator()
  .geolocation()
  .expect("Unable to get geolocation.");
let geo_callback_function =
    Closure::wrap(
        Box::new(|pos| geo_callback(pos)) as Box<dyn Fn(JsValue)>
    );
geolocation.get_current_position(
    &geo_callback_function.as_ref().unchecked_ref()
).expect("Unable to get position");
geo_callback_function.forget();

I attempted this route, but found that adding the line seed = "0.9.1" into my Cargo.toml produced compile errors, something about a type mismatch between a closure in wasm_bindgen and something in seed. Included here for completeness:

error[E0283]: type annotations needed for `Closure<T>`
   --> /home/djmcmath/.cargo/registry/src/github.com-1ecc6299db9ec823/seed- 
0.9.1/src/browser/service/routing.rs:87:9
    |
87  |     let closure = Closure::new(move |event: web_sys::Event| {
    |         ^^^^^^^
    |
    = note: cannot satisfy `_: WasmClosure`
note: required by a bound in `Closure::<T>::new`
   --> /home/djmcmath/.cargo/registry/src/github.com-1ecc6299db9ec823/wasm-bindgen- 
0.2.81/src/closure.rs:251:17
    |
251 |     T: ?Sized + WasmClosure,
    |                 ^^^^^^^^^^^ required by this bound in `Closure::<T>::new`
help: consider giving `closure` an explicit type, where the type for type parameter 
`T` is specified
    |
87  |     let closure: Closure<T> = Closure::new(move |event: web_sys::Event| {
    |                ++++++++++++
help: consider specifying the type argument in the function call
    |
87  |     let closure = Closure::new::<F>(move |event: web_sys::Event| {
    |                               +++++

After beating my head against that brick wall for a while, I decided to just not use Seed, but I don't know of another way to make a "cheap clone" of the app to make the lifetimes work out correctly. Without that, I get the predictable lifetime error on the geo_callback_function:

let geo_callback_function =
    Closure::wrap(
        Box::new(|pos: JsValue| geo_callback(pos)) as Box<dyn Fn(JsValue)>
    );

Error message:

error[E0597]: `geo_callback` does not live long enough
   --> src/ferry_route.rs:213:37
    |
213 |             Box::new(|pos: JsValue| geo_callback(pos)) as Box<dyn Fn(JsValue)>
    |             ------------------------^^^^^^^^^^^^------
    |             |        |              |
    |             |        |              borrowed value does not live long enough
    |             |        value captured here
    |             cast requires that `geo_callback` is borrowed for `'static`
...
221 | }
    | - `geo_callback` dropped here while still borrowed

So I'm at a loss, at this point. Seems like fetching user's location would be simpler than all of this. I'm open to any path that makes any of these work. (Definition of work: I can get the user's lat/lon in the create function of a Yew Component, so that I can do some math and pass useful decisions to child components.)

djmcmath
  • 97
  • 5

1 Answers1

2

Ok, I think I've got it. Or at least, I've got something that works. I'm not sure it's the most elegant solution. Basically, drop the Seed parts out of the function and just pull the coords from the JsValue, like so:

fn geo_callback(position: JsValue) {
    let pos = JsCast::unchecked_into::<GeolocationPosition>(position);
    let coords = pos.coords();
    log::info!(
        "Latitude: {}. Longitude: {}.",
        coords.latitude(),
        coords.longitude()
    );        
};

At this point, I'm pretty sure I can take those coords and do something useful with them.

djmcmath
  • 97
  • 5