refactor(device_management): migrate activity_meter to Riverpod

This commit is contained in:
2026-04-22 22:45:18 +02:00
parent 0530f892f2
commit 3470e1bfef
15 changed files with 438 additions and 246 deletions

View File

@@ -1,48 +1,28 @@
import 'package:design_system/design_system.dart';
import 'package:device_management/src/core/presentation/format_date.dart';
import 'package:device_management/src/core/presentation/time_range.dart';
import 'package:device_management/src/core/presentation/widgets/time_range_selector.dart';
import 'package:device_management/src/features/activity_meter/presentation/providers/activity_meter_controller.dart';
import 'package:device_management/src/features/activity_meter/presentation/providers/activity_meter_state.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/activity_footers.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/hourly_bar_chart.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/pedometer_toggle.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/section_header.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/steps_bar_chart.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/steps_history_section.dart';
import 'package:device_management/src/features/activity_meter/presentation/widgets/steps_progress_ring.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_ui/legacy_ui.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
import '../../../core/presentation/format_date.dart';
import '../../../core/presentation/time_range.dart';
import '../../../core/presentation/widgets/time_range_selector.dart';
import 'state/activity_meter_view_model.dart';
import 'state/activity_meter_view_state.dart';
import 'widgets/activity_footers.dart';
import 'widgets/hourly_bar_chart.dart';
import 'widgets/pedometer_toggle.dart';
import 'widgets/section_header.dart';
import 'widgets/steps_bar_chart.dart';
import 'widgets/steps_history_section.dart';
import 'widgets/steps_progress_ring.dart';
class ActivityMeterScreen extends ConsumerWidget {
const ActivityMeterScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isLoading = ref.watch(
activityMeterViewModelProvider.select((s) => s.isLoading),
);
ref.listen(activityMeterViewModelProvider.select((s) => s.errorEvent), (
_,
next,
) {
if (next == null) return;
final message = switch (next) {
ActivityMeterErrorEvent.loadData ||
ActivityMeterErrorEvent.loadMore => context.translate(
I18n.errorActivityData,
),
ActivityMeterErrorEvent.pedometer => context.translate(
I18n.errorPedometer,
),
};
showTopSnackbar(context, message: message, type: MessageType.error);
});
final isLoading =
ref.watch(activityMeterControllerProvider.select((s) => s.isLoading));
return LegacyPageLayout(
title: context.translate(I18n.activityMeter),
@@ -58,10 +38,9 @@ class _ActivityMeterBody extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(activityMeterViewModelProvider.notifier);
final timeRange = ref.watch(
activityMeterViewModelProvider.select((s) => s.timeRange),
);
final timeRange = ref
.watch(activityMeterControllerProvider.select((s) => s.timeRange));
final notifier = ref.read(activityMeterControllerProvider.notifier);
return SingleChildScrollView(
child: Column(
@@ -73,8 +52,8 @@ class _ActivityMeterBody extends ConsumerWidget {
const _ActivityRangeHeader(),
TimeRangeSelector(
selected: timeRange,
onSelected: vm.selectTimeRange,
onCustomTap: () => _pickCustomRange(context, vm),
onSelected: notifier.selectTimeRange,
onCustomTap: () => _pickCustomRange(context, notifier),
),
const _ActivitySection(),
if (timeRange != TimeRange.today) const _HistorySection(),
@@ -86,7 +65,7 @@ class _ActivityMeterBody extends ConsumerWidget {
Future<void> _pickCustomRange(
BuildContext context,
ActivityMeterViewModel vm,
ActivityMeterController notifier,
) async {
final now = DateTime.now();
final picked = await showDateRangePicker(
@@ -95,7 +74,7 @@ class _ActivityMeterBody extends ConsumerWidget {
lastDate: now,
);
if (picked != null) {
vm.selectCustomRange(picked.start, picked.end);
notifier.selectCustomRange(picked.start, picked.end);
}
}
}
@@ -106,7 +85,7 @@ class _TodaySection extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final (todayTotal, dailyGoal) = ref.watch(
activityMeterViewModelProvider.select(
activityMeterControllerProvider.select(
(s) => (s.todayTotal, s.dailyGoal),
),
);
@@ -125,7 +104,9 @@ class _TodaySection extends ConsumerWidget {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 13, big: 12),
color: Theme.of(context).colorScheme.onSurface
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.5),
),
),
@@ -141,7 +122,7 @@ class _ActivityRangeHeader extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final (start, end, timeRange) = ref.watch(
activityMeterViewModelProvider.select(
activityMeterControllerProvider.select(
(s) => (s.rangeStart, s.rangeEnd, s.timeRange),
),
);
@@ -174,7 +155,7 @@ class _ActivitySection extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final (timeRange, hourly, daily, stats, activeHours, hasData) = ref.watch(
activityMeterViewModelProvider.select(
activityMeterControllerProvider.select(
(s) => (
s.timeRange,
s.hourlyData,
@@ -199,7 +180,9 @@ class _ActivitySection extends ConsumerWidget {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 13, big: 12),
color: Theme.of(context).colorScheme.onSurface
color: Theme.of(context)
.colorScheme
.onSurface
.withValues(alpha: 0.5),
),
),
@@ -228,9 +211,9 @@ class _HistorySection extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(activityMeterViewModelProvider.notifier);
final notifier = ref.read(activityMeterControllerProvider.notifier);
final (history, dailyGoal, hasMore, isLoadingMore) = ref.watch(
activityMeterViewModelProvider.select(
activityMeterControllerProvider.select(
(s) => (s.historyData, s.dailyGoal, s.hasMoreHistory, s.isLoadingMore),
),
);
@@ -248,7 +231,7 @@ class _HistorySection extends ConsumerWidget {
dailyGoal: dailyGoal,
hasMore: hasMore,
isLoadingMore: isLoadingMore,
onLoadMore: vm.loadMoreHistory,
onLoadMore: notifier.loadMoreHistory,
),
],
);

View File

@@ -1,6 +1,6 @@
import '../../../../core/domain/entities/steps_entity.dart';
import '../../../../core/presentation/time_range.dart';
import 'activity_meter_view_state.dart';
import 'package:device_management/src/core/domain/entities/steps_entity.dart';
import 'package:device_management/src/core/presentation/time_range.dart';
import 'package:device_management/src/features/activity_meter/presentation/providers/activity_meter_state.dart';
List<DailySteps> groupByDay(List<StepsEntity> steps) {
final Map<DateTime, int> groups = {};

View File

@@ -1,36 +1,26 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_device_state/legacy_device_state.dart';
import 'package:device_management/src/core/data/datasources/health_query_builder.dart';
import 'package:device_management/src/core/domain/entities/steps_entity.dart';
import 'package:device_management/src/core/domain/repositories/steps_repository.dart';
import 'package:device_management/src/core/presentation/time_range.dart';
import 'package:device_management/src/core/providers/steps_repository_provider.dart';
import 'package:device_management/src/features/activity_meter/presentation/providers/activity_meter_aggregator.dart';
import 'package:device_management/src/features/activity_meter/presentation/providers/activity_meter_state.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:sf_tracking/sf_tracking.dart';
import '../../../../core/data/datasources/health_query_builder.dart';
import '../../../../core/domain/entities/steps_entity.dart';
import '../../../../core/domain/repositories/steps_repository.dart';
import '../../../../core/presentation/time_range.dart';
import '../../../../core/providers/steps_repository_provider.dart';
import 'activity_meter_aggregator.dart';
import 'activity_meter_view_state.dart';
part 'activity_meter_controller.g.dart';
final activityMeterViewModelProvider =
NotifierProvider.autoDispose<
ActivityMeterViewModel,
ActivityMeterViewState
>(ActivityMeterViewModel.new);
class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
late final StepsRepository _repository;
late final SfTrackingRepository _tracking;
static const int _historyPageSize = 20;
const int _historyPageSize = 20;
@riverpod
class ActivityMeterController extends _$ActivityMeterController {
@override
ActivityMeterViewState build() {
_repository = ref.read(stepsRepositoryProvider);
_tracking = ref.read(sfTrackingProvider);
ActivityMeterState build() {
Future.microtask(_loadFilteredData);
return const ActivityMeterViewState();
return const ActivityMeterState();
}
String? get _identificator =>
@@ -40,9 +30,9 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
if (range == state.timeRange) return;
state = state.copyWith(timeRange: range, isLoading: true);
unawaited(
_tracking.legacyDeviceActivityMeterTimeRangeChanged(
_timeRangeName(range),
),
ref
.read(sfTrackingProvider)
.legacyDeviceActivityMeterTimeRangeChanged(_timeRangeName(range)),
);
await _loadFilteredData();
}
@@ -54,22 +44,20 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
customEnd: end,
isLoading: true,
);
unawaited(_tracking.legacyDeviceActivityMeterTimeRangeChanged('custom'));
unawaited(
ref
.read(sfTrackingProvider)
.legacyDeviceActivityMeterTimeRangeChanged('custom'),
);
await _loadFilteredData();
}
String _timeRangeName(TimeRange range) {
switch (range) {
case TimeRange.today:
return 'today';
case TimeRange.sevenDays:
return 'seven_days';
case TimeRange.thirtyDays:
return 'thirty_days';
case TimeRange.custom:
return 'custom';
}
}
String _timeRangeName(TimeRange range) => switch (range) {
TimeRange.today => 'today',
TimeRange.sevenDays => 'seven_days',
TimeRange.thirtyDays => 'thirty_days',
TimeRange.custom => 'custom',
};
Future<void> loadMoreHistory() async {
if (state.isLoadingMore || !state.hasMoreHistory) return;
@@ -86,15 +74,16 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
end: end,
);
final steps = await _repository.getSteps(
identificator: identificator,
queryParameters: HealthQueryBuilder.build(
orderDirection: OrderDirection.desc,
page: nextPage,
pageSize: _historyPageSize,
filters: filters,
),
);
final steps =
await ref.read(stepsRepositoryProvider).getSteps(
identificator: identificator,
queryParameters: HealthQueryBuilder.build(
orderDirection: OrderDirection.desc,
page: nextPage,
pageSize: _historyPageSize,
filters: filters,
),
);
if (!ref.mounted) return;
final newDaily = groupByDay(steps);
@@ -108,10 +97,7 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
);
} catch (_) {
if (!ref.mounted) return;
state = state.copyWith(
isLoadingMore: false,
errorEvent: ActivityMeterErrorEvent.loadMore,
);
state = state.copyWith(isLoadingMore: false);
}
}
@@ -122,6 +108,7 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
return;
}
final repo = ref.read(stepsRepositoryProvider);
try {
final (start, end) = _resolveRange();
final filters = HealthQueryBuilder.timeRangeFilters(
@@ -130,14 +117,14 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
);
final (chartSteps, histSteps, todaySteps) = await (
_repository.getSteps(
repo.getSteps(
identificator: identificator,
queryParameters: HealthQueryBuilder.build(
orderDirection: OrderDirection.asc,
filters: filters,
),
),
_repository.getSteps(
repo.getSteps(
identificator: identificator,
queryParameters: HealthQueryBuilder.build(
orderDirection: OrderDirection.desc,
@@ -146,7 +133,7 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
filters: filters,
),
),
_fetchTodaySteps(identificator),
_fetchTodaySteps(repo, identificator),
).wait;
if (!ref.mounted) return;
@@ -174,21 +161,20 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
rangeStart: start,
rangeEnd: end,
isLoading: false,
errorEvent: null,
);
} catch (_) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorEvent: ActivityMeterErrorEvent.loadData,
);
state = state.copyWith(isLoading: false);
}
}
Future<List<StepsEntity>> _fetchTodaySteps(String identificator) {
Future<List<StepsEntity>> _fetchTodaySteps(
StepsRepository repo,
String identificator,
) {
final now = DateTime.now();
final todayStart = DateTime(now.year, now.month, now.day);
return _repository.getSteps(
return repo.getSteps(
identificator: identificator,
queryParameters: HealthQueryBuilder.build(
orderDirection: OrderDirection.asc,
@@ -201,35 +187,9 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
}
(DateTime, DateTime) _resolveRange() => resolveRange(
state.timeRange,
now: DateTime.now(),
customStart: state.customStart,
customEnd: state.customEnd,
);
Future<bool> togglePedometer({required bool enabled}) async {
final device = ref.read(selectedDeviceProvider).value;
if (device == null) return false;
state = state.copyWith(errorEvent: null);
try {
final updatedSettings = device.settings.copyWith(pedometer: enabled);
await ref
.read(deviceSettingsUpdateProvider)
.updateDeviceSettings(
device: device,
updatedSettings: updatedSettings,
);
if (!ref.mounted) return false;
ref.syncDeviceSettings(device, updatedSettings);
unawaited(_tracking.legacyDeviceActivityPedometerToggled(enabled));
return true;
} catch (_) {
if (!ref.mounted) return false;
state = state.copyWith(errorEvent: ActivityMeterErrorEvent.pedometer);
return false;
}
}
state.timeRange,
now: DateTime.now(),
customStart: state.customStart,
customEnd: state.customEnd,
);
}

View File

@@ -0,0 +1,64 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'activity_meter_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(ActivityMeterController)
const activityMeterControllerProvider = ActivityMeterControllerProvider._();
final class ActivityMeterControllerProvider
extends $NotifierProvider<ActivityMeterController, ActivityMeterState> {
const ActivityMeterControllerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'activityMeterControllerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$activityMeterControllerHash();
@$internal
@override
ActivityMeterController create() => ActivityMeterController();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(ActivityMeterState value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<ActivityMeterState>(value),
);
}
}
String _$activityMeterControllerHash() =>
r'693b7f7a3d5f2aca66d69fcba538cd3e5f2d9498';
abstract class _$ActivityMeterController extends $Notifier<ActivityMeterState> {
ActivityMeterState build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<ActivityMeterState, ActivityMeterState>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<ActivityMeterState, ActivityMeterState>,
ActivityMeterState,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}

View File

@@ -1,21 +1,22 @@
import 'package:device_management/src/core/presentation/time_range.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../../../core/presentation/time_range.dart';
part 'activity_meter_view_state.freezed.dart';
enum ActivityMeterErrorEvent { loadData, loadMore, pedometer }
part 'activity_meter_state.freezed.dart';
@freezed
abstract class DailySteps with _$DailySteps {
const factory DailySteps({required DateTime date, required int totalSteps}) =
_DailySteps;
const factory DailySteps({
required DateTime date,
required int totalSteps,
}) = _DailySteps;
}
@freezed
abstract class HourlySteps with _$HourlySteps {
const factory HourlySteps({required int hour, required int totalSteps}) =
_HourlySteps;
const factory HourlySteps({
required int hour,
required int totalSteps,
}) = _HourlySteps;
}
@freezed
@@ -30,8 +31,8 @@ abstract class StepsStats with _$StepsStats {
}
@freezed
abstract class ActivityMeterViewState with _$ActivityMeterViewState {
const factory ActivityMeterViewState({
abstract class ActivityMeterState with _$ActivityMeterState {
const factory ActivityMeterState({
@Default(0) int todayTotal,
@Default(8000) int dailyGoal,
@Default([]) List<DailySteps> chartData,
@@ -48,11 +49,10 @@ abstract class ActivityMeterViewState with _$ActivityMeterViewState {
DateTime? rangeEnd,
@Default(true) bool isLoading,
@Default(false) bool isLoadingMore,
ActivityMeterErrorEvent? errorEvent,
}) = _ActivityMeterViewState;
}) = _ActivityMeterState;
}
extension ActivityMeterViewStateX on ActivityMeterViewState {
extension ActivityMeterStateX on ActivityMeterState {
bool get hasActivityData => timeRange == TimeRange.today
? hourlyData.any((h) => h.totalSteps > 0)
: stats.total > 0;

View File

@@ -3,7 +3,7 @@
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'activity_meter_view_state.dart';
part of 'activity_meter_state.dart';
// **************************************************************************
// FreezedGenerator
@@ -825,40 +825,40 @@ $DailyStepsCopyWith<$Res>? get bestDay {
}
/// @nodoc
mixin _$ActivityMeterViewState {
mixin _$ActivityMeterState {
int get todayTotal; int get dailyGoal; List<DailySteps> get chartData; List<HourlySteps> get hourlyData; int get activeHoursToday; List<DailySteps> get historyData; int get currentHistoryPage; bool get hasMoreHistory; StepsStats get stats; TimeRange get timeRange; DateTime? get customStart; DateTime? get customEnd; DateTime? get rangeStart; DateTime? get rangeEnd; bool get isLoading; bool get isLoadingMore; ActivityMeterErrorEvent? get errorEvent;
/// Create a copy of ActivityMeterViewState
int get todayTotal; int get dailyGoal; List<DailySteps> get chartData; List<HourlySteps> get hourlyData; int get activeHoursToday; List<DailySteps> get historyData; int get currentHistoryPage; bool get hasMoreHistory; StepsStats get stats; TimeRange get timeRange; DateTime? get customStart; DateTime? get customEnd; DateTime? get rangeStart; DateTime? get rangeEnd; bool get isLoading; bool get isLoadingMore;
/// Create a copy of ActivityMeterState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ActivityMeterViewStateCopyWith<ActivityMeterViewState> get copyWith => _$ActivityMeterViewStateCopyWithImpl<ActivityMeterViewState>(this as ActivityMeterViewState, _$identity);
$ActivityMeterStateCopyWith<ActivityMeterState> get copyWith => _$ActivityMeterStateCopyWithImpl<ActivityMeterState>(this as ActivityMeterState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ActivityMeterViewState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other.chartData, chartData)&&const DeepCollectionEquality().equals(other.hourlyData, hourlyData)&&(identical(other.activeHoursToday, activeHoursToday) || other.activeHoursToday == activeHoursToday)&&const DeepCollectionEquality().equals(other.historyData, historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.rangeStart, rangeStart) || other.rangeStart == rangeStart)&&(identical(other.rangeEnd, rangeEnd) || other.rangeEnd == rangeEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ActivityMeterState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other.chartData, chartData)&&const DeepCollectionEquality().equals(other.hourlyData, hourlyData)&&(identical(other.activeHoursToday, activeHoursToday) || other.activeHoursToday == activeHoursToday)&&const DeepCollectionEquality().equals(other.historyData, historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.rangeStart, rangeStart) || other.rangeStart == rangeStart)&&(identical(other.rangeEnd, rangeEnd) || other.rangeEnd == rangeEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore));
}
@override
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(chartData),const DeepCollectionEquality().hash(hourlyData),activeHoursToday,const DeepCollectionEquality().hash(historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,rangeStart,rangeEnd,isLoading,isLoadingMore,errorEvent);
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(chartData),const DeepCollectionEquality().hash(hourlyData),activeHoursToday,const DeepCollectionEquality().hash(historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,rangeStart,rangeEnd,isLoading,isLoadingMore);
@override
String toString() {
return 'ActivityMeterViewState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, hourlyData: $hourlyData, activeHoursToday: $activeHoursToday, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, rangeStart: $rangeStart, rangeEnd: $rangeEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorEvent: $errorEvent)';
return 'ActivityMeterState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, hourlyData: $hourlyData, activeHoursToday: $activeHoursToday, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, rangeStart: $rangeStart, rangeEnd: $rangeEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore)';
}
}
/// @nodoc
abstract mixin class $ActivityMeterViewStateCopyWith<$Res> {
factory $ActivityMeterViewStateCopyWith(ActivityMeterViewState value, $Res Function(ActivityMeterViewState) _then) = _$ActivityMeterViewStateCopyWithImpl;
abstract mixin class $ActivityMeterStateCopyWith<$Res> {
factory $ActivityMeterStateCopyWith(ActivityMeterState value, $Res Function(ActivityMeterState) _then) = _$ActivityMeterStateCopyWithImpl;
@useResult
$Res call({
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore
});
@@ -866,16 +866,16 @@ $StepsStatsCopyWith<$Res> get stats;
}
/// @nodoc
class _$ActivityMeterViewStateCopyWithImpl<$Res>
implements $ActivityMeterViewStateCopyWith<$Res> {
_$ActivityMeterViewStateCopyWithImpl(this._self, this._then);
class _$ActivityMeterStateCopyWithImpl<$Res>
implements $ActivityMeterStateCopyWith<$Res> {
_$ActivityMeterStateCopyWithImpl(this._self, this._then);
final ActivityMeterViewState _self;
final $Res Function(ActivityMeterViewState) _then;
final ActivityMeterState _self;
final $Res Function(ActivityMeterState) _then;
/// Create a copy of ActivityMeterViewState
/// Create a copy of ActivityMeterState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? hourlyData = null,Object? activeHoursToday = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? rangeStart = freezed,Object? rangeEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorEvent = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? hourlyData = null,Object? activeHoursToday = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? rangeStart = freezed,Object? rangeEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,}) {
return _then(_self.copyWith(
todayTotal: null == todayTotal ? _self.todayTotal : todayTotal // ignore: cast_nullable_to_non_nullable
as int,dailyGoal: null == dailyGoal ? _self.dailyGoal : dailyGoal // ignore: cast_nullable_to_non_nullable
@@ -893,11 +893,10 @@ as DateTime?,rangeStart: freezed == rangeStart ? _self.rangeStart : rangeStart /
as DateTime?,rangeEnd: freezed == rangeEnd ? _self.rangeEnd : rangeEnd // ignore: cast_nullable_to_non_nullable
as DateTime?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
as ActivityMeterErrorEvent?,
as bool,
));
}
/// Create a copy of ActivityMeterViewState
/// Create a copy of ActivityMeterState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
@@ -910,8 +909,8 @@ $StepsStatsCopyWith<$Res> get stats {
}
/// Adds pattern-matching-related methods to [ActivityMeterViewState].
extension ActivityMeterViewStatePatterns on ActivityMeterViewState {
/// Adds pattern-matching-related methods to [ActivityMeterState].
extension ActivityMeterStatePatterns on ActivityMeterState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
@@ -924,10 +923,10 @@ extension ActivityMeterViewStatePatterns on ActivityMeterViewState {
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ActivityMeterViewState value)? $default,{required TResult orElse(),}){
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ActivityMeterState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ActivityMeterViewState() when $default != null:
case _ActivityMeterState() when $default != null:
return $default(_that);case _:
return orElse();
@@ -946,10 +945,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ActivityMeterViewState value) $default,){
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ActivityMeterState value) $default,){
final _that = this;
switch (_that) {
case _ActivityMeterViewState():
case _ActivityMeterState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
@@ -967,10 +966,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ActivityMeterViewState value)? $default,){
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ActivityMeterState value)? $default,){
final _that = this;
switch (_that) {
case _ActivityMeterViewState() when $default != null:
case _ActivityMeterState() when $default != null:
return $default(_that);case _:
return null;
@@ -988,10 +987,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ActivityMeterViewState() when $default != null:
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyData,_that.activeHoursToday,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.rangeStart,_that.rangeEnd,_that.isLoading,_that.isLoadingMore,_that.errorEvent);case _:
case _ActivityMeterState() when $default != null:
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyData,_that.activeHoursToday,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.rangeStart,_that.rangeEnd,_that.isLoading,_that.isLoadingMore);case _:
return orElse();
}
@@ -1009,10 +1008,10 @@ return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyDat
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore) $default,) {final _that = this;
switch (_that) {
case _ActivityMeterViewState():
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyData,_that.activeHoursToday,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.rangeStart,_that.rangeEnd,_that.isLoading,_that.isLoadingMore,_that.errorEvent);case _:
case _ActivityMeterState():
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyData,_that.activeHoursToday,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.rangeStart,_that.rangeEnd,_that.isLoading,_that.isLoadingMore);case _:
throw StateError('Unexpected subclass');
}
@@ -1029,10 +1028,10 @@ return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyDat
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore)? $default,) {final _that = this;
switch (_that) {
case _ActivityMeterViewState() when $default != null:
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyData,_that.activeHoursToday,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.rangeStart,_that.rangeEnd,_that.isLoading,_that.isLoadingMore,_that.errorEvent);case _:
case _ActivityMeterState() when $default != null:
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyData,_that.activeHoursToday,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.rangeStart,_that.rangeEnd,_that.isLoading,_that.isLoadingMore);case _:
return null;
}
@@ -1043,8 +1042,8 @@ return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.hourlyDat
/// @nodoc
class _ActivityMeterViewState implements ActivityMeterViewState {
const _ActivityMeterViewState({this.todayTotal = 0, this.dailyGoal = 8000, final List<DailySteps> chartData = const [], final List<HourlySteps> hourlyData = const [], this.activeHoursToday = 0, final List<DailySteps> historyData = const [], this.currentHistoryPage = 1, this.hasMoreHistory = false, this.stats = const StepsStats(), this.timeRange = TimeRange.today, this.customStart, this.customEnd, this.rangeStart, this.rangeEnd, this.isLoading = true, this.isLoadingMore = false, this.errorEvent}): _chartData = chartData,_hourlyData = hourlyData,_historyData = historyData;
class _ActivityMeterState implements ActivityMeterState {
const _ActivityMeterState({this.todayTotal = 0, this.dailyGoal = 8000, final List<DailySteps> chartData = const [], final List<HourlySteps> hourlyData = const [], this.activeHoursToday = 0, final List<DailySteps> historyData = const [], this.currentHistoryPage = 1, this.hasMoreHistory = false, this.stats = const StepsStats(), this.timeRange = TimeRange.today, this.customStart, this.customEnd, this.rangeStart, this.rangeEnd, this.isLoading = true, this.isLoadingMore = false}): _chartData = chartData,_hourlyData = hourlyData,_historyData = historyData;
@override@JsonKey() final int todayTotal;
@@ -1081,39 +1080,38 @@ class _ActivityMeterViewState implements ActivityMeterViewState {
@override final DateTime? rangeEnd;
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isLoadingMore;
@override final ActivityMeterErrorEvent? errorEvent;
/// Create a copy of ActivityMeterViewState
/// Create a copy of ActivityMeterState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ActivityMeterViewStateCopyWith<_ActivityMeterViewState> get copyWith => __$ActivityMeterViewStateCopyWithImpl<_ActivityMeterViewState>(this, _$identity);
_$ActivityMeterStateCopyWith<_ActivityMeterState> get copyWith => __$ActivityMeterStateCopyWithImpl<_ActivityMeterState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActivityMeterViewState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other._chartData, _chartData)&&const DeepCollectionEquality().equals(other._hourlyData, _hourlyData)&&(identical(other.activeHoursToday, activeHoursToday) || other.activeHoursToday == activeHoursToday)&&const DeepCollectionEquality().equals(other._historyData, _historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.rangeStart, rangeStart) || other.rangeStart == rangeStart)&&(identical(other.rangeEnd, rangeEnd) || other.rangeEnd == rangeEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActivityMeterState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other._chartData, _chartData)&&const DeepCollectionEquality().equals(other._hourlyData, _hourlyData)&&(identical(other.activeHoursToday, activeHoursToday) || other.activeHoursToday == activeHoursToday)&&const DeepCollectionEquality().equals(other._historyData, _historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.rangeStart, rangeStart) || other.rangeStart == rangeStart)&&(identical(other.rangeEnd, rangeEnd) || other.rangeEnd == rangeEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore));
}
@override
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(_chartData),const DeepCollectionEquality().hash(_hourlyData),activeHoursToday,const DeepCollectionEquality().hash(_historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,rangeStart,rangeEnd,isLoading,isLoadingMore,errorEvent);
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(_chartData),const DeepCollectionEquality().hash(_hourlyData),activeHoursToday,const DeepCollectionEquality().hash(_historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,rangeStart,rangeEnd,isLoading,isLoadingMore);
@override
String toString() {
return 'ActivityMeterViewState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, hourlyData: $hourlyData, activeHoursToday: $activeHoursToday, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, rangeStart: $rangeStart, rangeEnd: $rangeEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorEvent: $errorEvent)';
return 'ActivityMeterState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, hourlyData: $hourlyData, activeHoursToday: $activeHoursToday, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, rangeStart: $rangeStart, rangeEnd: $rangeEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore)';
}
}
/// @nodoc
abstract mixin class _$ActivityMeterViewStateCopyWith<$Res> implements $ActivityMeterViewStateCopyWith<$Res> {
factory _$ActivityMeterViewStateCopyWith(_ActivityMeterViewState value, $Res Function(_ActivityMeterViewState) _then) = __$ActivityMeterViewStateCopyWithImpl;
abstract mixin class _$ActivityMeterStateCopyWith<$Res> implements $ActivityMeterStateCopyWith<$Res> {
factory _$ActivityMeterStateCopyWith(_ActivityMeterState value, $Res Function(_ActivityMeterState) _then) = __$ActivityMeterStateCopyWithImpl;
@override @useResult
$Res call({
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<HourlySteps> hourlyData, int activeHoursToday, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, DateTime? rangeStart, DateTime? rangeEnd, bool isLoading, bool isLoadingMore
});
@@ -1121,17 +1119,17 @@ $Res call({
}
/// @nodoc
class __$ActivityMeterViewStateCopyWithImpl<$Res>
implements _$ActivityMeterViewStateCopyWith<$Res> {
__$ActivityMeterViewStateCopyWithImpl(this._self, this._then);
class __$ActivityMeterStateCopyWithImpl<$Res>
implements _$ActivityMeterStateCopyWith<$Res> {
__$ActivityMeterStateCopyWithImpl(this._self, this._then);
final _ActivityMeterViewState _self;
final $Res Function(_ActivityMeterViewState) _then;
final _ActivityMeterState _self;
final $Res Function(_ActivityMeterState) _then;
/// Create a copy of ActivityMeterViewState
/// Create a copy of ActivityMeterState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? hourlyData = null,Object? activeHoursToday = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? rangeStart = freezed,Object? rangeEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorEvent = freezed,}) {
return _then(_ActivityMeterViewState(
@override @pragma('vm:prefer-inline') $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? hourlyData = null,Object? activeHoursToday = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? rangeStart = freezed,Object? rangeEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,}) {
return _then(_ActivityMeterState(
todayTotal: null == todayTotal ? _self.todayTotal : todayTotal // ignore: cast_nullable_to_non_nullable
as int,dailyGoal: null == dailyGoal ? _self.dailyGoal : dailyGoal // ignore: cast_nullable_to_non_nullable
as int,chartData: null == chartData ? _self._chartData : chartData // ignore: cast_nullable_to_non_nullable
@@ -1148,12 +1146,11 @@ as DateTime?,rangeStart: freezed == rangeStart ? _self.rangeStart : rangeStart /
as DateTime?,rangeEnd: freezed == rangeEnd ? _self.rangeEnd : rangeEnd // ignore: cast_nullable_to_non_nullable
as DateTime?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
as ActivityMeterErrorEvent?,
as bool,
));
}
/// Create a copy of ActivityMeterViewState
/// Create a copy of ActivityMeterState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')

View File

@@ -0,0 +1,38 @@
import 'dart:async';
import 'package:legacy_device_state/legacy_device_state.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:sf_tracking/sf_tracking.dart';
part 'pedometer_controller.g.dart';
@riverpod
class PedometerController extends _$PedometerController {
bool _lastValue = false;
bool get lastValue => _lastValue;
@override
FutureOr<void> build() {}
Future<void> toggle({
required DeviceEntity device,
required bool enabled,
}) async {
_lastValue = enabled;
state = const AsyncLoading();
state = await AsyncValue.guard(() async {
final updated = device.settings.copyWith(pedometer: enabled);
await ref
.read(deviceSettingsUpdateProvider)
.updateDeviceSettings(device: device, updatedSettings: updated);
ref.syncDeviceSettings(device, updated);
unawaited(
ref.read(sfTrackingProvider).legacyDeviceActivityPedometerToggled(
enabled,
),
);
});
}
}

View File

@@ -0,0 +1,56 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'pedometer_controller.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(PedometerController)
const pedometerControllerProvider = PedometerControllerProvider._();
final class PedometerControllerProvider
extends $AsyncNotifierProvider<PedometerController, void> {
const PedometerControllerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'pedometerControllerProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$pedometerControllerHash();
@$internal
@override
PedometerController create() => PedometerController();
}
String _$pedometerControllerHash() =>
r'd6df6753b2713d7feee4164ba8b2d0f595c67290';
abstract class _$PedometerController extends $AsyncNotifier<void> {
FutureOr<void> build();
@$mustCallSuper
@override
void runBuild() {
build();
final ref = this.ref as $Ref<AsyncValue<void>, void>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<void>, void>,
AsyncValue<void>,
Object?,
Object?
>;
element.handleValue(ref, null);
}
}

View File

@@ -5,7 +5,7 @@ import 'package:utils/utils.dart';
import '../../../../core/presentation/format_date.dart';
import '../format_steps.dart';
import '../state/activity_meter_view_state.dart';
import '../providers/activity_meter_state.dart';
import 'period_stats_cards.dart';
class TodayActivityFooter extends ConsumerWidget {

View File

@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:utils/utils.dart';
import '../state/activity_meter_view_state.dart';
import '../providers/activity_meter_state.dart';
import 'activity_bar_chart_base.dart';
class HourlyBarChart extends ConsumerWidget {

View File

@@ -1,14 +1,12 @@
import 'package:design_system/design_system.dart';
import 'package:legacy_theme/legacy_theme.dart';
import 'package:device_management/src/features/activity_meter/presentation/providers/pedometer_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_device_state/legacy_device_state.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:legacy_theme/legacy_theme.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:utils/utils.dart';
import '../state/activity_meter_view_model.dart';
class PedometerToggle extends ConsumerWidget {
const PedometerToggle({super.key});
@@ -20,6 +18,22 @@ class PedometerToggle extends ConsumerWidget {
),
);
ref.listen(pedometerControllerProvider, (prev, next) async {
if (prev == null || !prev.isLoading || next.isLoading) return;
if (next.hasError) {
await showErrorDialog(context, I18n.errorPedometer);
return;
}
final lastValue =
ref.read(pedometerControllerProvider.notifier).lastValue;
await showSuccessDialog(
context,
lastValue
? I18n.activityMeterPedometerEnabled
: I18n.activityMeterPedometerDisabled,
);
});
return Padding(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
@@ -40,21 +54,11 @@ class PedometerToggle extends ConsumerWidget {
activeTrackColor: context.sfColors.legacyPrimary,
onChanged: (value) async {
if (!await guardDeviceCommand(context, ref)) return;
final success = await ref
.read(activityMeterViewModelProvider.notifier)
.togglePedometer(enabled: value);
if (!context.mounted) return;
if (success) {
showTopSnackbar(
context,
message: context.translate(
value
? I18n.activityMeterPedometerEnabled
: I18n.activityMeterPedometerDisabled,
),
type: MessageType.success,
);
}
final device = ref.read(selectedDeviceProvider).value;
if (device == null) return;
ref
.read(pedometerControllerProvider.notifier)
.toggle(device: device, enabled: value);
},
),
],

View File

@@ -4,7 +4,7 @@ import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
import '../format_steps.dart';
import '../state/activity_meter_view_state.dart';
import '../providers/activity_meter_state.dart';
class PeriodStatsCards extends StatelessWidget {
final StepsStats stats;

View File

@@ -5,7 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:utils/utils.dart';
import '../state/activity_meter_view_state.dart';
import '../providers/activity_meter_state.dart';
import 'activity_bar_chart_base.dart';
class StepsBarChart extends ConsumerWidget {

View File

@@ -5,7 +5,7 @@ import 'package:utils/utils.dart';
import '../../../../core/presentation/format_date.dart';
import '../format_steps.dart';
import '../state/activity_meter_view_state.dart';
import '../providers/activity_meter_state.dart';
class StepsHistorySection extends StatelessWidget {
final List<DailySteps> items;

View File

@@ -0,0 +1,90 @@
import 'package:device_management/src/features/activity_meter/presentation/providers/pedometer_controller.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:legacy_device_state/legacy_device_state.dart';
import 'package:mocktail/mocktail.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:sf_shared/testing.dart';
import 'package:sf_tracking/sf_tracking.dart';
class MockDeviceSettingsUpdateDatasource extends Mock
implements DeviceSettingsUpdateDatasource {}
const _device = DeviceEntity(
id: 'device-1',
identificator: 'imei-1',
carrierName: 'Watch',
settings: DeviceSettingsEntity(pedometer: false),
);
void main() {
setUpAll(() {
registerFallbackValue(_device);
registerFallbackValue(const DeviceSettingsEntity());
});
ProviderContainer buildContainer(DeviceSettingsUpdateDatasource ds) {
return makeContainer(
overrides: [
deviceSettingsUpdateProvider.overrideWithValue(ds),
sfTrackingProvider.overrideWithValue(
SfTrackingRepository(clients: const []),
),
],
);
}
group('PedometerController.toggle', () {
test('persists pedometer=true and transitions to AsyncData', () async {
final ds = MockDeviceSettingsUpdateDatasource();
when(
() => ds.updateDeviceSettings(
device: any(named: 'device'),
updatedSettings: any(named: 'updatedSettings'),
),
).thenAnswer((_) async {});
final container = buildContainer(ds);
addTearDown(container.dispose);
await container
.read(pedometerControllerProvider.notifier)
.toggle(device: _device, enabled: true);
expect(
container.read(pedometerControllerProvider),
isA<AsyncData<void>>(),
);
final captured = verify(
() => ds.updateDeviceSettings(
device: _device,
updatedSettings: captureAny(named: 'updatedSettings'),
),
).captured.single as DeviceSettingsEntity;
expect(captured.pedometer, isTrue);
});
test('exposes AsyncError when datasource fails', () async {
final ds = MockDeviceSettingsUpdateDatasource();
when(
() => ds.updateDeviceSettings(
device: any(named: 'device'),
updatedSettings: any(named: 'updatedSettings'),
),
).thenThrow(const ApiException(message: 'boom', isNetworkError: true));
final container = buildContainer(ds);
addTearDown(container.dispose);
await container
.read(pedometerControllerProvider.notifier)
.toggle(device: _device, enabled: true);
expect(
container.read(pedometerControllerProvider),
isA<AsyncError<void>>(),
);
});
});
}