1use super::ConsentState;
2use gloo_storage::Storage;
3use web_sys::console;
4use yew::prelude::*;
5
6fn load_state<T>(key: &str) -> Option<ConsentState<T>>
7where
8 for<'de> T: serde::Deserialize<'de>,
9{
10 gloo_storage::LocalStorage::get(key).ok()
11}
12
13fn store_state<T>(key: &str, state: Option<ConsentState<T>>)
14where
15 for<'de> T: serde::Serialize,
16{
17 match state {
18 Some(state) => {
19 if let Err(err) = gloo_storage::LocalStorage::set(key, state) {
20 console::error_2(&"Unable to store state".into(), &err.to_string().into());
21 }
22 }
23 None => gloo_storage::LocalStorage::delete(key),
24 }
25}
26
27#[derive(Clone, PartialEq)]
28pub struct ConsentContext<T = ()> {
29 callback: Callback<Option<ConsentState<T>>>,
30}
31
32impl<T> ConsentContext<T>
33where
34 for<'de> T: Clone + PartialEq,
35{
36 pub fn set(&self, state: impl Into<Option<ConsentState<T>>>) {
37 self.callback.emit(state.into());
38 }
39}
40
41#[derive(PartialEq, Properties)]
42pub struct ConsentProperties<T>
43where
44 T: PartialEq,
45{
46 #[prop_or("user.consent".into())]
47 pub consent_key: AttrValue,
48
49 #[prop_or_default]
50 pub children: Children,
51
52 pub ask: Callback<ConsentContext<T>, Html>,
53}
54
55#[function_component(Consent)]
56pub fn consent<T>(props: &ConsentProperties<T>) -> Html
57where
58 for<'de> T: Clone + PartialEq + serde::Deserialize<'de> + serde::Serialize + 'static,
59{
60 let consent = use_state_eq(|| load_state(&props.consent_key));
61
62 let callback = use_callback(
63 (props.consent_key.clone(), consent.clone()),
64 |state: Option<ConsentState<T>>, (consent_key, consent)| {
65 store_state(consent_key, state.clone());
66 consent.set(state);
67 },
68 );
69 let context = ConsentContext { callback };
70
71 html!(
72 <ContextProvider<ConsentContext<T>> context={context.clone()}>
73 {
74 match &*consent {
75 Some(state) => {
76 let children: Html = props.children.iter().collect();
77 match &state {
78 ConsentState::Yes(_) => html!(
79 <ContextProvider<ConsentState<T>> context={state.clone()}>
80 {children}
81 </ContextProvider<ConsentState<T>>>
82 ),
83 ConsentState::No => children,
84 }
85 }
86 None => props.ask.emit(context)
87 }
88 }
89 </ContextProvider<ConsentContext<T>>>
90 )
91}