refactor(legacy-account): migrate linked_devices to AsyncNotifier
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
import 'package:sf_shared/sf_shared.dart' show DeviceEntity;
|
||||
|
||||
abstract class DevicesRemoteDatasource {
|
||||
Future<void> deleteDevice({required String deviceId});
|
||||
|
||||
Future<void> updateDevice({required UpdateDeviceRequestEntity request});
|
||||
Future<void> updateDevice({required DeviceEntity device});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:account/src/core/data/models/update_device_request_dto.dart';
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
import 'package:legacy_device_state/legacy_device_state.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
import 'package:sf_shared/sf_shared.dart' show DeviceEntity;
|
||||
|
||||
import 'devices_remote_datasource.dart';
|
||||
|
||||
@@ -15,10 +15,11 @@ class DevicesRemoteDatasourceImpl implements DevicesRemoteDatasource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateDevice({
|
||||
required UpdateDeviceRequestEntity request,
|
||||
}) async {
|
||||
final body = request.toDto().toJson();
|
||||
await _repository.put<void>('/devices', body: body);
|
||||
Future<void> updateDevice({required DeviceEntity device}) async {
|
||||
final csvBase64 = DeviceCsvBuilder.buildBase64Csv(
|
||||
device: device,
|
||||
settings: device.settings,
|
||||
);
|
||||
await _repository.put<void>('/devices', body: {'csv': csvBase64});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'update_device_request_dto.freezed.dart';
|
||||
part 'update_device_request_dto.g.dart';
|
||||
|
||||
@freezed
|
||||
abstract class UpdateDeviceRequestDto with _$UpdateDeviceRequestDto {
|
||||
const factory UpdateDeviceRequestDto({
|
||||
required String identificator,
|
||||
required String carrierName,
|
||||
}) = _UpdateDeviceRequestDto;
|
||||
|
||||
factory UpdateDeviceRequestDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$UpdateDeviceRequestDtoFromJson(json);
|
||||
}
|
||||
|
||||
extension UpdateDeviceRequestDtoMapper on UpdateDeviceRequestEntity {
|
||||
UpdateDeviceRequestDto toDto() => UpdateDeviceRequestDto(
|
||||
identificator: identificator,
|
||||
carrierName: carrierName,
|
||||
);
|
||||
}
|
||||
@@ -1,280 +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 'update_device_request_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$UpdateDeviceRequestDto {
|
||||
|
||||
String get identificator; String get carrierName;
|
||||
/// Create a copy of UpdateDeviceRequestDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$UpdateDeviceRequestDtoCopyWith<UpdateDeviceRequestDto> get copyWith => _$UpdateDeviceRequestDtoCopyWithImpl<UpdateDeviceRequestDto>(this as UpdateDeviceRequestDto, _$identity);
|
||||
|
||||
/// Serializes this UpdateDeviceRequestDto to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateDeviceRequestDto&&(identical(other.identificator, identificator) || other.identificator == identificator)&&(identical(other.carrierName, carrierName) || other.carrierName == carrierName));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,identificator,carrierName);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UpdateDeviceRequestDto(identificator: $identificator, carrierName: $carrierName)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $UpdateDeviceRequestDtoCopyWith<$Res> {
|
||||
factory $UpdateDeviceRequestDtoCopyWith(UpdateDeviceRequestDto value, $Res Function(UpdateDeviceRequestDto) _then) = _$UpdateDeviceRequestDtoCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String identificator, String carrierName
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$UpdateDeviceRequestDtoCopyWithImpl<$Res>
|
||||
implements $UpdateDeviceRequestDtoCopyWith<$Res> {
|
||||
_$UpdateDeviceRequestDtoCopyWithImpl(this._self, this._then);
|
||||
|
||||
final UpdateDeviceRequestDto _self;
|
||||
final $Res Function(UpdateDeviceRequestDto) _then;
|
||||
|
||||
/// Create a copy of UpdateDeviceRequestDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? identificator = null,Object? carrierName = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
identificator: null == identificator ? _self.identificator : identificator // ignore: cast_nullable_to_non_nullable
|
||||
as String,carrierName: null == carrierName ? _self.carrierName : carrierName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [UpdateDeviceRequestDto].
|
||||
extension UpdateDeviceRequestDtoPatterns on UpdateDeviceRequestDto {
|
||||
/// 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( _UpdateDeviceRequestDto value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestDto() 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( _UpdateDeviceRequestDto value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestDto():
|
||||
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( _UpdateDeviceRequestDto value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestDto() 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 identificator, String carrierName)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestDto() when $default != null:
|
||||
return $default(_that.identificator,_that.carrierName);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 identificator, String carrierName) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestDto():
|
||||
return $default(_that.identificator,_that.carrierName);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 identificator, String carrierName)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestDto() when $default != null:
|
||||
return $default(_that.identificator,_that.carrierName);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _UpdateDeviceRequestDto implements UpdateDeviceRequestDto {
|
||||
const _UpdateDeviceRequestDto({required this.identificator, required this.carrierName});
|
||||
factory _UpdateDeviceRequestDto.fromJson(Map<String, dynamic> json) => _$UpdateDeviceRequestDtoFromJson(json);
|
||||
|
||||
@override final String identificator;
|
||||
@override final String carrierName;
|
||||
|
||||
/// Create a copy of UpdateDeviceRequestDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$UpdateDeviceRequestDtoCopyWith<_UpdateDeviceRequestDto> get copyWith => __$UpdateDeviceRequestDtoCopyWithImpl<_UpdateDeviceRequestDto>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$UpdateDeviceRequestDtoToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateDeviceRequestDto&&(identical(other.identificator, identificator) || other.identificator == identificator)&&(identical(other.carrierName, carrierName) || other.carrierName == carrierName));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,identificator,carrierName);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UpdateDeviceRequestDto(identificator: $identificator, carrierName: $carrierName)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$UpdateDeviceRequestDtoCopyWith<$Res> implements $UpdateDeviceRequestDtoCopyWith<$Res> {
|
||||
factory _$UpdateDeviceRequestDtoCopyWith(_UpdateDeviceRequestDto value, $Res Function(_UpdateDeviceRequestDto) _then) = __$UpdateDeviceRequestDtoCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String identificator, String carrierName
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$UpdateDeviceRequestDtoCopyWithImpl<$Res>
|
||||
implements _$UpdateDeviceRequestDtoCopyWith<$Res> {
|
||||
__$UpdateDeviceRequestDtoCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _UpdateDeviceRequestDto _self;
|
||||
final $Res Function(_UpdateDeviceRequestDto) _then;
|
||||
|
||||
/// Create a copy of UpdateDeviceRequestDto
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? identificator = null,Object? carrierName = null,}) {
|
||||
return _then(_UpdateDeviceRequestDto(
|
||||
identificator: null == identificator ? _self.identificator : identificator // ignore: cast_nullable_to_non_nullable
|
||||
as String,carrierName: null == carrierName ? _self.carrierName : carrierName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -1,21 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'update_device_request_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_UpdateDeviceRequestDto _$UpdateDeviceRequestDtoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _UpdateDeviceRequestDto(
|
||||
identificator: json['identificator'] as String,
|
||||
carrierName: json['carrierName'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UpdateDeviceRequestDtoToJson(
|
||||
_UpdateDeviceRequestDto instance,
|
||||
) => <String, dynamic>{
|
||||
'identificator': instance.identificator,
|
||||
'carrierName': instance.carrierName,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:account/src/core/data/datasource/devices_remote_datasource.dart';
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
import 'package:sf_shared/sf_shared.dart' show DeviceEntity;
|
||||
|
||||
import '../../domain/repositories/devices_repository.dart';
|
||||
|
||||
@@ -20,9 +20,9 @@ class DevicesRepositoryImpl implements DevicesRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateDevice({required UpdateDeviceRequestEntity request}) async {
|
||||
Future<void> updateDevice({required DeviceEntity device}) async {
|
||||
try {
|
||||
await _remote.updateDevice(request: request);
|
||||
await _remote.updateDevice(device: device);
|
||||
} on DioException catch (error) {
|
||||
throw mapDioError(error, defaultMessage: 'Error to update device');
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
import 'package:sf_shared/sf_shared.dart' show DeviceEntity;
|
||||
|
||||
abstract class DevicesRepository {
|
||||
Future<void> deleteDevice({required String deviceId});
|
||||
|
||||
Future<void> updateDevice({required UpdateDeviceRequestEntity request});
|
||||
Future<void> updateDevice({required DeviceEntity device});
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'update_device_request_entity.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class UpdateDeviceRequestEntity with _$UpdateDeviceRequestEntity {
|
||||
const factory UpdateDeviceRequestEntity({
|
||||
required String identificator,
|
||||
required String carrierName,
|
||||
}) = _UpdateDeviceRequestEntity;
|
||||
}
|
||||
@@ -1,274 +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 'update_device_request_entity.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$UpdateDeviceRequestEntity {
|
||||
|
||||
String get identificator; String get carrierName;
|
||||
/// Create a copy of UpdateDeviceRequestEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$UpdateDeviceRequestEntityCopyWith<UpdateDeviceRequestEntity> get copyWith => _$UpdateDeviceRequestEntityCopyWithImpl<UpdateDeviceRequestEntity>(this as UpdateDeviceRequestEntity, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateDeviceRequestEntity&&(identical(other.identificator, identificator) || other.identificator == identificator)&&(identical(other.carrierName, carrierName) || other.carrierName == carrierName));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,identificator,carrierName);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UpdateDeviceRequestEntity(identificator: $identificator, carrierName: $carrierName)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $UpdateDeviceRequestEntityCopyWith<$Res> {
|
||||
factory $UpdateDeviceRequestEntityCopyWith(UpdateDeviceRequestEntity value, $Res Function(UpdateDeviceRequestEntity) _then) = _$UpdateDeviceRequestEntityCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String identificator, String carrierName
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$UpdateDeviceRequestEntityCopyWithImpl<$Res>
|
||||
implements $UpdateDeviceRequestEntityCopyWith<$Res> {
|
||||
_$UpdateDeviceRequestEntityCopyWithImpl(this._self, this._then);
|
||||
|
||||
final UpdateDeviceRequestEntity _self;
|
||||
final $Res Function(UpdateDeviceRequestEntity) _then;
|
||||
|
||||
/// Create a copy of UpdateDeviceRequestEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? identificator = null,Object? carrierName = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
identificator: null == identificator ? _self.identificator : identificator // ignore: cast_nullable_to_non_nullable
|
||||
as String,carrierName: null == carrierName ? _self.carrierName : carrierName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [UpdateDeviceRequestEntity].
|
||||
extension UpdateDeviceRequestEntityPatterns on UpdateDeviceRequestEntity {
|
||||
/// 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( _UpdateDeviceRequestEntity value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestEntity() 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( _UpdateDeviceRequestEntity value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestEntity():
|
||||
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( _UpdateDeviceRequestEntity value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestEntity() 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 identificator, String carrierName)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestEntity() when $default != null:
|
||||
return $default(_that.identificator,_that.carrierName);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 identificator, String carrierName) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestEntity():
|
||||
return $default(_that.identificator,_that.carrierName);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 identificator, String carrierName)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _UpdateDeviceRequestEntity() when $default != null:
|
||||
return $default(_that.identificator,_that.carrierName);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _UpdateDeviceRequestEntity implements UpdateDeviceRequestEntity {
|
||||
const _UpdateDeviceRequestEntity({required this.identificator, required this.carrierName});
|
||||
|
||||
|
||||
@override final String identificator;
|
||||
@override final String carrierName;
|
||||
|
||||
/// Create a copy of UpdateDeviceRequestEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$UpdateDeviceRequestEntityCopyWith<_UpdateDeviceRequestEntity> get copyWith => __$UpdateDeviceRequestEntityCopyWithImpl<_UpdateDeviceRequestEntity>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateDeviceRequestEntity&&(identical(other.identificator, identificator) || other.identificator == identificator)&&(identical(other.carrierName, carrierName) || other.carrierName == carrierName));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,identificator,carrierName);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'UpdateDeviceRequestEntity(identificator: $identificator, carrierName: $carrierName)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$UpdateDeviceRequestEntityCopyWith<$Res> implements $UpdateDeviceRequestEntityCopyWith<$Res> {
|
||||
factory _$UpdateDeviceRequestEntityCopyWith(_UpdateDeviceRequestEntity value, $Res Function(_UpdateDeviceRequestEntity) _then) = __$UpdateDeviceRequestEntityCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String identificator, String carrierName
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$UpdateDeviceRequestEntityCopyWithImpl<$Res>
|
||||
implements _$UpdateDeviceRequestEntityCopyWith<$Res> {
|
||||
__$UpdateDeviceRequestEntityCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _UpdateDeviceRequestEntity _self;
|
||||
final $Res Function(_UpdateDeviceRequestEntity) _then;
|
||||
|
||||
/// Create a copy of UpdateDeviceRequestEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? identificator = null,Object? carrierName = null,}) {
|
||||
return _then(_UpdateDeviceRequestEntity(
|
||||
identificator: null == identificator ? _self.identificator : identificator // ignore: cast_nullable_to_non_nullable
|
||||
as String,carrierName: null == carrierName ? _self.carrierName : carrierName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -1 +0,0 @@
|
||||
part of 'update_device_request_entity.dart';
|
||||
@@ -1,28 +1,71 @@
|
||||
import 'package:account/src/features/linked_devices/presentation/state/linked_devices_view_model.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/providers/linked_devices_controller.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/providers/linked_devices_edit_mode_provider.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:legacy_ui/legacy_ui.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
class EditLinkedDeviceScreen extends ConsumerWidget {
|
||||
const EditLinkedDeviceScreen({super.key});
|
||||
class EditLinkedDeviceScreen extends ConsumerStatefulWidget {
|
||||
final DeviceEntity device;
|
||||
|
||||
const EditLinkedDeviceScreen({super.key, required this.device});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<EditLinkedDeviceScreen> createState() =>
|
||||
_EditLinkedDeviceScreenState();
|
||||
}
|
||||
|
||||
final vm = ref.read(linkedDevicesViewModelProvider.notifier);
|
||||
final device = ref.watch(
|
||||
linkedDevicesViewModelProvider.select((s) => s.selectedDevice),
|
||||
class _EditLinkedDeviceScreenState
|
||||
extends ConsumerState<EditLinkedDeviceScreen> {
|
||||
late final TextEditingController _nameController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nameController = TextEditingController(
|
||||
text: widget.device.carrierName ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onSubmit() {
|
||||
if (_nameController.text.trim().isEmpty) return;
|
||||
ref.read(linkedDevicesControllerProvider.notifier).updateDevice(
|
||||
device: widget.device,
|
||||
newName: _nameController.text,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ref.listen(linkedDevicesControllerProvider, (prev, next) async {
|
||||
next.showErrorOn(context);
|
||||
if (prev != null &&
|
||||
prev.isLoading &&
|
||||
!next.isLoading &&
|
||||
!next.hasError) {
|
||||
await showSuccessDialog(context, I18n.deviceUpdatedSuccess);
|
||||
if (context.mounted) {
|
||||
ref.read(linkedDevicesEditModeProvider.notifier).disable();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final submitState = ref.watch(linkedDevicesControllerProvider);
|
||||
|
||||
return LegacyPageLayout(
|
||||
title: context.translate(I18n.editDeviceTitle),
|
||||
showEdit: true,
|
||||
onEditChange: vm.toggleIsEditing,
|
||||
body: Column(
|
||||
children: [
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 20, big: 18)),
|
||||
@@ -33,100 +76,65 @@ class EditLinkedDeviceScreen extends ConsumerWidget {
|
||||
big: EdgeInsets.symmetric(horizontal: 47, vertical: 8),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
Stack(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.blueAccent),
|
||||
),
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
'assets/shared/images/profile.svg',
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 160,
|
||||
height: 160,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: IconButton(
|
||||
onPressed: () {},
|
||||
icon: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Color(0xFFCAC9C9),
|
||||
),
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
Icons.edit_outlined,
|
||||
color: Colors.white,
|
||||
size: SizeUtils.getByScreen(
|
||||
small: 32,
|
||||
big: 30,
|
||||
),
|
||||
),
|
||||
const DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.blueAccent),
|
||||
),
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
'assets/shared/images/profile.svg',
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: 160,
|
||||
height: 160,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: IconButton(
|
||||
onPressed: () {},
|
||||
icon: Container(
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Color(0xFFCAC9C9),
|
||||
),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
Icons.edit_outlined,
|
||||
color: Colors.white,
|
||||
size: SizeUtils.getByScreen(
|
||||
small: 32,
|
||||
big: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: SizeUtils.getByScreen(small: 24, big: 22),
|
||||
),
|
||||
CustomTextField(
|
||||
controller: vm.deviceNameController,
|
||||
hint: device!.carrierName!,
|
||||
label: context.translate(I18n.name),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
||||
CustomTextField(
|
||||
controller: _nameController,
|
||||
hint: widget.device.carrierName ?? '',
|
||||
label: context.translate(I18n.name),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
footer: _SaveSection(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SaveSection extends ConsumerWidget {
|
||||
const _SaveSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
final vm = ref.read(linkedDevicesViewModelProvider.notifier);
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
await vm.updateDevice();
|
||||
if (!context.mounted) return;
|
||||
|
||||
final errorMessage = ref.read(
|
||||
linkedDevicesViewModelProvider.select((s) => s.errorMessage),
|
||||
);
|
||||
|
||||
if (errorMessage.isNotEmpty) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: errorMessage,
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
},
|
||||
text: context.translate(I18n.save),
|
||||
color: context.sfColors.legacyPrimary,
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: submitState.isLoading ? null : _onSubmit,
|
||||
text: context.translate(I18n.save),
|
||||
color: context.sfColors.legacyPrimary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:account/src/features/linked_devices/presentation/edit_linked_device_screen.dart';
|
||||
import 'package:legacy_theme/legacy_theme.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/state/linked_devices_view_model.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/providers/linked_devices_controller.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/providers/linked_devices_edit_mode_provider.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/widgets/delete_device_dialog.dart';
|
||||
import 'package:design_system/design_system.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:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
@@ -18,60 +19,66 @@ class LinkedDevicesScreen extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final vm = ref.read(linkedDevicesViewModelProvider.notifier);
|
||||
final state = ref.watch(linkedDevicesViewModelProvider);
|
||||
final devicesAsync = ref.watch(legacyDevicesProvider);
|
||||
final isEditing = ref.watch(linkedDevicesEditModeProvider);
|
||||
final toggleEditing = ref
|
||||
.read(linkedDevicesEditModeProvider.notifier)
|
||||
.toggle;
|
||||
|
||||
ref.listen(linkedDevicesControllerProvider, (prev, next) async {
|
||||
next.showErrorOn(context);
|
||||
if (prev != null &&
|
||||
prev.isLoading &&
|
||||
!next.isLoading &&
|
||||
!next.hasError) {
|
||||
await showSuccessDialog(context, I18n.deviceDeletedSuccess);
|
||||
if (!context.mounted) return;
|
||||
ref.read(linkedDevicesEditModeProvider.notifier).disable();
|
||||
final remaining = ref.read(legacyDevicesProvider).value ?? const [];
|
||||
if (remaining.isEmpty) {
|
||||
navigationContract.goTo(AppRoutes.legacyDeviceSetup);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return LegacyPageLayout(
|
||||
title: context.translate(I18n.linkedDevices),
|
||||
showEdit: true,
|
||||
onEditChange: vm.toggleIsEditing,
|
||||
body: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 10, big: 12),
|
||||
),
|
||||
child: ListView.separated(
|
||||
itemBuilder: (BuildContext context, int index) =>
|
||||
_LinkedDeviceCard(
|
||||
navigationContract: navigationContract,
|
||||
device: state.linkedDevices[index],
|
||||
isEditing: state.isEditing,
|
||||
onDelete: () =>
|
||||
vm.deleteDevice(state.linkedDevices[index]),
|
||||
),
|
||||
separatorBuilder: (BuildContext context, int index) =>
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
|
||||
itemCount: state.linkedDevices.length,
|
||||
),
|
||||
onEditChange: toggleEditing,
|
||||
body: devicesAsync.when(
|
||||
data: (devices) => Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 10, big: 12),
|
||||
),
|
||||
child: ListView.separated(
|
||||
itemBuilder: (_, index) => _LinkedDeviceCard(
|
||||
device: devices[index],
|
||||
isEditing: isEditing,
|
||||
),
|
||||
separatorBuilder: (_, __) =>
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
|
||||
itemCount: devices.length,
|
||||
),
|
||||
),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (_, __) => const SizedBox.shrink(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LinkedDeviceCard extends ConsumerWidget {
|
||||
final NavigationContract navigationContract;
|
||||
class _LinkedDeviceCard extends StatelessWidget {
|
||||
final DeviceEntity device;
|
||||
final bool isEditing;
|
||||
final Function onDelete;
|
||||
|
||||
const _LinkedDeviceCard({
|
||||
required this.navigationContract,
|
||||
required this.device,
|
||||
required this.isEditing,
|
||||
required this.onDelete,
|
||||
});
|
||||
const _LinkedDeviceCard({required this.device, required this.isEditing});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
final vm = ref.read(linkedDevicesViewModelProvider.notifier);
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
|
||||
big: EdgeInsets.symmetric(horizontal: 21, vertical: 8),
|
||||
small: const EdgeInsets.symmetric(horizontal: 22, vertical: 10),
|
||||
big: const EdgeInsets.symmetric(horizontal: 21, vertical: 8),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
@@ -100,7 +107,7 @@ class _LinkedDeviceCard extends ConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
device.carrierName!,
|
||||
device.carrierName ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -119,38 +126,30 @@ class _LinkedDeviceCard extends ConsumerWidget {
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
child: DeleteDeviceDialog(
|
||||
navigationContract: navigationContract,
|
||||
device: device,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: Icon(Icons.close, color: Colors.white),
|
||||
onPressed: () => showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => Dialog(child: DeleteDeviceDialog(device: device)),
|
||||
),
|
||||
icon: const Icon(Icons.close, color: Colors.white),
|
||||
),
|
||||
),
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 14)),
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: context.sfColors.legacyPrimary,
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
vm.setSelectedDevice(device);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => EditLinkedDeviceScreen()),
|
||||
);
|
||||
},
|
||||
icon: Icon(Icons.edit_outlined, color: Colors.white),
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => EditLinkedDeviceScreen(device: device),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.edit_outlined, color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:account/src/core/providers/devices_repository_provider.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
part 'linked_devices_controller.g.dart';
|
||||
|
||||
@riverpod
|
||||
class LinkedDevicesController extends _$LinkedDevicesController {
|
||||
@override
|
||||
FutureOr<void> build() {}
|
||||
|
||||
Future<void> deleteDevice(DeviceEntity device) async {
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
await ref
|
||||
.read(devicesRepositoryProvider)
|
||||
.deleteDevice(deviceId: device.id);
|
||||
ref.read(legacyDevicesProvider.notifier).removeDevice(device.id);
|
||||
|
||||
final currentSelected = ref.read(selectedDeviceProvider).value;
|
||||
if (currentSelected?.id == device.id) {
|
||||
ref.invalidate(selectedDeviceProvider);
|
||||
}
|
||||
|
||||
unawaited(
|
||||
ref.read(sfTrackingProvider).legacyAccountLinkedDeviceUnlinked(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateDevice({
|
||||
required DeviceEntity device,
|
||||
required String newName,
|
||||
}) async {
|
||||
state = const AsyncLoading();
|
||||
state = await AsyncValue.guard(() async {
|
||||
final trimmed = newName.trim();
|
||||
final updated = device.copyWith(carrierName: trimmed);
|
||||
await ref.read(devicesRepositoryProvider).updateDevice(device: updated);
|
||||
ref
|
||||
.read(legacyDevicesProvider.notifier)
|
||||
.renameDevice(deviceId: device.id, newCarrierName: trimmed);
|
||||
unawaited(
|
||||
ref.read(sfTrackingProvider).legacyAccountLinkedDeviceRenamed(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'linked_devices_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(LinkedDevicesController)
|
||||
const linkedDevicesControllerProvider = LinkedDevicesControllerProvider._();
|
||||
|
||||
final class LinkedDevicesControllerProvider
|
||||
extends $AsyncNotifierProvider<LinkedDevicesController, void> {
|
||||
const LinkedDevicesControllerProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'linkedDevicesControllerProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$linkedDevicesControllerHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
LinkedDevicesController create() => LinkedDevicesController();
|
||||
}
|
||||
|
||||
String _$linkedDevicesControllerHash() =>
|
||||
r'5840696cb5ad0e8dd1b8f671180af74eeb42ba9d';
|
||||
|
||||
abstract class _$LinkedDevicesController 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'linked_devices_edit_mode_provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class LinkedDevicesEditMode extends _$LinkedDevicesEditMode {
|
||||
@override
|
||||
bool build() => false;
|
||||
|
||||
void toggle() => state = !state;
|
||||
|
||||
void disable() => state = false;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'linked_devices_edit_mode_provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(LinkedDevicesEditMode)
|
||||
const linkedDevicesEditModeProvider = LinkedDevicesEditModeProvider._();
|
||||
|
||||
final class LinkedDevicesEditModeProvider
|
||||
extends $NotifierProvider<LinkedDevicesEditMode, bool> {
|
||||
const LinkedDevicesEditModeProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'linkedDevicesEditModeProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$linkedDevicesEditModeHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
LinkedDevicesEditMode create() => LinkedDevicesEditMode();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(bool value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<bool>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$linkedDevicesEditModeHash() =>
|
||||
r'aea2da0d62667e721fa059fe5b48201689bbf9c2';
|
||||
|
||||
abstract class _$LinkedDevicesEditMode extends $Notifier<bool> {
|
||||
bool build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<bool, bool>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<bool, bool>,
|
||||
bool,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/state/linked_devices_view_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:sf_tracking/sf_tracking.dart';
|
||||
|
||||
import '../../../../core/domain/repositories/devices_repository.dart';
|
||||
import '../../../../core/providers/devices_repository_provider.dart';
|
||||
|
||||
final linkedDevicesViewModelProvider =
|
||||
NotifierProvider.autoDispose<
|
||||
LinkedDevicesViewModel,
|
||||
LinkedDevicesViewState
|
||||
>(LinkedDevicesViewModel.new);
|
||||
|
||||
class LinkedDevicesViewModel extends Notifier<LinkedDevicesViewState> {
|
||||
late final SharedDevicesRepository _getDevicesRepository;
|
||||
late final DevicesRepository _devicesRepository;
|
||||
late final SfTrackingRepository _tracking;
|
||||
|
||||
late final TextEditingController deviceNameController;
|
||||
|
||||
@override
|
||||
LinkedDevicesViewState build() {
|
||||
_getDevicesRepository = ref.read(sharedDevicesRepositoryProvider);
|
||||
_devicesRepository = ref.read(devicesRepositoryProvider);
|
||||
_tracking = ref.read(sfTrackingProvider);
|
||||
|
||||
_initControllers();
|
||||
_init();
|
||||
|
||||
return const LinkedDevicesViewState();
|
||||
}
|
||||
|
||||
void _initControllers() {
|
||||
deviceNameController = TextEditingController();
|
||||
deviceNameController.addListener(_onDeviceNameChanged);
|
||||
|
||||
ref.onDispose(disposeControllers);
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
final user = await ref.read(userInfoProvider.future);
|
||||
state = state.copyWith(loggedUser: user);
|
||||
|
||||
final linkedDevices = await _getDevicesRepository.getDevices();
|
||||
state = state.copyWith(linkedDevices: linkedDevices, isLoading: false);
|
||||
}
|
||||
|
||||
void toggleIsEditing() {
|
||||
state = state.copyWith(isEditing: !state.isEditing);
|
||||
}
|
||||
|
||||
void _onDeviceNameChanged() {
|
||||
final value = deviceNameController.text;
|
||||
|
||||
if (value == state.deviceName) return;
|
||||
|
||||
state = state.copyWith(deviceName: value);
|
||||
}
|
||||
|
||||
void setSelectedDevice(DeviceEntity value) {
|
||||
if (value == state.selectedDevice) return;
|
||||
|
||||
state = state.copyWith(selectedDevice: value);
|
||||
}
|
||||
|
||||
Future<void> deleteDevice(DeviceEntity device) async {
|
||||
try {
|
||||
state = state.copyWith(isLoading: true, isComplete: false);
|
||||
|
||||
await _devicesRepository.deleteDevice(deviceId: device.id);
|
||||
List<DeviceEntity> newList = state.linkedDevices.toList();
|
||||
newList.remove(device);
|
||||
|
||||
if (device == state.selectedDevice) {
|
||||
ref.invalidate(selectedDeviceProvider);
|
||||
}
|
||||
|
||||
ref.read(legacyDevicesProvider.notifier).removeDevice(device.id);
|
||||
|
||||
unawaited(_tracking.legacyAccountLinkedDeviceUnlinked());
|
||||
|
||||
state = state.copyWith(
|
||||
linkedDevices: newList,
|
||||
isLoading: false,
|
||||
isComplete: true,
|
||||
);
|
||||
} catch (e) {
|
||||
state = state.copyWith(isLoading: false, errorMessage: e.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDeviceRequestEntity _toRequest(DeviceEntity device) {
|
||||
return UpdateDeviceRequestEntity(
|
||||
identificator: device.identificator,
|
||||
carrierName: state.deviceName.trim(),
|
||||
// phone: /*state.dialCode.trim() + */state.phoneNumber.trim(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateDevice() async {
|
||||
final deviceName = state.deviceName;
|
||||
final device = state.selectedDevice!;
|
||||
|
||||
if (deviceName.isEmpty) return;
|
||||
|
||||
try {
|
||||
await _devicesRepository.updateDevice(request: _toRequest(device));
|
||||
ref.read(legacyDevicesProvider.notifier).renameDevice(
|
||||
deviceId: device.id,
|
||||
newCarrierName: deviceName.trim(),
|
||||
);
|
||||
unawaited(_tracking.legacyAccountLinkedDeviceRenamed());
|
||||
} catch (e) {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
isComplete: false,
|
||||
errorMessage: e.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void disposeControllers() {
|
||||
deviceNameController.removeListener(_onDeviceNameChanged);
|
||||
deviceNameController.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
part 'linked_devices_view_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class LinkedDevicesViewState with _$LinkedDevicesViewState {
|
||||
const factory LinkedDevicesViewState({
|
||||
@Default(true) bool isLoading,
|
||||
@Default(true) bool isComplete,
|
||||
UserEntity? loggedUser,
|
||||
DeviceEntity? selectedDevice,
|
||||
@Default([]) List<DeviceEntity> linkedDevices,
|
||||
@Default(false) bool isEditing,
|
||||
@Default('') String deviceName,
|
||||
@Default('') String errorMessage,
|
||||
}) = _LinkedDevicesViewState;
|
||||
}
|
||||
@@ -1,346 +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 'linked_devices_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$LinkedDevicesViewState {
|
||||
|
||||
bool get isLoading; bool get isComplete; UserEntity? get loggedUser; DeviceEntity? get selectedDevice; List<DeviceEntity> get linkedDevices; bool get isEditing; String get deviceName; String get errorMessage;
|
||||
/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$LinkedDevicesViewStateCopyWith<LinkedDevicesViewState> get copyWith => _$LinkedDevicesViewStateCopyWithImpl<LinkedDevicesViewState>(this as LinkedDevicesViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is LinkedDevicesViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.selectedDevice, selectedDevice) || other.selectedDevice == selectedDevice)&&const DeepCollectionEquality().equals(other.linkedDevices, linkedDevices)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,loggedUser,selectedDevice,const DeepCollectionEquality().hash(linkedDevices),isEditing,deviceName,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LinkedDevicesViewState(isLoading: $isLoading, isComplete: $isComplete, loggedUser: $loggedUser, selectedDevice: $selectedDevice, linkedDevices: $linkedDevices, isEditing: $isEditing, deviceName: $deviceName, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $LinkedDevicesViewStateCopyWith<$Res> {
|
||||
factory $LinkedDevicesViewStateCopyWith(LinkedDevicesViewState value, $Res Function(LinkedDevicesViewState) _then) = _$LinkedDevicesViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool isLoading, bool isComplete, UserEntity? loggedUser, DeviceEntity? selectedDevice, List<DeviceEntity> linkedDevices, bool isEditing, String deviceName, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
$UserEntityCopyWith<$Res>? get loggedUser;$DeviceEntityCopyWith<$Res>? get selectedDevice;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$LinkedDevicesViewStateCopyWithImpl<$Res>
|
||||
implements $LinkedDevicesViewStateCopyWith<$Res> {
|
||||
_$LinkedDevicesViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final LinkedDevicesViewState _self;
|
||||
final $Res Function(LinkedDevicesViewState) _then;
|
||||
|
||||
/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isComplete = null,Object? loggedUser = freezed,Object? selectedDevice = freezed,Object? linkedDevices = null,Object? isEditing = null,Object? deviceName = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable
|
||||
as bool,loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
|
||||
as UserEntity?,selectedDevice: freezed == selectedDevice ? _self.selectedDevice : selectedDevice // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceEntity?,linkedDevices: null == linkedDevices ? _self.linkedDevices : linkedDevices // ignore: cast_nullable_to_non_nullable
|
||||
as List<DeviceEntity>,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$UserEntityCopyWith<$Res>? get loggedUser {
|
||||
if (_self.loggedUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $UserEntityCopyWith<$Res>(_self.loggedUser!, (value) {
|
||||
return _then(_self.copyWith(loggedUser: value));
|
||||
});
|
||||
}/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$DeviceEntityCopyWith<$Res>? get selectedDevice {
|
||||
if (_self.selectedDevice == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $DeviceEntityCopyWith<$Res>(_self.selectedDevice!, (value) {
|
||||
return _then(_self.copyWith(selectedDevice: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [LinkedDevicesViewState].
|
||||
extension LinkedDevicesViewStatePatterns on LinkedDevicesViewState {
|
||||
/// 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( _LinkedDevicesViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LinkedDevicesViewState() 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( _LinkedDevicesViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LinkedDevicesViewState():
|
||||
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( _LinkedDevicesViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _LinkedDevicesViewState() 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( bool isLoading, bool isComplete, UserEntity? loggedUser, DeviceEntity? selectedDevice, List<DeviceEntity> linkedDevices, bool isEditing, String deviceName, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _LinkedDevicesViewState() when $default != null:
|
||||
return $default(_that.isLoading,_that.isComplete,_that.loggedUser,_that.selectedDevice,_that.linkedDevices,_that.isEditing,_that.deviceName,_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( bool isLoading, bool isComplete, UserEntity? loggedUser, DeviceEntity? selectedDevice, List<DeviceEntity> linkedDevices, bool isEditing, String deviceName, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _LinkedDevicesViewState():
|
||||
return $default(_that.isLoading,_that.isComplete,_that.loggedUser,_that.selectedDevice,_that.linkedDevices,_that.isEditing,_that.deviceName,_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( bool isLoading, bool isComplete, UserEntity? loggedUser, DeviceEntity? selectedDevice, List<DeviceEntity> linkedDevices, bool isEditing, String deviceName, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _LinkedDevicesViewState() when $default != null:
|
||||
return $default(_that.isLoading,_that.isComplete,_that.loggedUser,_that.selectedDevice,_that.linkedDevices,_that.isEditing,_that.deviceName,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _LinkedDevicesViewState implements LinkedDevicesViewState {
|
||||
const _LinkedDevicesViewState({this.isLoading = true, this.isComplete = true, this.loggedUser, this.selectedDevice, final List<DeviceEntity> linkedDevices = const [], this.isEditing = false, this.deviceName = '', this.errorMessage = ''}): _linkedDevices = linkedDevices;
|
||||
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isComplete;
|
||||
@override final UserEntity? loggedUser;
|
||||
@override final DeviceEntity? selectedDevice;
|
||||
final List<DeviceEntity> _linkedDevices;
|
||||
@override@JsonKey() List<DeviceEntity> get linkedDevices {
|
||||
if (_linkedDevices is EqualUnmodifiableListView) return _linkedDevices;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_linkedDevices);
|
||||
}
|
||||
|
||||
@override@JsonKey() final bool isEditing;
|
||||
@override@JsonKey() final String deviceName;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$LinkedDevicesViewStateCopyWith<_LinkedDevicesViewState> get copyWith => __$LinkedDevicesViewStateCopyWithImpl<_LinkedDevicesViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LinkedDevicesViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.selectedDevice, selectedDevice) || other.selectedDevice == selectedDevice)&&const DeepCollectionEquality().equals(other._linkedDevices, _linkedDevices)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.deviceName, deviceName) || other.deviceName == deviceName)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,loggedUser,selectedDevice,const DeepCollectionEquality().hash(_linkedDevices),isEditing,deviceName,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LinkedDevicesViewState(isLoading: $isLoading, isComplete: $isComplete, loggedUser: $loggedUser, selectedDevice: $selectedDevice, linkedDevices: $linkedDevices, isEditing: $isEditing, deviceName: $deviceName, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$LinkedDevicesViewStateCopyWith<$Res> implements $LinkedDevicesViewStateCopyWith<$Res> {
|
||||
factory _$LinkedDevicesViewStateCopyWith(_LinkedDevicesViewState value, $Res Function(_LinkedDevicesViewState) _then) = __$LinkedDevicesViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool isLoading, bool isComplete, UserEntity? loggedUser, DeviceEntity? selectedDevice, List<DeviceEntity> linkedDevices, bool isEditing, String deviceName, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
@override $UserEntityCopyWith<$Res>? get loggedUser;@override $DeviceEntityCopyWith<$Res>? get selectedDevice;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$LinkedDevicesViewStateCopyWithImpl<$Res>
|
||||
implements _$LinkedDevicesViewStateCopyWith<$Res> {
|
||||
__$LinkedDevicesViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _LinkedDevicesViewState _self;
|
||||
final $Res Function(_LinkedDevicesViewState) _then;
|
||||
|
||||
/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isComplete = null,Object? loggedUser = freezed,Object? selectedDevice = freezed,Object? linkedDevices = null,Object? isEditing = null,Object? deviceName = null,Object? errorMessage = null,}) {
|
||||
return _then(_LinkedDevicesViewState(
|
||||
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable
|
||||
as bool,loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
|
||||
as UserEntity?,selectedDevice: freezed == selectedDevice ? _self.selectedDevice : selectedDevice // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceEntity?,linkedDevices: null == linkedDevices ? _self._linkedDevices : linkedDevices // ignore: cast_nullable_to_non_nullable
|
||||
as List<DeviceEntity>,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,deviceName: null == deviceName ? _self.deviceName : deviceName // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$UserEntityCopyWith<$Res>? get loggedUser {
|
||||
if (_self.loggedUser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $UserEntityCopyWith<$Res>(_self.loggedUser!, (value) {
|
||||
return _then(_self.copyWith(loggedUser: value));
|
||||
});
|
||||
}/// Create a copy of LinkedDevicesViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$DeviceEntityCopyWith<$Res>? get selectedDevice {
|
||||
if (_self.selectedDevice == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $DeviceEntityCopyWith<$Res>(_self.selectedDevice!, (value) {
|
||||
return _then(_self.copyWith(selectedDevice: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -1,32 +1,23 @@
|
||||
import 'package:account/src/features/linked_devices/presentation/state/linked_devices_view_model.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/providers/linked_devices_controller.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:navigation/navigation.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 'package:legacy_theme/legacy_theme.dart';
|
||||
|
||||
class DeleteDeviceDialog extends ConsumerWidget {
|
||||
final NavigationContract navigationContract;
|
||||
final DeviceEntity device;
|
||||
|
||||
const DeleteDeviceDialog({
|
||||
super.key,
|
||||
required this.navigationContract,
|
||||
required this.device,
|
||||
});
|
||||
const DeleteDeviceDialog({super.key, required this.device});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
||||
final vm = ref.read(linkedDevicesViewModelProvider.notifier);
|
||||
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
|
||||
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18),
|
||||
small: const EdgeInsets.symmetric(horizontal: 32, vertical: 30),
|
||||
big: const EdgeInsets.symmetric(horizontal: 24, vertical: 18),
|
||||
),
|
||||
width: SizeUtils.getByScreen(small: 360, big: 350),
|
||||
child: Column(
|
||||
@@ -45,9 +36,7 @@ class DeleteDeviceDialog extends ConsumerWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onPressed: () => Navigator.pop(context),
|
||||
text: context.translate(I18n.cancel),
|
||||
color: context.sfColors.legacyPrimary,
|
||||
height: SizeUtils.getByScreen(small: 38, big: 36),
|
||||
@@ -57,29 +46,11 @@ class DeleteDeviceDialog extends ConsumerWidget {
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 4, big: 16)),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
await vm.deleteDevice(device);
|
||||
if (!context.mounted) return;
|
||||
|
||||
final isComplete = ref.read(
|
||||
linkedDevicesViewModelProvider.select(
|
||||
(s) => s.isComplete,
|
||||
),
|
||||
);
|
||||
if (isComplete) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
final noMoreDevices = ref
|
||||
.read(
|
||||
linkedDevicesViewModelProvider.select(
|
||||
(s) => s.linkedDevices,
|
||||
),
|
||||
)
|
||||
.isEmpty;
|
||||
if (noMoreDevices) {
|
||||
navigationContract.goTo(AppRoutes.legacyDeviceSetup);
|
||||
}
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
ref
|
||||
.read(linkedDevicesControllerProvider.notifier)
|
||||
.deleteDevice(device);
|
||||
},
|
||||
text: context.translate(I18n.delete),
|
||||
color: context.sfColors.legacyPrimary,
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import 'package:account/src/core/domain/repositories/devices_repository.dart';
|
||||
import 'package:account/src/core/providers/devices_repository_provider.dart';
|
||||
import 'package:account/src/features/linked_devices/presentation/providers/linked_devices_controller.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.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 MockDevicesRepository extends Mock implements DevicesRepository {}
|
||||
|
||||
class MockSharedDevicesRepository extends Mock
|
||||
implements SharedDevicesRepository {}
|
||||
|
||||
const _deviceA = DeviceEntity(
|
||||
id: 'device-A',
|
||||
identificator: 'imei-A',
|
||||
carrierName: 'Alice Watch',
|
||||
phone: '+34600000001',
|
||||
);
|
||||
|
||||
const _deviceB = DeviceEntity(
|
||||
id: 'device-B',
|
||||
identificator: 'imei-B',
|
||||
carrierName: 'Bob Watch',
|
||||
phone: '+34600000002',
|
||||
);
|
||||
|
||||
void main() {
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_deviceA);
|
||||
});
|
||||
|
||||
ProviderContainer buildContainer({
|
||||
required DevicesRepository devicesRepo,
|
||||
List<DeviceEntity> initialDevices = const [_deviceA, _deviceB],
|
||||
}) {
|
||||
final shared = MockSharedDevicesRepository();
|
||||
when(() => shared.getDevices()).thenAnswer((_) async => initialDevices);
|
||||
|
||||
return makeContainer(
|
||||
overrides: [
|
||||
sharedDevicesRepositoryProvider.overrideWithValue(shared),
|
||||
devicesRepositoryProvider.overrideWithValue(devicesRepo),
|
||||
sfTrackingProvider.overrideWithValue(
|
||||
SfTrackingRepository(clients: const []),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
group('LinkedDevicesController.deleteDevice', () {
|
||||
test('removes device from legacyDevicesProvider on success', () async {
|
||||
final repo = MockDevicesRepository();
|
||||
when(() => repo.deleteDevice(deviceId: any(named: 'deviceId')))
|
||||
.thenAnswer((_) async {});
|
||||
|
||||
final container = buildContainer(devicesRepo: repo);
|
||||
addTearDown(container.dispose);
|
||||
await container.read(legacyDevicesProvider.future);
|
||||
|
||||
await container
|
||||
.read(linkedDevicesControllerProvider.notifier)
|
||||
.deleteDevice(_deviceA);
|
||||
|
||||
final state = container.read(linkedDevicesControllerProvider);
|
||||
expect(state, isA<AsyncData<void>>());
|
||||
expect(state.error, isNull);
|
||||
|
||||
final remaining = container.read(legacyDevicesProvider).value;
|
||||
expect(remaining, [_deviceB]);
|
||||
|
||||
verify(() => repo.deleteDevice(deviceId: 'device-A')).called(1);
|
||||
});
|
||||
|
||||
test('exposes AsyncError when the repository fails', () async {
|
||||
final repo = MockDevicesRepository();
|
||||
when(() => repo.deleteDevice(deviceId: any(named: 'deviceId')))
|
||||
.thenThrow(const ApiException(message: 'boom', isNetworkError: true));
|
||||
|
||||
final container = buildContainer(devicesRepo: repo);
|
||||
addTearDown(container.dispose);
|
||||
await container.read(legacyDevicesProvider.future);
|
||||
|
||||
await container
|
||||
.read(linkedDevicesControllerProvider.notifier)
|
||||
.deleteDevice(_deviceA);
|
||||
|
||||
final state = container.read(linkedDevicesControllerProvider);
|
||||
expect(state, isA<AsyncError<void>>());
|
||||
expect(state.error, isA<ApiException>());
|
||||
});
|
||||
});
|
||||
|
||||
group('LinkedDevicesController.updateDevice', () {
|
||||
test('renames device in legacyDevicesProvider on success', () async {
|
||||
final repo = MockDevicesRepository();
|
||||
when(() => repo.updateDevice(device: any(named: 'device')))
|
||||
.thenAnswer((_) async {});
|
||||
|
||||
final container = buildContainer(devicesRepo: repo);
|
||||
addTearDown(container.dispose);
|
||||
await container.read(legacyDevicesProvider.future);
|
||||
|
||||
await container
|
||||
.read(linkedDevicesControllerProvider.notifier)
|
||||
.updateDevice(device: _deviceA, newName: 'Alice Sport');
|
||||
|
||||
final state = container.read(linkedDevicesControllerProvider);
|
||||
expect(state, isA<AsyncData<void>>());
|
||||
|
||||
final devices = container.read(legacyDevicesProvider).value!;
|
||||
expect(devices.firstWhere((d) => d.id == 'device-A').carrierName,
|
||||
'Alice Sport');
|
||||
expect(devices.firstWhere((d) => d.id == 'device-B').carrierName,
|
||||
'Bob Watch');
|
||||
|
||||
final captured = verify(
|
||||
() => repo.updateDevice(device: captureAny(named: 'device')),
|
||||
).captured.single as DeviceEntity;
|
||||
expect(captured.id, 'device-A');
|
||||
expect(captured.carrierName, 'Alice Sport');
|
||||
});
|
||||
|
||||
test('exposes AsyncError when the repository fails', () async {
|
||||
final repo = MockDevicesRepository();
|
||||
when(() => repo.updateDevice(device: any(named: 'device')))
|
||||
.thenThrow(const ApiException(message: 'boom', isNetworkError: true));
|
||||
|
||||
final container = buildContainer(devicesRepo: repo);
|
||||
addTearDown(container.dispose);
|
||||
await container.read(legacyDevicesProvider.future);
|
||||
|
||||
await container
|
||||
.read(linkedDevicesControllerProvider.notifier)
|
||||
.updateDevice(device: _deviceA, newName: 'Alice Sport');
|
||||
|
||||
final state = container.read(linkedDevicesControllerProvider);
|
||||
expect(state, isA<AsyncError<void>>());
|
||||
expect(state.error, isA<ApiException>());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'package:account/src/features/linked_devices/presentation/providers/linked_devices_edit_mode_provider.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:sf_shared/testing.dart';
|
||||
|
||||
void main() {
|
||||
group('LinkedDevicesEditMode', () {
|
||||
test('defaults to false', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
expect(container.read(linkedDevicesEditModeProvider), isFalse);
|
||||
});
|
||||
|
||||
test('toggle flips the state', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final notifier = container.read(
|
||||
linkedDevicesEditModeProvider.notifier,
|
||||
);
|
||||
|
||||
notifier.toggle();
|
||||
expect(container.read(linkedDevicesEditModeProvider), isTrue);
|
||||
|
||||
notifier.toggle();
|
||||
expect(container.read(linkedDevicesEditModeProvider), isFalse);
|
||||
});
|
||||
|
||||
test('disable forces state to false regardless of current value', () {
|
||||
final container = makeContainer();
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final notifier = container.read(
|
||||
linkedDevicesEditModeProvider.notifier,
|
||||
);
|
||||
|
||||
notifier.disable();
|
||||
expect(container.read(linkedDevicesEditModeProvider), isFalse);
|
||||
|
||||
notifier.toggle();
|
||||
expect(container.read(linkedDevicesEditModeProvider), isTrue);
|
||||
|
||||
notifier.disable();
|
||||
expect(container.read(linkedDevicesEditModeProvider), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user