190 lines
6.0 KiB
Rust
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>
|
|
}
|
|
}
|