switch to mongodb, add drag&drop
This commit is contained in:
97
frontend/src/components/base_page.rs
Normal file
97
frontend/src/components/base_page.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use crate::hooks::use_user_context;
|
||||
use crate::Route;
|
||||
use ybc::NavbarFixed::Top;
|
||||
use yew::prelude::*;
|
||||
use yew_hooks::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn BasePage(props: &Props) -> Html {
|
||||
let navigator = use_navigator().unwrap();
|
||||
let user_ctx = use_user_context();
|
||||
|
||||
let node = use_node_ref();
|
||||
let size = use_size(node.clone());
|
||||
|
||||
let authenticated = user_ctx.is_authenticated();
|
||||
let title = if authenticated {
|
||||
html! {"Photos"}
|
||||
} else {
|
||||
html! {"No photos"}
|
||||
};
|
||||
|
||||
let navbrand = html! {
|
||||
<ybc::NavbarItem>
|
||||
<ybc::Title
|
||||
classes={classes!("has-text-white")}
|
||||
size={ybc::HeaderSize::Is4}>
|
||||
{title}
|
||||
</ybc::Title>
|
||||
</ybc::NavbarItem>
|
||||
};
|
||||
|
||||
let account_button = if authenticated {
|
||||
let onclick = {
|
||||
Callback::from(move |_| {
|
||||
user_ctx.logout();
|
||||
})
|
||||
};
|
||||
html! {
|
||||
<ybc::ButtonAnchor
|
||||
classes={classes!("is-outlined")}
|
||||
rel={String::from("noopener noreferrer")}
|
||||
target={String::from("_blank")}
|
||||
href=""
|
||||
{onclick}>
|
||||
{"logout"}
|
||||
</ybc::ButtonAnchor>
|
||||
}
|
||||
} else {
|
||||
let onclick = Callback::from(move |_| navigator.push(&Route::Login));
|
||||
html! {
|
||||
<ybc::Button
|
||||
// classes={classes!("is-outlined")}
|
||||
{onclick}>
|
||||
{"login"}
|
||||
</ybc::Button>
|
||||
}
|
||||
};
|
||||
|
||||
let navstart = html! {};
|
||||
let navend = html! {
|
||||
<>
|
||||
// <ybc::NavbarItem>
|
||||
// <ybc::Button
|
||||
// // classes={classes!("is-outlined")}
|
||||
// >
|
||||
// {"Photos"}
|
||||
// </ybc::Button>
|
||||
// </ybc::NavbarItem>
|
||||
<ybc::NavbarItem>
|
||||
{account_button}
|
||||
</ybc::NavbarItem>
|
||||
</>
|
||||
};
|
||||
|
||||
html! {
|
||||
<>
|
||||
<ybc::Navbar
|
||||
fixed={Top}
|
||||
classes={classes!("is-info")}
|
||||
padded={true}
|
||||
{navbrand}
|
||||
{navstart}
|
||||
{navend}
|
||||
/>
|
||||
|
||||
<div>
|
||||
{ props.children.clone() }
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
41
frontend/src/components/home.js
Normal file
41
frontend/src/components/home.js
Normal file
@@ -0,0 +1,41 @@
|
||||
function getFilesDataTransferItems(dataTransferItems) {
|
||||
function traverseFileTreePromise(item, path = "", folder) {
|
||||
return new Promise(resolve => {
|
||||
if (item.isFile) {
|
||||
item.file(file => {
|
||||
file.filepath = path + "/" + file.name; //save full path
|
||||
folder.push(file);
|
||||
resolve(file);
|
||||
});
|
||||
} else if (item.isDirectory) {
|
||||
let dirReader = item.createReader();
|
||||
dirReader.readEntries(entries => {
|
||||
let entriesPromises = [];
|
||||
let subfolder = [];
|
||||
folder.push({ name: item.name, subfolder: subfolder });
|
||||
for (let entry of entries)
|
||||
entriesPromises.push(
|
||||
traverseFileTreePromise(entry, path + "/" + item.name, subfolder)
|
||||
);
|
||||
resolve(Promise.all(entriesPromises));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let files = [];
|
||||
return new Promise((resolve, reject) => {
|
||||
let entriesPromises = [];
|
||||
for (let it of dataTransferItems)
|
||||
entriesPromises.push(
|
||||
traverseFileTreePromise(it.webkitGetAsEntry(), "", files)
|
||||
);
|
||||
Promise.all(entriesPromises).then(entries => {
|
||||
resolve(files);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function get_files_data_transfer_items(data_transfer_items) {
|
||||
return getFilesDataTransferItems(data_transfer_items);
|
||||
}
|
||||
132
frontend/src/components/home.rs
Normal file
132
frontend/src/components/home.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use super::BasePage;
|
||||
use crate::gallery::Grid;
|
||||
use crate::hooks::use_user_context;
|
||||
use gloo_net::http::Request;
|
||||
|
||||
use js_sys::Array;
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
|
||||
use web_sys::{DataTransferItemList, File, FileSystemDirectoryEntry, FileSystemEntry};
|
||||
use weblog::console_log;
|
||||
use yew::prelude::*;
|
||||
use yew_hooks::prelude::*;
|
||||
|
||||
use common::OutputPicture;
|
||||
|
||||
#[function_component(Home)]
|
||||
pub fn home() -> Html {
|
||||
let user_ctx = use_user_context();
|
||||
|
||||
let node = use_node_ref();
|
||||
let size = use_size(node.clone());
|
||||
|
||||
let pictures = use_state(std::vec::Vec::new);
|
||||
|
||||
{
|
||||
let pictures = pictures.clone();
|
||||
use_effect_with_deps(
|
||||
move |_| {
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let url = "/api/pictures/";
|
||||
let fetched_pictures: Vec<OutputPicture> = Request::get(url)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
pictures.set(fetched_pictures);
|
||||
});
|
||||
|| ()
|
||||
},
|
||||
(),
|
||||
);
|
||||
}
|
||||
|
||||
let ondrop = Callback::from(move |e: DragEvent| {
|
||||
e.prevent_default();
|
||||
|
||||
let items = e.data_transfer().unwrap().items();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let promise = get_files_data_transfer_items(items);
|
||||
let result = wasm_bindgen_futures::JsFuture::from(promise).await.unwrap();
|
||||
|
||||
console_log!(&result);
|
||||
|
||||
let traverse = |d: JsValue| {
|
||||
let mut new_files: Vec<JsValue> = Vec::new();
|
||||
let mut files: Vec<File> = Vec::new();
|
||||
|
||||
if let Ok(fse) = d.clone().dyn_into::<File>() {
|
||||
console_log!(&fse);
|
||||
// fse.filepath = path;
|
||||
files.push(fse);
|
||||
} else if let Ok(a) = d.clone().dyn_into::<js_sys::Array>() {
|
||||
for i in 0..a.length() {
|
||||
let f = a.get(i);
|
||||
new_files.push(f);
|
||||
}
|
||||
} else if let Ok(o) = d.clone().dyn_into::<js_sys::Object>() {
|
||||
if let Ok(subfolder) = js_sys::Reflect::get(&o, &JsValue::from_str("subfolder"))
|
||||
{
|
||||
new_files.push(subfolder);
|
||||
}
|
||||
}
|
||||
(new_files, files)
|
||||
};
|
||||
|
||||
let mut files: Vec<File> = Vec::new();
|
||||
let mut new_files = vec![result];
|
||||
|
||||
loop {
|
||||
console_log!(files.len());
|
||||
let Some(current) = new_files.pop() else {
|
||||
console_log!("break");
|
||||
|
||||
break;
|
||||
};
|
||||
|
||||
let (news, fs) = traverse(current);
|
||||
news.iter().for_each(|x| new_files.push(x.clone()));
|
||||
fs.iter().for_each(|x| files.push(x.clone()));
|
||||
}
|
||||
let uiae: Vec<String> = files
|
||||
.iter()
|
||||
.map(|x| {
|
||||
if let Ok(filepath) = js_sys::Reflect::get(&x, &JsValue::from_str("filepath")) {
|
||||
filepath.as_string().unwrap_or("".to_string())
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
console_log!("end", uiae.join("\n"));
|
||||
});
|
||||
});
|
||||
|
||||
let ondragover = Callback::from(move |e: DragEvent| {
|
||||
e.prevent_default();
|
||||
});
|
||||
|
||||
let body = if user_ctx.is_authenticated() {
|
||||
html! {
|
||||
<Grid
|
||||
pictures={(*pictures).clone()}
|
||||
width={size.0}
|
||||
/>
|
||||
}
|
||||
} else {
|
||||
html! {}
|
||||
};
|
||||
html! {
|
||||
<BasePage>
|
||||
<div ref={node} {ondrop} {ondragover}>
|
||||
{body}
|
||||
</div>
|
||||
</BasePage>
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(module = "/src/components/home.js")]
|
||||
extern "C" {
|
||||
fn get_files_data_transfer_items(data_transfer_items: DataTransferItemList) -> js_sys::Promise;
|
||||
}
|
||||
48
frontend/src/components/list_errors.rs
Normal file
48
frontend/src/components/list_errors.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Properties, Clone, PartialEq, Eq)]
|
||||
pub struct Props {
|
||||
pub error: Option<Error>,
|
||||
}
|
||||
|
||||
#[function_component(ListErrors)]
|
||||
pub fn list_errors(props: &Props) -> Html {
|
||||
if let Some(error) = &props.error {
|
||||
html! {
|
||||
<ul class="error-messages">
|
||||
{
|
||||
match error {
|
||||
Error::UnprocessableEntity(error_info) => {
|
||||
html! {
|
||||
<>
|
||||
{for error_info.errors.iter().map(|(key, value)| {
|
||||
html! {
|
||||
<li>
|
||||
{ key }
|
||||
{for value.iter().map(|e| {
|
||||
html! {
|
||||
<>{" "} {e}</>
|
||||
}
|
||||
})}
|
||||
</li>
|
||||
}
|
||||
})}
|
||||
</>
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
html! {
|
||||
<li>{error}</li>
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
} else {
|
||||
html! {}
|
||||
}
|
||||
}
|
||||
148
frontend/src/components/login.rs
Normal file
148
frontend/src/components/login.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use web_sys::HtmlInputElement;
|
||||
|
||||
use ybc::TileCtx::{Ancestor, Child, Parent};
|
||||
use ybc::TileSize::*;
|
||||
use yew::prelude::*;
|
||||
use yew_hooks::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
use super::BasePage;
|
||||
use crate::components::list_errors::ListErrors;
|
||||
use crate::hooks::use_user_context;
|
||||
use crate::services::auth::*;
|
||||
use crate::types::{LoginInfo, LoginInfoWrapper, UserInfoWrapper};
|
||||
use crate::Route;
|
||||
|
||||
/// Login page
|
||||
#[function_component(Login)]
|
||||
pub fn login_page() -> Html {
|
||||
let user_ctx = use_user_context();
|
||||
let login_info = use_state(LoginInfo::default);
|
||||
let user_login = {
|
||||
let login_info = login_info.clone();
|
||||
use_async(async move {
|
||||
let request = LoginInfoWrapper {
|
||||
user: (*login_info).clone(),
|
||||
};
|
||||
login(&request).send::<UserInfoWrapper>().await
|
||||
})
|
||||
};
|
||||
|
||||
use_effect_with_deps(
|
||||
move |user_login| {
|
||||
if let Some(user_info) = &user_login.data {
|
||||
user_ctx.login(user_info.user.clone());
|
||||
}
|
||||
|| ()
|
||||
},
|
||||
user_login.clone(),
|
||||
);
|
||||
|
||||
let onsubmit = {
|
||||
let user_login = user_login.clone();
|
||||
Callback::from(move |e: SubmitEvent| {
|
||||
e.prevent_default(); /* Prevent event propagation */
|
||||
user_login.run();
|
||||
})
|
||||
};
|
||||
let oninput_email = {
|
||||
let login_info = login_info.clone();
|
||||
Callback::from(move |e: InputEvent| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
let mut info = (*login_info).clone();
|
||||
info.email = input.value();
|
||||
login_info.set(info);
|
||||
})
|
||||
};
|
||||
let oninput_password = {
|
||||
let login_info = login_info.clone();
|
||||
Callback::from(move |e: InputEvent| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
let mut info = (*login_info).clone();
|
||||
info.password = input.value();
|
||||
login_info.set(info);
|
||||
})
|
||||
};
|
||||
|
||||
let form = html! {
|
||||
<div class="auth-page">
|
||||
<div class="container page">
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-3 col-xs-12">
|
||||
<h1 class="text-xs-center">{ "Sign In" }</h1>
|
||||
<p class="text-xs-center">
|
||||
<Link<Route> to={Route::Register}>
|
||||
{ "Need an account?" }
|
||||
</Link<Route>>
|
||||
</p>
|
||||
<ListErrors error={user_login.error.clone()} />
|
||||
<form {onsubmit}>
|
||||
<fieldset>
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={login_info.email.clone()}
|
||||
oninput={oninput_email}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={login_info.password.clone()}
|
||||
oninput={oninput_password}
|
||||
/>
|
||||
</fieldset>
|
||||
<button
|
||||
class="btn btn-lg btn-primary pull-xs-right"
|
||||
type="submit"
|
||||
disabled=false>
|
||||
{ "Sign in" }
|
||||
</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
};
|
||||
|
||||
let hero_body = html! {
|
||||
<ybc::Container
|
||||
fluid=true
|
||||
|
||||
classes={
|
||||
classes!(
|
||||
"is-centered",
|
||||
"is-light",
|
||||
)
|
||||
}
|
||||
>
|
||||
<ybc::Tile ctx={Ancestor}>
|
||||
<ybc::Tile ctx={Parent} vertical=true size={Twelve}>
|
||||
<ybc::Tile ctx={Child} classes={classes!("box")}>
|
||||
{form}
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
</ybc::Container>
|
||||
};
|
||||
|
||||
html! {
|
||||
<BasePage>
|
||||
<ybc::Hero
|
||||
classes={
|
||||
classes!(
|
||||
"is-light",
|
||||
)
|
||||
}
|
||||
size={ybc::HeroSize::FullheightWithNavbar}
|
||||
body={hero_body}
|
||||
>
|
||||
</ybc::Hero>
|
||||
</BasePage>
|
||||
}
|
||||
}
|
||||
16
frontend/src/components/mod.rs
Normal file
16
frontend/src/components/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
pub mod home;
|
||||
pub use home::Home;
|
||||
|
||||
pub mod register;
|
||||
pub use register::Register;
|
||||
|
||||
pub mod login;
|
||||
pub use login::Login;
|
||||
|
||||
pub mod user_context_provider;
|
||||
pub use user_context_provider::*;
|
||||
|
||||
pub mod base_page;
|
||||
pub use base_page::*;
|
||||
|
||||
pub mod list_errors;
|
||||
168
frontend/src/components/register.rs
Normal file
168
frontend/src/components/register.rs
Normal file
@@ -0,0 +1,168 @@
|
||||
use web_sys::HtmlInputElement;
|
||||
|
||||
use ybc::TileCtx::{Ancestor, Child, Parent};
|
||||
use ybc::TileSize::*;
|
||||
use yew::prelude::*;
|
||||
use yew_hooks::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
use super::BasePage;
|
||||
use crate::components::list_errors::ListErrors;
|
||||
use crate::hooks::use_user_context;
|
||||
use crate::services::auth::*;
|
||||
use crate::types::{RegisterInfo, RegisterInfoWrapper, UserInfoWrapper};
|
||||
use crate::Route;
|
||||
|
||||
/// Register page
|
||||
#[function_component(Register)]
|
||||
pub fn register_page() -> Html {
|
||||
let user_ctx = use_user_context();
|
||||
let register_info = use_state(RegisterInfo::default);
|
||||
let user_register = {
|
||||
let register_info = register_info.clone();
|
||||
use_async(async move {
|
||||
let request = RegisterInfoWrapper {
|
||||
user: (*register_info).clone(),
|
||||
};
|
||||
register(&request).send::<UserInfoWrapper>().await
|
||||
})
|
||||
};
|
||||
|
||||
{
|
||||
use_effect_with_deps(
|
||||
move |user_register| {
|
||||
if let Some(user_info) = &user_register.data {
|
||||
user_ctx.login(user_info.user.clone());
|
||||
}
|
||||
|| ()
|
||||
},
|
||||
user_register.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
let onsubmit = {
|
||||
let user_register = user_register.clone();
|
||||
Callback::from(move |e: SubmitEvent| {
|
||||
e.prevent_default(); /* Prevent event propagation */
|
||||
user_register.run();
|
||||
})
|
||||
};
|
||||
let oninput_username = {
|
||||
let register_info = register_info.clone();
|
||||
Callback::from(move |e: InputEvent| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
let mut info = (*register_info).clone();
|
||||
info.username = input.value();
|
||||
register_info.set(info);
|
||||
})
|
||||
};
|
||||
let oninput_email = {
|
||||
let register_info = register_info.clone();
|
||||
Callback::from(move |e: InputEvent| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
let mut info = (*register_info).clone();
|
||||
info.email = input.value();
|
||||
register_info.set(info);
|
||||
})
|
||||
};
|
||||
let oninput_password = {
|
||||
let register_info = register_info.clone();
|
||||
Callback::from(move |e: InputEvent| {
|
||||
let input: HtmlInputElement = e.target_unchecked_into();
|
||||
let mut info = (*register_info).clone();
|
||||
info.password = input.value();
|
||||
register_info.set(info);
|
||||
})
|
||||
};
|
||||
|
||||
let form = html! {
|
||||
<div class="auth-page">
|
||||
<div class="container page">
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-3 col-xs-12">
|
||||
<h1 class="text-xs-center">{ "Sign Up" }</h1>
|
||||
<p class="text-xs-center">
|
||||
<Link<Route> to={Route::Login}>
|
||||
{ "Have an account?" }
|
||||
</Link<Route>>
|
||||
</p>
|
||||
<ListErrors error={user_register.error.clone()} />
|
||||
<form {onsubmit}>
|
||||
<fieldset>
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={register_info.username.clone()}
|
||||
oninput={oninput_username}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={register_info.email.clone()}
|
||||
oninput={oninput_email}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={register_info.password.clone()}
|
||||
oninput={oninput_password}
|
||||
/>
|
||||
</fieldset>
|
||||
<button
|
||||
class="btn btn-lg btn-primary pull-xs-right"
|
||||
type="submit"
|
||||
disabled=false>
|
||||
{ "Sign up" }
|
||||
</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
};
|
||||
|
||||
let hero_body = html! {
|
||||
<ybc::Container
|
||||
fluid=true
|
||||
|
||||
classes={
|
||||
classes!(
|
||||
"is-centered",
|
||||
"is-light",
|
||||
)
|
||||
}
|
||||
>
|
||||
<ybc::Tile ctx={Ancestor}>
|
||||
<ybc::Tile ctx={Parent} vertical=true size={Twelve}>
|
||||
<ybc::Tile ctx={Child} classes={classes!("box")}>
|
||||
{form}
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
</ybc::Container>
|
||||
};
|
||||
|
||||
html! {
|
||||
<BasePage>
|
||||
<ybc::Hero
|
||||
classes={
|
||||
classes!(
|
||||
"is-light",
|
||||
)
|
||||
}
|
||||
size={ybc::HeroSize::FullheightWithNavbar}
|
||||
body={hero_body}
|
||||
>
|
||||
</ybc::Hero>
|
||||
</BasePage>
|
||||
}
|
||||
}
|
||||
56
frontend/src/components/user_context_provider.rs
Normal file
56
frontend/src/components/user_context_provider.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
//! User context provider.
|
||||
|
||||
use weblog::console_log;
|
||||
use yew::prelude::*;
|
||||
use yew_hooks::prelude::*;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::services::{auth::*, get_token, set_token};
|
||||
use crate::types::{UserInfo, UserInfoWrapper};
|
||||
|
||||
#[derive(Properties, Clone, PartialEq)]
|
||||
pub struct Props {
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
/// User context provider.
|
||||
#[function_component(UserContextProvider)]
|
||||
pub fn user_context_provider(props: &Props) -> Html {
|
||||
let user_ctx = use_state(UserInfo::default);
|
||||
let current_user = use_async(async move { current().send::<UserInfoWrapper>().await });
|
||||
|
||||
{
|
||||
let current_user = current_user.clone();
|
||||
use_mount(move || {
|
||||
if get_token().is_some() {
|
||||
current_user.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
let user_ctx = user_ctx.clone();
|
||||
use_effect_with_deps(
|
||||
move |current_user| {
|
||||
if let Some(user_info) = ¤t_user.data {
|
||||
user_ctx.set(user_info.user.clone());
|
||||
}
|
||||
|
||||
if let Some(error) = ¤t_user.error {
|
||||
match error {
|
||||
Error::Unauthorized | Error::Forbidden => set_token(None),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|| ()
|
||||
},
|
||||
current_user,
|
||||
)
|
||||
}
|
||||
|
||||
html! {
|
||||
<ContextProvider<UseStateHandle<UserInfo>> context={user_ctx}>
|
||||
{ for props.children.iter() }
|
||||
</ContextProvider<UseStateHandle<UserInfo>>>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user