//! Pages for the Android app: `#device` inside its WebView, `#pair` in the browser. use api::{PairBegin, PairCode}; use leptos::prelude::*; use leptos::task::spawn_local; use wasm_bindgen::prelude::*; use crate::i18n::tr; use crate::{http, map}; #[wasm_bindgen(inline_js = r#" export function appToken() { return window.OtApp ? window.OtApp.token() : null; } "#)] extern "C" { #[wasm_bindgen(js_name = appToken)] fn app_token() -> Option; } /// The map of this device only. The app hands over its token through a JavaScript bridge. #[component] pub fn DevicePage() -> impl IntoView { match app_token() { Some(token) => { view! {
} .into_any() } None => { view! {

{tr("This page is for the opentracker app.")}

}.into_any() } } } /// A query parameter of `#pair?challenge=…&name=…`. fn hash_param(key: &str) -> Option { let hash = window().location().hash().ok()?; let query = hash.split_once('?')?.1; web_sys::UrlSearchParams::new_with_str(query).ok()?.get(key) } /// Confirms a new device for the signed-in user, then hands a one-time code back to the app. #[component] pub fn PairPage() -> impl IntoView { let challenge = hash_param("challenge"); let name = RwSignal::new(hash_param("name").unwrap_or_else(|| "Android".into())); let error = RwSignal::new(None::); let done = RwSignal::new(None::); let busy = RwSignal::new(false); let has_challenge = challenge.is_some(); let connect = move |ev: leptos::ev::SubmitEvent| { ev.prevent_default(); let Some(challenge) = challenge.clone() else { return; }; busy.set(true); let body = PairBegin { challenge, name: name.get_untracked(), }; spawn_local(async move { match http::post::("/api/devices/pair/begin", &body).await { Ok(c) => { let url = format!("opentracker://paired?code={}", c.code); let _ = window().location().assign(&url); done.set(Some(url)); } Err(e) => error.set(Some(e.to_string())), } busy.set(false); }); }; view! { } }