tallyweb_frontend/countable/
indexed.rs

1use 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}