1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use std::rc::Rc;
use crate::{
Computed, Context, DropResource, Value, driver_module::api::api_timers,
fetch::api_response::ApiResponse, struct_mut::ValueMut,
};
pub struct CacheValue<T: PartialEq + 'static> {
value_write: Value<ApiResponse<T>>,
value_read: Computed<ApiResponse<T>>,
}
impl<T: PartialEq> Clone for CacheValue<T> {
fn clone(&self) -> Self {
CacheValue {
value_write: self.value_write.clone(),
value_read: self.value_read.clone(),
}
}
}
impl<T: PartialEq + 'static> CacheValue<T> {
pub fn new(init_value: ApiResponse<T>, bearer_auth: Computed<Option<String>>) -> CacheValue<T> {
let value_write = Value::new(init_value);
// The token this cache was last revalidated against.
//
// `subscribe` runs its callback once on subscription, before anything has changed, and
// discarding the value there would throw away exactly the thing that arrived with the
// page: a response the server had already fetched into `data-fetch-cache`. Every
// server-rendered `LazyCache` would then re-request on hydration, whether or not the
// application uses bearer auth at all.
//
// So the token is remembered rather than the first call being counted. It lives out
// here, not inside `when_connect`, because that closure runs again on every reconnect:
// a token that changed while this value was disconnected still has to be noticed.
let last_token: Rc<ValueMut<Option<Option<String>>>> = Rc::new(ValueMut::new(None));
let value_read: Computed<ApiResponse<T>> = {
let value_write = value_write.clone();
let bearer_auth = bearer_auth;
value_write.to_computed().when_connect({
let bearer_auth = bearer_auth.clone();
move || {
let value_write = value_write.clone();
let last_token = last_token.clone();
let revalidate_trigger = bearer_auth.clone();
let drop = revalidate_trigger.subscribe(move |new_token| {
let changed = last_token.change(|last| match last {
Some(previous) if *previous == new_token => false,
_ => {
let first = last.is_none();
*last = Some(new_token.clone());
!first
}
});
if !changed {
return;
}
let value_write = value_write.clone();
api_timers().set_timeout_and_detach(0, move || {
value_write.set(ApiResponse::Uninitialized);
});
});
DropResource::new(move || {
drop.off();
})
}
})
};
CacheValue {
value_write,
value_read,
}
}
pub fn get(&self, context: &Context) -> ApiResponse<T> {
self.value_read.get(context)
}
pub fn set(&self, value: ApiResponse<T>) {
self.value_write.set(value);
}
}