From e37adc1f78240ec5cd5636d37977c803ed2ebe13 Mon Sep 17 00:00:00 2001 From: JulianAlcala Date: Wed, 22 Apr 2026 21:11:32 +0200 Subject: [PATCH] refactor(device_management): migrate call_history to Riverpod --- .../presentation/call_history_screen.dart | 230 ++++++++------ .../call_history_filter_provider.dart | 24 ++ .../call_history_filter_provider.g.dart | 63 ++++ .../providers/call_history_provider.dart | 15 + .../providers/call_history_provider.g.dart | 87 ++++++ .../state/call_history_view_model.dart | 94 ------ .../state/call_history_view_state.dart | 18 -- .../call_history_view_state.freezed.dart | 295 ------------------ .../call_history_filter_test.dart | 40 +++ 9 files changed, 370 insertions(+), 496 deletions(-) create mode 100644 modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.dart create mode 100644 modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.g.dart create mode 100644 modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.dart create mode 100644 modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.g.dart delete mode 100644 modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_model.dart delete mode 100644 modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.dart delete mode 100644 modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.freezed.dart create mode 100644 modules/legacy/modules/device_management/test/features/call_history/call_history_filter_test.dart diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/call_history_screen.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/call_history_screen.dart index f382badd..1e51b092 100644 --- a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/call_history_screen.dart +++ b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/call_history_screen.dart @@ -1,104 +1,140 @@ +import 'package:device_management/src/features/call_history/data/call_history_entity.dart'; +import 'package:device_management/src/features/call_history/presentation/providers/call_history_filter_provider.dart'; +import 'package:device_management/src/features/call_history/presentation/providers/call_history_provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:legacy_theme/legacy_theme.dart'; import 'package:legacy_ui/legacy_ui.dart'; import 'package:sf_localizations/sf_localizations.dart'; - -import '../data/call_history_entity.dart'; -import 'state/call_history_view_model.dart'; -import 'state/call_history_view_state.dart'; -import 'package:legacy_theme/legacy_theme.dart'; +import 'package:sf_shared/sf_shared.dart'; class CallHistoryScreen extends ConsumerWidget { const CallHistoryScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(callHistoryViewModelProvider); - final vm = ref.read(callHistoryViewModelProvider.notifier); - final filtered = state.filteredCalls; + final device = ref.watch(selectedDeviceProvider).value; + final filter = ref.watch(callHistoryFilterProvider); + + if (device == null) { + return LegacyPageLayout( + title: context.translate(I18n.callHistory), + body: const SizedBox.shrink(), + ); + } + + final callsAsync = ref.watch(callHistoryProvider(device.identificator)); return LegacyPageLayout( title: context.translate(I18n.callHistory), - body: state.isLoading - ? const Center(child: CircularProgressIndicator()) - : state.errorMessage.isNotEmpty - ? Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.error_outline, - size: 64, - color: Theme.of(context).colorScheme.outline, - ), - const SizedBox(height: 12), - Text( - state.errorMessage, - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 14), - textAlign: TextAlign.center, - ), - ], + body: callsAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (err, _) => Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.error_outline, + size: 64, + color: Theme.of(context).colorScheme.outline, ), - ) - : Column( - children: [ - _FilterBar(selected: state.filter, onChanged: vm.setFilter), - Expanded( - child: filtered.isEmpty - ? Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.phone_missed_outlined, - size: 64, - color: Theme.of(context).colorScheme.outline, + const SizedBox(height: 12), + Text( + err.toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + fontSize: 14, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + data: (calls) { + final filtered = _applyFilter(calls, filter); + return Column( + children: [ + _FilterBar( + selected: filter, + onChanged: + ref.read(callHistoryFilterProvider.notifier).select, + ), + Expanded( + child: filtered.isEmpty + ? Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.phone_missed_outlined, + size: 64, + color: Theme.of(context).colorScheme.outline, + ), + const SizedBox(height: 12), + Text( + context.translate(I18n.callHistoryEmpty), + style: TextStyle( + color: Theme.of(context) + .colorScheme + .onSurfaceVariant, + fontSize: 14, ), - const SizedBox(height: 12), - Text( - context.translate(I18n.callHistoryEmpty), - style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant, - fontSize: 14, - ), + ), + ], + ), + ) + : ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + itemCount: filtered.length, + itemBuilder: (context, index) { + final call = filtered[index]; + final showDateHeader = index == 0 || + !_isSameDay( + filtered[index - 1].occurredAt, + call.occurredAt, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (showDateHeader) + _DateHeader(timestamp: call.occurredAt), + _CallTile( + call: call, + primaryColor: + context.sfColors.legacyPrimary, ), ], - ), - ) - : ListView.builder( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 4, - ), - itemCount: filtered.length, - itemBuilder: (context, index) { - final call = filtered[index]; - final showDateHeader = - index == 0 || - !_isSameDay( - filtered[index - 1].occurredAt, - call.occurredAt, - ); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (showDateHeader) - _DateHeader(timestamp: call.occurredAt), - _CallTile( - call: call, - primaryColor: context.sfColors.legacyPrimary, - ), - ], - ); - }, - ), - ), - ], - ), + ); + }, + ), + ), + ], + ); + }, + ), ); } + List _applyFilter( + List calls, + CallFilter filter, + ) { + switch (filter) { + case CallFilter.all: + return calls; + case CallFilter.incoming: + return calls.where((c) => c.isIncoming).toList(); + case CallFilter.outgoing: + return calls.where((c) => !c.isIncoming).toList(); + case CallFilter.missed: + return calls.where((c) => !c.isAccepted).toList(); + } + } + static bool _isSameDay(int ts1, int ts2) { final d1 = DateTime.fromMillisecondsSinceEpoch(ts1); final d2 = DateTime.fromMillisecondsSinceEpoch(ts2); @@ -156,9 +192,12 @@ class _FilterBar extends StatelessWidget { children: filters.map((filter) { final isSelected = filter == selected; final label = switch (filter) { - CallFilter.all => context.translate(I18n.locationListAll), - CallFilter.incoming => context.translate(I18n.callIncoming), - CallFilter.outgoing => context.translate(I18n.callOutgoing), + CallFilter.all => + context.translate(I18n.locationListAll), + CallFilter.incoming => + context.translate(I18n.callIncoming), + CallFilter.outgoing => + context.translate(I18n.callOutgoing), CallFilter.missed => context.translate(I18n.callMissed), }; @@ -176,7 +215,9 @@ class _FilterBar extends StatelessWidget { : FontWeight.w500, color: isSelected ? Colors.black87 - : Theme.of(context).colorScheme.onSurfaceVariant, + : Theme.of(context) + .colorScheme + .onSurfaceVariant, ), ), ), @@ -281,7 +322,9 @@ class _CallTile extends StatelessWidget { style: TextStyle( fontSize: 15, fontWeight: FontWeight.w500, - color: isAccepted ? Colors.black87 : Theme.of(context).colorScheme.error, + color: isAccepted + ? Colors.black87 + : Theme.of(context).colorScheme.error, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -297,20 +340,29 @@ class _CallTile extends StatelessWidget { if (subtitle != null) ...[ Text( subtitle, - style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), const SizedBox(width: 8), ], if (isAccepted) Text( durationStr, - style: TextStyle(fontSize: 12, color: Theme.of(context).colorScheme.onSurfaceVariant), + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ], ), trailing: Text( timeStr, - style: TextStyle(fontSize: 13, color: Theme.of(context).colorScheme.onSurfaceVariant), + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ), ); diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.dart new file mode 100644 index 00000000..e3e50c42 --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.dart @@ -0,0 +1,24 @@ +import 'dart:async'; + +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:sf_tracking/sf_tracking.dart'; + +part 'call_history_filter_provider.g.dart'; + +enum CallFilter { all, incoming, outgoing, missed } + +@riverpod +class CallHistoryFilter extends _$CallHistoryFilter { + @override + CallFilter build() => CallFilter.all; + + void select(CallFilter filter) { + if (filter == state) return; + unawaited( + ref + .read(sfTrackingProvider) + .legacyDeviceCallHistoryFilterChanged(filter.name), + ); + state = filter; + } +} diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.g.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.g.dart new file mode 100644 index 00000000..feda3c62 --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_filter_provider.g.dart @@ -0,0 +1,63 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'call_history_filter_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(CallHistoryFilter) +const callHistoryFilterProvider = CallHistoryFilterProvider._(); + +final class CallHistoryFilterProvider + extends $NotifierProvider { + const CallHistoryFilterProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'callHistoryFilterProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$callHistoryFilterHash(); + + @$internal + @override + CallHistoryFilter create() => CallHistoryFilter(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(CallFilter value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$callHistoryFilterHash() => r'f0a91c1ced288a1cb51d741963b8f230fcdadef3'; + +abstract class _$CallHistoryFilter extends $Notifier { + CallFilter build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + CallFilter, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.dart new file mode 100644 index 00000000..f4207fc9 --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.dart @@ -0,0 +1,15 @@ +import 'package:device_management/src/features/call_history/data/call_history_datasource_provider.dart'; +import 'package:device_management/src/features/call_history/data/call_history_entity.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'call_history_provider.g.dart'; + +@riverpod +Future> callHistory( + Ref ref, + String deviceIdentificator, +) async { + return ref + .read(callHistoryDatasourceProvider) + .getCallHistory(deviceIdentificator: deviceIdentificator); +} diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.g.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.g.dart new file mode 100644 index 00000000..c4dcdbf6 --- /dev/null +++ b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/providers/call_history_provider.g.dart @@ -0,0 +1,87 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'call_history_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(callHistory) +const callHistoryProvider = CallHistoryFamily._(); + +final class CallHistoryProvider + extends + $FunctionalProvider< + AsyncValue>, + List, + FutureOr> + > + with + $FutureModifier>, + $FutureProvider> { + const CallHistoryProvider._({ + required CallHistoryFamily super.from, + required String super.argument, + }) : super( + retry: null, + name: r'callHistoryProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$callHistoryHash(); + + @override + String toString() { + return r'callHistoryProvider' + '' + '($argument)'; + } + + @$internal + @override + $FutureProviderElement> $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr> create(Ref ref) { + final argument = this.argument as String; + return callHistory(ref, argument); + } + + @override + bool operator ==(Object other) { + return other is CallHistoryProvider && other.argument == argument; + } + + @override + int get hashCode { + return argument.hashCode; + } +} + +String _$callHistoryHash() => r'f5b99a06bc69f62660d1cf7b66648a279724f216'; + +final class CallHistoryFamily extends $Family + with $FunctionalFamilyOverride>, String> { + const CallHistoryFamily._() + : super( + retry: null, + name: r'callHistoryProvider', + dependencies: null, + $allTransitiveDependencies: null, + isAutoDispose: true, + ); + + CallHistoryProvider call(String deviceIdentificator) => + CallHistoryProvider._(argument: deviceIdentificator, from: this); + + @override + String toString() => r'callHistoryProvider'; +} diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_model.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_model.dart deleted file mode 100644 index ed1e93c0..00000000 --- a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_model.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'dart:async'; - -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:sf_shared/sf_shared.dart'; -import 'package:sf_tracking/sf_tracking.dart'; - -import '../../data/call_history_datasource.dart'; -import '../../data/call_history_datasource_provider.dart'; -import '../../data/call_history_entity.dart'; -import 'call_history_view_state.dart'; - -final callHistoryViewModelProvider = - NotifierProvider.autoDispose( - CallHistoryViewModel.new, - ); - -class CallHistoryViewModel extends Notifier { - late final CallHistoryDatasource _datasource; - late final SfTrackingRepository _tracking; - - @override - CallHistoryViewState build() { - _datasource = ref.read(callHistoryDatasourceProvider); - _tracking = ref.read(sfTrackingProvider); - Future.microtask(() => _load()); - return const CallHistoryViewState(); - } - - Future _load() async { - final device = ref.read(selectedDeviceProvider).value; - if (device == null) { - state = state.copyWith(isLoading: false); - return; - } - - try { - final calls = await _datasource.getCallHistory( - deviceIdentificator: device.identificator, - ); - if (!ref.mounted) return; - - state = state.copyWith( - calls: calls, - filteredCalls: calls, - isLoading: false, - ); - } catch (e) { - if (!ref.mounted) return; - state = state.copyWith(isLoading: false, errorMessage: e.toString()); - } - } - - void setFilter(CallFilter filter) { - if (filter == state.filter) return; - - unawaited( - _tracking.legacyDeviceCallHistoryFilterChanged(_filterName(filter)), - ); - - state = state.copyWith( - filter: filter, - filteredCalls: _applyFilter(state.calls, filter), - ); - } - - List _applyFilter( - List calls, - CallFilter filter, - ) { - switch (filter) { - case CallFilter.all: - return calls; - case CallFilter.incoming: - return calls.where((c) => c.isIncoming).toList(); - case CallFilter.outgoing: - return calls.where((c) => !c.isIncoming).toList(); - case CallFilter.missed: - return calls.where((c) => !c.isAccepted).toList(); - } - } - - String _filterName(CallFilter filter) { - switch (filter) { - case CallFilter.all: - return 'all'; - case CallFilter.incoming: - return 'incoming'; - case CallFilter.outgoing: - return 'outgoing'; - case CallFilter.missed: - return 'missed'; - } - } -} diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.dart deleted file mode 100644 index 8d832443..00000000 --- a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../../data/call_history_entity.dart'; - -part 'call_history_view_state.freezed.dart'; - -enum CallFilter { all, incoming, outgoing, missed } - -@freezed -abstract class CallHistoryViewState with _$CallHistoryViewState { - const factory CallHistoryViewState({ - @Default(true) bool isLoading, - @Default([]) List calls, - @Default([]) List filteredCalls, - @Default(CallFilter.all) CallFilter filter, - @Default('') String errorMessage, - }) = _CallHistoryViewState; -} diff --git a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.freezed.dart b/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.freezed.dart deleted file mode 100644 index af3bfd1d..00000000 --- a/modules/legacy/modules/device_management/lib/src/features/call_history/presentation/state/call_history_view_state.freezed.dart +++ /dev/null @@ -1,295 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND -// coverage:ignore-file -// 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 'call_history_view_state.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -// dart format off -T _$identity(T value) => value; -/// @nodoc -mixin _$CallHistoryViewState { - - bool get isLoading; List get calls; List get filteredCalls; CallFilter get filter; String get errorMessage; -/// Create a copy of CallHistoryViewState -/// with the given fields replaced by the non-null parameter values. -@JsonKey(includeFromJson: false, includeToJson: false) -@pragma('vm:prefer-inline') -$CallHistoryViewStateCopyWith get copyWith => _$CallHistoryViewStateCopyWithImpl(this as CallHistoryViewState, _$identity); - - - -@override -bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is CallHistoryViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other.calls, calls)&&const DeepCollectionEquality().equals(other.filteredCalls, filteredCalls)&&(identical(other.filter, filter) || other.filter == filter)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); -} - - -@override -int get hashCode => Object.hash(runtimeType,isLoading,const DeepCollectionEquality().hash(calls),const DeepCollectionEquality().hash(filteredCalls),filter,errorMessage); - -@override -String toString() { - return 'CallHistoryViewState(isLoading: $isLoading, calls: $calls, filteredCalls: $filteredCalls, filter: $filter, errorMessage: $errorMessage)'; -} - - -} - -/// @nodoc -abstract mixin class $CallHistoryViewStateCopyWith<$Res> { - factory $CallHistoryViewStateCopyWith(CallHistoryViewState value, $Res Function(CallHistoryViewState) _then) = _$CallHistoryViewStateCopyWithImpl; -@useResult -$Res call({ - bool isLoading, List calls, List filteredCalls, CallFilter filter, String errorMessage -}); - - - - -} -/// @nodoc -class _$CallHistoryViewStateCopyWithImpl<$Res> - implements $CallHistoryViewStateCopyWith<$Res> { - _$CallHistoryViewStateCopyWithImpl(this._self, this._then); - - final CallHistoryViewState _self; - final $Res Function(CallHistoryViewState) _then; - -/// Create a copy of CallHistoryViewState -/// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? calls = null,Object? filteredCalls = null,Object? filter = null,Object? errorMessage = null,}) { - return _then(_self.copyWith( -isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable -as bool,calls: null == calls ? _self.calls : calls // ignore: cast_nullable_to_non_nullable -as List,filteredCalls: null == filteredCalls ? _self.filteredCalls : filteredCalls // ignore: cast_nullable_to_non_nullable -as List,filter: null == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable -as CallFilter,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable -as String, - )); -} - -} - - -/// Adds pattern-matching-related methods to [CallHistoryViewState]. -extension CallHistoryViewStatePatterns on CallHistoryViewState { -/// A variant of `map` that fallback to returning `orElse`. -/// -/// It is equivalent to doing: -/// ```dart -/// switch (sealedClass) { -/// case final Subclass value: -/// return ...; -/// case _: -/// return orElse(); -/// } -/// ``` - -@optionalTypeArgs TResult maybeMap(TResult Function( _CallHistoryViewState value)? $default,{required TResult orElse(),}){ -final _that = this; -switch (_that) { -case _CallHistoryViewState() when $default != null: -return $default(_that);case _: - return orElse(); - -} -} -/// A `switch`-like method, using callbacks. -/// -/// Callbacks receives the raw object, upcasted. -/// It is equivalent to doing: -/// ```dart -/// switch (sealedClass) { -/// case final Subclass value: -/// return ...; -/// case final Subclass2 value: -/// return ...; -/// } -/// ``` - -@optionalTypeArgs TResult map(TResult Function( _CallHistoryViewState value) $default,){ -final _that = this; -switch (_that) { -case _CallHistoryViewState(): -return $default(_that);case _: - throw StateError('Unexpected subclass'); - -} -} -/// A variant of `map` that fallback to returning `null`. -/// -/// It is equivalent to doing: -/// ```dart -/// switch (sealedClass) { -/// case final Subclass value: -/// return ...; -/// case _: -/// return null; -/// } -/// ``` - -@optionalTypeArgs TResult? mapOrNull(TResult? Function( _CallHistoryViewState value)? $default,){ -final _that = this; -switch (_that) { -case _CallHistoryViewState() when $default != null: -return $default(_that);case _: - return null; - -} -} -/// A variant of `when` that fallback to an `orElse` callback. -/// -/// It is equivalent to doing: -/// ```dart -/// switch (sealedClass) { -/// case Subclass(:final field): -/// return ...; -/// case _: -/// return orElse(); -/// } -/// ``` - -@optionalTypeArgs TResult maybeWhen(TResult Function( bool isLoading, List calls, List filteredCalls, CallFilter filter, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this; -switch (_that) { -case _CallHistoryViewState() when $default != null: -return $default(_that.isLoading,_that.calls,_that.filteredCalls,_that.filter,_that.errorMessage);case _: - return orElse(); - -} -} -/// A `switch`-like method, using callbacks. -/// -/// As opposed to `map`, this offers destructuring. -/// It is equivalent to doing: -/// ```dart -/// switch (sealedClass) { -/// case Subclass(:final field): -/// return ...; -/// case Subclass2(:final field2): -/// return ...; -/// } -/// ``` - -@optionalTypeArgs TResult when(TResult Function( bool isLoading, List calls, List filteredCalls, CallFilter filter, String errorMessage) $default,) {final _that = this; -switch (_that) { -case _CallHistoryViewState(): -return $default(_that.isLoading,_that.calls,_that.filteredCalls,_that.filter,_that.errorMessage);case _: - throw StateError('Unexpected subclass'); - -} -} -/// A variant of `when` that fallback to returning `null` -/// -/// It is equivalent to doing: -/// ```dart -/// switch (sealedClass) { -/// case Subclass(:final field): -/// return ...; -/// case _: -/// return null; -/// } -/// ``` - -@optionalTypeArgs TResult? whenOrNull(TResult? Function( bool isLoading, List calls, List filteredCalls, CallFilter filter, String errorMessage)? $default,) {final _that = this; -switch (_that) { -case _CallHistoryViewState() when $default != null: -return $default(_that.isLoading,_that.calls,_that.filteredCalls,_that.filter,_that.errorMessage);case _: - return null; - -} -} - -} - -/// @nodoc - - -class _CallHistoryViewState implements CallHistoryViewState { - const _CallHistoryViewState({this.isLoading = true, final List calls = const [], final List filteredCalls = const [], this.filter = CallFilter.all, this.errorMessage = ''}): _calls = calls,_filteredCalls = filteredCalls; - - -@override@JsonKey() final bool isLoading; - final List _calls; -@override@JsonKey() List get calls { - if (_calls is EqualUnmodifiableListView) return _calls; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_calls); -} - - final List _filteredCalls; -@override@JsonKey() List get filteredCalls { - if (_filteredCalls is EqualUnmodifiableListView) return _filteredCalls; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_filteredCalls); -} - -@override@JsonKey() final CallFilter filter; -@override@JsonKey() final String errorMessage; - -/// Create a copy of CallHistoryViewState -/// with the given fields replaced by the non-null parameter values. -@override @JsonKey(includeFromJson: false, includeToJson: false) -@pragma('vm:prefer-inline') -_$CallHistoryViewStateCopyWith<_CallHistoryViewState> get copyWith => __$CallHistoryViewStateCopyWithImpl<_CallHistoryViewState>(this, _$identity); - - - -@override -bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallHistoryViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other._calls, _calls)&&const DeepCollectionEquality().equals(other._filteredCalls, _filteredCalls)&&(identical(other.filter, filter) || other.filter == filter)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); -} - - -@override -int get hashCode => Object.hash(runtimeType,isLoading,const DeepCollectionEquality().hash(_calls),const DeepCollectionEquality().hash(_filteredCalls),filter,errorMessage); - -@override -String toString() { - return 'CallHistoryViewState(isLoading: $isLoading, calls: $calls, filteredCalls: $filteredCalls, filter: $filter, errorMessage: $errorMessage)'; -} - - -} - -/// @nodoc -abstract mixin class _$CallHistoryViewStateCopyWith<$Res> implements $CallHistoryViewStateCopyWith<$Res> { - factory _$CallHistoryViewStateCopyWith(_CallHistoryViewState value, $Res Function(_CallHistoryViewState) _then) = __$CallHistoryViewStateCopyWithImpl; -@override @useResult -$Res call({ - bool isLoading, List calls, List filteredCalls, CallFilter filter, String errorMessage -}); - - - - -} -/// @nodoc -class __$CallHistoryViewStateCopyWithImpl<$Res> - implements _$CallHistoryViewStateCopyWith<$Res> { - __$CallHistoryViewStateCopyWithImpl(this._self, this._then); - - final _CallHistoryViewState _self; - final $Res Function(_CallHistoryViewState) _then; - -/// Create a copy of CallHistoryViewState -/// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? calls = null,Object? filteredCalls = null,Object? filter = null,Object? errorMessage = null,}) { - return _then(_CallHistoryViewState( -isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable -as bool,calls: null == calls ? _self._calls : calls // ignore: cast_nullable_to_non_nullable -as List,filteredCalls: null == filteredCalls ? _self._filteredCalls : filteredCalls // ignore: cast_nullable_to_non_nullable -as List,filter: null == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable -as CallFilter,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable -as String, - )); -} - - -} - -// dart format on diff --git a/modules/legacy/modules/device_management/test/features/call_history/call_history_filter_test.dart b/modules/legacy/modules/device_management/test/features/call_history/call_history_filter_test.dart new file mode 100644 index 00000000..65f50ef3 --- /dev/null +++ b/modules/legacy/modules/device_management/test/features/call_history/call_history_filter_test.dart @@ -0,0 +1,40 @@ +import 'package:device_management/src/features/call_history/presentation/providers/call_history_filter_provider.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sf_shared/testing.dart'; +import 'package:sf_tracking/sf_tracking.dart'; + +void main() { + ProviderContainer buildContainer() { + return makeContainer( + overrides: [ + sfTrackingProvider.overrideWithValue( + SfTrackingRepository(clients: const []), + ), + ], + ); + } + + group('CallHistoryFilter', () { + test('starts with all', () { + final container = buildContainer(); + addTearDown(container.dispose); + expect(container.read(callHistoryFilterProvider), CallFilter.all); + }); + + test('select updates state and is no-op when same', () { + final container = buildContainer(); + addTearDown(container.dispose); + + final notifier = container.read(callHistoryFilterProvider.notifier); + notifier.select(CallFilter.missed); + expect(container.read(callHistoryFilterProvider), CallFilter.missed); + + notifier.select(CallFilter.missed); + expect(container.read(callHistoryFilterProvider), CallFilter.missed); + + notifier.select(CallFilter.incoming); + expect(container.read(callHistoryFilterProvider), CallFilter.incoming); + }); + }); +}