create wallet, treezor session heartbeat, get payment profile, device setup refactor, flow management from splash screen, sca pin dynamic

This commit is contained in:
2026-02-12 09:02:43 +01:00
parent ca79856cd8
commit 417f0021ad
50 changed files with 2588 additions and 611 deletions

View File

@@ -0,0 +1 @@
{"version":2,"entries":[{"package":"sf_app_platform_mono_repo","rootUri":"../","packageUri":"lib/"}]}

View File

@@ -10,6 +10,7 @@ import 'package:sf_app_platform/navigation/app_router.dart';
import 'package:navigation/navigation_module.dart';
import 'package:sf_app_platform/providers/app_state_provider.dart';
import 'package:sf_app_platform/providers/permissions/permissions_provider.dart';
import 'package:sf_app_platform/providers/wallet_heartbeat_service.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
@@ -36,14 +37,19 @@ class PlatformApp extends ConsumerStatefulWidget {
class PlatformAppState extends ConsumerState<PlatformApp>
with WidgetsBindingObserver {
late final WalletHeartbeatService walletHeartbeat;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
walletHeartbeat = WalletHeartbeatService.create();
walletHeartbeat.start();
}
@override
void dispose() {
walletHeartbeat.stop();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@@ -53,7 +59,10 @@ class PlatformAppState extends ConsumerState<PlatformApp>
debugPrint('State: $state');
ref.read(appLifecycleStateProvider.notifier).setState(state);
if (state == AppLifecycleState.resumed) {
walletHeartbeat.start();
ref.read(permissionsProvider.notifier).checkPermissions();
} else if (state == AppLifecycleState.paused) {
walletHeartbeat.stop();
}
super.didChangeAppLifecycleState(state);
}

View File

@@ -16,7 +16,7 @@ late final GoRouter appRouter;
void configureAppRouter() {
appRouter = GoRouter(
navigatorKey: rootNavigatorKey,
initialLocation: AppRoutes.login,
initialLocation: AppRoutes.splash,
debugLogDiagnostics: true,
routes: [
GoRoute(

View File

@@ -0,0 +1,56 @@
import 'dart:async';
import 'package:auth/auth.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
class WalletHeartbeatService {
WalletHeartbeatService({
required QuestiaRepository repository,
required SessionLocalDatasource sessionLocal,
}) : _repository = repository,
_sessionLocal = sessionLocal;
final QuestiaRepository _repository;
final SessionLocalDatasource _sessionLocal;
Timer? _timer;
static const _interval = Duration(minutes: 4);
factory WalletHeartbeatService.create() {
return WalletHeartbeatService(
repository: GetIt.I<QuestiaRepository>(),
sessionLocal: SessionLocalDatasourceImpl(),
);
}
void start() {
if (_timer != null) return;
_beat();
_timer = Timer.periodic(_interval, (_) => _beat());
debugPrint('[WalletHeartbeat] started');
}
void stop() {
_timer?.cancel();
_timer = null;
debugPrint('[WalletHeartbeat] stopped');
}
Future<void> _beat() async {
final walletId = await _sessionLocal.getPaymentProfileId();
if (walletId == null || walletId.isEmpty) return;
try {
final response = await _repository.get<Map<String, dynamic>>(
'/wallets/$walletId',
);
debugPrint(
'[WalletHeartbeat] /wallets/$walletId => ${response.statusCode}',
);
} catch (e) {
debugPrint('[WalletHeartbeat] error: $e');
}
}
}

View File

@@ -6,3 +6,5 @@ export 'src/features/recover_password/presentation/request_recovery/request_reco
export 'src/features/device_setup/device_setup_builder.dart';
export 'src/features/sign_up/sign_up_builder.dart';
export 'src/features/sca_treezor/sca_treezor_builder.dart';
export 'src/core/data/datasource/session_local_datasource.dart';
export 'src/core/data/datasource/session_local_datasource_impl.dart';

View File

@@ -1,4 +1,5 @@
import 'package:auth/src/core/data/models/child_profile_response_model.dart';
import 'package:auth/src/core/data/models/payment_profile_response_model.dart';
import 'package:auth/src/core/data/models/user_response_model.dart';
import 'package:auth/src/core/data/models/sign_up_request_model.dart';
import 'package:auth/src/core/data/models/sign_up_response_model.dart';
@@ -41,6 +42,10 @@ abstract class AuthRemoteDatasource {
Future<void> recoverPassword({required newPassword, required token});
Future<void> createWallet();
Future<PaymentProfileResponseModel> getPaymentProfile({required String userId});
Future<ChildProfileResponseModel> createChildProfile({
required String id,
required String parentId,

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:auth/src/core/data/models/child_profile_response_model.dart';
import 'package:auth/src/core/data/models/payment_profile_response_model.dart';
import 'package:auth/src/core/data/models/user_response_model.dart';
import 'package:auth/src/core/data/models/sign_up_request_model.dart';
import 'package:auth/src/core/data/models/sign_up_response_model.dart';
@@ -270,6 +271,41 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource {
}
}
@override
Future<void> createWallet() async {
try {
await _repository.post<dynamic>(
'/wallets',
body: <String, dynamic>{'walletName': 'default'},
);
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in /wallets');
}
}
@override
Future<PaymentProfileResponseModel> getPaymentProfile({
required String userId,
}) async {
try {
final response = await _repository.get<Map<String, dynamic>>(
'/users/$userId/payment-profile',
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /users/$userId/payment-profile');
}
return PaymentProfileResponseModel.fromJson(data);
} on DioException catch (error) {
throw _mapDioError(
error,
defaultMessage: 'Error in /users/$userId/payment-profile',
);
}
}
@override
Future<ChildProfileResponseModel> createChildProfile({
required String id,

View File

@@ -0,0 +1,5 @@
abstract class SessionLocalDatasource {
Future<void> savePaymentProfileId(String id);
Future<String?> getPaymentProfileId();
Future<void> clearSession();
}

View File

@@ -0,0 +1,25 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'session_local_datasource.dart';
class SessionLocalDatasourceImpl implements SessionLocalDatasource {
static const _paymentProfileIdKey = 'session_payment_profile_id';
@override
Future<void> savePaymentProfileId(String id) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_paymentProfileIdKey, id);
}
@override
Future<String?> getPaymentProfileId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_paymentProfileIdKey);
}
@override
Future<void> clearSession() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_paymentProfileIdKey);
}
}

View File

@@ -0,0 +1,107 @@
import 'package:auth/src/features/login/domain/entities/payment_profile_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'payment_profile_response_model.freezed.dart';
part 'payment_profile_response_model.g.dart';
@freezed
abstract class PaymentProfileResponseModel with _$PaymentProfileResponseModel {
const factory PaymentProfileResponseModel({
required PaymentProfileItemModel item,
}) = _PaymentProfileResponseModel;
factory PaymentProfileResponseModel.fromJson(Map<String, dynamic> json) =>
_$PaymentProfileResponseModelFromJson(json);
}
@freezed
abstract class PaymentProfileItemModel with _$PaymentProfileItemModel {
const factory PaymentProfileItemModel({
required String id,
required String userId,
required String paymentProfileId,
String? jwt,
required int bornAt,
required String phone,
required List<PaymentProfileAddressModel> taxResidences,
required List<PaymentProfileAddressModel> addresses,
required String status,
//talk with Hector to change those names in the backend to be more consistent with the rest of the fields
// ignore: non_constant_identifier_names
required String KYCStatus,
// ignore: non_constant_identifier_names
required String KYCLevel,
required String placeOfBirth,
required String birthCountry,
required String nationality,
required String documentType,
required String document,
String? paymentWalletId,
required int createdAt,
}) = _PaymentProfileItemModel;
factory PaymentProfileItemModel.fromJson(Map<String, dynamic> json) =>
_$PaymentProfileItemModelFromJson(json);
}
@freezed
abstract class PaymentProfileAddressModel with _$PaymentProfileAddressModel {
const factory PaymentProfileAddressModel({
required String street,
required String city,
required String province,
required String state,
required String country,
required int postCode,
}) = _PaymentProfileAddressModel;
factory PaymentProfileAddressModel.fromJson(Map<String, dynamic> json) =>
_$PaymentProfileAddressModelFromJson(json);
}
extension PaymentProfileResponseModelMapper on PaymentProfileResponseModel {
PaymentProfileEntity toEntity() {
return PaymentProfileEntity(
id: item.id,
userId: item.userId,
paymentProfileId: item.paymentProfileId,
jwt: item.jwt,
bornAt: item.bornAt,
phone: item.phone,
taxResidences: item.taxResidences
.map(
(a) => PaymentProfileAddressEntity(
street: a.street,
city: a.city,
province: a.province,
state: a.state,
country: a.country,
postCode: a.postCode,
),
)
.toList(),
addresses: item.addresses
.map(
(a) => PaymentProfileAddressEntity(
street: a.street,
city: a.city,
province: a.province,
state: a.state,
country: a.country,
postCode: a.postCode,
),
)
.toList(),
status: item.status,
kycStatus: item.KYCStatus,
kycLevel: item.KYCLevel,
placeOfBirth: item.placeOfBirth,
birthCountry: item.birthCountry,
nationality: item.nationality,
documentType: item.documentType,
document: item.document,
paymentWalletId: item.paymentWalletId,
createdAt: item.createdAt,
);
}
}

View File

@@ -0,0 +1,905 @@
// 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 'payment_profile_response_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$PaymentProfileResponseModel {
PaymentProfileItemModel get item;
/// Create a copy of PaymentProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PaymentProfileResponseModelCopyWith<PaymentProfileResponseModel> get copyWith => _$PaymentProfileResponseModelCopyWithImpl<PaymentProfileResponseModel>(this as PaymentProfileResponseModel, _$identity);
/// Serializes this PaymentProfileResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentProfileResponseModel&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,item);
@override
String toString() {
return 'PaymentProfileResponseModel(item: $item)';
}
}
/// @nodoc
abstract mixin class $PaymentProfileResponseModelCopyWith<$Res> {
factory $PaymentProfileResponseModelCopyWith(PaymentProfileResponseModel value, $Res Function(PaymentProfileResponseModel) _then) = _$PaymentProfileResponseModelCopyWithImpl;
@useResult
$Res call({
PaymentProfileItemModel item
});
$PaymentProfileItemModelCopyWith<$Res> get item;
}
/// @nodoc
class _$PaymentProfileResponseModelCopyWithImpl<$Res>
implements $PaymentProfileResponseModelCopyWith<$Res> {
_$PaymentProfileResponseModelCopyWithImpl(this._self, this._then);
final PaymentProfileResponseModel _self;
final $Res Function(PaymentProfileResponseModel) _then;
/// Create a copy of PaymentProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? item = null,}) {
return _then(_self.copyWith(
item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as PaymentProfileItemModel,
));
}
/// Create a copy of PaymentProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$PaymentProfileItemModelCopyWith<$Res> get item {
return $PaymentProfileItemModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [PaymentProfileResponseModel].
extension PaymentProfileResponseModelPatterns on PaymentProfileResponseModel {
/// 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( _PaymentProfileResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _PaymentProfileResponseModel() 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( _PaymentProfileResponseModel value) $default,){
final _that = this;
switch (_that) {
case _PaymentProfileResponseModel():
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( _PaymentProfileResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _PaymentProfileResponseModel() 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( PaymentProfileItemModel item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PaymentProfileResponseModel() when $default != null:
return $default(_that.item);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( PaymentProfileItemModel item) $default,) {final _that = this;
switch (_that) {
case _PaymentProfileResponseModel():
return $default(_that.item);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( PaymentProfileItemModel item)? $default,) {final _that = this;
switch (_that) {
case _PaymentProfileResponseModel() when $default != null:
return $default(_that.item);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _PaymentProfileResponseModel implements PaymentProfileResponseModel {
const _PaymentProfileResponseModel({required this.item});
factory _PaymentProfileResponseModel.fromJson(Map<String, dynamic> json) => _$PaymentProfileResponseModelFromJson(json);
@override final PaymentProfileItemModel item;
/// Create a copy of PaymentProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$PaymentProfileResponseModelCopyWith<_PaymentProfileResponseModel> get copyWith => __$PaymentProfileResponseModelCopyWithImpl<_PaymentProfileResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PaymentProfileResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentProfileResponseModel&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,item);
@override
String toString() {
return 'PaymentProfileResponseModel(item: $item)';
}
}
/// @nodoc
abstract mixin class _$PaymentProfileResponseModelCopyWith<$Res> implements $PaymentProfileResponseModelCopyWith<$Res> {
factory _$PaymentProfileResponseModelCopyWith(_PaymentProfileResponseModel value, $Res Function(_PaymentProfileResponseModel) _then) = __$PaymentProfileResponseModelCopyWithImpl;
@override @useResult
$Res call({
PaymentProfileItemModel item
});
@override $PaymentProfileItemModelCopyWith<$Res> get item;
}
/// @nodoc
class __$PaymentProfileResponseModelCopyWithImpl<$Res>
implements _$PaymentProfileResponseModelCopyWith<$Res> {
__$PaymentProfileResponseModelCopyWithImpl(this._self, this._then);
final _PaymentProfileResponseModel _self;
final $Res Function(_PaymentProfileResponseModel) _then;
/// Create a copy of PaymentProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? item = null,}) {
return _then(_PaymentProfileResponseModel(
item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as PaymentProfileItemModel,
));
}
/// Create a copy of PaymentProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$PaymentProfileItemModelCopyWith<$Res> get item {
return $PaymentProfileItemModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$PaymentProfileItemModel {
String get id; String get userId; String get paymentProfileId; String? get jwt; int get bornAt; String get phone; List<PaymentProfileAddressModel> get taxResidences; List<PaymentProfileAddressModel> get addresses; String get status;//talk with Hector to change those names in the backend to be more consistent with the rest of the fields
// ignore: non_constant_identifier_names
String get KYCStatus;// ignore: non_constant_identifier_names
String get KYCLevel; String get placeOfBirth; String get birthCountry; String get nationality; String get documentType; String get document; String? get paymentWalletId; int get createdAt;
/// Create a copy of PaymentProfileItemModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PaymentProfileItemModelCopyWith<PaymentProfileItemModel> get copyWith => _$PaymentProfileItemModelCopyWithImpl<PaymentProfileItemModel>(this as PaymentProfileItemModel, _$identity);
/// Serializes this PaymentProfileItemModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentProfileItemModel&&(identical(other.id, id) || other.id == id)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.paymentProfileId, paymentProfileId) || other.paymentProfileId == paymentProfileId)&&(identical(other.jwt, jwt) || other.jwt == jwt)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.status, status) || other.status == status)&&(identical(other.KYCStatus, KYCStatus) || other.KYCStatus == KYCStatus)&&(identical(other.KYCLevel, KYCLevel) || other.KYCLevel == KYCLevel)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.nationality, nationality) || other.nationality == nationality)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.document, document) || other.document == document)&&(identical(other.paymentWalletId, paymentWalletId) || other.paymentWalletId == paymentWalletId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,userId,paymentProfileId,jwt,bornAt,phone,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),status,KYCStatus,KYCLevel,placeOfBirth,birthCountry,nationality,documentType,document,paymentWalletId,createdAt);
@override
String toString() {
return 'PaymentProfileItemModel(id: $id, userId: $userId, paymentProfileId: $paymentProfileId, jwt: $jwt, bornAt: $bornAt, phone: $phone, taxResidences: $taxResidences, addresses: $addresses, status: $status, KYCStatus: $KYCStatus, KYCLevel: $KYCLevel, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, nationality: $nationality, documentType: $documentType, document: $document, paymentWalletId: $paymentWalletId, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class $PaymentProfileItemModelCopyWith<$Res> {
factory $PaymentProfileItemModelCopyWith(PaymentProfileItemModel value, $Res Function(PaymentProfileItemModel) _then) = _$PaymentProfileItemModelCopyWithImpl;
@useResult
$Res call({
String id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressModel> taxResidences, List<PaymentProfileAddressModel> addresses, String status, String KYCStatus, String KYCLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt
});
}
/// @nodoc
class _$PaymentProfileItemModelCopyWithImpl<$Res>
implements $PaymentProfileItemModelCopyWith<$Res> {
_$PaymentProfileItemModelCopyWithImpl(this._self, this._then);
final PaymentProfileItemModel _self;
final $Res Function(PaymentProfileItemModel) _then;
/// Create a copy of PaymentProfileItemModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? userId = null,Object? paymentProfileId = null,Object? jwt = freezed,Object? bornAt = null,Object? phone = null,Object? taxResidences = null,Object? addresses = null,Object? status = null,Object? KYCStatus = null,Object? KYCLevel = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? nationality = null,Object? documentType = null,Object? document = null,Object? paymentWalletId = freezed,Object? createdAt = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,paymentProfileId: null == paymentProfileId ? _self.paymentProfileId : paymentProfileId // ignore: cast_nullable_to_non_nullable
as String,jwt: freezed == jwt ? _self.jwt : jwt // ignore: cast_nullable_to_non_nullable
as String?,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self.taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressModel>,addresses: null == addresses ? _self.addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressModel>,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,KYCStatus: null == KYCStatus ? _self.KYCStatus : KYCStatus // ignore: cast_nullable_to_non_nullable
as String,KYCLevel: null == KYCLevel ? _self.KYCLevel : KYCLevel // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,nationality: null == nationality ? _self.nationality : nationality // ignore: cast_nullable_to_non_nullable
as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,document: null == document ? _self.document : document // ignore: cast_nullable_to_non_nullable
as String,paymentWalletId: freezed == paymentWalletId ? _self.paymentWalletId : paymentWalletId // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [PaymentProfileItemModel].
extension PaymentProfileItemModelPatterns on PaymentProfileItemModel {
/// 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( _PaymentProfileItemModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _PaymentProfileItemModel() 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( _PaymentProfileItemModel value) $default,){
final _that = this;
switch (_that) {
case _PaymentProfileItemModel():
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( _PaymentProfileItemModel value)? $default,){
final _that = this;
switch (_that) {
case _PaymentProfileItemModel() 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 id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressModel> taxResidences, List<PaymentProfileAddressModel> addresses, String status, String KYCStatus, String KYCLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PaymentProfileItemModel() when $default != null:
return $default(_that.id,_that.userId,_that.paymentProfileId,_that.jwt,_that.bornAt,_that.phone,_that.taxResidences,_that.addresses,_that.status,_that.KYCStatus,_that.KYCLevel,_that.placeOfBirth,_that.birthCountry,_that.nationality,_that.documentType,_that.document,_that.paymentWalletId,_that.createdAt);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 id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressModel> taxResidences, List<PaymentProfileAddressModel> addresses, String status, String KYCStatus, String KYCLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt) $default,) {final _that = this;
switch (_that) {
case _PaymentProfileItemModel():
return $default(_that.id,_that.userId,_that.paymentProfileId,_that.jwt,_that.bornAt,_that.phone,_that.taxResidences,_that.addresses,_that.status,_that.KYCStatus,_that.KYCLevel,_that.placeOfBirth,_that.birthCountry,_that.nationality,_that.documentType,_that.document,_that.paymentWalletId,_that.createdAt);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 id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressModel> taxResidences, List<PaymentProfileAddressModel> addresses, String status, String KYCStatus, String KYCLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt)? $default,) {final _that = this;
switch (_that) {
case _PaymentProfileItemModel() when $default != null:
return $default(_that.id,_that.userId,_that.paymentProfileId,_that.jwt,_that.bornAt,_that.phone,_that.taxResidences,_that.addresses,_that.status,_that.KYCStatus,_that.KYCLevel,_that.placeOfBirth,_that.birthCountry,_that.nationality,_that.documentType,_that.document,_that.paymentWalletId,_that.createdAt);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _PaymentProfileItemModel implements PaymentProfileItemModel {
const _PaymentProfileItemModel({required this.id, required this.userId, required this.paymentProfileId, this.jwt, required this.bornAt, required this.phone, required final List<PaymentProfileAddressModel> taxResidences, required final List<PaymentProfileAddressModel> addresses, required this.status, required this.KYCStatus, required this.KYCLevel, required this.placeOfBirth, required this.birthCountry, required this.nationality, required this.documentType, required this.document, this.paymentWalletId, required this.createdAt}): _taxResidences = taxResidences,_addresses = addresses;
factory _PaymentProfileItemModel.fromJson(Map<String, dynamic> json) => _$PaymentProfileItemModelFromJson(json);
@override final String id;
@override final String userId;
@override final String paymentProfileId;
@override final String? jwt;
@override final int bornAt;
@override final String phone;
final List<PaymentProfileAddressModel> _taxResidences;
@override List<PaymentProfileAddressModel> get taxResidences {
if (_taxResidences is EqualUnmodifiableListView) return _taxResidences;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_taxResidences);
}
final List<PaymentProfileAddressModel> _addresses;
@override List<PaymentProfileAddressModel> get addresses {
if (_addresses is EqualUnmodifiableListView) return _addresses;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_addresses);
}
@override final String status;
//talk with Hector to change those names in the backend to be more consistent with the rest of the fields
// ignore: non_constant_identifier_names
@override final String KYCStatus;
// ignore: non_constant_identifier_names
@override final String KYCLevel;
@override final String placeOfBirth;
@override final String birthCountry;
@override final String nationality;
@override final String documentType;
@override final String document;
@override final String? paymentWalletId;
@override final int createdAt;
/// Create a copy of PaymentProfileItemModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$PaymentProfileItemModelCopyWith<_PaymentProfileItemModel> get copyWith => __$PaymentProfileItemModelCopyWithImpl<_PaymentProfileItemModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PaymentProfileItemModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentProfileItemModel&&(identical(other.id, id) || other.id == id)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.paymentProfileId, paymentProfileId) || other.paymentProfileId == paymentProfileId)&&(identical(other.jwt, jwt) || other.jwt == jwt)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.status, status) || other.status == status)&&(identical(other.KYCStatus, KYCStatus) || other.KYCStatus == KYCStatus)&&(identical(other.KYCLevel, KYCLevel) || other.KYCLevel == KYCLevel)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.nationality, nationality) || other.nationality == nationality)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.document, document) || other.document == document)&&(identical(other.paymentWalletId, paymentWalletId) || other.paymentWalletId == paymentWalletId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,userId,paymentProfileId,jwt,bornAt,phone,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),status,KYCStatus,KYCLevel,placeOfBirth,birthCountry,nationality,documentType,document,paymentWalletId,createdAt);
@override
String toString() {
return 'PaymentProfileItemModel(id: $id, userId: $userId, paymentProfileId: $paymentProfileId, jwt: $jwt, bornAt: $bornAt, phone: $phone, taxResidences: $taxResidences, addresses: $addresses, status: $status, KYCStatus: $KYCStatus, KYCLevel: $KYCLevel, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, nationality: $nationality, documentType: $documentType, document: $document, paymentWalletId: $paymentWalletId, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class _$PaymentProfileItemModelCopyWith<$Res> implements $PaymentProfileItemModelCopyWith<$Res> {
factory _$PaymentProfileItemModelCopyWith(_PaymentProfileItemModel value, $Res Function(_PaymentProfileItemModel) _then) = __$PaymentProfileItemModelCopyWithImpl;
@override @useResult
$Res call({
String id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressModel> taxResidences, List<PaymentProfileAddressModel> addresses, String status, String KYCStatus, String KYCLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt
});
}
/// @nodoc
class __$PaymentProfileItemModelCopyWithImpl<$Res>
implements _$PaymentProfileItemModelCopyWith<$Res> {
__$PaymentProfileItemModelCopyWithImpl(this._self, this._then);
final _PaymentProfileItemModel _self;
final $Res Function(_PaymentProfileItemModel) _then;
/// Create a copy of PaymentProfileItemModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? userId = null,Object? paymentProfileId = null,Object? jwt = freezed,Object? bornAt = null,Object? phone = null,Object? taxResidences = null,Object? addresses = null,Object? status = null,Object? KYCStatus = null,Object? KYCLevel = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? nationality = null,Object? documentType = null,Object? document = null,Object? paymentWalletId = freezed,Object? createdAt = null,}) {
return _then(_PaymentProfileItemModel(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,paymentProfileId: null == paymentProfileId ? _self.paymentProfileId : paymentProfileId // ignore: cast_nullable_to_non_nullable
as String,jwt: freezed == jwt ? _self.jwt : jwt // ignore: cast_nullable_to_non_nullable
as String?,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self._taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressModel>,addresses: null == addresses ? _self._addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressModel>,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,KYCStatus: null == KYCStatus ? _self.KYCStatus : KYCStatus // ignore: cast_nullable_to_non_nullable
as String,KYCLevel: null == KYCLevel ? _self.KYCLevel : KYCLevel // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,nationality: null == nationality ? _self.nationality : nationality // ignore: cast_nullable_to_non_nullable
as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,document: null == document ? _self.document : document // ignore: cast_nullable_to_non_nullable
as String,paymentWalletId: freezed == paymentWalletId ? _self.paymentWalletId : paymentWalletId // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
mixin _$PaymentProfileAddressModel {
String get street; String get city; String get province; String get state; String get country; int get postCode;
/// Create a copy of PaymentProfileAddressModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PaymentProfileAddressModelCopyWith<PaymentProfileAddressModel> get copyWith => _$PaymentProfileAddressModelCopyWithImpl<PaymentProfileAddressModel>(this as PaymentProfileAddressModel, _$identity);
/// Serializes this PaymentProfileAddressModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentProfileAddressModel&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'PaymentProfileAddressModel(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class $PaymentProfileAddressModelCopyWith<$Res> {
factory $PaymentProfileAddressModelCopyWith(PaymentProfileAddressModel value, $Res Function(PaymentProfileAddressModel) _then) = _$PaymentProfileAddressModelCopyWithImpl;
@useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class _$PaymentProfileAddressModelCopyWithImpl<$Res>
implements $PaymentProfileAddressModelCopyWith<$Res> {
_$PaymentProfileAddressModelCopyWithImpl(this._self, this._then);
final PaymentProfileAddressModel _self;
final $Res Function(PaymentProfileAddressModel) _then;
/// Create a copy of PaymentProfileAddressModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_self.copyWith(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [PaymentProfileAddressModel].
extension PaymentProfileAddressModelPatterns on PaymentProfileAddressModel {
/// 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( _PaymentProfileAddressModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _PaymentProfileAddressModel() 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( _PaymentProfileAddressModel value) $default,){
final _that = this;
switch (_that) {
case _PaymentProfileAddressModel():
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( _PaymentProfileAddressModel value)? $default,){
final _that = this;
switch (_that) {
case _PaymentProfileAddressModel() 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 street, String city, String province, String state, String country, int postCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PaymentProfileAddressModel() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);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 street, String city, String province, String state, String country, int postCode) $default,) {final _that = this;
switch (_that) {
case _PaymentProfileAddressModel():
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);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 street, String city, String province, String state, String country, int postCode)? $default,) {final _that = this;
switch (_that) {
case _PaymentProfileAddressModel() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _PaymentProfileAddressModel implements PaymentProfileAddressModel {
const _PaymentProfileAddressModel({required this.street, required this.city, required this.province, required this.state, required this.country, required this.postCode});
factory _PaymentProfileAddressModel.fromJson(Map<String, dynamic> json) => _$PaymentProfileAddressModelFromJson(json);
@override final String street;
@override final String city;
@override final String province;
@override final String state;
@override final String country;
@override final int postCode;
/// Create a copy of PaymentProfileAddressModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$PaymentProfileAddressModelCopyWith<_PaymentProfileAddressModel> get copyWith => __$PaymentProfileAddressModelCopyWithImpl<_PaymentProfileAddressModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PaymentProfileAddressModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentProfileAddressModel&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'PaymentProfileAddressModel(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class _$PaymentProfileAddressModelCopyWith<$Res> implements $PaymentProfileAddressModelCopyWith<$Res> {
factory _$PaymentProfileAddressModelCopyWith(_PaymentProfileAddressModel value, $Res Function(_PaymentProfileAddressModel) _then) = __$PaymentProfileAddressModelCopyWithImpl;
@override @useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class __$PaymentProfileAddressModelCopyWithImpl<$Res>
implements _$PaymentProfileAddressModelCopyWith<$Res> {
__$PaymentProfileAddressModelCopyWithImpl(this._self, this._then);
final _PaymentProfileAddressModel _self;
final $Res Function(_PaymentProfileAddressModel) _then;
/// Create a copy of PaymentProfileAddressModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_PaymentProfileAddressModel(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -0,0 +1,93 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'payment_profile_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_PaymentProfileResponseModel _$PaymentProfileResponseModelFromJson(
Map<String, dynamic> json,
) => _PaymentProfileResponseModel(
item: PaymentProfileItemModel.fromJson(json['item'] as Map<String, dynamic>),
);
Map<String, dynamic> _$PaymentProfileResponseModelToJson(
_PaymentProfileResponseModel instance,
) => <String, dynamic>{'item': instance.item};
_PaymentProfileItemModel _$PaymentProfileItemModelFromJson(
Map<String, dynamic> json,
) => _PaymentProfileItemModel(
id: json['id'] as String,
userId: json['userId'] as String,
paymentProfileId: json['paymentProfileId'] as String,
jwt: json['jwt'] as String?,
bornAt: (json['bornAt'] as num).toInt(),
phone: json['phone'] as String,
taxResidences: (json['taxResidences'] as List<dynamic>)
.map(
(e) => PaymentProfileAddressModel.fromJson(e as Map<String, dynamic>),
)
.toList(),
addresses: (json['addresses'] as List<dynamic>)
.map(
(e) => PaymentProfileAddressModel.fromJson(e as Map<String, dynamic>),
)
.toList(),
status: json['status'] as String,
KYCStatus: json['KYCStatus'] as String,
KYCLevel: json['KYCLevel'] as String,
placeOfBirth: json['placeOfBirth'] as String,
birthCountry: json['birthCountry'] as String,
nationality: json['nationality'] as String,
documentType: json['documentType'] as String,
document: json['document'] as String,
paymentWalletId: json['paymentWalletId'] as String?,
createdAt: (json['createdAt'] as num).toInt(),
);
Map<String, dynamic> _$PaymentProfileItemModelToJson(
_PaymentProfileItemModel instance,
) => <String, dynamic>{
'id': instance.id,
'userId': instance.userId,
'paymentProfileId': instance.paymentProfileId,
'jwt': instance.jwt,
'bornAt': instance.bornAt,
'phone': instance.phone,
'taxResidences': instance.taxResidences,
'addresses': instance.addresses,
'status': instance.status,
'KYCStatus': instance.KYCStatus,
'KYCLevel': instance.KYCLevel,
'placeOfBirth': instance.placeOfBirth,
'birthCountry': instance.birthCountry,
'nationality': instance.nationality,
'documentType': instance.documentType,
'document': instance.document,
'paymentWalletId': instance.paymentWalletId,
'createdAt': instance.createdAt,
};
_PaymentProfileAddressModel _$PaymentProfileAddressModelFromJson(
Map<String, dynamic> json,
) => _PaymentProfileAddressModel(
street: json['street'] as String,
city: json['city'] as String,
province: json['province'] as String,
state: json['state'] as String,
country: json['country'] as String,
postCode: (json['postCode'] as num).toInt(),
);
Map<String, dynamic> _$PaymentProfileAddressModelToJson(
_PaymentProfileAddressModel instance,
) => <String, dynamic>{
'street': instance.street,
'city': instance.city,
'province': instance.province,
'state': instance.state,
'country': instance.country,
'postCode': instance.postCode,
};

View File

@@ -1,12 +1,14 @@
import 'package:auth/src/core/data/datasource/auth_remote_datasource.dart';
import 'package:auth/src/core/data/mappers/user_model_mapper.dart';
import 'package:auth/src/core/data/models/child_profile_response_model.dart';
import 'package:auth/src/core/data/models/payment_profile_response_model.dart';
import 'package:auth/src/core/data/models/sign_up_request_model.dart';
import 'package:auth/src/core/data/models/sign_up_response_model.dart';
import 'package:auth/src/core/data/models/two_fa_secret_response_model.dart';
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/device_setup/domain/entities/child_profile_entity.dart';
import 'package:auth/src/features/login/domain/entities/login_response_entity.dart';
import 'package:auth/src/features/login/domain/entities/payment_profile_entity.dart';
import 'package:auth/src/features/login/domain/entities/user_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_response_entity.dart';
@@ -64,6 +66,19 @@ class AuthRepositoryImpl implements AuthRepository {
return model.toEntity();
}
@override
Future<void> createWallet() {
return _remote.createWallet();
}
@override
Future<PaymentProfileEntity> getPaymentProfile({
required String userId,
}) async {
final model = await _remote.getPaymentProfile(userId: userId);
return model.toEntity();
}
// @override
// Future<String> totpLogin({required String token, required String code}) {
// return _remote.totpLogin(token: token, code: code);

View File

@@ -2,6 +2,7 @@ import 'package:auth/src/core/data/models/child_profile_response_model.dart';
import 'package:auth/src/core/data/models/two_fa_secret_response_model.dart';
import 'package:auth/src/features/device_setup/domain/entities/child_profile_entity.dart';
import 'package:auth/src/features/login/domain/entities/login_response_entity.dart';
import 'package:auth/src/features/login/domain/entities/payment_profile_entity.dart';
import 'package:auth/src/features/login/domain/entities/user_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_response_entity.dart';
@@ -26,6 +27,10 @@ abstract class AuthRepository {
});
Future<UserEntity> getUserInfo();
Future<void> createWallet();
Future<PaymentProfileEntity> getPaymentProfile({required String userId});
// Future<String> totpLogin({required String token, required String code});
Future<SignUpResponseEntity> signUp({required SignUpRequestEntity request});

View File

@@ -0,0 +1,7 @@
import 'package:auth/src/core/data/datasource/session_local_datasource.dart';
import 'package:auth/src/core/data/datasource/session_local_datasource_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final sessionLocalDatasourceProvider = Provider<SessionLocalDatasource>((ref) {
return SessionLocalDatasourceImpl();
});

View File

@@ -14,8 +14,6 @@ extension AddKidStepMapper on AddKidStep {
return AddKidMainStep.success;
case AddKidStep.intro:
return AddKidMainStep.linkDevice;
case AddKidStep.sca:
return AddKidMainStep.success;
}
}
}

View File

@@ -4,6 +4,7 @@ import 'package:auth/src/features/device_setup/presentation/enums/add_kid_main_s
import 'package:auth/src/features/device_setup/presentation/enums/add_kid_step.dart';
import 'package:auth/src/features/device_setup/presentation/step_body.dart';
import 'package:auth/src/features/device_setup/presentation/widgets/flow_footer.dart';
import 'package:auth/src/features/sca_treezor/sca_pin_view.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -74,23 +75,24 @@ class DeviceSetupScreen extends ConsumerWidget {
),
FlowFooter(
error: state.errorMessage,
error: context.translate(state.errorMessage),
primaryText: context.translate(primaryButtonText(state.step)),
secondaryText: state.step == AddKidStep.success
? context.translate(I18n.deviceSetup_addAnotherKid)
: null,
onPrimary: () async {
onPrimary: () {
if (state.step == AddKidStep.success) {
navigationContract.pushTo(AppRoutes.dashboardHome);
return;
}
if (state.step == AddKidStep.sca) {
await vm.createChildProfile();
if (state.step == AddKidStep.profile) {
if (!vm.validateProfile()) return;
_pushScaPinScreen(context, ref);
return;
}
vm.next();
},
onSecondary: state.step == AddKidStep.success ? () {} : null,
onSecondary: state.step == AddKidStep.success ? vm.resetForNewKid : null,
theme: theme,
),
],
@@ -100,6 +102,14 @@ class DeviceSetupScreen extends ConsumerWidget {
);
}
void _pushScaPinScreen(BuildContext context, WidgetRef ref) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => _ScaPinScreen(navigationContract: navigationContract),
),
);
}
String primaryButtonText(AddKidStep step) {
switch (step) {
case AddKidStep.intro:
@@ -111,3 +121,44 @@ class DeviceSetupScreen extends ConsumerWidget {
}
}
}
class _ScaPinScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const _ScaPinScreen({required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(deviceSetupViewModelProvider);
final vm = ref.read(deviceSetupViewModelProvider.notifier);
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Center(
child: ScaPinView(
title: 'Introduce tu PIN para firmar',
pin: state.pin,
isProcessing: state.isSigning || state.isLoading,
processingText:
state.isSigning ? 'Firmando...' : 'Creando perfil...',
canSubmit:
vm.canSubmitPin && !state.isSigning && !state.isLoading,
submitText: 'Confirmar',
errorMessage: state.errorMessage.isNotEmpty
? context.translate(state.errorMessage)
: null,
onDigitPressed: vm.onDigitPressed,
onBackspacePressed: vm.onBackspacePressed,
onClearPin: vm.clearPin,
onSubmit: () async {
await vm.createChildProfile(pin: state.pin);
if (!context.mounted) return;
Navigator.of(context).pop();
},
),
),
),
);
}
}

View File

@@ -1 +1 @@
enum AddKidStep { intro, linkInfo, scanStrap, scanWatch, profile, success, sca }
enum AddKidStep { intro, linkInfo, scanStrap, scanWatch, profile, success }

View File

@@ -75,7 +75,7 @@ class _QrScannerScreenState extends State<QrScannerScreen> {
Positioned(
left: 0,
right: 0,
bottom: 26,
bottom: 50,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Container(

View File

@@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:sca_treezor/sca_treezor.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:uuid/uuid.dart';
final deviceSetupViewModelProvider =
@@ -77,34 +78,27 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
state = state.copyWith(step: AddKidStep.scanStrap);
return;
case AddKidStep.scanStrap:
// if (state.strapQr.isEmpty) {
// state = state.copyWith(
// errorMessage: 'Escanea la correa para continuar',
// );
// return;
// }
state = state.copyWith(step: AddKidStep.scanWatch);
final hasStrap = state.strapQr.isNotEmpty || state.strapCode.isNotEmpty;
if (!hasStrap) {
state = state.copyWith(
errorMessage: I18n.errorScanStrapRequired,
);
return;
}
state = state.copyWith(step: AddKidStep.scanWatch, errorMessage: '');
return;
case AddKidStep.scanWatch:
// final hasWatch = state.watchQr.isNotEmpty || state.watchCode.isNotEmpty;
// if (!hasWatch) {
// state = state.copyWith(
// errorMessage: 'Escanea el reloj o introduce el código',
// );
// return;
// }
state = state.copyWith(step: AddKidStep.profile);
final hasWatch = state.watchQr.isNotEmpty || state.watchCode.isNotEmpty;
if (!hasWatch) {
state = state.copyWith(
errorMessage: I18n.errorScanWatchRequired,
);
return;
}
state = state.copyWith(step: AddKidStep.profile, errorMessage: '');
return;
case AddKidStep.profile:
final isValid = _validateProfile();
if (!isValid) return;
state = state.copyWith(step: AddKidStep.sca);
return;
case AddKidStep.sca:
state = state.copyWith(step: AddKidStep.success);
return;
case AddKidStep.success:
return;
}
@@ -127,9 +121,6 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
case AddKidStep.profile:
state = state.copyWith(step: AddKidStep.scanWatch);
return;
case AddKidStep.sca:
state = state.copyWith(step: AddKidStep.profile);
return;
case AddKidStep.success:
state = state.copyWith(step: AddKidStep.profile);
return;
@@ -174,9 +165,10 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
state = state.copyWith(bornAt: date);
}
Future<void> createChildProfile() async {
Future<bool> createChildProfile({required String pin}) async {
state = state.copyWith(pin: pin);
await generateJwsWithPin();
// if (!_validateProfile()) return;
if (state.lastSignature.isEmpty) return false;
await getUserInfo();
final firstName = state.firstName.trim();
final lastName = state.lastName.trim();
@@ -202,17 +194,19 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
scaProof: state.lastSignature,
);
if (!ref.mounted) return;
if (!ref.mounted) return false;
state = state.copyWith(
isLoading: false,
isSuccess: true,
step: AddKidStep.success,
);
return true;
} catch (e) {
if (!ref.mounted) return;
if (!ref.mounted) return false;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
return false;
}
}
@@ -238,7 +232,7 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
}
}
bool _validateProfile() {
bool validateProfile() {
final isInvalid =
state.firstName.trim().isEmpty ||
state.lastName.trim().isEmpty ||
@@ -248,7 +242,7 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
state.relationType.trim().isEmpty;
if (isInvalid) {
state = state.copyWith(errorMessage: 'Completa todos los campos');
state = state.copyWith(errorMessage: I18n.errorAllFieldsRequired);
return false;
}
return true;
@@ -367,7 +361,7 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
if (state.isSigning) return;
if (!canSubmitPin) {
state = state.copyWith(errorMessage: '⚠️ PIN de 6 dígitos');
state = state.copyWith(errorMessage: I18n.errorPinRequired);
return;
}
@@ -402,12 +396,23 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
state = state.copyWith(
isSigning: false,
isSuccess: false,
errorMessage: ' Error firmando operación sensible: $e',
errorMessage: '${I18n.errorSigningOperation}: $e',
pin: '',
);
}
}
void resetForNewKid() {
firstNameController.clear();
lastNameController.clear();
bornAtController.clear();
addressController.clear();
strapCodeController.clear();
watchCodeController.clear();
state = DeviceSetupViewState(id: const Uuid().v4());
}
void disposeControllers() {
// firstNameController.dispose();
// lastNameController.dispose();

View File

@@ -4,7 +4,6 @@ import 'package:auth/src/features/device_setup/presentation/enums/scan_link_step
import 'package:auth/src/features/device_setup/presentation/steps/intro_step.dart';
import 'package:auth/src/features/device_setup/presentation/steps/link_info_step.dart';
import 'package:auth/src/features/device_setup/presentation/steps/profile_step.dart';
import 'package:auth/src/features/device_setup/presentation/steps/sca_step.dart';
import 'package:auth/src/features/device_setup/presentation/steps/scan_strap_and_watch_step.dart';
import 'package:auth/src/features/device_setup/presentation/steps/success_step.dart';
import 'package:flutter/material.dart';
@@ -26,8 +25,6 @@ class StepBody extends StatelessWidget {
return ScanStrapAndWatchStepScreen(step: ScanLinkStep.watch);
case AddKidStep.profile:
return ProfileStepScreen();
case AddKidStep.sca:
return SCAScreenStep();
case AddKidStep.success:
return SuccessStepScreen();
}

View File

@@ -1,212 +0,0 @@
import 'package:auth/src/features/device_setup/presentation/state/device_setup_view_model.dart';
import 'package:auth/src/features/device_setup/presentation/state/device_setup_view_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SCAScreenStep extends ConsumerWidget {
const SCAScreenStep({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(deviceSetupViewModelProvider);
final vm = ref.read(deviceSetupViewModelProvider.notifier);
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: _SignatureBody(state: state, vm: vm),
),
),
);
}
}
class _PinDots extends StatelessWidget {
final int length;
final int max;
const _PinDots({required this.length, required this.max});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(max, (i) {
final filled = i < length;
return AnimatedContainer(
duration: const Duration(milliseconds: 120),
curve: Curves.easeOut,
width: 12,
height: 12,
margin: const EdgeInsets.symmetric(horizontal: 7),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: filled ? Colors.black : const Color(0xFFD1D1D6),
),
);
}),
);
}
}
class _DialPad extends StatelessWidget {
final void Function(String digit) onDigitPressed;
const _DialPad({
required this.onDigitPressed,
required void Function() onBackspacePressed,
});
static const _keys = <_DialKey>[
_DialKey('1'),
_DialKey('2'),
_DialKey('3'),
_DialKey('4'),
_DialKey('5'),
_DialKey('6'),
_DialKey('7'),
_DialKey('8'),
_DialKey('9'),
_DialKey(''),
_DialKey('0'),
];
@override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _keys.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 30,
),
itemBuilder: (_, index) {
final key = _keys[index];
if (key.digit.isEmpty) {
return const SizedBox.shrink();
}
return Center(
child: _DialButton(
digit: key.digit,
onTap: () => onDigitPressed(key.digit),
),
);
},
);
}
}
class _DialButton extends StatefulWidget {
final String digit;
final VoidCallback onTap;
const _DialButton({required this.digit, required this.onTap});
@override
State<_DialButton> createState() => _DialButtonState();
}
class _DialButtonState extends State<_DialButton> {
bool _pressed = false;
void _setPressed(bool value) {
if (_pressed == value) return;
setState(() => _pressed = value);
}
@override
Widget build(BuildContext context) {
const double size = 56;
final bg = _pressed ? Colors.grey : Colors.white;
return GestureDetector(
onTapDown: (_) => _setPressed(true),
onTapCancel: () => _setPressed(false),
onTapUp: (_) => _setPressed(false),
onTap: () {
HapticFeedback.lightImpact();
widget.onTap();
},
child: SizedBox(
width: size,
height: size,
child: AnimatedContainer(
duration: const Duration(milliseconds: 120),
curve: Curves.easeOut,
decoration: BoxDecoration(shape: BoxShape.circle, color: bg),
child: Center(
child: Text(
widget.digit,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
),
),
);
}
}
class _DialKey {
final String digit;
const _DialKey(this.digit);
}
class _SignatureBody extends StatelessWidget {
final DeviceSetupViewState state;
final DeviceSetupViewModel vm;
const _SignatureBody({required this.state, required this.vm});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 18),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Introduce tu PIN para firmar:'),
const SizedBox(height: 12),
_PinDots(length: state.pin.length, max: 6),
const SizedBox(height: 12),
_DialPad(
onDigitPressed: vm.onDigitPressed,
onBackspacePressed: vm.onBackspacePressed,
),
const SizedBox(height: 16),
if (state.isSigning) ...[
const CircularProgressIndicator(),
const SizedBox(height: 8),
const Text('Firmando...'),
] else ...[
// ElevatedButton(
// onPressed: vm.canSubmitPin ? vm.generateJwsWithPin : null,
// child: const Text('Firmar SCA para JWT'),
// ),
// const SizedBox(height: 8),
],
const SizedBox(height: 16),
const SizedBox(height: 8),
TextButton(onPressed: vm.clearPin, child: const Text('Borrar PIN')),
],
),
);
}
}

