feat(events): A.4 — emit SnapshotChanged from the watchdog probe
Adds a new variant to the lifecycle event catalogue so the UI can
auto-refresh the channel/client tree without an independent polling
timer on the Dart side. The supervisor's existing 5 s snapshot probe
is the source of truth: it already pulls a full snapshot to keep
the watchdog honest, so we piggyback on it.
* `chanora_core::SessionEvent::SnapshotChanged { channels, clients }`
carries the latest channel and client counts.
* The supervisor compares the probe result to `last_counts` and
fires the event only when the count actually changes. `last_counts`
is reset to `None` on a successful reconnect so the freshly
dialled session re-emits its initial counts.
* `chanora_bridge::api::BridgeEvent::SnapshotChanged` is the
cross-bridge mirror.
* Flutter routes the event through `_onEvent`, which calls
`_onRefresh()` to repopulate the snapshot view.
The probe-driven detection has known limits — pure within-channel
client moves do not change the count and so are not surfaced. That
gap will close when the supervisor tracks a content hash in
addition to the count; the count-only signal is sufficient for the
common "someone joined / someone left" case observed on cn.teamspeak.app.
This commit is contained in:
@@ -123,6 +123,16 @@ pub enum SessionEvent {
|
||||
/// Audio engine stopped (e.g. before a reconnect cycle, or by
|
||||
/// explicit user action).
|
||||
AudioStopped,
|
||||
/// A snapshot probe observed a change in channel or client
|
||||
/// counts. Useful for UI auto-refresh without active polling
|
||||
/// from the Dart side. Carries the counts so subscribers can
|
||||
/// decide whether to re-fetch.
|
||||
SnapshotChanged {
|
||||
/// Number of channels in the latest probe snapshot.
|
||||
channels: u32,
|
||||
/// Number of clients in the latest probe snapshot.
|
||||
clients: u32,
|
||||
},
|
||||
}
|
||||
|
||||
/// Coarse OS-reported network state. Populated by the Flutter side
|
||||
@@ -449,6 +459,10 @@ async fn supervisor_loop(
|
||||
let mut lost_rx = initial_lost_rx;
|
||||
let mut probe = initial_probe;
|
||||
let mut cfg = initial_cfg;
|
||||
// Last snapshot signature observed by the watchdog. Used to
|
||||
// emit `SessionEvent::SnapshotChanged` only when the channel
|
||||
// or client counts actually change.
|
||||
let mut last_counts: Option<(u32, u32)> = None;
|
||||
|
||||
loop {
|
||||
// Watch the current connection: race the protocol task's
|
||||
@@ -515,7 +529,7 @@ async fn supervisor_loop(
|
||||
}
|
||||
_ = watchdog.tick() => {
|
||||
match tokio::time::timeout(WATCHDOG_PROBE_TIMEOUT, probe.probe()).await {
|
||||
Ok(Ok(_)) => {
|
||||
Ok(Ok(snap)) => {
|
||||
if misses > 0 {
|
||||
info!(
|
||||
target: "chanora_core",
|
||||
@@ -524,6 +538,17 @@ async fn supervisor_loop(
|
||||
);
|
||||
}
|
||||
misses = 0;
|
||||
// A.4 SnapshotChanged: emit when the
|
||||
// channel or client count differs from
|
||||
// the previously observed snapshot.
|
||||
let counts = (snap.channels.len() as u32, snap.clients.len() as u32);
|
||||
if last_counts != Some(counts) {
|
||||
last_counts = Some(counts);
|
||||
let _ = events_tx.send(SessionEvent::SnapshotChanged {
|
||||
channels: counts.0,
|
||||
clients: counts.1,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
misses = misses.saturating_add(1);
|
||||
@@ -725,6 +750,9 @@ async fn supervisor_loop(
|
||||
// Loop back to waiting for the next loss.
|
||||
lost_rx = new_lost_rx;
|
||||
probe = new_probe;
|
||||
// Force re-emission of SnapshotChanged
|
||||
// for the freshly reconnected session.
|
||||
last_counts = None;
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
Reference in New Issue
Block a user