use crate::prelude::*;
use crate::props::{IntoLoadCallback, LoadCallback};
-use crate::state::{
- SharedState, SharedStateObserver, SharedStateReadGuard, SharedStateWriteGuard,
-};
+use crate::state::{SharedState, SharedStateObserver, SharedStateReadGuard, SharedStateWriteGuard};
use crate::widget::{error_message, Button, Fa};
+use super::StorageLocation;
+
/// Shared HTTP load state
///
/// This struct stores the state (loading) and the result of the load.
pub struct LoaderState<T> {
loading: u64,
state_id: Option<AttrValue>,
+ storage_location: StorageLocation,
pub loader: Option<LoadCallback<T>>,
pub data: Option<Result<Rc<T>, Error>>,
}
None => return,
};
- if let Some(data) = super::load_state(state_id) {
+ if let Some(data) = super::load_state(state_id, self.storage_location) {
self.data = Some(Ok(Rc::new(data)));
}
}
match &self.data {
Some(Ok(data)) => {
- super::store_state(state_id, data);
+ super::store_state(state_id, data, self.storage_location);
}
_ => {
- super::delete_state(state_id);
+ super::delete_state(state_id, self.storage_location);
}
}
}
/// - clnonable, shared state with change notifications.
/// - stores load result as `Option<Result<Rc<T>, Error>>`.
/// - tracks load state `self.loading()`.
-/// - ability to cache result in local storage by setting `state_id`.
+/// - ability to cache result in local (default) or session storage by setting `state_id`.
/// - helper to simplify renderering `self.render`.
///
#[derive(Derivative)]
state_id: None,
data: None,
loader: None,
+ storage_location: StorageLocation::Session,
};
Self(SharedState::new(state))
}
me.load_from_cache();
}
+ /// Method to set the [StorageLocation]
+ pub fn set_storage_location(&mut self, storage_location: StorageLocation) {
+ let mut me = self.write();
+ me.storage_location = storage_location;
+ }
+
+ /// Builder style method to set the [StorageLocation]
+ pub fn storage(mut self, storage_location: StorageLocation) -> Self {
+ self.set_storage_location(storage_location);
+ self
+ }
+
pub fn on_change(mut self, cb: impl IntoEventCallback<Loader<T>>) -> Self {
let me = self.clone();
match cb.into_event_callback() {
get_available_languages, set_available_languages, Language, LanguageInfo, LanguageObserver,
};
+/// Where the state should be saved
+#[derive(Clone, Copy)]
+pub enum StorageLocation {
+ /// saved in the browser local storage
+ /// https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
+ Local,
+ /// saved in the browser session storage
+ /// https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
+ Session,
+}
+
/// Helper function to get the window session [Storage](web_sys::Storage)
pub fn session_storage() -> Option<web_sys::Storage> {
let window = match web_sys::window() {
Some(store)
}
-fn resolve_state_id(state_id: &str) -> Option<(&str, web_sys::Storage)> {
- let (use_session_storage, state_id) = if let Some(state_id) = state_id.strip_prefix("*") {
- (true, state_id)
- } else {
- (false, state_id)
- };
-
- let store = if use_session_storage {
- match session_storage() {
- Some(store) => store,
- None => return None,
- }
- } else {
- match local_storage() {
- Some(store) => store,
- None => return None,
- }
- };
- Some((state_id, store))
+fn resolve_storage(storage: StorageLocation) -> Option<web_sys::Storage> {
+ match storage {
+ StorageLocation::Local => local_storage(),
+ StorageLocation::Session => session_storage(),
+ }
}
-pub fn delete_state(state_id: &str) {
- if let Some((state_id, store)) = resolve_state_id(state_id) {
+pub fn delete_state(state_id: &str, storage: StorageLocation) {
+ if let Some(store) = resolve_storage(storage) {
let _ = store.delete(state_id);
}
}
-pub fn load_state<T: 'static + DeserializeOwned>(state_id: &str) -> Option<T> {
- if let Some((state_id, store)) = resolve_state_id(state_id) {
+pub fn load_state<T: 'static + DeserializeOwned>(
+ state_id: &str,
+ storage: StorageLocation,
+) -> Option<T> {
+ if let Some(store) = resolve_storage(storage) {
if let Ok(Some(item_str)) = store.get_item(state_id) {
if let Ok(data) = serde_json::from_str(&item_str) {
return Some(data);
None
}
-pub fn store_state<T: 'static + Serialize>(state_id: &str, data: &T) {
- if let Some((state_id, store)) = resolve_state_id(state_id) {
+pub fn store_state<T: 'static + Serialize>(state_id: &str, data: &T, storage: StorageLocation) {
+ if let Some(store) = resolve_storage(storage) {
let item_str = serde_json::to_string(data).unwrap();
match store.set_item(state_id, &item_str) {
Err(err) => log::error!(
use serde::{de::DeserializeOwned, Serialize};
use std::ops::Deref;
+use super::StorageLocation;
+
/// Helper to store data persitently using window local [Storage](web_sys::Storage)
///
/// Usage:
/// ```
/// # fn test() {
/// use pwt::state::PersistentState;
+/// use pwt::state::StorageLocation;
///
/// let mut state = PersistentState::<bool>::new("my-storage-key-name");
///
/// state.update(true); // update the value
/// # }
/// ```
+///
+/// in session storage instead of local storage:
+/// ```
+/// # fn test() {
+/// use pwt::state::PersistentState;
+/// use pwt::state::StorageLocation;
+///
+/// let mut state = PersistentState::<bool>::with_location("my-storage-key-name", StorageLocation::Session);
+///
+/// let cunnent_value: bool = *state; // acess value with Deref
+///
+/// state.update(true); // update the value
+/// # }
+/// ```
pub struct PersistentState<T> {
+ storage: StorageLocation,
state_id: String,
data: T,
}
}
impl<T: 'static + Default + Serialize + DeserializeOwned> PersistentState<T> {
- /// Create a new instance, using 'state_id' as storage key.
+ /// Create a new instance, using 'state_id' as storage key in the
+ /// local storage.
+ ///
+ /// See [Self::with_location] for details.
+ pub fn new(state_id: &str) -> Self {
+ Self::with_location(state_id, StorageLocation::Local)
+ }
+
+ /// Create a new instance, using 'state_id' as storage key from the given
+ /// [StorageLocation]
///
/// This automatically loads data from the storage.
///
///
/// Any errors are logged and ignored. Returns the default value
/// in case of errors.
- pub fn new(state_id: &str) -> Self {
+ pub fn with_location(state_id: &str, storage: StorageLocation) -> Self {
let mut me = Self {
state_id: state_id.into(),
data: T::default(),
+ storage,
};
me.load();
me
}
fn load(&mut self) {
- if let Some(data) = super::load_state(&self.state_id) {
+ if let Some(data) = super::load_state(&self.state_id, self.storage) {
self.data = data;
}
}
fn store(&self) {
- super::store_state(&self.state_id, &self.data)
+ super::store_state(&self.state_id, &self.data, self.storage)
}
/// Update data and write the new value back to the storage.