tallyweb_frontend/countable/
indexed.rs1use super::*;
2use leptos::{create_effect, expect_context};
3
4#[cfg(not(docsrs))]
5const IDB_VERSION: &str = env!("IDB_TALLYWEB_VERSION");
6#[cfg(docsrs)]
7const IDB_VERSION: &str = "1";
8
9#[allow(dead_code)]
10#[derive(Clone)]
11pub struct IndexedSaveHandler {
12 version: u32,
13}
14
15impl IndexedSaveHandler {
16 pub async fn new() -> Result<Self, AppError> {
17 let version = IDB_VERSION
18 .parse()
19 .map_err(|_| AppError::Environment("IDB_VERSION".to_string()))?;
20
21 let factory = indexed_db::Factory::<AppError>::get()?;
22 factory
23 .open("TallyWeb", version, |evt| async move {
24 evt.database().delete_object_store("Countable")?;
25 let obj_builder = evt.database().build_object_store("Countable");
26 obj_builder.create()?;
27 Ok(())
28 })
29 .await?;
30
31 Ok(Self { version })
32 }
33
34 pub async fn reset() -> Result<(), AppError> {
35 let factory = indexed_db::Factory::<AppError>::get()?;
36 let db = factory.open_latest_version("TallyWeb").await?;
37 db.transaction(&["Countable"])
38 .rw()
39 .run(|transaction| async move {
40 transaction.object_store("Countable")?.clear().await?;
41 Ok(())
42 })
43 .await?;
44
45 Ok(())
46 }
47
48 pub async fn sync_store(&self, store: &mut CountableStore) -> Result<(), AppError> {
49 let factory = indexed_db::Factory::get()?;
50 let owner = store.owner();
51 let db = factory.open_latest_version("TallyWeb").await?;
52 let map = db
53 .transaction(&["Countable"])
54 .run(move |evt| async move {
55 let obj = evt.object_store("Countable")?;
56 let map = obj
57 .get_all(None)
58 .await?
59 .into_iter()
60 .map(Countable::from_js)
61 .collect::<Result<Vec<Countable>, AppError>>()?
62 .into_iter()
63 .map(|c| (c.uuid().into(), c))
64 .collect::<std::collections::HashMap<CountableId, Countable>>();
65 Ok(map)
66 })
67 .await?;
68
69 let local_store = CountableStore::new(owner, map);
70 store.merge_checked(local_store)?;
71 self.save(Box::new(store.clone()), Box::new(|_| ()))?;
72
73 Ok(())
74 }
75}
76
77impl SaveHandler for IndexedSaveHandler {
78 fn save(
79 &self,
80 value: Box<dyn Savable>,
81 on_error: Box<dyn Fn(&dyn std::error::Error) + 'static>,
82 ) -> Result<(), AppError> {
83 let msg = expect_context::<components::MessageJar>();
84
85 #[allow(clippy::borrowed_box)]
86 let action = leptos::create_action(move |value: &Box<dyn Savable>| {
87 let value = value.clone_box();
88 async move {
89 let factory = indexed_db::Factory::<AppError>::get()?;
90 let db = factory.open_latest_version("TallyWeb").await?;
91 let store_name = value.indexed_db_name();
92
93 db.transaction(&[store_name.as_str()])
94 .rw()
95 .run(move |tr| {
96 let obj = tr.object_store(&store_name);
97 async move {
98 value.save_indexed(obj?).await?;
99 Ok(())
100 }
101 })
102 .await?;
103
104 Ok::<(), AppError>(())
105 }
106 });
107
108 action.dispatch(value);
109
110 #[allow(clippy::single_match)]
111 create_effect(move |_| match action.value()() {
112 Some(Err(err)) => {
113 on_error(&err);
114 msg.without_timeout().set_err(err)
115 }
116 _ => {}
117 });
118
119 Ok(())
120 }
121
122 fn clone_box(&self) -> Box<dyn SaveHandler> {
123 Box::new(self.clone())
124 }
125}