Merge remote-tracking branch 'origin/feature/contacts' into fusion-app

This commit is contained in:
2026-03-25 02:30:09 +01:00
11 changed files with 66 additions and 36 deletions

View File

@@ -25,7 +25,7 @@ class ContactsScreen extends ConsumerWidget {
contactsViewModelProvider.select((s) => s.errorMessage), contactsViewModelProvider.select((s) => s.errorMessage),
(previous, next) { (previous, next) {
if (next.isNotEmpty) { if (next.isNotEmpty) {
showTopSnackbar(context, message: next, type: MessageType.error); showTopSnackbar(context, message: context.translate(next), type: MessageType.error);
} }
}, },
); );
@@ -58,14 +58,21 @@ class ContactsScreen extends ConsumerWidget {
shape: const CircleBorder(), shape: const CircleBorder(),
child: InkWell( child: InkWell(
customBorder: const CircleBorder(), customBorder: const CircleBorder(),
onTap: () => showDialog( onTap: () {
context: context, if (state.contacts.length == state.maxLimit) {
builder: (_) => const Dialog( showTopSnackbar(context, message: context.translate(I18n.errorContactsMax));
backgroundColor: Colors.transparent, return;
child: NewContactDialog(), }
),
), showDialog(
child: SizedBox( context: context,
builder: (_) => const Dialog(
backgroundColor: Colors.transparent,
child: NewContactDialog(),
),
);
},
child: SizedBox(
width: SizeUtils.getByScreen(small: 48, big: 46), width: SizeUtils.getByScreen(small: 48, big: 46),
height: SizeUtils.getByScreen(small: 48, big: 46), height: SizeUtils.getByScreen(small: 48, big: 46),
child: Icon( child: Icon(

View File

@@ -134,6 +134,11 @@ class ContactsViewModel extends Notifier<ContactsViewState> {
Future<bool> deleteContact(ContactEntity contact) async { Future<bool> deleteContact(ContactEntity contact) async {
if (state.isLoading) return false; if (state.isLoading) return false;
if (state.contacts.length == 1){
state = state.copyWith(errorMessage: I18n.errorContactsMin);
return true;
}
try { try {
state = state.copyWith(isLoading: true, errorMessage: ''); state = state.copyWith(isLoading: true, errorMessage: '');

View File

@@ -8,9 +8,10 @@ part 'contacts_view_state.freezed.dart';
abstract class ContactsViewState with _$ContactsViewState { abstract class ContactsViewState with _$ContactsViewState {
const factory ContactsViewState({ const factory ContactsViewState({
@Default([]) List<ContactEntity> contacts, @Default([]) List<ContactEntity> contacts,
@Default(10) int maxLimit,
@Default('+34') String dialCode, @Default('+34') String dialCode,
@Default(true) bool isLoading, @Default(true) bool isLoading,
@Default(false) bool isEditing, @Default(false) bool isEditing,
@Default('') String errorMessage, @Default('') String errorMessage
}) = _ContactsViewState; }) = _ContactsViewState;
} }

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$ContactsViewState { mixin _$ContactsViewState {
List<ContactEntity> get contacts; String get dialCode; bool get isLoading; bool get isEditing; String get errorMessage; List<ContactEntity> get contacts; int get maxLimit; String get dialCode; bool get isLoading; bool get isEditing; String get errorMessage;
/// Create a copy of ContactsViewState /// Create a copy of ContactsViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $ContactsViewStateCopyWith<ContactsViewState> get copyWith => _$ContactsViewStat
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ContactsViewState&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); return identical(this, other) || (other.runtimeType == runtimeType&&other is ContactsViewState&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.maxLimit, maxLimit) || other.maxLimit == maxLimit)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
} }
@override @override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(contacts),dialCode,isLoading,isEditing,errorMessage); int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(contacts),maxLimit,dialCode,isLoading,isEditing,errorMessage);
@override @override
String toString() { String toString() {
return 'ContactsViewState(contacts: $contacts, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)'; return 'ContactsViewState(contacts: $contacts, maxLimit: $maxLimit, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)';
} }
@@ -45,7 +45,7 @@ abstract mixin class $ContactsViewStateCopyWith<$Res> {
factory $ContactsViewStateCopyWith(ContactsViewState value, $Res Function(ContactsViewState) _then) = _$ContactsViewStateCopyWithImpl; factory $ContactsViewStateCopyWith(ContactsViewState value, $Res Function(ContactsViewState) _then) = _$ContactsViewStateCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage
}); });
@@ -62,10 +62,11 @@ class _$ContactsViewStateCopyWithImpl<$Res>
/// Create a copy of ContactsViewState /// Create a copy of ContactsViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? contacts = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? contacts = null,Object? maxLimit = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
contacts: null == contacts ? _self.contacts : contacts // ignore: cast_nullable_to_non_nullable contacts: null == contacts ? _self.contacts : contacts // ignore: cast_nullable_to_non_nullable
as List<ContactEntity>,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable as List<ContactEntity>,maxLimit: null == maxLimit ? _self.maxLimit : maxLimit // ignore: cast_nullable_to_non_nullable
as int,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable as bool,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
@@ -154,10 +155,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _ContactsViewState() when $default != null: case _ContactsViewState() when $default != null:
return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _: return $default(_that.contacts,_that.maxLimit,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
return orElse(); return orElse();
} }
@@ -175,10 +176,10 @@ return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_t
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _ContactsViewState(): case _ContactsViewState():
return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _: return $default(_that.contacts,_that.maxLimit,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
throw StateError('Unexpected subclass'); throw StateError('Unexpected subclass');
} }
@@ -195,10 +196,10 @@ return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_t
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _ContactsViewState() when $default != null: case _ContactsViewState() when $default != null:
return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _: return $default(_that.contacts,_that.maxLimit,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
return null; return null;
} }
@@ -210,7 +211,7 @@ return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_t
class _ContactsViewState implements ContactsViewState { class _ContactsViewState implements ContactsViewState {
const _ContactsViewState({final List<ContactEntity> contacts = const [], this.dialCode = '+34', this.isLoading = true, this.isEditing = false, this.errorMessage = ''}): _contacts = contacts; const _ContactsViewState({final List<ContactEntity> contacts = const [], this.maxLimit = 10, this.dialCode = '+34', this.isLoading = true, this.isEditing = false, this.errorMessage = ''}): _contacts = contacts;
final List<ContactEntity> _contacts; final List<ContactEntity> _contacts;
@@ -220,6 +221,7 @@ class _ContactsViewState implements ContactsViewState {
return EqualUnmodifiableListView(_contacts); return EqualUnmodifiableListView(_contacts);
} }
@override@JsonKey() final int maxLimit;
@override@JsonKey() final String dialCode; @override@JsonKey() final String dialCode;
@override@JsonKey() final bool isLoading; @override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isEditing; @override@JsonKey() final bool isEditing;
@@ -235,16 +237,16 @@ _$ContactsViewStateCopyWith<_ContactsViewState> get copyWith => __$ContactsViewS
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ContactsViewState&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _ContactsViewState&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.maxLimit, maxLimit) || other.maxLimit == maxLimit)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
} }
@override @override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_contacts),dialCode,isLoading,isEditing,errorMessage); int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_contacts),maxLimit,dialCode,isLoading,isEditing,errorMessage);
@override @override
String toString() { String toString() {
return 'ContactsViewState(contacts: $contacts, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)'; return 'ContactsViewState(contacts: $contacts, maxLimit: $maxLimit, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)';
} }
@@ -255,7 +257,7 @@ abstract mixin class _$ContactsViewStateCopyWith<$Res> implements $ContactsViewS
factory _$ContactsViewStateCopyWith(_ContactsViewState value, $Res Function(_ContactsViewState) _then) = __$ContactsViewStateCopyWithImpl; factory _$ContactsViewStateCopyWith(_ContactsViewState value, $Res Function(_ContactsViewState) _then) = __$ContactsViewStateCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage
}); });
@@ -272,10 +274,11 @@ class __$ContactsViewStateCopyWithImpl<$Res>
/// Create a copy of ContactsViewState /// Create a copy of ContactsViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? contacts = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? contacts = null,Object? maxLimit = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) {
return _then(_ContactsViewState( return _then(_ContactsViewState(
contacts: null == contacts ? _self._contacts : contacts // ignore: cast_nullable_to_non_nullable contacts: null == contacts ? _self._contacts : contacts // ignore: cast_nullable_to_non_nullable
as List<ContactEntity>,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable as List<ContactEntity>,maxLimit: null == maxLimit ? _self.maxLimit : maxLimit // ignore: cast_nullable_to_non_nullable
as int,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable as bool,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable

View File

@@ -661,5 +661,7 @@
"activityMeterPedometerEnabled": "Schrittzähler aktiviert", "activityMeterPedometerEnabled": "Schrittzähler aktiviert",
"activityMeterPedometerDisabled": "Schrittzähler deaktiviert", "activityMeterPedometerDisabled": "Schrittzähler deaktiviert",
"errorActivityData": "Aktivitätsdaten konnten nicht geladen werden", "errorActivityData": "Aktivitätsdaten konnten nicht geladen werden",
"errorPedometer": "Der Schrittzähler konnte nicht aktualisiert werden" "errorPedometer": "Der Schrittzähler konnte nicht aktualisiert werden",
"errorContactsMin": "Das Gerät muss mindestens einen Kontakt haben",
"errorContactsMax": "Das Gerät kann nicht mehr als 10 Kontakte haben"
} }

View File

@@ -793,5 +793,7 @@
"activityMeterPedometerEnabled": "Pedometer enabled", "activityMeterPedometerEnabled": "Pedometer enabled",
"activityMeterPedometerDisabled": "Pedometer disabled", "activityMeterPedometerDisabled": "Pedometer disabled",
"errorActivityData": "Could not load activity data", "errorActivityData": "Could not load activity data",
"errorPedometer": "Could not update pedometer" "errorPedometer": "Could not update pedometer",
"errorContactsMin": "The device must have at least one contact",
"errorContactsMax": "The device cannot have more than 10 contacts"
} }

