This issue was discovered in the course of making the fix in PR #1468 : a message to say that only weight was detected gets preempted by the regular save message. I'm submitting an issue instead of a fix here because the proper fix may affect every user instead of just being for one scale, so I thought it might need more discussion. Here's a Claude-generated description of the issue:
Handler messages emitted around a measurement never reach the user
What happens
A userInfo/userWarn call made by a handler at publish time is displayed for well under a second and then wiped, so in practice the user sees nothing.
I hit this adding a "body composition was not measured" notice to OneByoneHandler (#1468 ). The message was emitted correctly — it is right there in the log:
18:25:08.207 I/OneByoneHandler: No impedance in frame - publishing weight only (103.00 kg)
18:25:08.209 D/BleConnector: BluetoothEvent received: DeviceMessage(message=Weight saved. Body composition was not measured — weigh again barefoot with dry feet., ...)
18:25:08.212 I/OneByoneHandler: ← publish measurement to app
…and the user saw no message at all. I removed it from the PR rather than ship a workaround.
Why
Two debounces and a single-slot snackbar host:
- The handler's message goes out at T+0 and reaches the snackbar host at ~T+150 ms (
AppNavigation debounces the merged flow by 150 ms).
publish() triggers a save, which signals savedBurstSignal, which is debounced by 700 ms before emitting the "measurement saved" snackbar — BleConnector.kt:111.
- Each new snackbar explicitly dismisses the current one —
AppNavigation.kt:182:
currentSnackbarData?.dismiss()
showSnackbar(message = msg, actionLabel = action, duration = evt.duration)
So any handler message emitted alongside a measurement is guaranteed to be replaced ~700 ms later by the generic save notice, regardless of the SnackbarDuration it asked for.
Scope
Currently latent for other handlers. I checked the other userInfo/userWarn call sites near a publish — EufyP2Handler, RobiS9Handler, MedisanaBs44xHandler, TaylorBIAHandler all emit bt_info_step_on_scale at connect time, far from any save, so they don't race. HoffenBbs8107Handler's "measuring weight" message is the closest to one.
So this isn't causing visible breakage today. It does mean handlers have no working way to explain anything about a measurement at the moment it is saved, which is why I couldn't land the weight-only notice.
Options
1. Fold the condition into the save notice. Detect a weight-only measurement in saveMeasurementFromEvent and choose a different string for the saved snackbar. Roughly 15 lines, one place, no races. Downside: BleConnector has to know what "incomplete" means, and it grows a case per condition anyone wants to surface.
2. Give SnackbarEvent a priority, and don't let a lower-priority event dismiss a higher-priority one. Generalises to any handler wanting to say something at save time. Downside: touches the shared snackbar pipeline, and "measurement saved" losing to a handler message is a judgement call.
3. Queue instead of replace. Drop currentSnackbarData?.dismiss() and let snackbars play in sequence. Most faithful to what handlers expect. Downside: changes global UX for every snackbar in the app, and a burst of history imports would queue up a lot of them.
4. Attach the state to the measurement rather than to a message. Mark the measurement as weight-only and let the UI show it wherever the reading is displayed. Most durable — it survives past the snackbar, which matters since the answer to "why is body fat empty?" is often asked later, not in the two seconds after weighing. Biggest change.
I'd lean toward 1 as the immediate fix and 4 as the right long-term answer, but this is a UX call for the project rather than something I should decide in a scale handler.
Happy to implement whichever is preferred.
Reproducing
Any scale that can produce a weight-only measurement. On a 1byone "Health Scale", weighing in socks blocks the bioimpedance measurement and yields a valid weight with impedance 0; add a userInfo in the no-impedance branch of OneByoneHandler.parseMeasurementFrame and watch it flash and vanish.
This issue was discovered in the course of making the fix in PR #1468 : a message to say that only weight was detected gets preempted by the regular save message. I'm submitting an issue instead of a fix here because the proper fix may affect every user instead of just being for one scale, so I thought it might need more discussion. Here's a Claude-generated description of the issue:
Handler messages emitted around a measurement never reach the user
What happens
A
userInfo/userWarncall made by a handler at publish time is displayed for well under a second and then wiped, so in practice the user sees nothing.I hit this adding a "body composition was not measured" notice to
OneByoneHandler(#1468 ). The message was emitted correctly — it is right there in the log:…and the user saw no message at all. I removed it from the PR rather than ship a workaround.
Why
Two debounces and a single-slot snackbar host:
AppNavigationdebounces the merged flow by 150 ms).publish()triggers a save, which signalssavedBurstSignal, which is debounced by 700 ms before emitting the "measurement saved" snackbar —BleConnector.kt:111.AppNavigation.kt:182:So any handler message emitted alongside a measurement is guaranteed to be replaced ~700 ms later by the generic save notice, regardless of the
SnackbarDurationit asked for.Scope
Currently latent for other handlers. I checked the other
userInfo/userWarncall sites near apublish—EufyP2Handler,RobiS9Handler,MedisanaBs44xHandler,TaylorBIAHandlerall emitbt_info_step_on_scaleat connect time, far from any save, so they don't race.HoffenBbs8107Handler's "measuring weight" message is the closest to one.So this isn't causing visible breakage today. It does mean handlers have no working way to explain anything about a measurement at the moment it is saved, which is why I couldn't land the weight-only notice.
Options
1. Fold the condition into the save notice. Detect a weight-only measurement in
saveMeasurementFromEventand choose a different string for the saved snackbar. Roughly 15 lines, one place, no races. Downside:BleConnectorhas to know what "incomplete" means, and it grows a case per condition anyone wants to surface.2. Give
SnackbarEventa priority, and don't let a lower-priority event dismiss a higher-priority one. Generalises to any handler wanting to say something at save time. Downside: touches the shared snackbar pipeline, and "measurement saved" losing to a handler message is a judgement call.3. Queue instead of replace. Drop
currentSnackbarData?.dismiss()and let snackbars play in sequence. Most faithful to what handlers expect. Downside: changes global UX for every snackbar in the app, and a burst of history imports would queue up a lot of them.4. Attach the state to the measurement rather than to a message. Mark the measurement as weight-only and let the UI show it wherever the reading is displayed. Most durable — it survives past the snackbar, which matters since the answer to "why is body fat empty?" is often asked later, not in the two seconds after weighing. Biggest change.
I'd lean toward 1 as the immediate fix and 4 as the right long-term answer, but this is a UX call for the project rather than something I should decide in a scale handler.
Happy to implement whichever is preferred.
Reproducing
Any scale that can produce a weight-only measurement. On a 1byone "Health Scale", weighing in socks blocks the bioimpedance measurement and yields a valid weight with impedance 0; add a
userInfoin the no-impedance branch ofOneByoneHandler.parseMeasurementFrameand watch it flash and vanish.