feat: merge health feature and add measure command

- Add REQUEST_HEART_RATE command with measure button in health screen
  - Add ref.mounted checks and fix early return in measure()
  - Remove unused SET_LANGUAGE from DeviceCommand enum
This commit is contained in:
2026-03-22 05:15:22 +01:00
11 changed files with 74 additions and 17 deletions

View File

@@ -56,14 +56,14 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
final state = ref.watch(healthViewModelProvider);
final vm = ref.read(healthViewModelProvider.notifier);
ref.listen(
healthViewModelProvider.select((s) => s.errorMessage),
(previous, next) {
if (next.isNotEmpty) {
showTopSnackbar(context, message: next, type: MessageType.error);
}
},
);
ref.listen(healthViewModelProvider.select((s) => s.errorMessage), (
previous,
next,
) {
if (next.isNotEmpty) {
showTopSnackbar(context, message: next, type: MessageType.error);
}
});
return LegacyPageLayout(
theme: theme,
@@ -130,6 +130,29 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
),
],
),
footer: _SaveSection(),
);
}
}
class _SaveSection extends ConsumerWidget {
const _SaveSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final vm = ref.read(healthViewModelProvider.notifier);
return Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: PrimaryButton(
onPressed: () async {
await vm.measure();
},
text: context.translate(I18n.measure),
color: theme.getColorFor(ThemeCode.legacyPrimary),
),
);
}
}

View File

@@ -14,12 +14,14 @@ final healthViewModelProvider =
class HealthViewModel extends Notifier<HealthViewState> {
late final HealthRepository _repository;
late final CommandsRepository _commandsRepository;
static const int _historyPageSize = 20;
@override
HealthViewState build() {
_repository = ref.read(healthRepositoryProvider);
_commandsRepository = ref.read(commandsRepositoryProvider);
_init();
return const HealthViewState();
}
@@ -247,4 +249,29 @@ class HealthViewModel extends Notifier<HealthViewState> {
final msg = e.toString();
return msg.startsWith('Exception: ') ? msg.substring(11) : msg;
}
Future<void> measure() async {
final device = ref.read(selectedDeviceProvider);
if (device == null) return;
try {
state = state.copyWith(isLoading: true);
final request = SendCommandRequestModel(
device: device.identificator,
command: DeviceCommand.requestHeartRate,
);
await _commandsRepository.send(request: request);
if (!ref.mounted) return;
state = state.copyWith(isLoading: false);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: _formatError(e),
);
}
}
}

View File

@@ -12,12 +12,12 @@ enum DeviceCommand {
findDevice,
@JsonValue('REQUEST_PHOTO')
requestPhoto,
@JsonValue('REQUEST_HEART_RATE')
requestHeartRate,
@JsonValue('RESTART')
restart,
@JsonValue('REWARDS')
rewards,
@JsonValue('SET_LANGUAGE')
setLanguage,
@JsonValue('SHUTDOWN')
shutdown,
@JsonValue('SET_SOUND_MODE')

View File

@@ -27,9 +27,9 @@ const _$DeviceCommandEnumMap = {
DeviceCommand.factory: 'FACTORY',
DeviceCommand.findDevice: 'FIND_DEVICE',
DeviceCommand.requestPhoto: 'REQUEST_PHOTO',
DeviceCommand.requestHeartRate: 'REQUEST_HEART_RATE',
DeviceCommand.restart: 'RESTART',
DeviceCommand.rewards: 'REWARDS',
DeviceCommand.setLanguage: 'SET_LANGUAGE',
DeviceCommand.shutdown: 'SHUTDOWN',
DeviceCommand.setSoundMode: 'SET_SOUND_MODE',
};