Files
k-frame/crates/application/src/data_projection.rs
Gabriel Kaszewski 26ebfad3a2 add SPA config UI, wire media/rss adapters, event-driven layout push
- 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)
2026-06-19 00:12:42 +02:00

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