ui race condition fix

This commit is contained in:
legop3
2026-08-18 23:15:24 -04:00
parent 8e7d31dcdd
commit 271197f33c
16 changed files with 138 additions and 63 deletions
+8
View File
@@ -217,6 +217,14 @@ export function TelemetryProvider({ children }) {
currentValue: selector(framesRef.current[roverId] ?? EMPTY_FRAME),
};
listeners.add(entry);
// React renders before effects subscribe. A sensor frame can therefore
// arrive after the hook's render-time read but before this entry exists.
// Publish the exact snapshot used to initialize the subscription so the
// component cannot remain stuck on that stale render-time value until a
// selected field changes again. Registering first is important: any
// frame arriving after this point will also notify the listener normally.
listener(entry.currentValue);
return () => {
const current = selectorSubscribersRef.current.get(roverId);
if (!current) return;
+3 -21
View File
@@ -37,27 +37,6 @@ const EMPTY_MAIN_BRUSH_AUDIO = Object.freeze({
mainBrushOvercurrent: false,
});
// These labels represent an active charging relationship reported by the
// Roomba. That is independently useful dock evidence: some already-docked
// rovers report charging before the home-base contact bit changes again.
const DOCKED_CHARGING_STATES = new Set([
'reconditioning charging',
'full charging',
'trickle charging',
'waiting',
]);
export function isDockedChargingState(chargingStateLabel) {
return DOCKED_CHARGING_STATES.has(String(chargingStateLabel || '').trim().toLowerCase());
}
export function resolveDocked(dockTelemetry) {
// Dock contact and active charging are complementary sensor evidence, not
// fallback state. Treating either as sufficient prevents UI controls from
// claiming a charging rover should be driven onto a dock it already occupies.
return Boolean(dockTelemetry?.homeBase) || isDockedChargingState(dockTelemetry?.chargingStateLabel);
}
function bucketNumber(value, step) {
// Visual widgets do not benefit from repainting for tiny analog jitter. The
// bucket step intentionally applies only to display selectors; raw telemetry
@@ -147,6 +126,9 @@ export function selectDockTelemetry(frame) {
return {
oiModeLabel: sensors.oiMode?.label || 'Unknown',
chargingStateLabel: sensors.chargingState?.label || '',
// OI packet 34 is the authoritative charging-sources packet. Charging
// state describes the battery charger state and must not stand in for the
// physical home-base contact bit.
homeBase: Boolean(sensors.chargingSources?.homeBase),
};
}