View File

@@ -791,5 +791,7 @@
"activityMeterPedometerEnabled": "Podómetro activado", "activityMeterPedometerEnabled": "Podómetro activado",
"activityMeterPedometerDisabled": "Podómetro desactivado", "activityMeterPedometerDisabled": "Podómetro desactivado",
"errorActivityData": "No se pudieron cargar los datos de actividad", "errorActivityData": "No se pudieron cargar los datos de actividad",
"errorPedometer": "No se pudo actualizar el podómetro" "errorPedometer": "No se pudo actualizar el podómetro",
"errorContactsMin": "El dispositivo debe tener al menos un contacto",
"errorContactsMax": "El dispositivo no puede tener más de 10 contactos"
} }

View File

@@ -661,5 +661,7 @@
"activityMeterPedometerEnabled": "Podomètre activé", "activityMeterPedometerEnabled": "Podomètre activé",
"activityMeterPedometerDisabled": "Podomètre désactivé", "activityMeterPedometerDisabled": "Podomètre désactivé",
"errorActivityData": "Impossible de charger les données d'activité", "errorActivityData": "Impossible de charger les données d'activité",
"errorPedometer": "Impossible de mettre à jour le podomètre" "errorPedometer": "Impossible de mettre à jour le podomètre",
"errorContactsMin": "L'appareil doit avoir au moins un contact",
"errorContactsMax": "L'appareil ne peut pas avoir plus de 10 contacts"
} }

