Files
photos/frontend2/src/lib.rs
Johannes Heuel a576e572d9
All checks were successful
continuous-integration/drone/push Build is passing
leptos
2023-10-02 14:31:22 +02:00

190 lines
6.0 KiB
Rust

use std::collections::HashMap;
use gloo_net::http::Request;
use gloo_storage::{LocalStorage, Storage};
use leptos::*;
use leptos_router::*;
use api_boundary::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::Url;
mod api;
mod components;
mod gallery;
mod pages;
pub(crate) mod web_sys_ext;
use self::{components::*, pages::*};
const DEFAULT_API_URL: &str = "/api";
const API_TOKEN_STORAGE_KEY: &str = "photos-super-fancy-token";
#[component]
pub fn App(cx: Scope) -> impl IntoView {
// -- signals -- //
let authorized_api = create_rw_signal(cx, None::<api::AuthorizedApi>);
let user_info = create_rw_signal(cx, None::<UserWithToken>);
let photos = create_rw_signal(cx, None::<Vec<OutputPicture>>);
let urls = create_rw_signal(cx, None::<HashMap<String, String>>);
let logged_in = Signal::derive(cx, move || authorized_api.get().is_some());
// -- actions -- //
let fetch_user_info = create_action(cx, move |_| async move {
match authorized_api.get() {
Some(api) => match api.user_info().await {
Ok(info) => {
log::info!("{:?}", info);
user_info.update(|i| *i = Some(info.user));
}
Err(err) => {
log::error!("Unable to fetch user info: {err}")
}
},
None => {
log::error!("Unable to fetch user info: not logged in")
}
}
});
let get_url = create_action(cx, move |thumb: &String| {
let thumb = thumb.clone();
async move {
let resp = Request::get(&thumb).send().await.unwrap();
if resp.ok() {
let blob = JsFuture::from(resp.as_raw().blob().unwrap()).await.unwrap();
let url = Url::create_object_url_with_blob(&blob.dyn_into().unwrap()).unwrap();
urls.update(|u| match u {
Some(_u) => {
let mut n = _u.clone();
n.insert(thumb, url);
*u = Some(n);
}
None => {
let mut n = HashMap::new();
n.insert(thumb, url);
*u = Some(n);
}
});
}
}
});
let fetch_photos = create_action(cx, move |_| async move {
match authorized_api.get() {
Some(api) => match api.get_photos().await {
Ok(info) => {
log::info!("{:?}", info);
info.iter().for_each(|p| {
if let Some(thumb) = p.thumbnail.clone() {
get_url.dispatch(thumb);
}
});
photos.update(|i| *i = Some(info));
}
Err(err) => {
log::error!("Unable to fetch photos: {err}")
}
},
None => {
log::error!("Unable to fetch photos: not logged in")
}
}
});
let logout = create_action(cx, move |_| async move {
match authorized_api.get() {
Some(api) => match api.logout().await {
Ok(_) => {
authorized_api.update(|a| *a = None);
user_info.update(|i| *i = None);
log::debug!("logout: delete token from LocalStorage");
LocalStorage::delete(API_TOKEN_STORAGE_KEY);
}
Err(err) => {
log::error!("Unable to logout: {err}")
}
},
None => {
log::error!("Unable to logout user: not logged in")
}
}
});
// -- callbacks -- //
let on_logout = move || {
logout.dispatch(());
};
// -- init API -- //
let unauthorized_api = api::UnauthorizedApi::new(DEFAULT_API_URL);
if let Ok(token) = LocalStorage::get(API_TOKEN_STORAGE_KEY) {
let api = api::AuthorizedApi::new(DEFAULT_API_URL, token);
authorized_api.update(|a| *a = Some(api));
fetch_user_info.dispatch(());
fetch_photos.dispatch(());
}
log::debug!("User is logged in: {}", logged_in.get());
// -- effects -- //
create_effect(cx, move |_| {
log::debug!("API authorization state changed");
match authorized_api.get() {
Some(api) => {
log::debug!("API is now authorized: save token in LocalStorage");
LocalStorage::set(API_TOKEN_STORAGE_KEY, api.token()).expect("LocalStorage::set");
}
None => {
log::debug!("API is no longer authorized: delete token from LocalStorage");
LocalStorage::delete(API_TOKEN_STORAGE_KEY);
}
}
});
view! { cx,
<Router>
<NavBar logged_in on_logout />
<main>
<Routes>
<Route
path=Page::Home.path()
view=move |cx| view! { cx,
<Home user_info = user_info.into() photos=photos.into() urls=urls.into() />
}
/>
<Route
path=Page::Login.path()
view=move |cx| view! { cx,
<Login
api = unauthorized_api
on_success = move |api| {
log::info!("Successfully logged in");
authorized_api.update(|v| *v = Some(api));
let navigate = use_navigate(cx);
navigate(Page::Home.path(), Default::default()).expect("Home route");
fetch_user_info.dispatch(());
fetch_photos.dispatch(());
} />
}
/>
<Route
path=Page::Register.path()
view=move |cx| view! { cx,
<Register api = unauthorized_api />
}
/>
</Routes>
</main>
</Router>
}
}