arch: push wire types out of ClientApp, extract event_service, cleanup dead code

- ClientApp stores domain types, RepaintCommand carries DisplayHint + Vec<(String,Value)>
- adapters no longer convert Wire→Domain (eliminated duplication in esp32 + desktop)
- event_service in application layer handles LayoutChanged/WebhookDataReceived/ThemeChanged
- bootstrap event_handler reduced to 10-line dispatcher
- polling_service reuses event_service::apply_and_broadcast (deduplicated broadcast pattern)
- AppState.config_service() replaces 11 inline ConfigService::new() calls
- delete unused poll_interval_secs parameter chain
- delete unused StoragePort/ClientConfig (zero implementations)
This commit is contained in:
2026-06-19 18:30:14 +02:00
parent 7001b5e911
commit fa097771d4
21 changed files with 264 additions and 217 deletions

View File

@@ -1,24 +1,24 @@
use crate::conversions::wire_to_layout;
use crate::conversions::{wire_to_display_hint, wire_to_layout, wire_to_widget_state};
use client_domain::{BoundingBox, Color, LayoutEngine, RenderTree, ThemeConfig};
use protocol::{
ServerMessage, WidgetDescriptor, WireColor, WireDisplayHint, WireLayoutNode, WireWidgetState,
};
use domain::{DisplayHint, Value, WidgetError, WidgetState};
use protocol::{ServerMessage, WidgetDescriptor, WireColor, WireLayoutNode};
use std::collections::HashMap;
pub struct ClientApp {
screen: BoundingBox,
render_tree: Option<RenderTree>,
widget_states: HashMap<u16, (WireDisplayHint, WireWidgetState)>,
widget_states: HashMap<u16, (DisplayHint, WidgetState)>,
theme: ThemeConfig,
theme_changed: bool,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub struct RepaintCommand {
pub widget_id: u16,
pub bounds: BoundingBox,
pub display_hint: WireDisplayHint,
pub state: WireWidgetState,
pub display_hint: DisplayHint,
pub data: Vec<(String, Value)>,
pub error: Option<WidgetError>,
}
impl ClientApp {
@@ -77,9 +77,10 @@ impl ClientApp {
let new_tree = LayoutEngine::compute(&layout, self.screen);
self.widget_states.clear();
for w in &widgets {
self.widget_states
.insert(w.id, (w.display_hint.clone(), w.state.clone()));
for w in widgets {
let hint = wire_to_display_hint(w.display_hint);
let state = wire_to_widget_state(w.state);
self.widget_states.insert(w.id, (hint, state));
}
let repaints = self.build_repaints_for_all(&new_tree);
@@ -96,21 +97,19 @@ impl ClientApp {
let mut repaints = Vec::new();
for w in widgets {
let hint = wire_to_display_hint(w.display_hint);
let state = wire_to_widget_state(w.state);
let changed = self
.widget_states
.get(&w.id)
.is_none_or(|(_, prev_state)| *prev_state != w.state);
.is_none_or(|(_, prev)| *prev != state);
if changed {
if let Some(bounds) = tree.get_widget_bounds(w.id) {
repaints.push(RepaintCommand {
widget_id: w.id,
bounds: *bounds,
display_hint: w.display_hint.clone(),
state: w.state.clone(),
});
repaints.push(Self::make_repaint(w.id, *bounds, &hint, &state));
}
self.widget_states.insert(w.id, (w.display_hint, w.state));
self.widget_states.insert(w.id, (hint, state));
}
}
@@ -122,18 +121,32 @@ impl ClientApp {
for (id, (hint, state)) in &self.widget_states {
if let Some(bounds) = tree.get_widget_bounds(*id) {
repaints.push(RepaintCommand {
widget_id: *id,
bounds: *bounds,
display_hint: hint.clone(),
state: state.clone(),
});
repaints.push(Self::make_repaint(*id, *bounds, hint, state));
}
}
repaints.sort_by_key(|r| r.widget_id);
repaints
}
fn make_repaint(
id: u16,
bounds: BoundingBox,
hint: &DisplayHint,
state: &WidgetState,
) -> RepaintCommand {
RepaintCommand {
widget_id: id,
bounds,
display_hint: hint.clone(),
data: state
.data
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
error: state.error.clone(),
}
}
}
fn wire_color(c: WireColor) -> Color {

View File

@@ -1,4 +1,4 @@
use client_application::{ClientApp, RepaintCommand};
use client_application::ClientApp;
use client_domain::BoundingBox;
use protocol::{
ServerMessage, WidgetDescriptor, WireAlignItems, WireContainerNode, WireDirection,
@@ -84,8 +84,8 @@ fn data_update_only_repaints_changed_widgets() {
assert_eq!(repaints.len(), 1);
assert_eq!(repaints[0].widget_id, 1);
assert_eq!(
repaints[0].state.data[0].value,
WireValue::String("6.1°C".into())
repaints[0].data[0],
("temperature".into(), domain::Value::String("6.1°C".into()))
);
}