refactor(device_management): migrate call_watch to Riverpod
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
part 'call_watch_form_provider.g.dart';
|
||||
|
||||
class CallWatchFormState {
|
||||
const CallWatchFormState({
|
||||
this.isoCode = 'ES',
|
||||
this.errorMessage = '',
|
||||
});
|
||||
|
||||
final String isoCode;
|
||||
final String errorMessage;
|
||||
|
||||
CallWatchFormState copyWith({String? isoCode, String? errorMessage}) {
|
||||
return CallWatchFormState(
|
||||
isoCode: isoCode ?? this.isoCode,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class CallWatchForm extends _$CallWatchForm {
|
||||
@override
|
||||
CallWatchFormState build() => const CallWatchFormState();
|
||||
|
||||
void setIsoCode(String isoCode) {
|
||||
if (isoCode == state.isoCode) return;
|
||||
state = state.copyWith(isoCode: isoCode, errorMessage: '');
|
||||
}
|
||||
|
||||
void clearError() {
|
||||
if (state.errorMessage.isEmpty) return;
|
||||
state = state.copyWith(errorMessage: '');
|
||||
}
|
||||
|
||||
Future<void> call(String rawPhone) async {
|
||||
final phone = rawPhone.trim();
|
||||
if (phone.isEmpty) {
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsEmpty);
|
||||
return;
|
||||
}
|
||||
|
||||
final parsed = SfPhoneNumber.tryParse(phone, defaultIsoCode: state.isoCode);
|
||||
if (parsed == null) {
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
|
||||
return;
|
||||
}
|
||||
|
||||
final url = Uri(scheme: 'tel', path: parsed.e164);
|
||||
if (await canLaunchUrl(url)) {
|
||||
await launchUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'call_watch_form_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(CallWatchForm)
|
||||
const callWatchFormProvider = CallWatchFormProvider._();
|
||||
|
||||
final class CallWatchFormProvider
|
||||
extends $NotifierProvider<CallWatchForm, CallWatchFormState> {
|
||||
const CallWatchFormProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'callWatchFormProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$callWatchFormHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
CallWatchForm create() => CallWatchForm();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(CallWatchFormState value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<CallWatchFormState>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$callWatchFormHash() => r'1b1aadd777456786b3df5cb9f12b1e95794c7ccc';
|
||||
|
||||
abstract class _$CallWatchForm extends $Notifier<CallWatchFormState> {
|
||||
CallWatchFormState build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<CallWatchFormState, CallWatchFormState>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<CallWatchFormState, CallWatchFormState>,
|
||||
CallWatchFormState,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:device_management/src/features/device_management/state/call_watch_view_state.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final callWatchViewModelProvider =
|
||||
NotifierProvider.autoDispose<CallWatchViewModel, CallWatchViewState>(
|
||||
CallWatchViewModel.new,
|
||||
);
|
||||
|
||||
class CallWatchViewModel extends Notifier<CallWatchViewState> {
|
||||
late final TextEditingController phoneController;
|
||||
|
||||
@override
|
||||
CallWatchViewState build() {
|
||||
phoneController = TextEditingController();
|
||||
phoneController.addListener(_onPhoneChanged);
|
||||
|
||||
ref.onDispose(_disposeControllers);
|
||||
|
||||
return const CallWatchViewState();
|
||||
}
|
||||
|
||||
void _onPhoneChanged() {
|
||||
final text = phoneController.text;
|
||||
if (text == state.phone) return;
|
||||
state = state.copyWith(phone: text, errorMessage: '');
|
||||
}
|
||||
|
||||
void updateCountry(String isoCode) {
|
||||
if (isoCode == state.isoCode) return;
|
||||
state = state.copyWith(isoCode: isoCode, errorMessage: '');
|
||||
}
|
||||
|
||||
Future<void> call() async {
|
||||
final phone = state.phone.trim();
|
||||
if (phone.isEmpty) {
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsEmpty);
|
||||
return;
|
||||
}
|
||||
|
||||
final parsed = SfPhoneNumber.tryParse(phone, defaultIsoCode: state.isoCode);
|
||||
if (parsed == null) {
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
|
||||
return;
|
||||
}
|
||||
|
||||
final url = Uri(scheme: 'tel', path: parsed.e164);
|
||||
|
||||
if (await canLaunchUrl(url)) {
|
||||
launchUrl(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
void _disposeControllers() {
|
||||
phoneController.removeListener(_onPhoneChanged);
|
||||
phoneController.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'call_watch_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class CallWatchViewState with _$CallWatchViewState {
|
||||
const factory CallWatchViewState({
|
||||
@Default('ES') String isoCode,
|
||||
@Default('') String phone,
|
||||
@Default('') String errorMessage,
|
||||
}) = _CallWatchViewState;
|
||||
}
|
||||
@@ -1,277 +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_watch_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$CallWatchViewState {
|
||||
|
||||
String get isoCode; String get phone; String get errorMessage;
|
||||
/// Create a copy of CallWatchViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$CallWatchViewStateCopyWith<CallWatchViewState> get copyWith => _$CallWatchViewStateCopyWithImpl<CallWatchViewState>(this as CallWatchViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallWatchViewState&&(identical(other.isoCode, isoCode) || other.isoCode == isoCode)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isoCode,phone,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallWatchViewState(isoCode: $isoCode, phone: $phone, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $CallWatchViewStateCopyWith<$Res> {
|
||||
factory $CallWatchViewStateCopyWith(CallWatchViewState value, $Res Function(CallWatchViewState) _then) = _$CallWatchViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String isoCode, String phone, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$CallWatchViewStateCopyWithImpl<$Res>
|
||||
implements $CallWatchViewStateCopyWith<$Res> {
|
||||
_$CallWatchViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final CallWatchViewState _self;
|
||||
final $Res Function(CallWatchViewState) _then;
|
||||
|
||||
/// Create a copy of CallWatchViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? isoCode = null,Object? phone = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
isoCode: null == isoCode ? _self.isoCode : isoCode // ignore: cast_nullable_to_non_nullable
|
||||
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [CallWatchViewState].
|
||||
extension CallWatchViewStatePatterns on CallWatchViewState {
|
||||
/// 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 extends Object?>(TResult Function( _CallWatchViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState() 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 extends Object?>(TResult Function( _CallWatchViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState():
|
||||
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 extends Object?>(TResult? Function( _CallWatchViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState() 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 extends Object?>(TResult Function( String isoCode, String phone, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState() when $default != null:
|
||||
return $default(_that.isoCode,_that.phone,_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 extends Object?>(TResult Function( String isoCode, String phone, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState():
|
||||
return $default(_that.isoCode,_that.phone,_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 extends Object?>(TResult? Function( String isoCode, String phone, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState() when $default != null:
|
||||
return $default(_that.isoCode,_that.phone,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _CallWatchViewState implements CallWatchViewState {
|
||||
const _CallWatchViewState({this.isoCode = 'ES', this.phone = '', this.errorMessage = ''});
|
||||
|
||||
|
||||
@override@JsonKey() final String isoCode;
|
||||
@override@JsonKey() final String phone;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of CallWatchViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$CallWatchViewStateCopyWith<_CallWatchViewState> get copyWith => __$CallWatchViewStateCopyWithImpl<_CallWatchViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallWatchViewState&&(identical(other.isoCode, isoCode) || other.isoCode == isoCode)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isoCode,phone,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallWatchViewState(isoCode: $isoCode, phone: $phone, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$CallWatchViewStateCopyWith<$Res> implements $CallWatchViewStateCopyWith<$Res> {
|
||||
factory _$CallWatchViewStateCopyWith(_CallWatchViewState value, $Res Function(_CallWatchViewState) _then) = __$CallWatchViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String isoCode, String phone, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$CallWatchViewStateCopyWithImpl<$Res>
|
||||
implements _$CallWatchViewStateCopyWith<$Res> {
|
||||
__$CallWatchViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _CallWatchViewState _self;
|
||||
final $Res Function(_CallWatchViewState) _then;
|
||||
|
||||
/// Create a copy of CallWatchViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? isoCode = null,Object? phone = null,Object? errorMessage = null,}) {
|
||||
return _then(_CallWatchViewState(
|
||||
isoCode: null == isoCode ? _self.isoCode : isoCode // ignore: cast_nullable_to_non_nullable
|
||||
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -1,24 +1,46 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:device_management/src/features/device_management/presentation/providers/call_watch_form_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:device_management/src/features/device_management/state/call_watch_view_model.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
|
||||
class CallWatchDialog extends ConsumerWidget {
|
||||
class CallWatchDialog extends ConsumerStatefulWidget {
|
||||
const CallWatchDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<CallWatchDialog> createState() => _CallWatchDialogState();
|
||||
}
|
||||
|
||||
final viewModel = ref.read(callWatchViewModelProvider.notifier);
|
||||
final viewState = ref.watch(callWatchViewModelProvider);
|
||||
class _CallWatchDialogState extends ConsumerState<CallWatchDialog> {
|
||||
final _phoneController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_phoneController.addListener(_onPhoneChanged);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_phoneController.removeListener(_onPhoneChanged);
|
||||
_phoneController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onPhoneChanged() {
|
||||
ref.read(callWatchFormProvider.notifier).clearError();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final formState = ref.watch(callWatchFormProvider);
|
||||
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 26, vertical: 20),
|
||||
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18),
|
||||
small: const EdgeInsets.symmetric(horizontal: 26, vertical: 20),
|
||||
big: const EdgeInsets.symmetric(horizontal: 24, vertical: 18),
|
||||
),
|
||||
width: SizeUtils.getByScreen(small: 390, big: 380),
|
||||
child: Column(
|
||||
@@ -38,9 +60,7 @@ class CallWatchDialog extends ConsumerWidget {
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: context.sfColors.legacyPrimary,
|
||||
@@ -64,10 +84,14 @@ class CallWatchDialog extends ConsumerWidget {
|
||||
children: [
|
||||
CountryPrefixPicker(
|
||||
headerText: context.translate(I18n.selectYourCountry),
|
||||
initialSelection: viewState.isoCode,
|
||||
initialSelection: formState.isoCode,
|
||||
onChanged: (country) {
|
||||
final code = country.code;
|
||||
if (code != null) viewModel.updateCountry(code);
|
||||
if (code != null) {
|
||||
ref
|
||||
.read(callWatchFormProvider.notifier)
|
||||
.setIsoCode(code);
|
||||
}
|
||||
},
|
||||
width: 80,
|
||||
backgroundColor: Colors.transparent,
|
||||
@@ -76,18 +100,18 @@ class CallWatchDialog extends ConsumerWidget {
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 8, big: 7)),
|
||||
Expanded(
|
||||
child: CustomTextField(
|
||||
controller: viewModel.phoneController,
|
||||
controller: _phoneController,
|
||||
hint: context.translate(I18n.mainContactPhoneNumber),
|
||||
keyboardType: TextInputType.phone,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (viewState.errorMessage.isNotEmpty)
|
||||
if (formState.errorMessage.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
context.translate(viewState.errorMessage),
|
||||
context.translate(formState.errorMessage),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
@@ -97,7 +121,9 @@ class CallWatchDialog extends ConsumerWidget {
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
|
||||
PrimaryButton(
|
||||
onPressed: viewModel.call,
|
||||
onPressed: () => ref
|
||||
.read(callWatchFormProvider.notifier)
|
||||
.call(_phoneController.text),
|
||||
text: context.translate(I18n.call),
|
||||
color: context.sfColors.legacyPrimary,
|
||||
height: SizeUtils.getByScreen(small: 38, big: 36),
|
||||
|
||||
@@ -55,6 +55,7 @@ dependencies:
|
||||
get_it: ^9.0.5
|
||||
go_router: ^17.0.0
|
||||
flutter_riverpod: ^3.0.3
|
||||
riverpod_annotation: ^3.0.3
|
||||
freezed_annotation: ^3.1.0
|
||||
freezed: ^3.2.3
|
||||
dio: ^5.9.2
|
||||
@@ -76,6 +77,9 @@ dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^5.0.0
|
||||
build_runner: ^2.7.1
|
||||
riverpod_generator: ^3.0.3
|
||||
mocktail: ^1.0.4
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:device_management/src/features/device_management/presentation/providers/call_watch_form_provider.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/testing.dart';
|
||||
|
||||
void main() {
|
||||
group('CallWatchForm', () {
|
||||
test('starts with ES isoCode and no error', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final state = container.read(callWatchFormProvider);
|
||||
expect(state.isoCode, 'ES');
|
||||
expect(state.errorMessage, isEmpty);
|
||||
});
|
||||
|
||||
test('setIsoCode updates state and clears error', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final notifier = container.read(callWatchFormProvider.notifier);
|
||||
notifier.call('invalid');
|
||||
expect(
|
||||
container.read(callWatchFormProvider).errorMessage,
|
||||
isNotEmpty,
|
||||
);
|
||||
|
||||
notifier.setIsoCode('FR');
|
||||
final state = container.read(callWatchFormProvider);
|
||||
expect(state.isoCode, 'FR');
|
||||
expect(state.errorMessage, isEmpty);
|
||||
});
|
||||
|
||||
test('call sets errorMessagePhoneIsEmpty when phone is blank', () async {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
await container.read(callWatchFormProvider.notifier).call(' ');
|
||||
|
||||
expect(
|
||||
container.read(callWatchFormProvider).errorMessage,
|
||||
I18n.errorMessagePhoneIsEmpty,
|
||||
);
|
||||
});
|
||||
|
||||
test('call sets errorMessagePhoneIsInvalid when phone is unparseable',
|
||||
() async {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
await container.read(callWatchFormProvider.notifier).call('abc');
|
||||
|
||||
expect(
|
||||
container.read(callWatchFormProvider).errorMessage,
|
||||
I18n.errorMessagePhoneIsInvalid,
|
||||
);
|
||||
});
|
||||
|
||||
test('clearError is no-op when error already empty', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
container.read(callWatchFormProvider.notifier).clearError();
|
||||
expect(container.read(callWatchFormProvider).errorMessage, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user