This commit is contained in:
189
frontend2/src/lib.rs
Normal file
189
frontend2/src/lib.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
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>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user