View File

@@ -661,5 +661,7 @@
"activityMeterPedometerEnabled": "Contapassi attivato", "activityMeterPedometerEnabled": "Contapassi attivato",
"activityMeterPedometerDisabled": "Contapassi disattivato", "activityMeterPedometerDisabled": "Contapassi disattivato",
"errorActivityData": "Impossibile caricare i dati di attività", "errorActivityData": "Impossibile caricare i dati di attività",
"errorPedometer": "Impossibile aggiornare il contapassi" "errorPedometer": "Impossibile aggiornare il contapassi",
"errorContactsMin": "Il dispositivo deve avere almeno un contatto",
"errorContactsMax": "Il dispositivo non può avere più di 10 contatti"
} }

View File

@@ -661,5 +661,7 @@
"activityMeterPedometerEnabled": "Pedómetro ativado", "activityMeterPedometerEnabled": "Pedómetro ativado",
"activityMeterPedometerDisabled": "Pedómetro desativado", "activityMeterPedometerDisabled": "Pedómetro desativado",
"errorActivityData": "Não foi possível carregar os dados de atividade", "errorActivityData": "Não foi possível carregar os dados de atividade",
"errorPedometer": "Não foi possível atualizar o pedómetro" "errorPedometer": "Não foi possível atualizar o pedómetro",
"errorContactsMin": "O dispositivo deve ter pelo menos um contacto",
"errorContactsMax": "O dispositivo não pode ter mais de 10 contactos"
} }

View File

@@ -798,4 +798,6 @@ class I18n {
static const String activityMeterPedometerDisabled = 'activityMeterPedometerDisabled'; static const String activityMeterPedometerDisabled = 'activityMeterPedometerDisabled';
static const String errorActivityData = 'errorActivityData'; static const String errorActivityData = 'errorActivityData';
static const String errorPedometer = 'errorPedometer'; static const String errorPedometer = 'errorPedometer';
static const String errorContactsMin = 'errorContactsMin';
static const String errorContactsMax = 'errorContactsMax';
} }