190 lines
4.6 KiB
Rust
190 lines
4.6 KiB
Rust
|
|
use std::{
|
||
|
|
cell::{Cell, Ref, RefCell},
|
||
|
|
collections::HashSet,
|
||
|
|
rc::Rc,
|
||
|
|
sync::Arc,
|
||
|
|
};
|
||
|
|
|
||
|
|
use dioxus::prelude::*;
|
||
|
|
use dioxus_router::use_router;
|
||
|
|
use gloo_storage::{errors::StorageError, LocalStorage, Storage};
|
||
|
|
use uuid::Uuid;
|
||
|
|
|
||
|
|
use crate::{HouseholdInfo, LoginInfo, RedirectorProps};
|
||
|
|
|
||
|
|
pub struct RefreshHandle {
|
||
|
|
run: Box<dyn FnOnce()>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl RefreshHandle {
|
||
|
|
pub fn refresh(self) {
|
||
|
|
(self.run)()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Copy, Clone)]
|
||
|
|
pub struct FullContextState<'a> {
|
||
|
|
root: &'a ProvidedFullContext,
|
||
|
|
value: &'a Rc<RefCell<FullContext>>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<'a> FullContextState<'a> {
|
||
|
|
pub fn read(&self) -> Ref<'_, FullContext> {
|
||
|
|
self.value.borrow()
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn refresh(&self) {
|
||
|
|
let r = self.root.borrow();
|
||
|
|
|
||
|
|
r.needs_regen.set(true);
|
||
|
|
(r.update_root)();
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn refresh_handle(&self) -> RefreshHandle {
|
||
|
|
let r = self.root.clone();
|
||
|
|
RefreshHandle {
|
||
|
|
run: Box::new(move || {
|
||
|
|
let root = r.borrow();
|
||
|
|
root.needs_regen.set(true);
|
||
|
|
(root.update_root)();
|
||
|
|
}),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
struct FullContextStateInner {
|
||
|
|
root: ProvidedFullContext,
|
||
|
|
value: Rc<RefCell<FullContext>>,
|
||
|
|
scope_id: ScopeId,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Drop for FullContextStateInner {
|
||
|
|
fn drop(&mut self) {
|
||
|
|
let mut root = self.root.borrow_mut();
|
||
|
|
root.consumers.remove(&self.scope_id);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn use_full_context(cx: &ScopeState) -> FullContextState {
|
||
|
|
let state = cx.use_hook(|| {
|
||
|
|
let scope_id = cx.scope_id();
|
||
|
|
let root = cx
|
||
|
|
.consume_context::<ProvidedFullContext>()
|
||
|
|
.expect("Called use_full_context not in a full context scope");
|
||
|
|
|
||
|
|
let mut r = root.borrow_mut();
|
||
|
|
|
||
|
|
r.consumers.insert(scope_id);
|
||
|
|
let value = r.value.clone();
|
||
|
|
|
||
|
|
drop(r);
|
||
|
|
FullContextStateInner {
|
||
|
|
root,
|
||
|
|
value,
|
||
|
|
scope_id,
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
FullContextState {
|
||
|
|
root: &state.root,
|
||
|
|
value: &state.value,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn use_trimmed_context(cx: &ScopeState) -> (String, Uuid) {
|
||
|
|
let binding = use_full_context(cx);
|
||
|
|
let ctx = binding.read();
|
||
|
|
|
||
|
|
(ctx.login.token.clone(), ctx.household.id)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Clone)]
|
||
|
|
pub struct FullContext {
|
||
|
|
pub login: LoginInfo,
|
||
|
|
pub household: HouseholdInfo,
|
||
|
|
}
|
||
|
|
|
||
|
|
type ProvidedFullContext = Rc<RefCell<ProvidedFullContextInner>>;
|
||
|
|
|
||
|
|
struct ProvidedFullContextInner {
|
||
|
|
value: Rc<RefCell<FullContext>>,
|
||
|
|
notify_any: Arc<dyn Fn(ScopeId)>,
|
||
|
|
consumers: HashSet<ScopeId>,
|
||
|
|
needs_regen: Cell<bool>,
|
||
|
|
update_root: Arc<dyn Fn()>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl ProvidedFullContextInner {
|
||
|
|
fn notify_consumers(&mut self) {
|
||
|
|
for &consumer in &self.consumers {
|
||
|
|
(self.notify_any)(consumer)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn use_full_context_setter(cx: &ScopeState) {
|
||
|
|
let gen = || {
|
||
|
|
let login = LocalStorage::get::<LoginInfo>("token").expect("Not called in a full context");
|
||
|
|
let household =
|
||
|
|
LocalStorage::get::<HouseholdInfo>("household").expect("Not called in a full context");
|
||
|
|
|
||
|
|
FullContext { login, household }
|
||
|
|
};
|
||
|
|
|
||
|
|
let hook = cx.use_hook(move || {
|
||
|
|
let state = Rc::new(RefCell::new(ProvidedFullContextInner {
|
||
|
|
value: Rc::new(RefCell::new(gen())),
|
||
|
|
consumers: HashSet::new(),
|
||
|
|
notify_any: cx.schedule_update_any(),
|
||
|
|
update_root: cx.schedule_update(),
|
||
|
|
needs_regen: Cell::new(false),
|
||
|
|
}));
|
||
|
|
|
||
|
|
cx.provide_context(state.clone());
|
||
|
|
|
||
|
|
state
|
||
|
|
});
|
||
|
|
|
||
|
|
if hook.borrow().needs_regen.get() {
|
||
|
|
let mut hook = (**hook).borrow_mut();
|
||
|
|
*(*hook.value).borrow_mut() = gen();
|
||
|
|
hook.notify_consumers();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn FullContextRedirectInner<'a>(cx: Scope<'a, RedirectorProps<'a>>) -> Element {
|
||
|
|
use_full_context_setter(cx);
|
||
|
|
|
||
|
|
cx.render(rsx! {&cx.props.children})
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn FullContextRedirect<'a>(cx: Scope<'a, RedirectorProps<'a>>) -> Element {
|
||
|
|
let router = use_router(cx);
|
||
|
|
|
||
|
|
let check_token = match LocalStorage::get::<LoginInfo>("token") {
|
||
|
|
Ok(_) => true,
|
||
|
|
Err(StorageError::KeyNotFound(_)) => {
|
||
|
|
router.navigate_to("/login");
|
||
|
|
false
|
||
|
|
}
|
||
|
|
Err(e) => unreachable!("Could not get token: {e:?}"),
|
||
|
|
};
|
||
|
|
|
||
|
|
let check_household = match LocalStorage::get::<HouseholdInfo>("household") {
|
||
|
|
Ok(_) => true,
|
||
|
|
Err(StorageError::KeyNotFound(_)) => {
|
||
|
|
router.navigate_to("/household_selection");
|
||
|
|
false
|
||
|
|
}
|
||
|
|
Err(e) => unreachable!("Could not get household: {e:?}"),
|
||
|
|
};
|
||
|
|
|
||
|
|
if check_token && check_household {
|
||
|
|
cx.render(rsx! {
|
||
|
|
FullContextRedirectInner { &cx.props.children }
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
None
|
||
|
|
}
|
||
|
|
}
|