use std::time::Duration;
use tauri::{Emitter, Manager, Runtime};
use super::Widget;
impl<R: Runtime> Widget<R> {
pub fn set_widget_data(
&self,
widget_id: &str,
data: &serde_json::Value,
instance: Option<&str>,
group: Option<&str>,
) -> crate::Result<bool> {
let group = crate::group::resolve_group(group)?;
crate::group::validate_widget_id(widget_id)?;
let instance = crate::group::validate_instance_id(instance)?;
let key = crate::store::data_key(widget_id, instance);
let raw = if data.is_null() {
String::new()
} else {
serde_json::to_string(data)?
};
if raw.len() > crate::resolve::MAX_SOURCE_BYTES {
return Err(crate::Error::new(format!(
"widget data is {} bytes; the limit is {}",
raw.len(),
crate::resolve::MAX_SOURCE_BYTES
)));
}
if self.get_items(&key, &group)?.as_deref() == Some(raw.as_str()) {
return Ok(false);
}
self.set_items(&key, &raw, &group)?;
self.data_changed(&group, widget_id, instance);
let _ = self.app.emit(
"widget-data-push",
serde_json::json!({
"group": group,
"widgetId": widget_id,
"instance": instance,
"data": data,
}),
);
self.reload_all_timelines()?;
Ok(true)
}
pub fn register_poll_source<F>(&self, widget_id: &str, interval: Duration, mut poll: F)
where
F: FnMut() -> serde_json::Value + Send + 'static,
{
let interval = interval.max(Duration::from_millis(100));
self.spawn_source(widget_id, move |alive, push| {
while alive() {
push(poll());
std::thread::sleep(interval);
}
});
}
pub fn register_listen_source<I>(&self, widget_id: &str, values: I)
where
I: IntoIterator<Item = serde_json::Value>,
I::IntoIter: Send + 'static,
{
let iter = values.into_iter();
self.spawn_source(widget_id, move |alive, push| {
for v in iter {
if !alive() {
break;
}
push(v);
}
});
}
pub fn unregister_source(&self, widget_id: &str) -> bool {
self.sources.lock().unwrap().remove(widget_id).is_some()
}
fn spawn_source<B>(&self, widget_id: &str, body: B)
where
B: FnOnce(&dyn Fn() -> bool, &dyn Fn(serde_json::Value)) + Send + 'static,
{
static NEXT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
let gen = NEXT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
self.sources
.lock()
.unwrap()
.insert(widget_id.to_string(), gen);
let app = self.app.clone();
let id = widget_id.to_string();
std::thread::spawn(move || {
let alive = || {
app.try_state::<Widget<R>>()
.is_some_and(|w| w.sources.lock().unwrap().get(&id) == Some(&gen))
};
let push = |v: serde_json::Value| {
if let Some(w) = app.try_state::<Widget<R>>() {
if let Err(e) = w.set_widget_data(&id, &v, None, None) {
log::warn!("widgets: data source {id}: {e}");
}
}
};
body(&alive, &push);
if let Some(w) = app.try_state::<Widget<R>>() {
let mut s = w.sources.lock().unwrap();
if s.get(&id) == Some(&gen) {
s.remove(&id);
}
}
});
}
}