View File

@@ -0,0 +1,40 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'payment_profile_entity.freezed.dart';
@freezed
abstract class PaymentProfileEntity with _$PaymentProfileEntity {
const factory PaymentProfileEntity({
required String id,
required String userId,
required String paymentProfileId,
String? jwt,
required int bornAt,
required String phone,
required List<PaymentProfileAddressEntity> taxResidences,
required List<PaymentProfileAddressEntity> addresses,
required String status,
required String kycStatus,
required String kycLevel,
required String placeOfBirth,
required String birthCountry,
required String nationality,
required String documentType,
required String document,
String? paymentWalletId,
required int createdAt,
}) = _PaymentProfileEntity;
}
@freezed
abstract class PaymentProfileAddressEntity
with _$PaymentProfileAddressEntity {
const factory PaymentProfileAddressEntity({
required String street,
required String city,
required String province,
required String state,
required String country,
required int postCode,
}) = _PaymentProfileAddressEntity;
}

View File

@@ -0,0 +1,606 @@
// 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 'payment_profile_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$PaymentProfileEntity {
String get id; String get userId; String get paymentProfileId; String? get jwt; int get bornAt; String get phone; List<PaymentProfileAddressEntity> get taxResidences; List<PaymentProfileAddressEntity> get addresses; String get status; String get kycStatus; String get kycLevel; String get placeOfBirth; String get birthCountry; String get nationality; String get documentType; String get document; String? get paymentWalletId; int get createdAt;
/// Create a copy of PaymentProfileEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PaymentProfileEntityCopyWith<PaymentProfileEntity> get copyWith => _$PaymentProfileEntityCopyWithImpl<PaymentProfileEntity>(this as PaymentProfileEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentProfileEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.paymentProfileId, paymentProfileId) || other.paymentProfileId == paymentProfileId)&&(identical(other.jwt, jwt) || other.jwt == jwt)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.status, status) || other.status == status)&&(identical(other.kycStatus, kycStatus) || other.kycStatus == kycStatus)&&(identical(other.kycLevel, kycLevel) || other.kycLevel == kycLevel)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.nationality, nationality) || other.nationality == nationality)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.document, document) || other.document == document)&&(identical(other.paymentWalletId, paymentWalletId) || other.paymentWalletId == paymentWalletId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@override
int get hashCode => Object.hash(runtimeType,id,userId,paymentProfileId,jwt,bornAt,phone,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),status,kycStatus,kycLevel,placeOfBirth,birthCountry,nationality,documentType,document,paymentWalletId,createdAt);
@override
String toString() {
return 'PaymentProfileEntity(id: $id, userId: $userId, paymentProfileId: $paymentProfileId, jwt: $jwt, bornAt: $bornAt, phone: $phone, taxResidences: $taxResidences, addresses: $addresses, status: $status, kycStatus: $kycStatus, kycLevel: $kycLevel, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, nationality: $nationality, documentType: $documentType, document: $document, paymentWalletId: $paymentWalletId, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class $PaymentProfileEntityCopyWith<$Res> {
factory $PaymentProfileEntityCopyWith(PaymentProfileEntity value, $Res Function(PaymentProfileEntity) _then) = _$PaymentProfileEntityCopyWithImpl;
@useResult
$Res call({
String id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressEntity> taxResidences, List<PaymentProfileAddressEntity> addresses, String status, String kycStatus, String kycLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt
});
}
/// @nodoc
class _$PaymentProfileEntityCopyWithImpl<$Res>
implements $PaymentProfileEntityCopyWith<$Res> {
_$PaymentProfileEntityCopyWithImpl(this._self, this._then);
final PaymentProfileEntity _self;
final $Res Function(PaymentProfileEntity) _then;
/// Create a copy of PaymentProfileEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? userId = null,Object? paymentProfileId = null,Object? jwt = freezed,Object? bornAt = null,Object? phone = null,Object? taxResidences = null,Object? addresses = null,Object? status = null,Object? kycStatus = null,Object? kycLevel = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? nationality = null,Object? documentType = null,Object? document = null,Object? paymentWalletId = freezed,Object? createdAt = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,paymentProfileId: null == paymentProfileId ? _self.paymentProfileId : paymentProfileId // ignore: cast_nullable_to_non_nullable
as String,jwt: freezed == jwt ? _self.jwt : jwt // ignore: cast_nullable_to_non_nullable
as String?,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self.taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressEntity>,addresses: null == addresses ? _self.addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressEntity>,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,kycStatus: null == kycStatus ? _self.kycStatus : kycStatus // ignore: cast_nullable_to_non_nullable
as String,kycLevel: null == kycLevel ? _self.kycLevel : kycLevel // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,nationality: null == nationality ? _self.nationality : nationality // ignore: cast_nullable_to_non_nullable
as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,document: null == document ? _self.document : document // ignore: cast_nullable_to_non_nullable
as String,paymentWalletId: freezed == paymentWalletId ? _self.paymentWalletId : paymentWalletId // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [PaymentProfileEntity].
extension PaymentProfileEntityPatterns on PaymentProfileEntity {
/// 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( _PaymentProfileEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _PaymentProfileEntity() 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( _PaymentProfileEntity value) $default,){
final _that = this;
switch (_that) {
case _PaymentProfileEntity():
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( _PaymentProfileEntity value)? $default,){
final _that = this;
switch (_that) {
case _PaymentProfileEntity() 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 id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressEntity> taxResidences, List<PaymentProfileAddressEntity> addresses, String status, String kycStatus, String kycLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PaymentProfileEntity() when $default != null:
return $default(_that.id,_that.userId,_that.paymentProfileId,_that.jwt,_that.bornAt,_that.phone,_that.taxResidences,_that.addresses,_that.status,_that.kycStatus,_that.kycLevel,_that.placeOfBirth,_that.birthCountry,_that.nationality,_that.documentType,_that.document,_that.paymentWalletId,_that.createdAt);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 id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressEntity> taxResidences, List<PaymentProfileAddressEntity> addresses, String status, String kycStatus, String kycLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt) $default,) {final _that = this;
switch (_that) {
case _PaymentProfileEntity():
return $default(_that.id,_that.userId,_that.paymentProfileId,_that.jwt,_that.bornAt,_that.phone,_that.taxResidences,_that.addresses,_that.status,_that.kycStatus,_that.kycLevel,_that.placeOfBirth,_that.birthCountry,_that.nationality,_that.documentType,_that.document,_that.paymentWalletId,_that.createdAt);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 id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressEntity> taxResidences, List<PaymentProfileAddressEntity> addresses, String status, String kycStatus, String kycLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt)? $default,) {final _that = this;
switch (_that) {
case _PaymentProfileEntity() when $default != null:
return $default(_that.id,_that.userId,_that.paymentProfileId,_that.jwt,_that.bornAt,_that.phone,_that.taxResidences,_that.addresses,_that.status,_that.kycStatus,_that.kycLevel,_that.placeOfBirth,_that.birthCountry,_that.nationality,_that.documentType,_that.document,_that.paymentWalletId,_that.createdAt);case _:
return null;
}
}
}
/// @nodoc
class _PaymentProfileEntity implements PaymentProfileEntity {
const _PaymentProfileEntity({required this.id, required this.userId, required this.paymentProfileId, this.jwt, required this.bornAt, required this.phone, required final List<PaymentProfileAddressEntity> taxResidences, required final List<PaymentProfileAddressEntity> addresses, required this.status, required this.kycStatus, required this.kycLevel, required this.placeOfBirth, required this.birthCountry, required this.nationality, required this.documentType, required this.document, this.paymentWalletId, required this.createdAt}): _taxResidences = taxResidences,_addresses = addresses;
@override final String id;
@override final String userId;
@override final String paymentProfileId;
@override final String? jwt;
@override final int bornAt;
@override final String phone;
final List<PaymentProfileAddressEntity> _taxResidences;
@override List<PaymentProfileAddressEntity> get taxResidences {
if (_taxResidences is EqualUnmodifiableListView) return _taxResidences;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_taxResidences);
}
final List<PaymentProfileAddressEntity> _addresses;
@override List<PaymentProfileAddressEntity> get addresses {
if (_addresses is EqualUnmodifiableListView) return _addresses;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_addresses);
}
@override final String status;
@override final String kycStatus;
@override final String kycLevel;
@override final String placeOfBirth;
@override final String birthCountry;
@override final String nationality;
@override final String documentType;
@override final String document;
@override final String? paymentWalletId;
@override final int createdAt;
/// Create a copy of PaymentProfileEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$PaymentProfileEntityCopyWith<_PaymentProfileEntity> get copyWith => __$PaymentProfileEntityCopyWithImpl<_PaymentProfileEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentProfileEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.paymentProfileId, paymentProfileId) || other.paymentProfileId == paymentProfileId)&&(identical(other.jwt, jwt) || other.jwt == jwt)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.status, status) || other.status == status)&&(identical(other.kycStatus, kycStatus) || other.kycStatus == kycStatus)&&(identical(other.kycLevel, kycLevel) || other.kycLevel == kycLevel)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.nationality, nationality) || other.nationality == nationality)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.document, document) || other.document == document)&&(identical(other.paymentWalletId, paymentWalletId) || other.paymentWalletId == paymentWalletId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@override
int get hashCode => Object.hash(runtimeType,id,userId,paymentProfileId,jwt,bornAt,phone,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),status,kycStatus,kycLevel,placeOfBirth,birthCountry,nationality,documentType,document,paymentWalletId,createdAt);
@override
String toString() {
return 'PaymentProfileEntity(id: $id, userId: $userId, paymentProfileId: $paymentProfileId, jwt: $jwt, bornAt: $bornAt, phone: $phone, taxResidences: $taxResidences, addresses: $addresses, status: $status, kycStatus: $kycStatus, kycLevel: $kycLevel, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, nationality: $nationality, documentType: $documentType, document: $document, paymentWalletId: $paymentWalletId, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class _$PaymentProfileEntityCopyWith<$Res> implements $PaymentProfileEntityCopyWith<$Res> {
factory _$PaymentProfileEntityCopyWith(_PaymentProfileEntity value, $Res Function(_PaymentProfileEntity) _then) = __$PaymentProfileEntityCopyWithImpl;
@override @useResult
$Res call({
String id, String userId, String paymentProfileId, String? jwt, int bornAt, String phone, List<PaymentProfileAddressEntity> taxResidences, List<PaymentProfileAddressEntity> addresses, String status, String kycStatus, String kycLevel, String placeOfBirth, String birthCountry, String nationality, String documentType, String document, String? paymentWalletId, int createdAt
});
}
/// @nodoc
class __$PaymentProfileEntityCopyWithImpl<$Res>
implements _$PaymentProfileEntityCopyWith<$Res> {
__$PaymentProfileEntityCopyWithImpl(this._self, this._then);
final _PaymentProfileEntity _self;
final $Res Function(_PaymentProfileEntity) _then;
/// Create a copy of PaymentProfileEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? userId = null,Object? paymentProfileId = null,Object? jwt = freezed,Object? bornAt = null,Object? phone = null,Object? taxResidences = null,Object? addresses = null,Object? status = null,Object? kycStatus = null,Object? kycLevel = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? nationality = null,Object? documentType = null,Object? document = null,Object? paymentWalletId = freezed,Object? createdAt = null,}) {
return _then(_PaymentProfileEntity(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,paymentProfileId: null == paymentProfileId ? _self.paymentProfileId : paymentProfileId // ignore: cast_nullable_to_non_nullable
as String,jwt: freezed == jwt ? _self.jwt : jwt // ignore: cast_nullable_to_non_nullable
as String?,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self._taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressEntity>,addresses: null == addresses ? _self._addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<PaymentProfileAddressEntity>,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,kycStatus: null == kycStatus ? _self.kycStatus : kycStatus // ignore: cast_nullable_to_non_nullable
as String,kycLevel: null == kycLevel ? _self.kycLevel : kycLevel // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,nationality: null == nationality ? _self.nationality : nationality // ignore: cast_nullable_to_non_nullable
as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,document: null == document ? _self.document : document // ignore: cast_nullable_to_non_nullable
as String,paymentWalletId: freezed == paymentWalletId ? _self.paymentWalletId : paymentWalletId // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
mixin _$PaymentProfileAddressEntity {
String get street; String get city; String get province; String get state; String get country; int get postCode;
/// Create a copy of PaymentProfileAddressEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PaymentProfileAddressEntityCopyWith<PaymentProfileAddressEntity> get copyWith => _$PaymentProfileAddressEntityCopyWithImpl<PaymentProfileAddressEntity>(this as PaymentProfileAddressEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentProfileAddressEntity&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'PaymentProfileAddressEntity(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class $PaymentProfileAddressEntityCopyWith<$Res> {
factory $PaymentProfileAddressEntityCopyWith(PaymentProfileAddressEntity value, $Res Function(PaymentProfileAddressEntity) _then) = _$PaymentProfileAddressEntityCopyWithImpl;
@useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class _$PaymentProfileAddressEntityCopyWithImpl<$Res>
implements $PaymentProfileAddressEntityCopyWith<$Res> {
_$PaymentProfileAddressEntityCopyWithImpl(this._self, this._then);
final PaymentProfileAddressEntity _self;
final $Res Function(PaymentProfileAddressEntity) _then;
/// Create a copy of PaymentProfileAddressEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_self.copyWith(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [PaymentProfileAddressEntity].
extension PaymentProfileAddressEntityPatterns on PaymentProfileAddressEntity {
/// 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( _PaymentProfileAddressEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _PaymentProfileAddressEntity() 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( _PaymentProfileAddressEntity value) $default,){
final _that = this;
switch (_that) {
case _PaymentProfileAddressEntity():
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( _PaymentProfileAddressEntity value)? $default,){
final _that = this;
switch (_that) {
case _PaymentProfileAddressEntity() 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 street, String city, String province, String state, String country, int postCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PaymentProfileAddressEntity() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);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 street, String city, String province, String state, String country, int postCode) $default,) {final _that = this;
switch (_that) {
case _PaymentProfileAddressEntity():
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);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 street, String city, String province, String state, String country, int postCode)? $default,) {final _that = this;
switch (_that) {
case _PaymentProfileAddressEntity() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
return null;
}
}
}
/// @nodoc
class _PaymentProfileAddressEntity implements PaymentProfileAddressEntity {
const _PaymentProfileAddressEntity({required this.street, required this.city, required this.province, required this.state, required this.country, required this.postCode});
@override final String street;
@override final String city;
@override final String province;
@override final String state;
@override final String country;
@override final int postCode;
/// Create a copy of PaymentProfileAddressEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$PaymentProfileAddressEntityCopyWith<_PaymentProfileAddressEntity> get copyWith => __$PaymentProfileAddressEntityCopyWithImpl<_PaymentProfileAddressEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentProfileAddressEntity&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'PaymentProfileAddressEntity(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class _$PaymentProfileAddressEntityCopyWith<$Res> implements $PaymentProfileAddressEntityCopyWith<$Res> {
factory _$PaymentProfileAddressEntityCopyWith(_PaymentProfileAddressEntity value, $Res Function(_PaymentProfileAddressEntity) _then) = __$PaymentProfileAddressEntityCopyWithImpl;
@override @useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class __$PaymentProfileAddressEntityCopyWithImpl<$Res>
implements _$PaymentProfileAddressEntityCopyWith<$Res> {
__$PaymentProfileAddressEntityCopyWithImpl(this._self, this._then);
final _PaymentProfileAddressEntity _self;
final $Res Function(_PaymentProfileAddressEntity) _then;
/// Create a copy of PaymentProfileAddressEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_PaymentProfileAddressEntity(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -226,12 +226,10 @@ class LoginViewModel extends Notifier<LoginViewState> {
try {
final user = await _getUserInfoUseCase.getUserInfo();
if (ref.mounted) {
state = state.copyWith(isLoading: false);
}
debugPrint('[getUserInfo] userId => ${user.id}');
return user;
} catch (e) {
if (ref.mounted) {

View File

@@ -0,0 +1,242 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class ScaPinView extends StatelessWidget {
final String title;
final String pin;
final bool isProcessing;
final String processingText;
final bool canSubmit;
final String submitText;
final void Function(String digit) onDigitPressed;
final VoidCallback onBackspacePressed;
final VoidCallback onClearPin;
final VoidCallback onSubmit;
final String? errorMessage;
const ScaPinView({
super.key,
required this.title,
required this.pin,
required this.isProcessing,
this.processingText = 'Procesando...',
required this.canSubmit,
this.submitText = 'Confirmar',
required this.onDigitPressed,
required this.onBackspacePressed,
required this.onClearPin,
required this.onSubmit,
this.errorMessage,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 18),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 17),
),
const SizedBox(height: 18),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 44),
_PinDots(length: pin.length, max: 6),
const SizedBox(width: 12),
SizedBox(
width: 44,
height: 44,
child: IconButton(
onPressed: pin.isEmpty ? null : onBackspacePressed,
icon: const Icon(CupertinoIcons.delete_left),
splashRadius: 20,
color: Colors.black87,
),
),
],
),
_DialPad(onDigitPressed: onDigitPressed),
if (isProcessing) ...[
const CupertinoActivityIndicator(radius: 12),
Text(
processingText,
style: const TextStyle(fontSize: 14, color: Colors.black54),
),
] else ...[
IgnorePointer(
ignoring: !canSubmit,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 150),
opacity: canSubmit ? 1 : 0.35,
child: GestureDetector(
onTap: onSubmit,
child: Container(
width: 74,
height: 74,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF34C759),
boxShadow: [
BoxShadow(
blurRadius: 18,
offset: Offset(0, 8),
color: Color(0x22000000),
),
],
),
child: Center(
child: Text(
submitText,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
),
),
),
),
],
const SizedBox(height: 10),
TextButton(
onPressed: pin.isEmpty ? null : onClearPin,
child: const Text('Borrar PIN'),
),
if (errorMessage != null && errorMessage!.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
errorMessage!,
style: const TextStyle(color: Colors.red, fontSize: 13),
textAlign: TextAlign.center,
),
],
],
),
);
}
}
class _PinDots extends StatelessWidget {
final int length;
final int max;
const _PinDots({required this.length, required this.max});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(max, (i) {
final filled = i < length;
return AnimatedContainer(
duration: const Duration(milliseconds: 120),
curve: Curves.easeOut,
width: 12,
height: 12,
margin: const EdgeInsets.symmetric(horizontal: 7),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: filled ? Colors.black : const Color(0xFFD1D1D6),
),
);
}),
);
}
}
class _DialPad extends StatelessWidget {
final void Function(String digit) onDigitPressed;
const _DialPad({required this.onDigitPressed});
static const _digits = [
'1', '2', '3',
'4', '5', '6',
'7', '8', '9',
'', '0', '',
];
@override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _digits.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 30,
),
itemBuilder: (_, index) {
final digit = _digits[index];
if (digit.isEmpty) return const SizedBox.shrink();
return Center(
child: _DialButton(
digit: digit,
onTap: () => onDigitPressed(digit),
),
);
},
);
}
}
class _DialButton extends StatefulWidget {
final String digit;
final VoidCallback onTap;
const _DialButton({required this.digit, required this.onTap});
@override
State<_DialButton> createState() => _DialButtonState();
}
class _DialButtonState extends State<_DialButton> {
bool _pressed = false;
void _setPressed(bool value) {
if (_pressed == value) return;
setState(() => _pressed = value);
}
@override
Widget build(BuildContext context) {
const double size = 56;
final bg = _pressed ? Colors.grey : Colors.white;
return GestureDetector(
onTapDown: (_) => _setPressed(true),
onTapCancel: () => _setPressed(false),
onTapUp: (_) => _setPressed(false),
onTap: () {
HapticFeedback.lightImpact();
widget.onTap();
},
child: SizedBox(
width: size,
height: size,
child: AnimatedContainer(
duration: const Duration(milliseconds: 120),
curve: Curves.easeOut,
decoration: BoxDecoration(shape: BoxShape.circle, color: bg),
child: Center(
child: Text(
widget.digit,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
),
),
);
}
}

View File

@@ -1,10 +1,10 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/app_routes.dart';
import 'package:navigation/navigation_contract.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'sca_pin_view.dart';
import 'sca_treezor_view_model.dart';
import 'sca_treezor_view_state.dart';
@@ -18,44 +18,6 @@ class SCATreezorScreen extends ConsumerWidget {
final SCATreezorViewState state = ref.watch(scaTreezorViewModelProvider);
final vm = ref.read(scaTreezorViewModelProvider.notifier);
ref.listen(scaTreezorViewModelProvider.select((s) => s.errorMessage), (
prev,
next,
) {
if (next.isEmpty) return;
if (prev == next) return;
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(next)));
});
ref.listen(scaTreezorViewModelProvider.select((s) => s.success), (
prev,
next,
) {
if (next == false) return;
if (prev == next) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
state.isConnected
? '✅ Wallet conectado'
: '✅ Wallet provisionado exitosamente',
),
),
);
});
ref.listen<String>(
scaTreezorViewModelProvider.select((s) => s.lastSignature),
(prev, next) {
final hadSignature = (prev ?? '').isNotEmpty;
final hasSignature = next.isNotEmpty;
if (!hadSignature && hasSignature) {
navigationContract.goTo(AppRoutes.deviceSetup);
}
},
);
return Scaffold(
backgroundColor: Colors.white,
body: Center(
@@ -64,10 +26,30 @@ class SCATreezorScreen extends ConsumerWidget {
child: state.isLoading
? const CircularProgressIndicator()
: state.isProvisioned
? _ConnectionBody(
state: state,
vm: vm,
navigationContract: navigationContract,
? ScaPinView(
title: state.isFirstConnectionDone
? 'Introduce tu PIN para conectar'
: 'Crea tu PIN para conectar tu wallet',
pin: state.pin,
isProcessing: state.isConnecting || state.isSigning,
processingText: state.isConnecting
? 'Conectando...'
: 'Firmando...',
canSubmit: vm.canSubmitPin && !state.isConnecting,
submitText: 'Conectar',
errorMessage: state.errorMessage.isNotEmpty
? context.translate(state.errorMessage)
: null,
onDigitPressed: vm.onDigitPressed,
onBackspacePressed: vm.onBackspacePressed,
onClearPin: vm.clearPin,
onSubmit: () async {
final success = await vm.connectAndSignJwtSca();
if (!context.mounted) return;
if (success) {
navigationContract.goTo(AppRoutes.deviceSetup);
}
},
)
: _ProvisioningBody(state: state, vm: vm),
),
@@ -88,6 +70,14 @@ class _ProvisioningBody extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 16),
if (state.errorMessage.isNotEmpty) ...[
Text(
context.translate(state.errorMessage),
style: const TextStyle(color: Colors.red, fontSize: 13),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
],
if (state.isProvisioning) ...[
const CircularProgressIndicator(),
const SizedBox(height: 8),
@@ -102,257 +92,3 @@ class _ProvisioningBody extends StatelessWidget {
);
}
}
class _ConnectionBody extends StatelessWidget {
final SCATreezorViewState state;
final SCATreezorViewModel vm;
const _ConnectionBody({
required this.state,
required this.vm,
required NavigationContract navigationContract,
});
@override
Widget build(BuildContext context) {
final title = state.isFirstConnectionDone
? 'Introduce tu PIN para conectar'
: 'Crea tu PIN para conectar tu wallet';
final canSubmit = vm.canSubmitPin && !state.isConnecting;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 18),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 17),
),
const SizedBox(height: 18),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 44),
_PinDots(length: state.pin.length, max: 6),
const SizedBox(width: 12),
SizedBox(
width: 44,
height: 44,
child: IconButton(
onPressed: state.pin.isEmpty ? null : vm.onBackspacePressed,
icon: const Icon(CupertinoIcons.delete_left),
splashRadius: 20,
color: Colors.black87,
),
),
],
),
_DialPad(
onDigitPressed: vm.onDigitPressed,
onBackspacePressed: vm.onBackspacePressed,
),
if (state.isConnecting) ...[
const CupertinoActivityIndicator(radius: 12),
const Text(
'Conectando...',
style: TextStyle(fontSize: 14, color: Colors.black54),
),
] else if (state.isSigning) ...[
const CircularProgressIndicator(),
const SizedBox(height: 8),
const Text('Firmando...'),
] else ...[
IgnorePointer(
ignoring: !canSubmit,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 150),
opacity: canSubmit ? 1 : 0.35,
child: GestureDetector(
onTap: vm.connectAndSignJwtSca,
child: Container(
width: 74,
height: 74,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF34C759),
boxShadow: [
BoxShadow(
blurRadius: 18,
offset: Offset(0, 8),
color: Color(0x22000000),
),
],
),
child: Center(
child: const Text(
'Conectar',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
),
),
),
),
],
const SizedBox(height: 10),
TextButton(
onPressed: state.pin.isEmpty ? null : vm.clearPin,
child: const Text('Borrar PIN'),
),
],
),
);
}
}
class _PinDots extends StatelessWidget {
final int length;
final int max;
const _PinDots({required this.length, required this.max});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(max, (i) {
final filled = i < length;
return AnimatedContainer(
duration: const Duration(milliseconds: 120),
curve: Curves.easeOut,
width: 12,
height: 12,
margin: const EdgeInsets.symmetric(horizontal: 7),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: filled ? Colors.black : const Color(0xFFD1D1D6),
),
);
}),
);
}
}
class _DialPad extends StatelessWidget {
final void Function(String digit) onDigitPressed;
const _DialPad({
required this.onDigitPressed,
required void Function() onBackspacePressed,
});
static const _keys = <_DialKey>[
_DialKey('1'),
_DialKey('2'),
_DialKey('3'),
_DialKey('4'),
_DialKey('5'),
_DialKey('6'),
_DialKey('7'),
_DialKey('8'),
_DialKey('9'),
_DialKey(''),
_DialKey('0'),
];
@override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _keys.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 30,
),
itemBuilder: (_, index) {
final key = _keys[index];
if (key.digit.isEmpty) {
return const SizedBox.shrink();
}
return Center(
child: _DialButton(
digit: key.digit,
onTap: () => onDigitPressed(key.digit),
),
);
},
);
}
}
class _DialButton extends StatefulWidget {
final String digit;
final VoidCallback onTap;
const _DialButton({required this.digit, required this.onTap});
@override
State<_DialButton> createState() => _DialButtonState();
}
class _DialButtonState extends State<_DialButton> {
bool _pressed = false;
void _setPressed(bool value) {
if (_pressed == value) return;
setState(() => _pressed = value);
}
@override
Widget build(BuildContext context) {
const double size = 56;
final bg = _pressed ? Colors.grey : Colors.white;
return GestureDetector(
onTapDown: (_) => _setPressed(true),
onTapCancel: () => _setPressed(false),
onTapUp: (_) => _setPressed(false),
onTap: () {
HapticFeedback.lightImpact();
widget.onTap();
},
child: SizedBox(
width: size,
height: size,
child: AnimatedContainer(
duration: const Duration(milliseconds: 120),
curve: Curves.easeOut,
decoration: BoxDecoration(shape: BoxShape.circle, color: bg),
child: Center(
child: Text(
widget.digit,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
),
),
);
}
}
class _DialKey {
final String digit;
const _DialKey(this.digit);
}

View File

@@ -4,8 +4,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:sca_treezor/sca_treezor.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:auth/src/core/data/datasource/session_local_datasource.dart';
import 'package:auth/src/core/providers/session_local_datasource_provider.dart';
import '../../core/domain/repositories/auth_repository.dart';
import '../../core/providers/auth_repository_provider.dart';
import 'sca_treezor_view_state.dart';
@@ -25,6 +31,8 @@ class SCATreezorViewModel extends Notifier<SCATreezorViewState> {
late final TreezorWalletProvisioningService _provisioningService;
late final TreezorWalletConnectionService _connectionService;
late final TreezorWalletSignatureService _signatureService;
late final AuthRepository _authRepository;
late final SessionLocalDatasource _sessionLocal;
late final TextEditingController codeController;
@@ -43,7 +51,8 @@ class SCATreezorViewModel extends Notifier<SCATreezorViewState> {
_provisioningService = GetIt.I<TreezorWalletProvisioningService>();
_connectionService = GetIt.I<TreezorWalletConnectionService>();
_signatureService = GetIt.I<TreezorWalletSignatureService>();
_authRepository = ref.read(authRepositoryProvider);
_sessionLocal = ref.read(sessionLocalDatasourceProvider);
codeController = TextEditingController();
codeController.addListener(_onCodeChanged);
@@ -133,7 +142,7 @@ class SCATreezorViewModel extends Notifier<SCATreezorViewState> {
final activationCode = state.activationCode.trim();
if (activationCode.isEmpty) {
state = state.copyWith(errorMessage: '⚠️ Activation code vacío');
state = state.copyWith(errorMessage: I18n.errorActivationCodeEmpty);
return;
}
@@ -203,14 +212,12 @@ class SCATreezorViewModel extends Notifier<SCATreezorViewState> {
if (state.isConnecting) return false;
if (!state.isProvisioned) {
state = state.copyWith(errorMessage: '⚠️ No está provisionado aún');
state = state.copyWith(errorMessage: I18n.errorWalletNotProvisioned);
return false;
}
if (!canSubmitPin) {
state = state.copyWith(
errorMessage: '⚠️ El PIN debe tener $_pinLength dígitos',
);
state = state.copyWith(errorMessage: I18n.errorPinRequired);
return false;
}
@@ -262,12 +269,12 @@ class SCATreezorViewModel extends Notifier<SCATreezorViewState> {
if (state.isSigning) return;
if (state.isConnected == false) {
state = state.copyWith(errorMessage: '⚠️ Conecta la wallet primero');
state = state.copyWith(errorMessage: I18n.errorWalletConnectFirst);
return;
}
if (!canSubmitPin) {
state = state.copyWith(errorMessage: '⚠️ PIN de $_pinLength dígitos');
state = state.copyWith(errorMessage: I18n.errorPinRequired);
return;
}
@@ -303,13 +310,30 @@ class SCATreezorViewModel extends Notifier<SCATreezorViewState> {
}
}
Future<void> connectAndSignJwtSca() async {
if (state.isConnecting || state.isSigning) return;
Future<bool> connectAndSignJwtSca() async {
if (state.isConnecting || state.isSigning) return false;
final connected = await connectWithPin();
if (!connected) return;
if (!connected) return false;
await signJwtSca();
final user = await _authRepository.getUserInfo();
final paymentProfile = await _authRepository.getPaymentProfile(
userId: user.id,
);
debugPrint(
'[connectWithPin] paymentWalletId => ${paymentProfile.paymentWalletId}',
);
if (paymentProfile.paymentWalletId == null ||
paymentProfile.paymentWalletId!.isEmpty) {
await _authRepository.createWallet();
await _sessionLocal.savePaymentProfileId(paymentProfile.paymentWalletId!);
}
// remove this when the backend starts returning the walletId on getUserInfo or getPaymentProfile
await _sessionLocal.savePaymentProfileId(paymentProfile.paymentWalletId!);
return state.lastSignature.isNotEmpty;
}
String _sanitizeActivationCode(String code) {

View File

@@ -7,6 +7,7 @@ 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:sf_localizations/sf_localizations.dart';
class SignupScreen extends ConsumerWidget {
final NavigationContract navigationContract;
@@ -54,7 +55,7 @@ class SignupScreen extends ConsumerWidget {
currentStep: index + 1,
numSteps: steps.length,
body: step.bodyBuilder(context, ref),
errorMessage: state.errorMessage,
errorMessage: context.translate(state.errorMessage),
onBackPressed: state.currentIndex == 0
? navigationContract.goBack
: vm.back,

View File

@@ -401,22 +401,22 @@ class SignUpViewModel extends Notifier<SignUpViewState> {
errorMessage: '',
);
if (state.firstName.trim().isEmpty) {
state = state.copyWith(errorMessage: 'El nombre es obligatorio');
state = state.copyWith(errorMessage: I18n.errorFirstNameRequired);
return false;
}
if (state.lastName.trim().isEmpty) {
state = state.copyWith(errorMessage: 'El apellido es obligatorio');
state = state.copyWith(errorMessage: I18n.errorLastNameRequired);
return false;
}
if (state.documentType.trim().isEmpty) {
state = state.copyWith(
errorMessage: 'El tipo de documento es obligatorio',
errorMessage: I18n.errorDocumentTypeRequired,
);
return false;
}
if (state.document.trim().isEmpty) {
state = state.copyWith(
errorMessage: 'El número de documento es obligatorio',
errorMessage: I18n.errorDocumentNumberRequired,
);
return false;
}
@@ -430,7 +430,7 @@ class SignUpViewModel extends Notifier<SignUpViewState> {
}
if (!state.acceptTerms) {
state = state.copyWith(
errorMessage: 'Debes aceptar los términos y condiciones',
errorMessage: I18n.errorAcceptTerms,
);
return false;
}
@@ -445,27 +445,27 @@ class SignUpViewModel extends Notifier<SignUpViewState> {
}
if (state.bornAt == null) {
state = state.copyWith(
errorMessage: 'Selecciona una fecha de nacimiento válida (DD/MM/AAAA)',
errorMessage: I18n.errorBirthDateRequired,
);
return false;
}
if (state.relationType.trim().isEmpty) {
state = state.copyWith(errorMessage: 'La relación es obligatoria');
state = state.copyWith(errorMessage: I18n.errorRelationshipRequired);
return false;
}
if (state.placeOfBirth.trim().isEmpty) {
state = state.copyWith(errorMessage: 'Falta el lugar de nacimiento');
state = state.copyWith(errorMessage: I18n.errorPlaceOfBirthRequired);
return false;
}
if (state.birthCountry.trim().isEmpty) {
state = state.copyWith(errorMessage: 'Falta el país de nacimiento');
state = state.copyWith(errorMessage: I18n.errorBirthCountryRequired);
return false;
}
if (!_isAddressValid(state.address)) {
state = state.copyWith(errorMessage: 'Completa la dirección');
state = state.copyWith(errorMessage: I18n.errorAddressRequired);
return false;
}

View File

@@ -0,0 +1,3 @@
abstract class SplashRemoteDatasource {
Future<List<dynamic>> getChildProfiles();
}

View File

@@ -0,0 +1,19 @@
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'splash_remote_datasource.dart';
class SplashRemoteDatasourceImpl implements SplashRemoteDatasource {
SplashRemoteDatasourceImpl(this._repository);
final QuestiaRepository _repository;
@override
Future<List<dynamic>> getChildProfiles() async {
final response =
await _repository.get<Map<String, dynamic>>('/child-profiles');
final data = response.data;
if (data == null) return [];
final items = data['items'] ?? data['data'] ?? [];
return items is List ? items : [];
}
}

View File

@@ -0,0 +1,5 @@
import 'initial_route.dart';
abstract class CheckSessionUseCase {
Future<InitialRoute> execute();
}

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import '../data/splash_remote_datasource.dart';
import 'check_session_use_case.dart';
import 'initial_route.dart';
class CheckSessionUseCaseImpl implements CheckSessionUseCase {
CheckSessionUseCaseImpl(this._datasource);
final SplashRemoteDatasource _datasource;
@override
Future<InitialRoute> execute() async {
try {
final profiles = await _datasource.getChildProfiles();
return profiles.isEmpty ? InitialRoute.deviceSetup : InitialRoute.home;
} catch (e) {
debugPrint('[CheckSessionUseCase] error: $e');
return InitialRoute.login;
}
}
}

View File

@@ -0,0 +1 @@
enum InitialRoute { login, deviceSetup, home }

View File

@@ -4,10 +4,18 @@ import 'package:flutter/material.dart';
import 'package:navigation/app_routes.dart';
import 'package:navigation/navigation_contract.dart';
import 'package:page_transition/page_transition.dart';
import 'package:splash/src/domain/check_session_use_case.dart';
import 'package:splash/src/domain/initial_route.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key, required this.navigationContract});
const SplashScreen({
super.key,
required this.navigationContract,
required this.checkSessionUseCase,
});
final NavigationContract navigationContract;
final CheckSessionUseCase checkSessionUseCase;
@override
State<SplashScreen> createState() => _SplashScreenState();
@@ -21,9 +29,22 @@ class _SplashScreenState extends State<SplashScreen> {
}
Future<void> nextRoute() async {
await Future.delayed(const Duration(milliseconds: 3500));
final results = await Future.wait([
widget.checkSessionUseCase.execute(),
Future.delayed(const Duration(milliseconds: 3500)),
]);
if (!mounted) return;
widget.navigationContract.goTo(AppRoutes.onboarding);
final route = results[0] as InitialRoute;
switch (route) {
case InitialRoute.login:
widget.navigationContract.goTo(AppRoutes.login);
case InitialRoute.deviceSetup:
widget.navigationContract.goTo(AppRoutes.deviceSetup);
case InitialRoute.home:
widget.navigationContract.goTo(AppRoutes.dashboardHome);
}
}
@override

View File

@@ -2,16 +2,24 @@ import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:navigation/navigation_contract.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:splash/src/data/splash_remote_datasource_impl.dart';
import 'package:splash/src/domain/check_session_use_case_impl.dart';
import 'package:splash/src/presentation/splash_screen.dart';
class SplashBuilder {
const SplashBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
final navigationContract = GetIt.I<NavigationContract>();
final datasource = SplashRemoteDatasourceImpl(GetIt.I<QuestiaRepository>());
final checkSessionUseCase = CheckSessionUseCaseImpl(datasource);
return NoTransitionPage(
child: SplashScreen(navigationContract: navigationContract),
child: SplashScreen(
navigationContract: navigationContract,
checkSessionUseCase: checkSessionUseCase,
),
);
}
}

View File

@@ -17,6 +17,8 @@ dependencies:
page_transition: ^2.2.1
navigation:
path: ../../packages/navigation
sf_infrastructure:
path: ../../packages/sf_infrastructure
dev_dependencies:
flutter_test:
sdk: flutter

View File

@@ -7,6 +7,6 @@
<versions>
<version>2.6.4</version>
</versions>
<lastUpdated>20260127000000</lastUpdated>
<lastUpdated>20260210000000</lastUpdated>
</versioning>
</metadata>

View File

@@ -1 +1 @@
d3de74b51e78f073e96d4f8f7e37e091
d77d08495d6aeaec19114b634957c84c

View File

@@ -1 +1 @@
7bc6215d7d14b8165f428c6cffca46f51c260d44
bdcbc45dfb9b2a6ccb485f2e1114aeef96a11ce7

View File

@@ -12,14 +12,13 @@ export 'src/repositories/questia_repository.dart';
final getIt = GetIt.instance;
Future<void> configureDependencies(EnvConfig env, {bool log = false}) async {
getIt.registerLazySingleton<Dio>(
() => buildDioClient(
baseUrl: env.apiBaseUrl,
origin: env.apiOrigin,
// apiKey: env.apiKey,
log: log,
),
final dio = await buildDioClient(
baseUrl: env.apiBaseUrl,
origin: env.apiOrigin,
log: log,
);
getIt.registerLazySingleton<Dio>(() => dio);
getIt.registerLazySingleton<QuestiaApi>(() => QuestiaApi(getIt<Dio>()));
getIt.registerLazySingleton<QuestiaRepository>(
() => QuestiaRepositoryImpl(getIt<QuestiaApi>()),

View File

@@ -1,13 +1,14 @@
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:path_provider/path_provider.dart';
Dio buildDioClient({
Future<Dio> buildDioClient({
required String baseUrl,
required String origin,
bool log = false,
CookieJar? cookieJar,
}) {
}) async {
final dio = Dio(
BaseOptions(
baseUrl: baseUrl,
@@ -22,7 +23,7 @@ Dio buildDioClient({
),
);
final jar = cookieJar ?? CookieJar();
final jar = cookieJar ?? await _buildPersistCookieJar();
dio.interceptors.add(CookieManager(jar));
if (log) {
@@ -40,3 +41,8 @@ Dio buildDioClient({
return dio;
}
Future<PersistCookieJar> _buildPersistCookieJar() async {
final dir = await getApplicationDocumentsDirectory();
return PersistCookieJar(storage: FileStorage('${dir.path}/.cookies/'));
}

View File

@@ -148,7 +148,7 @@
"deviceSetup_linkInfo_item2_prefix": "Scanne die ",
"deviceSetup_linkInfo_item2_boldWord": "Uhr",
"deviceSetup_linkInfo_item2_subtitle": "Du kannst die getätigten Ausgaben sehen",
"deviceSetup_watchCode_orInsert": "Oder gib den Code der Uhr ein",
"deviceSetup_watchCode_orInsert": "Oder gib den Code",
"deviceSetup_watchCode_continueWithCode": "Mit Code fortfahren",
"deviceSetup_linkTroubleshoot_title": "Wenn du das Armband oder die Uhr nicht verbinden kannst",
"deviceSetup_contactUs": "Kontaktiere uns",
@@ -159,5 +159,24 @@
"deviceSetup_start": "Los geht's!",
"deviceSetup_giveFirstAllowance": "Gib das erste Taschengeld",
"deviceSetup_scanQr": "QR scannen",
"deviceSetup_scanQr_hint": "Richte den QR-Code innerhalb des Rahmens aus"
"deviceSetup_scanQr_hint": "Richte den QR-Code innerhalb des Rahmens aus",
"errorScanStrapRequired": "Scanne das Armband oder gib den Code ein, um fortzufahren",
"errorScanWatchRequired": "Scanne die Uhr oder gib den Code ein, um fortzufahren",
"errorAllFieldsRequired": "Bitte fülle alle Felder aus",
"errorPinRequired": "Die PIN muss 6 Ziffern haben",
"errorSigningOperation": "Fehler beim Signieren des Vorgangs",
"errorFirstNameRequired": "Vorname ist erforderlich",
"errorLastNameRequired": "Nachname ist erforderlich",
"errorDocumentTypeRequired": "Dokumenttyp ist erforderlich",
"errorDocumentNumberRequired": "Dokumentnummer ist erforderlich",
"errorAcceptTerms": "Du musst die Allgemeinen Geschäftsbedingungen akzeptieren",
"errorBirthDateRequired": "Wähle ein gültiges Geburtsdatum (TT/MM/JJJJ)",
"errorRelationshipRequired": "Die Beziehung ist erforderlich",
"errorPlaceOfBirthRequired": "Der Geburtsort fehlt",
"errorBirthCountryRequired": "Das Geburtsland fehlt",
"errorAddressRequired": "Bitte vervollständige die Adresse",
"errorActivationCodeEmpty": "Aktivierungscode ist leer",
"errorWalletNotProvisioned": "Wallet ist noch nicht provisioniert",
"errorPinLength": "Die PIN muss {length} Ziffern haben",
"errorWalletConnectFirst": "Verbinde zuerst die Wallet"
}

View File

@@ -148,7 +148,7 @@
"deviceSetup_linkInfo_item2_prefix": "Scan the ",
"deviceSetup_linkInfo_item2_boldWord": "watch",
"deviceSetup_linkInfo_item2_subtitle": "You'll be able to see the expenses made",
"deviceSetup_watchCode_orInsert": "Or enter the watch code",
"deviceSetup_watchCode_orInsert": "Or enter code",
"deviceSetup_watchCode_continueWithCode": "Continue with code",
"deviceSetup_linkTroubleshoot_title": "If you can't link their band or watch",
"deviceSetup_contactUs": "Contact us",
@@ -159,5 +159,24 @@
"deviceSetup_start": "Start!",
"deviceSetup_giveFirstAllowance": "Give their first allowance",
"deviceSetup_scanQr": "Scan QR",
"deviceSetup_scanQr_hint": "Center the QR inside the frame"
"deviceSetup_scanQr_hint": "Center the QR inside the frame",
"errorScanStrapRequired": "Scan the band or enter the code to continue",
"errorScanWatchRequired": "Scan the watch or enter the code to continue",
"errorAllFieldsRequired": "Please fill in all fields",
"errorPinRequired": "PIN must be 6 digits",
"errorSigningOperation": "Error signing operation",
"errorFirstNameRequired": "First name is required",
"errorLastNameRequired": "Last name is required",
"errorDocumentTypeRequired": "Document type is required",
"errorDocumentNumberRequired": "Document number is required",
"errorAcceptTerms": "You must accept the terms and conditions",
"errorBirthDateRequired": "Select a valid date of birth (DD/MM/YYYY)",
"errorRelationshipRequired": "Relationship is required",
"errorPlaceOfBirthRequired": "Place of birth is required",
"errorBirthCountryRequired": "Country of birth is required",
"errorAddressRequired": "Please complete the address",
"errorActivationCodeEmpty": "Activation code is empty",
"errorWalletNotProvisioned": "Wallet is not provisioned yet",
"errorPinLength": "PIN must be {length} digits",
"errorWalletConnectFirst": "Connect the wallet first"
}

View File

@@ -148,7 +148,7 @@
"deviceSetup_linkInfo_item2_prefix": "Escanea el ",
"deviceSetup_linkInfo_item2_boldWord": "reloj",
"deviceSetup_linkInfo_item2_subtitle": "Visualizarás los gastos que se hagan",
"deviceSetup_watchCode_orInsert": "O inserta el código del reloj",
"deviceSetup_watchCode_orInsert": "O inserta el código",
"deviceSetup_watchCode_continueWithCode": "Continuar con código",
"deviceSetup_linkTroubleshoot_title": "Si no consigues vincular su correa o reloj",
"deviceSetup_contactUs": "Contáctanos",
@@ -159,5 +159,24 @@
"deviceSetup_start": "¡Empezar!",
"deviceSetup_giveFirstAllowance": "Dale su primera paga",
"deviceSetup_scanQr": "Escanear QR",
"deviceSetup_scanQr_hint": "Centra el QR dentro del recuadro"
"deviceSetup_scanQr_hint": "Centra el QR dentro del recuadro",
"errorScanStrapRequired": "Escanea la correa o introduce el código para continuar",
"errorScanWatchRequired": "Escanea el reloj o introduce el código para continuar",
"errorAllFieldsRequired": "Completa todos los campos",
"errorPinRequired": "El PIN debe tener 6 dígitos",
"errorSigningOperation": "Error firmando operación sensible",
"errorFirstNameRequired": "El nombre es obligatorio",
"errorLastNameRequired": "El apellido es obligatorio",
"errorDocumentTypeRequired": "El tipo de documento es obligatorio",
"errorDocumentNumberRequired": "El número de documento es obligatorio",
"errorAcceptTerms": "Debes aceptar los términos y condiciones",
"errorBirthDateRequired": "Selecciona una fecha de nacimiento válida (DD/MM/AAAA)",
"errorRelationshipRequired": "La relación es obligatoria",
"errorPlaceOfBirthRequired": "Falta el lugar de nacimiento",
"errorBirthCountryRequired": "Falta el país de nacimiento",
"errorAddressRequired": "Completa la dirección",
"errorActivationCodeEmpty": "El código de activación está vacío",
"errorWalletNotProvisioned": "No está provisionado aún",
"errorPinLength": "El PIN debe tener {length} dígitos",
"errorWalletConnectFirst": "Conecta la wallet primero"
}

View File

@@ -148,7 +148,7 @@
"deviceSetup_linkInfo_item2_prefix": "Scanne la ",
"deviceSetup_linkInfo_item2_boldWord": "montre",
"deviceSetup_linkInfo_item2_subtitle": "Vous verrez les dépenses effectuées",
"deviceSetup_watchCode_orInsert": "Ou saisissez le code de la montre",
"deviceSetup_watchCode_orInsert": "Ou saisissez le code",
"deviceSetup_watchCode_continueWithCode": "Continuer avec un code",
"deviceSetup_linkTroubleshoot_title": "Si vous n'arrivez pas à associer son bracelet ou sa montre",
"deviceSetup_contactUs": "Contactez-nous",
@@ -159,5 +159,24 @@
"deviceSetup_start": "Commencer!",
"deviceSetup_giveFirstAllowance": "Donner sa première allocation",
"deviceSetup_scanQr": "Scanner le QR",
"deviceSetup_scanQr_hint": "Place le QR au centre du cadre"
"deviceSetup_scanQr_hint": "Place le QR au centre du cadre",
"errorScanStrapRequired": "Scannez le bracelet ou saisissez le code pour continuer",
"errorScanWatchRequired": "Scannez la montre ou saisissez le code pour continuer",
"errorAllFieldsRequired": "Veuillez remplir tous les champs",
"errorPinRequired": "Le PIN doit contenir 6 chiffres",
"errorSigningOperation": "Erreur lors de la signature de l'opération",
"errorFirstNameRequired": "Le prénom est obligatoire",
"errorLastNameRequired": "Le nom est obligatoire",
"errorDocumentTypeRequired": "Le type de document est obligatoire",
"errorDocumentNumberRequired": "Le numéro de document est obligatoire",
"errorAcceptTerms": "Vous devez accepter les conditions générales",
"errorBirthDateRequired": "Sélectionnez une date de naissance valide (JJ/MM/AAAA)",
"errorRelationshipRequired": "Le lien de parenté est obligatoire",
"errorPlaceOfBirthRequired": "Le lieu de naissance est requis",
"errorBirthCountryRequired": "Le pays de naissance est requis",
"errorAddressRequired": "Veuillez compléter l'adresse",
"errorActivationCodeEmpty": "Le code d'activation est vide",
"errorWalletNotProvisioned": "Le portefeuille n'est pas encore provisionné",
"errorPinLength": "Le PIN doit contenir {length} chiffres",
"errorWalletConnectFirst": "Connectez d'abord le portefeuille"
}

View File

@@ -148,7 +148,7 @@
"deviceSetup_linkInfo_item2_prefix": "Scansiona l'",
"deviceSetup_linkInfo_item2_boldWord": "orologio",
"deviceSetup_linkInfo_item2_subtitle": "Potrai visualizzare le spese effettuate",
"deviceSetup_watchCode_orInsert": "Oppure inserisci il codice dell'orologio",
"deviceSetup_watchCode_orInsert": "Oppure inserisci il codice",
"deviceSetup_watchCode_continueWithCode": "Continua con il codice",
"deviceSetup_linkTroubleshoot_title": "Se non riesci a collegare il cinturino o l'orologio",
"deviceSetup_contactUs": "Contactez-nous",
@@ -159,5 +159,24 @@
"deviceSetup_start": "Inizia!",
"deviceSetup_giveFirstAllowance": "Dagli la sua prima paghetta",
"deviceSetup_scanQr": "Scansiona QR",
"deviceSetup_scanQr_hint": "Centra il QR allinterno del riquadro"
"deviceSetup_scanQr_hint": "Centra il QR all'interno del riquadro",
"errorScanStrapRequired": "Scansiona il cinturino o inserisci il codice per continuare",
"errorScanWatchRequired": "Scansiona l'orologio o inserisci il codice per continuare",
"errorAllFieldsRequired": "Compila tutti i campi",
"errorPinRequired": "Il PIN deve avere 6 cifre",
"errorSigningOperation": "Errore durante la firma dell'operazione",
"errorFirstNameRequired": "Il nome è obbligatorio",
"errorLastNameRequired": "Il cognome è obbligatorio",
"errorDocumentTypeRequired": "Il tipo di documento è obbligatorio",
"errorDocumentNumberRequired": "Il numero di documento è obbligatorio",
"errorAcceptTerms": "Devi accettare i termini e condizioni",
"errorBirthDateRequired": "Seleziona una data di nascita valida (GG/MM/AAAA)",
"errorRelationshipRequired": "Il rapporto di parentela è obbligatorio",
"errorPlaceOfBirthRequired": "Manca il luogo di nascita",
"errorBirthCountryRequired": "Manca il paese di nascita",
"errorAddressRequired": "Completa l'indirizzo",
"errorActivationCodeEmpty": "Il codice di attivazione è vuoto",
"errorWalletNotProvisioned": "Il portafoglio non è ancora provisionato",
"errorPinLength": "Il PIN deve avere {length} cifre",
"errorWalletConnectFirst": "Connetti prima il portafoglio"
}

View File

@@ -148,7 +148,7 @@
"deviceSetup_linkInfo_item2_prefix": "Digitaliza o ",
"deviceSetup_linkInfo_item2_boldWord": "relógio",
"deviceSetup_linkInfo_item2_subtitle": "Poderás visualizar os gastos efetuados",
"deviceSetup_watchCode_orInsert": "Ou introduz o código do relógio",
"deviceSetup_watchCode_orInsert": "Ou introduz o código",
"deviceSetup_watchCode_continueWithCode": "Continuar com código",
"deviceSetup_linkTroubleshoot_title": "Se não conseguires vincular a pulseira ou o relógio",
"deviceSetup_contactUs": "Contacta-nos",
@@ -159,5 +159,24 @@
"deviceSetup_start": "Começar!",
"deviceSetup_giveFirstAllowance": "Dá-lhe a primeira mesada",
"deviceSetup_scanQr": "Digitalizar QR",
"deviceSetup_scanQr_hint": "Centraliza o QR dentro da moldura"
"deviceSetup_scanQr_hint": "Centraliza o QR dentro da moldura",
"errorScanStrapRequired": "Digitaliza a pulseira ou introduz o código para continuar",
"errorScanWatchRequired": "Digitaliza o relógio ou introduz o código para continuar",
"errorAllFieldsRequired": "Preenche todos os campos",
"errorPinRequired": "O PIN deve ter 6 dígitos",
"errorSigningOperation": "Erro ao assinar a operação",
"errorFirstNameRequired": "O nome é obrigatório",
"errorLastNameRequired": "O apelido é obrigatório",
"errorDocumentTypeRequired": "O tipo de documento é obrigatório",
"errorDocumentNumberRequired": "O número de documento é obrigatório",
"errorAcceptTerms": "Deves aceitar os termos e condições",
"errorBirthDateRequired": "Seleciona uma data de nascimento válida (DD/MM/AAAA)",
"errorRelationshipRequired": "O grau de parentesco é obrigatório",
"errorPlaceOfBirthRequired": "Falta o local de nascimento",
"errorBirthCountryRequired": "Falta o país de nascimento",
"errorAddressRequired": "Completa a morada",
"errorActivationCodeEmpty": "O código de ativação está vazio",
"errorWalletNotProvisioned": "A carteira ainda não está provisionada",
"errorPinLength": "O PIN deve ter {length} dígitos",
"errorWalletConnectFirst": "Conecta a carteira primeiro"
}

View File

@@ -207,4 +207,24 @@ class I18n {
'deviceSetup_giveFirstAllowance';
static const String deviceSetup_scanQr = 'deviceSetup_scanQr';
static const String deviceSetup_scanQr_hint = 'deviceSetup_scanQr_hint';
static const String errorScanStrapRequired = 'errorScanStrapRequired';
static const String errorScanWatchRequired = 'errorScanWatchRequired';
static const String errorAllFieldsRequired = 'errorAllFieldsRequired';
static const String errorPinRequired = 'errorPinRequired';
static const String errorSigningOperation = 'errorSigningOperation';
static const String errorFirstNameRequired = 'errorFirstNameRequired';
static const String errorLastNameRequired = 'errorLastNameRequired';
static const String errorDocumentTypeRequired = 'errorDocumentTypeRequired';
static const String errorDocumentNumberRequired =
'errorDocumentNumberRequired';
static const String errorAcceptTerms = 'errorAcceptTerms';
static const String errorBirthDateRequired = 'errorBirthDateRequired';
static const String errorRelationshipRequired = 'errorRelationshipRequired';
static const String errorPlaceOfBirthRequired = 'errorPlaceOfBirthRequired';
static const String errorBirthCountryRequired = 'errorBirthCountryRequired';
static const String errorAddressRequired = 'errorAddressRequired';
static const String errorActivationCodeEmpty = 'errorActivationCodeEmpty';
static const String errorWalletNotProvisioned = 'errorWalletNotProvisioned';
static const String errorPinLength = 'errorPinLength';
static const String errorWalletConnectFirst = 'errorWalletConnectFirst';
}