- React SPA: dashboard, data sources CRUD, widgets CRUD, layout builder, presets. TanStack Router + Query, shadcn/ui, Vite proxy to :3000 - wire media + rss adapters into polling loop, remove xtb source type - media adapter: read username/password from headers, proper subsonic auth - event handler: subscribe to LayoutChanged, push screen update to clients - fix clippy warnings across workspace (Default impls, collapsible ifs, redundant closures, is_none_or, unused imports)
47 lines
1.1 KiB
Rust
47 lines
1.1 KiB
Rust
use domain::{DataSourceId, Value, WidgetConfig, WidgetId, WidgetState};
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Default)]
|
|
pub struct DataProjection {
|
|
current: HashMap<WidgetId, WidgetState>,
|
|
}
|
|
|
|
impl DataProjection {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn get_state(&self, widget_id: WidgetId) -> Option<&WidgetState> {
|
|
self.current.get(&widget_id)
|
|
}
|
|
|
|
pub fn apply_poll_result(
|
|
&mut self,
|
|
data_source_id: DataSourceId,
|
|
raw: &Value,
|
|
widget_configs: &[WidgetConfig],
|
|
) -> Vec<(WidgetId, WidgetState)> {
|
|
let mut changed = Vec::new();
|
|
|
|
for config in widget_configs {
|
|
if config.data_source_id != data_source_id {
|
|
continue;
|
|
}
|
|
|
|
let new_state = config.extract(raw);
|
|
|
|
let is_changed = self
|
|
.current
|
|
.get(&config.id)
|
|
.is_none_or(|prev| *prev != new_state);
|
|
|
|
if is_changed {
|
|
self.current.insert(config.id, new_state.clone());
|
|
changed.push((config.id, new_state));
|
|
}
|
|
}
|
|
|
|
changed
|
|
}
|
|
}
|