treezor , sca proff and create child profile

This commit is contained in:
2026-01-27 02:28:52 +03:00
parent ddbfbc1ebf
commit 9d1bb6c22a
107 changed files with 5705 additions and 469 deletions

View File

@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"flutter_secure_storage_macos","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"flutter_secure_storage_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"flutter_secure_storage_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[{"name":"flutter_secure_storage_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2026-01-13 12:16:52.485090","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"flutter_secure_storage_macos","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"flutter_secure_storage_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"flutter_secure_storage_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[{"name":"flutter_secure_storage_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2026-01-22 07:50:55.192120","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}

1
.idea/modules.xml generated
View File

@@ -12,6 +12,7 @@
<module fileurl="file://$PROJECT_DIR$/packages/navigation/melos_navigation.iml" filepath="$PROJECT_DIR$/packages/navigation/melos_navigation.iml" /> <module fileurl="file://$PROJECT_DIR$/packages/navigation/melos_navigation.iml" filepath="$PROJECT_DIR$/packages/navigation/melos_navigation.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/notifications/melos_notifications.iml" filepath="$PROJECT_DIR$/modules/notifications/melos_notifications.iml" /> <module fileurl="file://$PROJECT_DIR$/modules/notifications/melos_notifications.iml" filepath="$PROJECT_DIR$/modules/notifications/melos_notifications.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/profile/melos_profile.iml" filepath="$PROJECT_DIR$/modules/profile/melos_profile.iml" /> <module fileurl="file://$PROJECT_DIR$/modules/profile/melos_profile.iml" filepath="$PROJECT_DIR$/modules/profile/melos_profile.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/sca_treezor/melos_sca_treezor.iml" filepath="$PROJECT_DIR$/packages/sca_treezor/melos_sca_treezor.iml" />
<module fileurl="file://$PROJECT_DIR$/apps/mobile_app/melos_sf_app_platform.iml" filepath="$PROJECT_DIR$/apps/mobile_app/melos_sf_app_platform.iml" /> <module fileurl="file://$PROJECT_DIR$/apps/mobile_app/melos_sf_app_platform.iml" filepath="$PROJECT_DIR$/apps/mobile_app/melos_sf_app_platform.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/sf_infrastructure/melos_sf_infrastructure.iml" filepath="$PROJECT_DIR$/packages/sf_infrastructure/melos_sf_infrastructure.iml" /> <module fileurl="file://$PROJECT_DIR$/packages/sf_infrastructure/melos_sf_infrastructure.iml" filepath="$PROJECT_DIR$/packages/sf_infrastructure/melos_sf_infrastructure.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/sf_localizations/melos_sf_localizations.iml" filepath="$PROJECT_DIR$/packages/sf_localizations/melos_sf_localizations.iml" /> <module fileurl="file://$PROJECT_DIR$/packages/sf_localizations/melos_sf_localizations.iml" filepath="$PROJECT_DIR$/packages/sf_localizations/melos_sf_localizations.iml" />

View File

@@ -0,0 +1,7 @@
<!-- Generated by Melos -->
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Flutter Test -&gt; 'sca_treezor'" type="FlutterTestConfigType" factoryName="Flutter Test">
<option name="testDir" value="$PROJECT_DIR$/packages/sca_treezor/test" />
<method v="2" />
</configuration>
</component>

View File

@@ -12,6 +12,9 @@ PODS:
- FlutterMacOS - FlutterMacOS
- permission_handler_apple (9.3.0): - permission_handler_apple (9.3.0):
- Flutter - Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
DEPENDENCIES: DEPENDENCIES:
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
@@ -20,6 +23,7 @@ DEPENDENCIES:
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`) - mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
EXTERNAL SOURCES: EXTERNAL SOURCES:
Flutter: Flutter:
@@ -34,6 +38,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple: permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios" :path: ".symlinks/plugins/permission_handler_apple/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
@@ -42,6 +48,7 @@ SPEC CHECKSUMS:
mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
PODFILE CHECKSUM: c828a38c253d5f47218258786de668d23a9f4505 PODFILE CHECKSUM: c828a38c253d5f47218258786de668d23a9f4505

View File

@@ -4,6 +4,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:sca_treezor/sca_treezor_module.dart';
import 'package:sf_app_platform/config/env/questia_env_config.dart'; import 'package:sf_app_platform/config/env/questia_env_config.dart';
import 'package:sf_app_platform/navigation/app_router.dart'; import 'package:sf_app_platform/navigation/app_router.dart';
import 'package:navigation/navigation_module.dart'; import 'package:navigation/navigation_module.dart';
@@ -17,6 +18,7 @@ import 'package:fonts/fonts.dart';
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
navigationModule(); navigationModule();
scaTreezorModule();
configureAppRouter(); configureAppRouter();
themePackages(); themePackages();
await dotenv.load(fileName: '.env'); await dotenv.load(fileName: '.env');

View File

@@ -16,7 +16,7 @@ late final GoRouter appRouter;
void configureAppRouter() { void configureAppRouter() {
appRouter = GoRouter( appRouter = GoRouter(
navigatorKey: rootNavigatorKey, navigatorKey: rootNavigatorKey,
initialLocation: AppRoutes.splash, initialLocation: AppRoutes.login,
debugLogDiagnostics: true, debugLogDiagnostics: true,
routes: [ routes: [
GoRoute( GoRoute(
@@ -29,6 +29,12 @@ void configureAppRouter() {
name: 'login', name: 'login',
pageBuilder: LoginBuilder().buildPage, pageBuilder: LoginBuilder().buildPage,
), ),
GoRoute(
path: AppRoutes.scaTreezor,
name: 'sca_treezor',
pageBuilder: SCATreezorBuilder().buildPage,
),
GoRoute( GoRoute(
path: AppRoutes.signup, path: AppRoutes.signup,
name: 'signup', name: 'signup',

View File

@@ -583,6 +583,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.11.2" version: "6.11.2"
l10n_countries:
dependency: transitive
description:
name: l10n_countries
sha256: dfeb4d4925f71d61d2c495a0f0e1430fe66ce664cf87533dd10dc445cab0ff02
url: "https://pub.dev"
source: hosted
version: "1.3.1"
l10n_currencies:
dependency: transitive
description:
name: l10n_currencies
sha256: "7a64e1e2ae7e8aae00cca3d079f7bef333f255bff98976ddf765121e2e5eb87e"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
l10n_languages:
dependency: transitive
description:
name: l10n_languages
sha256: "808f25db80df1697f9e8f05cdc9f2dad11a21ea6473f14f7bb65bef7082b27d3"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@@ -884,6 +908,37 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.3"
sca_treezor:
dependency: "direct main"
description:
path: "../../packages/sca_treezor"
relative: true
source: path
version: "0.0.1"
sealed_countries:
dependency: transitive
description:
name: sealed_countries
sha256: bfb57edd964ca553da380ef181f665307b2847db418144b5d896a03e1adb06cc
url: "https://pub.dev"
source: hosted
version: "2.8.0"
sealed_currencies:
dependency: transitive
description:
name: sealed_currencies
sha256: "5885c77381cca83348529afd25d2ae7c0da295e972a6310c1acd1a8ae0033734"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
sealed_languages:
dependency: transitive
description:
name: sealed_languages
sha256: "488a2bc6ccf01bbc0182b8c3203af9cc1a85dc1c6aa3d39792539643d11d199c"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
sf_infrastructure: sf_infrastructure:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -905,6 +960,62 @@ packages:
relative: true relative: true
source: path source: path
version: "0.0.1" version: "0.0.1"
shared_preferences:
dependency: transitive
description:
name: shared_preferences
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
url: "https://pub.dev"
source: hosted
version: "2.4.18"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:

View File

@@ -64,6 +64,8 @@ dependencies:
path: ../../packages/utils path: ../../packages/utils
flutter_treezor_entrust_sdk_bridge: flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge path: ../../packages/flutter_treezor_entrust_sdk_bridge
sca_treezor:
path: ../../packages/sca_treezor
#dependencies go here #dependencies go here
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
flutter_svg: ^2.2.1 flutter_svg: ^2.2.1

View File

@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: auth,dashboard_shell,design_system,home,navigation,notifications,profile,sf_shared,utils,sf_localizations,fonts,sf_infrastructure,flutter_treezor_entrust_sdk_bridge,splash # melos_managed_dependency_overrides: auth,dashboard_shell,design_system,home,navigation,notifications,profile,sf_shared,utils,sf_localizations,fonts,sf_infrastructure,flutter_treezor_entrust_sdk_bridge,splash,sca_treezor
dependency_overrides: dependency_overrides:
auth: auth:
path: ../../modules/auth path: ../../modules/auth
@@ -18,6 +18,8 @@ dependency_overrides:
path: ../../modules/notifications path: ../../modules/notifications
profile: profile:
path: ../../modules/profile path: ../../modules/profile
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure: sf_infrastructure:
path: ../../packages/sf_infrastructure path: ../../packages/sf_infrastructure
sf_localizations: sf_localizations:

View File

@@ -5,3 +5,4 @@ export 'src/features/login/login_builder.dart';
export 'src/features/recover_password/presentation/request_recovery/request_recovery_builder.dart'; export 'src/features/recover_password/presentation/request_recovery/request_recovery_builder.dart';
export 'src/features/device_setup/device_setup_builder.dart'; export 'src/features/device_setup/device_setup_builder.dart';
export 'src/features/sign_up/sign_up_builder.dart'; export 'src/features/sign_up/sign_up_builder.dart';
export 'src/features/sca_treezor/sca_treezor_builder.dart';

View File

@@ -1,3 +1,4 @@
import 'package:auth/src/core/data/models/child_profile_response_model.dart';
import 'package:auth/src/core/data/models/user_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_request_model.dart';
import 'package:auth/src/core/data/models/sign_up_response_model.dart'; import 'package:auth/src/core/data/models/sign_up_response_model.dart';
@@ -40,13 +41,13 @@ abstract class AuthRemoteDatasource {
Future<void> recoverPassword({required newPassword, required token}); Future<void> recoverPassword({required newPassword, required token});
Future<String> createChildProfile({ Future<ChildProfileResponseModel> createChildProfile({
required String id, required String id,
required String parentId, required String parentId,
required String firstName, required String firstName,
required String lastName, required String lastName,
required int bornAt, required int bornAt,
required String gender, required String genrer,
required String relationType, required String relationType,
required String address, required String address,
required String cardPublicKey, required String cardPublicKey,

View File

@@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:auth/src/core/data/models/child_profile_response_model.dart';
import 'package:auth/src/core/data/models/user_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_request_model.dart';
import 'package:auth/src/core/data/models/sign_up_response_model.dart'; import 'package:auth/src/core/data/models/sign_up_response_model.dart';
@@ -270,13 +271,13 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource {
} }
@override @override
Future<String> createChildProfile({ Future<ChildProfileResponseModel> createChildProfile({
required String id, required String id,
required String parentId, required String parentId,
required String firstName, required String firstName,
required String lastName, required String lastName,
required int bornAt, required int bornAt,
required String gender, required String genrer,
required String relationType, required String relationType,
required String address, required String address,
required String cardPublicKey, required String cardPublicKey,
@@ -285,27 +286,28 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource {
}) async { }) async {
try { try {
final response = await _repository.post<Map<String, dynamic>>( final response = await _repository.post<Map<String, dynamic>>(
'/auth/child-profiles', '/child-profiles',
body: <String, dynamic>{ body: <String, dynamic>{
'id': id,
'parentId': parentId,
'firstName': firstName,
'lastName': lastName,
'bornAt': bornAt,
'gender': gender,
'relationType': relationType,
'address': address, 'address': address,
'bornAt': bornAt,
'cardPublicKey': cardPublicKey, 'cardPublicKey': cardPublicKey,
'deviceActivationCode': deviceActivationCode, 'deviceActivationCode': deviceActivationCode,
'firstName': firstName,
'genre': genrer,
'id': id,
'lastName': lastName,
'parentId': parentId,
'relationType': relationType,
'scaProof': scaProof, 'scaProof': scaProof,
}, },
); );
final data = response.data; final data = response.data;
if (data == null || data.isEmpty) { if (data == null || data.isEmpty) {
throw Exception('Empty response from /auth/child-profiles'); throw Exception('Empty response from /auth/child-profiles');
} else {
return data['id'];
} }
return ChildProfileResponseModel.fromJson(data);
} on DioException catch (error) { } on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in createChildProfile'); throw _mapDioError(error, defaultMessage: 'Error in createChildProfile');
} }

View File

@@ -0,0 +1,60 @@
import 'package:auth/src/features/device_setup/domain/entities/child_profile_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'child_profile_response_model.freezed.dart';
part 'child_profile_response_model.g.dart';
@freezed
abstract class ChildProfileResponseModel with _$ChildProfileResponseModel {
const factory ChildProfileResponseModel({
required bool isCreated,
required ChildProfileItemResponseModel item,
}) = _ChildProfileResponseModel;
factory ChildProfileResponseModel.fromJson(Map<String, dynamic> json) =>
_$ChildProfileResponseModelFromJson(json);
}
@freezed
abstract class ChildProfileItemResponseModel
with _$ChildProfileItemResponseModel {
const factory ChildProfileItemResponseModel({
required String id,
required String deviceIdentificator,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String walletId,
required String treezorUserId,
required String address,
required int createdAt,
int? updatedAt,
String? profileImageId,
}) = _ChildProfileItemResponseModel;
factory ChildProfileItemResponseModel.fromJson(Map<String, dynamic> json) =>
_$ChildProfileItemResponseModelFromJson(json);
}
extension ChildProfileResponseModelMapper on ChildProfileResponseModel {
ChildProfileEntity toEntity() {
return ChildProfileEntity(
isCreated: isCreated,
item: ChildProfileItemEntity(
id: item.id,
deviceIdentificator: item.deviceIdentificator,
parentId: item.parentId,
firstName: item.firstName,
lastName: item.lastName,
bornAt: item.bornAt,
walletId: item.walletId,
treezorUserId: item.treezorUserId,
address: item.address,
createdAt: item.createdAt,
updatedAt: item.updatedAt,
profileImageId: item.profileImageId,
),
);
}
}

View File

@@ -0,0 +1,594 @@
// 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 'child_profile_response_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ChildProfileResponseModel {
bool get isCreated; ChildProfileItemResponseModel get item;
/// Create a copy of ChildProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ChildProfileResponseModelCopyWith<ChildProfileResponseModel> get copyWith => _$ChildProfileResponseModelCopyWithImpl<ChildProfileResponseModel>(this as ChildProfileResponseModel, _$identity);
/// Serializes this ChildProfileResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChildProfileResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ChildProfileResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class $ChildProfileResponseModelCopyWith<$Res> {
factory $ChildProfileResponseModelCopyWith(ChildProfileResponseModel value, $Res Function(ChildProfileResponseModel) _then) = _$ChildProfileResponseModelCopyWithImpl;
@useResult
$Res call({
bool isCreated, ChildProfileItemResponseModel item
});
$ChildProfileItemResponseModelCopyWith<$Res> get item;
}
/// @nodoc
class _$ChildProfileResponseModelCopyWithImpl<$Res>
implements $ChildProfileResponseModelCopyWith<$Res> {
_$ChildProfileResponseModelCopyWithImpl(this._self, this._then);
final ChildProfileResponseModel _self;
final $Res Function(ChildProfileResponseModel) _then;
/// Create a copy of ChildProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_self.copyWith(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ChildProfileItemResponseModel,
));
}
/// Create a copy of ChildProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileItemResponseModelCopyWith<$Res> get item {
return $ChildProfileItemResponseModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [ChildProfileResponseModel].
extension ChildProfileResponseModelPatterns on ChildProfileResponseModel {
/// 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( _ChildProfileResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ChildProfileResponseModel() 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( _ChildProfileResponseModel value) $default,){
final _that = this;
switch (_that) {
case _ChildProfileResponseModel():
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( _ChildProfileResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _ChildProfileResponseModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isCreated, ChildProfileItemResponseModel item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ChildProfileResponseModel() when $default != null:
return $default(_that.isCreated,_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( bool isCreated, ChildProfileItemResponseModel item) $default,) {final _that = this;
switch (_that) {
case _ChildProfileResponseModel():
return $default(_that.isCreated,_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( bool isCreated, ChildProfileItemResponseModel item)? $default,) {final _that = this;
switch (_that) {
case _ChildProfileResponseModel() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _ChildProfileResponseModel implements ChildProfileResponseModel {
const _ChildProfileResponseModel({required this.isCreated, required this.item});
factory _ChildProfileResponseModel.fromJson(Map<String, dynamic> json) => _$ChildProfileResponseModelFromJson(json);
@override final bool isCreated;
@override final ChildProfileItemResponseModel item;
/// Create a copy of ChildProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ChildProfileResponseModelCopyWith<_ChildProfileResponseModel> get copyWith => __$ChildProfileResponseModelCopyWithImpl<_ChildProfileResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ChildProfileResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChildProfileResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ChildProfileResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class _$ChildProfileResponseModelCopyWith<$Res> implements $ChildProfileResponseModelCopyWith<$Res> {
factory _$ChildProfileResponseModelCopyWith(_ChildProfileResponseModel value, $Res Function(_ChildProfileResponseModel) _then) = __$ChildProfileResponseModelCopyWithImpl;
@override @useResult
$Res call({
bool isCreated, ChildProfileItemResponseModel item
});
@override $ChildProfileItemResponseModelCopyWith<$Res> get item;
}
/// @nodoc
class __$ChildProfileResponseModelCopyWithImpl<$Res>
implements _$ChildProfileResponseModelCopyWith<$Res> {
__$ChildProfileResponseModelCopyWithImpl(this._self, this._then);
final _ChildProfileResponseModel _self;
final $Res Function(_ChildProfileResponseModel) _then;
/// Create a copy of ChildProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_ChildProfileResponseModel(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ChildProfileItemResponseModel,
));
}
/// Create a copy of ChildProfileResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileItemResponseModelCopyWith<$Res> get item {
return $ChildProfileItemResponseModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$ChildProfileItemResponseModel {
String get id; String get deviceIdentificator; String get parentId; String get firstName; String get lastName; int get bornAt; String get walletId; String get treezorUserId; String get address; int get createdAt; int? get updatedAt; String? get profileImageId;
/// Create a copy of ChildProfileItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ChildProfileItemResponseModelCopyWith<ChildProfileItemResponseModel> get copyWith => _$ChildProfileItemResponseModelCopyWithImpl<ChildProfileItemResponseModel>(this as ChildProfileItemResponseModel, _$identity);
/// Serializes this ChildProfileItemResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChildProfileItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.parentId, parentId) || other.parentId == parentId)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.walletId, walletId) || other.walletId == walletId)&&(identical(other.treezorUserId, treezorUserId) || other.treezorUserId == treezorUserId)&&(identical(other.address, address) || other.address == address)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.profileImageId, profileImageId) || other.profileImageId == profileImageId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,parentId,firstName,lastName,bornAt,walletId,treezorUserId,address,createdAt,updatedAt,profileImageId);
@override
String toString() {
return 'ChildProfileItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, parentId: $parentId, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, walletId: $walletId, treezorUserId: $treezorUserId, address: $address, createdAt: $createdAt, updatedAt: $updatedAt, profileImageId: $profileImageId)';
}
}
/// @nodoc
abstract mixin class $ChildProfileItemResponseModelCopyWith<$Res> {
factory $ChildProfileItemResponseModelCopyWith(ChildProfileItemResponseModel value, $Res Function(ChildProfileItemResponseModel) _then) = _$ChildProfileItemResponseModelCopyWithImpl;
@useResult
$Res call({
String id, String deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId
});
}
/// @nodoc
class _$ChildProfileItemResponseModelCopyWithImpl<$Res>
implements $ChildProfileItemResponseModelCopyWith<$Res> {
_$ChildProfileItemResponseModelCopyWithImpl(this._self, this._then);
final ChildProfileItemResponseModel _self;
final $Res Function(ChildProfileItemResponseModel) _then;
/// Create a copy of ChildProfileItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceIdentificator = null,Object? parentId = null,Object? firstName = null,Object? lastName = null,Object? bornAt = null,Object? walletId = null,Object? treezorUserId = null,Object? address = null,Object? createdAt = null,Object? updatedAt = freezed,Object? profileImageId = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,parentId: null == parentId ? _self.parentId : parentId // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,walletId: null == walletId ? _self.walletId : walletId // ignore: cast_nullable_to_non_nullable
as String,treezorUserId: null == treezorUserId ? _self.treezorUserId : treezorUserId // ignore: cast_nullable_to_non_nullable
as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as int?,profileImageId: freezed == profileImageId ? _self.profileImageId : profileImageId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [ChildProfileItemResponseModel].
extension ChildProfileItemResponseModelPatterns on ChildProfileItemResponseModel {
/// 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( _ChildProfileItemResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ChildProfileItemResponseModel() 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( _ChildProfileItemResponseModel value) $default,){
final _that = this;
switch (_that) {
case _ChildProfileItemResponseModel():
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( _ChildProfileItemResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _ChildProfileItemResponseModel() 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 deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ChildProfileItemResponseModel() when $default != null:
return $default(_that.id,_that.deviceIdentificator,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.walletId,_that.treezorUserId,_that.address,_that.createdAt,_that.updatedAt,_that.profileImageId);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 deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId) $default,) {final _that = this;
switch (_that) {
case _ChildProfileItemResponseModel():
return $default(_that.id,_that.deviceIdentificator,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.walletId,_that.treezorUserId,_that.address,_that.createdAt,_that.updatedAt,_that.profileImageId);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 deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId)? $default,) {final _that = this;
switch (_that) {
case _ChildProfileItemResponseModel() when $default != null:
return $default(_that.id,_that.deviceIdentificator,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.walletId,_that.treezorUserId,_that.address,_that.createdAt,_that.updatedAt,_that.profileImageId);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _ChildProfileItemResponseModel implements ChildProfileItemResponseModel {
const _ChildProfileItemResponseModel({required this.id, required this.deviceIdentificator, required this.parentId, required this.firstName, required this.lastName, required this.bornAt, required this.walletId, required this.treezorUserId, required this.address, required this.createdAt, this.updatedAt, this.profileImageId});
factory _ChildProfileItemResponseModel.fromJson(Map<String, dynamic> json) => _$ChildProfileItemResponseModelFromJson(json);
@override final String id;
@override final String deviceIdentificator;
@override final String parentId;
@override final String firstName;
@override final String lastName;
@override final int bornAt;
@override final String walletId;
@override final String treezorUserId;
@override final String address;
@override final int createdAt;
@override final int? updatedAt;
@override final String? profileImageId;
/// Create a copy of ChildProfileItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ChildProfileItemResponseModelCopyWith<_ChildProfileItemResponseModel> get copyWith => __$ChildProfileItemResponseModelCopyWithImpl<_ChildProfileItemResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ChildProfileItemResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChildProfileItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.parentId, parentId) || other.parentId == parentId)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.walletId, walletId) || other.walletId == walletId)&&(identical(other.treezorUserId, treezorUserId) || other.treezorUserId == treezorUserId)&&(identical(other.address, address) || other.address == address)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.profileImageId, profileImageId) || other.profileImageId == profileImageId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,parentId,firstName,lastName,bornAt,walletId,treezorUserId,address,createdAt,updatedAt,profileImageId);
@override
String toString() {
return 'ChildProfileItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, parentId: $parentId, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, walletId: $walletId, treezorUserId: $treezorUserId, address: $address, createdAt: $createdAt, updatedAt: $updatedAt, profileImageId: $profileImageId)';
}
}
/// @nodoc
abstract mixin class _$ChildProfileItemResponseModelCopyWith<$Res> implements $ChildProfileItemResponseModelCopyWith<$Res> {
factory _$ChildProfileItemResponseModelCopyWith(_ChildProfileItemResponseModel value, $Res Function(_ChildProfileItemResponseModel) _then) = __$ChildProfileItemResponseModelCopyWithImpl;
@override @useResult
$Res call({
String id, String deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId
});
}
/// @nodoc
class __$ChildProfileItemResponseModelCopyWithImpl<$Res>
implements _$ChildProfileItemResponseModelCopyWith<$Res> {
__$ChildProfileItemResponseModelCopyWithImpl(this._self, this._then);
final _ChildProfileItemResponseModel _self;
final $Res Function(_ChildProfileItemResponseModel) _then;
/// Create a copy of ChildProfileItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceIdentificator = null,Object? parentId = null,Object? firstName = null,Object? lastName = null,Object? bornAt = null,Object? walletId = null,Object? treezorUserId = null,Object? address = null,Object? createdAt = null,Object? updatedAt = freezed,Object? profileImageId = freezed,}) {
return _then(_ChildProfileItemResponseModel(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,parentId: null == parentId ? _self.parentId : parentId // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,walletId: null == walletId ? _self.walletId : walletId // ignore: cast_nullable_to_non_nullable
as String,treezorUserId: null == treezorUserId ? _self.treezorUserId : treezorUserId // ignore: cast_nullable_to_non_nullable
as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as int?,profileImageId: freezed == profileImageId ? _self.profileImageId : profileImageId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on

View File

@@ -0,0 +1,54 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'child_profile_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_ChildProfileResponseModel _$ChildProfileResponseModelFromJson(
Map<String, dynamic> json,
) => _ChildProfileResponseModel(
isCreated: json['isCreated'] as bool,
item: ChildProfileItemResponseModel.fromJson(
json['item'] as Map<String, dynamic>,
),
);
Map<String, dynamic> _$ChildProfileResponseModelToJson(
_ChildProfileResponseModel instance,
) => <String, dynamic>{'isCreated': instance.isCreated, 'item': instance.item};
_ChildProfileItemResponseModel _$ChildProfileItemResponseModelFromJson(
Map<String, dynamic> json,
) => _ChildProfileItemResponseModel(
id: json['id'] as String,
deviceIdentificator: json['deviceIdentificator'] as String,
parentId: json['parentId'] as String,
firstName: json['firstName'] as String,
lastName: json['lastName'] as String,
bornAt: (json['bornAt'] as num).toInt(),
walletId: json['walletId'] as String,
treezorUserId: json['treezorUserId'] as String,
address: json['address'] as String,
createdAt: (json['createdAt'] as num).toInt(),
updatedAt: (json['updatedAt'] as num?)?.toInt(),
profileImageId: json['profileImageId'] as String?,
);
Map<String, dynamic> _$ChildProfileItemResponseModelToJson(
_ChildProfileItemResponseModel instance,
) => <String, dynamic>{
'id': instance.id,
'deviceIdentificator': instance.deviceIdentificator,
'parentId': instance.parentId,
'firstName': instance.firstName,
'lastName': instance.lastName,
'bornAt': instance.bornAt,
'walletId': instance.walletId,
'treezorUserId': instance.treezorUserId,
'address': instance.address,
'createdAt': instance.createdAt,
'updatedAt': instance.updatedAt,
'profileImageId': instance.profileImageId,
};

View File

@@ -1,9 +1,11 @@
import 'package:auth/src/core/data/datasource/auth_remote_datasource.dart'; 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/mappers/user_model_mapper.dart';
import 'package:auth/src/core/data/models/child_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_request_model.dart';
import 'package:auth/src/core/data/models/sign_up_response_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/data/models/two_fa_secret_response_model.dart';
import 'package:auth/src/core/domain/repositories/auth_repository.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/login_response_entity.dart';
import 'package:auth/src/features/login/domain/entities/user_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_request_entity.dart';
@@ -105,32 +107,38 @@ class AuthRepositoryImpl implements AuthRepository {
} }
@override @override
Future<String> createChildProfile({ Future<ChildProfileEntity> createChildProfile({
required String id, required String id,
required String parentId, required String parentId,
required String firstName, required String firstName,
required String lastName, required String lastName,
required int bornAt, required int bornAt,
required String gender, required String genrer,
required String relationType, required String relationType,
required String address, required String address,
required String cardPublicKey, required String cardPublicKey,
required String deviceActivationCode, required String deviceActivationCode,
required String scaProof, required String scaProof,
}) { }) async {
return _remote.createChildProfile( final model = await _remote.createChildProfile(
id: id, id: id,
parentId: parentId, parentId: parentId,
firstName: firstName, firstName: firstName,
lastName: lastName, lastName: lastName,
bornAt: bornAt, bornAt: bornAt,
gender: gender, genrer: genrer,
relationType: relationType, relationType: relationType,
address: address, address: address,
cardPublicKey: cardPublicKey, cardPublicKey: cardPublicKey,
deviceActivationCode: deviceActivationCode, deviceActivationCode: deviceActivationCode,
scaProof: scaProof, scaProof: scaProof,
); );
if (!model.isCreated) {
throw Exception('Child profile creation failed: isCreated=false');
}
return model.toEntity();
} }
} }

View File

@@ -1,4 +1,6 @@
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/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/login_response_entity.dart';
import 'package:auth/src/features/login/domain/entities/user_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_request_entity.dart';
@@ -42,13 +44,13 @@ abstract class AuthRepository {
required String token, required String token,
}); });
Future<String> createChildProfile({ Future<ChildProfileEntity> createChildProfile({
required String id, required String id,
required String parentId, required String parentId,
required String firstName, required String firstName,
required String lastName, required String lastName,
required int bornAt, required int bornAt,
required String gender, required String genrer,
required String relationType, required String relationType,
required String address, required String address,
required String cardPublicKey, required String cardPublicKey,

View File

@@ -1,11 +1,13 @@
import 'package:auth/src/features/device_setup/domain/entities/child_profile_entity.dart';
abstract class CreateChildProfileUseCase { abstract class CreateChildProfileUseCase {
Future<String> createChildProfile({ Future<ChildProfileEntity> createChildProfile({
required String id, required String id,
required String parentId, required String parentId,
required String firstName, required String firstName,
required String lastName, required String lastName,
required int bornAt, required int bornAt,
required String gender, required String genrer,
required String relationType, required String relationType,
required String address, required String address,
required String cardPublicKey, required String cardPublicKey,

View File

@@ -1,5 +1,6 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart'; import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/device_setup/domain/create_child_profile_use_case.dart'; import 'package:auth/src/features/device_setup/domain/create_child_profile_use_case.dart';
import 'package:auth/src/features/device_setup/domain/entities/child_profile_entity.dart';
class CreateChildProfileUseCaseImpl implements CreateChildProfileUseCase { class CreateChildProfileUseCaseImpl implements CreateChildProfileUseCase {
CreateChildProfileUseCaseImpl(this._repository); CreateChildProfileUseCaseImpl(this._repository);
@@ -7,13 +8,13 @@ class CreateChildProfileUseCaseImpl implements CreateChildProfileUseCase {
final AuthRepository _repository; final AuthRepository _repository;
@override @override
Future<String> createChildProfile({ Future<ChildProfileEntity> createChildProfile({
required String id, required String id,
required String parentId, required String parentId,
required String firstName, required String firstName,
required String lastName, required String lastName,
required int bornAt, required int bornAt,
required String gender, required String genrer,
required String relationType, required String relationType,
required String address, required String address,
required String cardPublicKey, required String cardPublicKey,
@@ -26,7 +27,7 @@ class CreateChildProfileUseCaseImpl implements CreateChildProfileUseCase {
firstName: firstName, firstName: firstName,
lastName: lastName, lastName: lastName,
bornAt: bornAt, bornAt: bornAt,
gender: gender, genrer: genrer,
relationType: relationType, relationType: relationType,
address: address, address: address,
cardPublicKey: cardPublicKey, cardPublicKey: cardPublicKey,

View File

@@ -0,0 +1,29 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'child_profile_entity.freezed.dart';
@freezed
abstract class ChildProfileEntity with _$ChildProfileEntity {
const factory ChildProfileEntity({
required bool isCreated,
required ChildProfileItemEntity item,
}) = _ChildProfileEntity;
}
@freezed
abstract class ChildProfileItemEntity with _$ChildProfileItemEntity {
const factory ChildProfileItemEntity({
required String id,
required String deviceIdentificator,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String walletId,
required String treezorUserId,
required String address,
required int createdAt,
int? updatedAt,
String? profileImageId,
}) = _ChildProfileItemEntity;
}

View File

@@ -0,0 +1,582 @@
// 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 'child_profile_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ChildProfileEntity {
bool get isCreated; ChildProfileItemEntity get item;
/// Create a copy of ChildProfileEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ChildProfileEntityCopyWith<ChildProfileEntity> get copyWith => _$ChildProfileEntityCopyWithImpl<ChildProfileEntity>(this as ChildProfileEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChildProfileEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ChildProfileEntity(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class $ChildProfileEntityCopyWith<$Res> {
factory $ChildProfileEntityCopyWith(ChildProfileEntity value, $Res Function(ChildProfileEntity) _then) = _$ChildProfileEntityCopyWithImpl;
@useResult
$Res call({
bool isCreated, ChildProfileItemEntity item
});
$ChildProfileItemEntityCopyWith<$Res> get item;
}
/// @nodoc
class _$ChildProfileEntityCopyWithImpl<$Res>
implements $ChildProfileEntityCopyWith<$Res> {
_$ChildProfileEntityCopyWithImpl(this._self, this._then);
final ChildProfileEntity _self;
final $Res Function(ChildProfileEntity) _then;
/// Create a copy of ChildProfileEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_self.copyWith(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ChildProfileItemEntity,
));
}
/// Create a copy of ChildProfileEntity
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileItemEntityCopyWith<$Res> get item {
return $ChildProfileItemEntityCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [ChildProfileEntity].
extension ChildProfileEntityPatterns on ChildProfileEntity {
/// 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( _ChildProfileEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ChildProfileEntity() 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( _ChildProfileEntity value) $default,){
final _that = this;
switch (_that) {
case _ChildProfileEntity():
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( _ChildProfileEntity value)? $default,){
final _that = this;
switch (_that) {
case _ChildProfileEntity() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isCreated, ChildProfileItemEntity item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ChildProfileEntity() when $default != null:
return $default(_that.isCreated,_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( bool isCreated, ChildProfileItemEntity item) $default,) {final _that = this;
switch (_that) {
case _ChildProfileEntity():
return $default(_that.isCreated,_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( bool isCreated, ChildProfileItemEntity item)? $default,) {final _that = this;
switch (_that) {
case _ChildProfileEntity() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return null;
}
}
}
/// @nodoc
class _ChildProfileEntity implements ChildProfileEntity {
const _ChildProfileEntity({required this.isCreated, required this.item});
@override final bool isCreated;
@override final ChildProfileItemEntity item;
/// Create a copy of ChildProfileEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ChildProfileEntityCopyWith<_ChildProfileEntity> get copyWith => __$ChildProfileEntityCopyWithImpl<_ChildProfileEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChildProfileEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ChildProfileEntity(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class _$ChildProfileEntityCopyWith<$Res> implements $ChildProfileEntityCopyWith<$Res> {
factory _$ChildProfileEntityCopyWith(_ChildProfileEntity value, $Res Function(_ChildProfileEntity) _then) = __$ChildProfileEntityCopyWithImpl;
@override @useResult
$Res call({
bool isCreated, ChildProfileItemEntity item
});
@override $ChildProfileItemEntityCopyWith<$Res> get item;
}
/// @nodoc
class __$ChildProfileEntityCopyWithImpl<$Res>
implements _$ChildProfileEntityCopyWith<$Res> {
__$ChildProfileEntityCopyWithImpl(this._self, this._then);
final _ChildProfileEntity _self;
final $Res Function(_ChildProfileEntity) _then;
/// Create a copy of ChildProfileEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_ChildProfileEntity(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ChildProfileItemEntity,
));
}
/// Create a copy of ChildProfileEntity
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileItemEntityCopyWith<$Res> get item {
return $ChildProfileItemEntityCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$ChildProfileItemEntity {
String get id; String get deviceIdentificator; String get parentId; String get firstName; String get lastName; int get bornAt; String get walletId; String get treezorUserId; String get address; int get createdAt; int? get updatedAt; String? get profileImageId;
/// Create a copy of ChildProfileItemEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ChildProfileItemEntityCopyWith<ChildProfileItemEntity> get copyWith => _$ChildProfileItemEntityCopyWithImpl<ChildProfileItemEntity>(this as ChildProfileItemEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChildProfileItemEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.parentId, parentId) || other.parentId == parentId)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.walletId, walletId) || other.walletId == walletId)&&(identical(other.treezorUserId, treezorUserId) || other.treezorUserId == treezorUserId)&&(identical(other.address, address) || other.address == address)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.profileImageId, profileImageId) || other.profileImageId == profileImageId));
}
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,parentId,firstName,lastName,bornAt,walletId,treezorUserId,address,createdAt,updatedAt,profileImageId);
@override
String toString() {
return 'ChildProfileItemEntity(id: $id, deviceIdentificator: $deviceIdentificator, parentId: $parentId, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, walletId: $walletId, treezorUserId: $treezorUserId, address: $address, createdAt: $createdAt, updatedAt: $updatedAt, profileImageId: $profileImageId)';
}
}
/// @nodoc
abstract mixin class $ChildProfileItemEntityCopyWith<$Res> {
factory $ChildProfileItemEntityCopyWith(ChildProfileItemEntity value, $Res Function(ChildProfileItemEntity) _then) = _$ChildProfileItemEntityCopyWithImpl;
@useResult
$Res call({
String id, String deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId
});
}
/// @nodoc
class _$ChildProfileItemEntityCopyWithImpl<$Res>
implements $ChildProfileItemEntityCopyWith<$Res> {
_$ChildProfileItemEntityCopyWithImpl(this._self, this._then);
final ChildProfileItemEntity _self;
final $Res Function(ChildProfileItemEntity) _then;
/// Create a copy of ChildProfileItemEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceIdentificator = null,Object? parentId = null,Object? firstName = null,Object? lastName = null,Object? bornAt = null,Object? walletId = null,Object? treezorUserId = null,Object? address = null,Object? createdAt = null,Object? updatedAt = freezed,Object? profileImageId = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,parentId: null == parentId ? _self.parentId : parentId // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,walletId: null == walletId ? _self.walletId : walletId // ignore: cast_nullable_to_non_nullable
as String,treezorUserId: null == treezorUserId ? _self.treezorUserId : treezorUserId // ignore: cast_nullable_to_non_nullable
as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as int?,profileImageId: freezed == profileImageId ? _self.profileImageId : profileImageId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [ChildProfileItemEntity].
extension ChildProfileItemEntityPatterns on ChildProfileItemEntity {
/// 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( _ChildProfileItemEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ChildProfileItemEntity() 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( _ChildProfileItemEntity value) $default,){
final _that = this;
switch (_that) {
case _ChildProfileItemEntity():
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( _ChildProfileItemEntity value)? $default,){
final _that = this;
switch (_that) {
case _ChildProfileItemEntity() 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 deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ChildProfileItemEntity() when $default != null:
return $default(_that.id,_that.deviceIdentificator,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.walletId,_that.treezorUserId,_that.address,_that.createdAt,_that.updatedAt,_that.profileImageId);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 deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId) $default,) {final _that = this;
switch (_that) {
case _ChildProfileItemEntity():
return $default(_that.id,_that.deviceIdentificator,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.walletId,_that.treezorUserId,_that.address,_that.createdAt,_that.updatedAt,_that.profileImageId);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 deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId)? $default,) {final _that = this;
switch (_that) {
case _ChildProfileItemEntity() when $default != null:
return $default(_that.id,_that.deviceIdentificator,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.walletId,_that.treezorUserId,_that.address,_that.createdAt,_that.updatedAt,_that.profileImageId);case _:
return null;
}
}
}
/// @nodoc
class _ChildProfileItemEntity implements ChildProfileItemEntity {
const _ChildProfileItemEntity({required this.id, required this.deviceIdentificator, required this.parentId, required this.firstName, required this.lastName, required this.bornAt, required this.walletId, required this.treezorUserId, required this.address, required this.createdAt, this.updatedAt, this.profileImageId});
@override final String id;
@override final String deviceIdentificator;
@override final String parentId;
@override final String firstName;
@override final String lastName;
@override final int bornAt;
@override final String walletId;
@override final String treezorUserId;
@override final String address;
@override final int createdAt;
@override final int? updatedAt;
@override final String? profileImageId;
/// Create a copy of ChildProfileItemEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ChildProfileItemEntityCopyWith<_ChildProfileItemEntity> get copyWith => __$ChildProfileItemEntityCopyWithImpl<_ChildProfileItemEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChildProfileItemEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.parentId, parentId) || other.parentId == parentId)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.walletId, walletId) || other.walletId == walletId)&&(identical(other.treezorUserId, treezorUserId) || other.treezorUserId == treezorUserId)&&(identical(other.address, address) || other.address == address)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.profileImageId, profileImageId) || other.profileImageId == profileImageId));
}
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,parentId,firstName,lastName,bornAt,walletId,treezorUserId,address,createdAt,updatedAt,profileImageId);
@override
String toString() {
return 'ChildProfileItemEntity(id: $id, deviceIdentificator: $deviceIdentificator, parentId: $parentId, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, walletId: $walletId, treezorUserId: $treezorUserId, address: $address, createdAt: $createdAt, updatedAt: $updatedAt, profileImageId: $profileImageId)';
}
}
/// @nodoc
abstract mixin class _$ChildProfileItemEntityCopyWith<$Res> implements $ChildProfileItemEntityCopyWith<$Res> {
factory _$ChildProfileItemEntityCopyWith(_ChildProfileItemEntity value, $Res Function(_ChildProfileItemEntity) _then) = __$ChildProfileItemEntityCopyWithImpl;
@override @useResult
$Res call({
String id, String deviceIdentificator, String parentId, String firstName, String lastName, int bornAt, String walletId, String treezorUserId, String address, int createdAt, int? updatedAt, String? profileImageId
});
}
/// @nodoc
class __$ChildProfileItemEntityCopyWithImpl<$Res>
implements _$ChildProfileItemEntityCopyWith<$Res> {
__$ChildProfileItemEntityCopyWithImpl(this._self, this._then);
final _ChildProfileItemEntity _self;
final $Res Function(_ChildProfileItemEntity) _then;
/// Create a copy of ChildProfileItemEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceIdentificator = null,Object? parentId = null,Object? firstName = null,Object? lastName = null,Object? bornAt = null,Object? walletId = null,Object? treezorUserId = null,Object? address = null,Object? createdAt = null,Object? updatedAt = freezed,Object? profileImageId = freezed,}) {
return _then(_ChildProfileItemEntity(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,parentId: null == parentId ? _self.parentId : parentId // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,walletId: null == walletId ? _self.walletId : walletId // ignore: cast_nullable_to_non_nullable
as String,treezorUserId: null == treezorUserId ? _self.treezorUserId : treezorUserId // ignore: cast_nullable_to_non_nullable
as String,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,updatedAt: freezed == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as int?,profileImageId: freezed == profileImageId ? _self.profileImageId : profileImageId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on

View File

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

View File

@@ -22,37 +22,70 @@ class DeviceSetupScreen extends ConsumerWidget {
final theme = ref.watch(themePortProvider); final theme = ref.watch(themePortProvider);
final mainStep = state.step.mainStep; final mainStep = state.step.mainStep;
return Scaffold( final isIntroOrSuccess =
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), state.step == AddKidStep.intro || state.step == AddKidStep.success;
body: SafeArea(
child: Column( final canPopRoute = state.step == AddKidStep.intro;
children: [
state.step == AddKidStep.intro || state.step == AddKidStep.success return PopScope(
? const SizedBox(height: 24) canPop: canPopRoute,
: StepIndicator( onPopInvokedWithResult: (didPop, result) {
total: AddKidMainStep.values.length, if (didPop) return;
current: mainStep.index + 1, vm.back();
color: theme.getColorFor(ThemeCode.buttonPrimary), },
child: Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: SafeArea(
child: Column(
children: [
if (isIntroOrSuccess)
const SizedBox(height: 24)
else
Padding(
padding: const EdgeInsets.only(top: 12, left: 8, right: 8),
child: Row(
children: [
IconButton(
onPressed: vm.back,
icon: const Icon(Icons.arrow_back_ios_new_rounded),
color: theme.getColorFor(ThemeCode.textPrimary),
tooltip: MaterialLocalizations.of(
context,
).backButtonTooltip,
),
Expanded(
child: StepIndicator(
total: AddKidMainStep.values.length,
current: mainStep.index + 1,
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
),
const SizedBox(width: 48),
],
), ),
),
Expanded( Expanded(
child: AnimatedSwitcher( child: AnimatedSwitcher(
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
child: StepBody(key: ValueKey(state.step), state: state), child: StepBody(key: ValueKey(state.step), state: state),
),
), ),
),
FlowFooter( FlowFooter(
error: state.errorMessage, error: state.errorMessage,
primaryText: context.translate(primaryButtonText(state.step)), primaryText: context.translate(primaryButtonText(state.step)),
secondaryText: state.step == AddKidStep.success secondaryText: state.step == AddKidStep.success
? context.translate(I18n.deviceSetup_addAnotherKid) ? context.translate(I18n.deviceSetup_addAnotherKid)
: null, : null,
onPrimary: vm.next, onPrimary: state.step == AddKidStep.sca
onSecondary: state.step == AddKidStep.success ? () {} : null, ? vm.createChildProfile
theme: theme, : vm.next,
), onSecondary: state.step == AddKidStep.success ? () {} : null,
], theme: theme,
),
],
),
), ),
), ),
); );

View File

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

View File

@@ -1,8 +1,18 @@
import 'dart:convert';
import 'package:auth/src/features/device_setup/domain/create_child_profile_use_case.dart';
import 'package:auth/src/features/device_setup/presentation/enums/scan_link_step.dart'; import 'package:auth/src/features/device_setup/presentation/enums/scan_link_step.dart';
import 'package:auth/src/features/device_setup/presentation/providers/create_child_profile_use_case_provider.dart';
import 'package:auth/src/features/device_setup/presentation/state/device_setup_view_state.dart'; import 'package:auth/src/features/device_setup/presentation/state/device_setup_view_state.dart';
import 'package:auth/src/features/device_setup/presentation/enums/add_kid_step.dart'; import 'package:auth/src/features/device_setup/presentation/enums/add_kid_step.dart';
import 'package:auth/src/features/login/domain/entities/user_entity.dart';
import 'package:auth/src/features/login/domain/get_me_user_use_case.dart';
import 'package:auth/src/features/login/presentation/providers/get_user_info_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:sca_treezor/sca_treezor.dart';
import 'package:uuid/uuid.dart';
final deviceSetupViewModelProvider = final deviceSetupViewModelProvider =
NotifierProvider<DeviceSetupViewModel, DeviceSetupViewState>( NotifierProvider<DeviceSetupViewModel, DeviceSetupViewState>(
@@ -10,15 +20,20 @@ final deviceSetupViewModelProvider =
); );
class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> { class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
late final CreateChildProfileUseCase _createChildProfileUseCase;
late final GetUserInfoUseCase _getUserInfoUseCase;
late final TreezorWalletSignatureService _signatureService;
late final TextEditingController bornAtController; late final TextEditingController bornAtController;
late final TextEditingController firstNameController; late final TextEditingController firstNameController;
late final TextEditingController lastNameController; late final TextEditingController lastNameController;
late final TextEditingController addressController;
late final TextEditingController strapCodeController;
late final TextEditingController watchCodeController;
@override @override
DeviceSetupViewState build() { DeviceSetupViewState build() {
// _signUpUseCase = ref.read(signUpUseCaseProvider); final initial = DeviceSetupViewState(id: const Uuid().v4());
final initial = DeviceSetupViewState();
_initControllers(initial); _initControllers(initial);
_addListeners(); _addListeners();
@@ -28,17 +43,29 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
} }
void _initControllers(DeviceSetupViewState s) { void _initControllers(DeviceSetupViewState s) {
_createChildProfileUseCase = ref.read(createChildProfileUseCaseProvider);
_getUserInfoUseCase = ref.read(getUserInfoUseCaseProvider);
_signatureService = GetIt.I<TreezorWalletSignatureService>();
firstNameController = TextEditingController(text: s.firstName); firstNameController = TextEditingController(text: s.firstName);
lastNameController = TextEditingController(text: s.lastName); lastNameController = TextEditingController(text: s.lastName);
bornAtController = TextEditingController( bornAtController = TextEditingController(
text: s.bornAt == null ? '' : _formatDate(s.bornAt!), text: s.bornAt == null ? '' : _formatDate(s.bornAt!),
); );
addressController = TextEditingController(text: s.address);
strapCodeController = TextEditingController(text: s.strapCode);
watchCodeController = TextEditingController(text: s.watchCode);
} }
void _addListeners() { void _addListeners() {
firstNameController.addListener(_onFirstNameChanged); firstNameController.addListener(_onFirstNameChanged);
lastNameController.addListener(_onLastNameChanged); lastNameController.addListener(_onLastNameChanged);
bornAtController.addListener(_onBornAtTextChanged); bornAtController.addListener(_onBornAtTextChanged);
addressController.addListener(_onAddressChanged);
strapCodeController.addListener(_onStrapCodeChanged);
watchCodeController.addListener(_onWatchCodeChanged);
} }
void next() { void next() {
@@ -50,27 +77,31 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
state = state.copyWith(step: AddKidStep.scanStrap); state = state.copyWith(step: AddKidStep.scanStrap);
return; return;
case AddKidStep.scanStrap: case AddKidStep.scanStrap:
if (state.strapQr.isEmpty) { // if (state.strapQr.isEmpty) {
state = state.copyWith( // state = state.copyWith(
errorMessage: 'Escanea la correa para continuar', // errorMessage: 'Escanea la correa para continuar',
); // );
return; // return;
} // }
state = state.copyWith(step: AddKidStep.scanWatch); state = state.copyWith(step: AddKidStep.scanWatch);
return; return;
case AddKidStep.scanWatch: case AddKidStep.scanWatch:
final hasWatch = state.watchQr.isNotEmpty || state.watchCode.isNotEmpty; // final hasWatch = state.watchQr.isNotEmpty || state.watchCode.isNotEmpty;
if (!hasWatch) { // if (!hasWatch) {
state = state.copyWith( // state = state.copyWith(
errorMessage: 'Escanea el reloj o introduce el código', // errorMessage: 'Escanea el reloj o introduce el código',
); // );
return; // return;
} // }
state = state.copyWith(step: AddKidStep.profile); state = state.copyWith(step: AddKidStep.profile);
return; return;
case AddKidStep.profile: case AddKidStep.profile:
// final isValid = _validateProfile(); final isValid = _validateProfile();
// if (!isValid) return; if (!isValid) return;
state = state.copyWith(step: AddKidStep.sca);
return;
case AddKidStep.sca:
state = state.copyWith(step: AddKidStep.success); state = state.copyWith(step: AddKidStep.success);
return; return;
@@ -96,6 +127,9 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
case AddKidStep.profile: case AddKidStep.profile:
state = state.copyWith(step: AddKidStep.scanWatch); state = state.copyWith(step: AddKidStep.scanWatch);
return; return;
case AddKidStep.sca:
state = state.copyWith(step: AddKidStep.profile);
return;
case AddKidStep.success: case AddKidStep.success:
state = state.copyWith(step: AddKidStep.profile); state = state.copyWith(step: AddKidStep.profile);
return; return;
@@ -140,11 +174,80 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
state = state.copyWith(bornAt: date); state = state.copyWith(bornAt: date);
} }
Future<void> createChildProfile() async {
await generateJwsWithPin();
// if (!_validateProfile()) return;
await getUserInfo();
final firstName = state.firstName.trim();
final lastName = state.lastName.trim();
final bornAt = state.bornAt!.millisecondsSinceEpoch;
final address = state.address.trim();
final genrer = state.genrer.trim();
final relationType = state.relationType.trim();
state = state.copyWith(isLoading: true);
try {
await _createChildProfileUseCase.createChildProfile(
id: state.id,
parentId: state.parentId,
firstName: firstName,
lastName: lastName,
bornAt: bornAt,
genrer: genrer,
relationType: relationType,
address: address,
cardPublicKey: state.strapCode,
deviceActivationCode: state.watchCode,
scaProof: state.lastSignature,
);
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
isSuccess: true,
step: AddKidStep.success,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
}
}
Future<UserEntity?> getUserInfo() async {
state = state.copyWith(isLoading: true, errorMessage: '');
try {
final user = await _getUserInfoUseCase.getUserInfo();
if (ref.mounted) {
state = state.copyWith(isLoading: false);
}
debugPrint('[getUserInfo] userId => ${user.id}');
state = state.copyWith(parentId: user.id);
return user;
} catch (e) {
if (ref.mounted) {
state = state.copyWith(isLoading: false, errorMessage: e.toString());
}
return null;
}
}
bool _validateProfile() { bool _validateProfile() {
if (state.firstName.trim().isEmpty || final isInvalid =
state.firstName.trim().isEmpty ||
state.lastName.trim().isEmpty || state.lastName.trim().isEmpty ||
state.bornAt == null || state.bornAt == null ||
state.address.trim().isEmpty) { state.address.trim().isEmpty ||
state.genrer.trim().isEmpty ||
state.relationType.trim().isEmpty;
if (isInvalid) {
state = state.copyWith(errorMessage: 'Completa todos los campos'); state = state.copyWith(errorMessage: 'Completa todos los campos');
return false; return false;
} }
@@ -186,6 +289,36 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
} }
} }
void _onAddressChanged() {
final text = addressController.text;
if (text == state.address) return;
state = state.copyWith(address: text, errorMessage: '');
}
void _onStrapCodeChanged() {
final text = strapCodeController.text;
if (text == state.strapCode) return;
state = state.copyWith(strapCode: text, errorMessage: '');
}
void _onWatchCodeChanged() {
final text = watchCodeController.text;
if (text == state.watchCode) return;
state = state.copyWith(watchCode: text, errorMessage: '');
}
void onGenrerChanged(String? value) {
final v = value ?? '';
if (v == state.genrer) return;
state = state.copyWith(genrer: v, errorMessage: '');
}
void onRelationTypeChanged(String? value) {
final v = value ?? '';
if (v == state.relationType) return;
state = state.copyWith(relationType: v, errorMessage: '');
}
DateTime? _tryParseDate(String value) { DateTime? _tryParseDate(String value) {
final v = value.trim(); final v = value.trim();
if (v.isEmpty) return null; if (v.isEmpty) return null;
@@ -205,6 +338,76 @@ class DeviceSetupViewModel extends Notifier<DeviceSetupViewState> {
return date; return date;
} }
void onDigitPressed(String digit) {
if (state.pin.length >= 6) return;
state = state.copyWith(
pin: '${state.pin}$digit',
errorMessage: '',
isSuccess: false,
);
}
void onBackspacePressed() {
if (state.pin.isEmpty) return;
state = state.copyWith(
pin: state.pin.substring(0, state.pin.length - 1),
errorMessage: '',
isSuccess: false,
);
}
void clearPin() {
state = state.copyWith(pin: '', errorMessage: '', isSuccess: false);
}
bool get canSubmitPin => state.pin.length == 6;
Future<void> generateJwsWithPin() async {
if (state.isSigning) return;
if (!canSubmitPin) {
state = state.copyWith(errorMessage: '⚠️ PIN de 6 dígitos');
return;
}
state = state.copyWith(
errorMessage: '',
isSigning: true,
isSuccess: false,
lastSignature: '',
);
try {
final url =
'https://savefamily.sandbox.treezor.co/v1/cards/${state.strapCode}/public-token-activation';
// final input = Uint8List.fromList(
// utf8.encode(jsonEncode({'url': url, 'body': {}})),
// );
final jws = await _signatureService.generateJwsWithPin(
message: '',
input: jsonEncode({'url': url, 'body': {}}),
pin: state.pin,
);
state = state.copyWith(
isSigning: false,
isSuccess: true,
lastSignature: jws,
errorMessage: '',
pin: '',
);
} catch (e) {
state = state.copyWith(
isSigning: false,
isSuccess: false,
errorMessage: ' Error firmando operación sensible: $e',
pin: '',
);
}
}
void disposeControllers() { void disposeControllers() {
// firstNameController.dispose(); // firstNameController.dispose();
// lastNameController.dispose(); // lastNameController.dispose();

View File

@@ -7,17 +7,26 @@ part 'device_setup_view_state.freezed.dart';
abstract class DeviceSetupViewState with _$DeviceSetupViewState { abstract class DeviceSetupViewState with _$DeviceSetupViewState {
const factory DeviceSetupViewState({ const factory DeviceSetupViewState({
@Default(AddKidStep.intro) AddKidStep step, @Default(AddKidStep.intro) AddKidStep step,
@Default('') String id,
@Default('') String parentId,
@Default('') String firstName, @Default('') String firstName,
@Default('') String lastName, @Default('') String lastName,
DateTime? bornAt, DateTime? bornAt,
@Default('') String address, @Default('') String address,
@Default('') String genrer,
@Default('') String relationType,
@Default('') String strapQr, @Default('') String strapQr,
@Default('') String strapCode,
@Default('') String watchQr, @Default('') String watchQr,
@Default('') String watchCode, @Default('') String watchCode,
@Default(false) bool loading, @Default(false) bool isLoading,
@Default('') String errorMessage, @Default('') String errorMessage,
@Default(false) bool isSuccess,
@Default('') String pin,
@Default(false) bool isSigning,
@Default('') String lastSignature,
}) = _AddKidFlowState; }) = _AddKidFlowState;
} }

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$DeviceSetupViewState { mixin _$DeviceSetupViewState {
AddKidStep get step; String get firstName; String get lastName; DateTime? get bornAt; String get address; String get strapQr; String get watchQr; String get watchCode; bool get loading; String get errorMessage; AddKidStep get step; String get id; String get parentId; String get firstName; String get lastName; DateTime? get bornAt; String get address; String get genrer; String get relationType; String get strapQr; String get strapCode; String get watchQr; String get watchCode; bool get isLoading; String get errorMessage; bool get isSuccess; String get pin; bool get isSigning; String get lastSignature;
/// Create a copy of DeviceSetupViewState /// Create a copy of DeviceSetupViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $DeviceSetupViewStateCopyWith<DeviceSetupViewState> get copyWith => _$DeviceSetu
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DeviceSetupViewState&&(identical(other.step, step) || other.step == step)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.address, address) || other.address == address)&&(identical(other.strapQr, strapQr) || other.strapQr == strapQr)&&(identical(other.watchQr, watchQr) || other.watchQr == watchQr)&&(identical(other.watchCode, watchCode) || other.watchCode == watchCode)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); return identical(this, other) || (other.runtimeType == runtimeType&&other is DeviceSetupViewState&&(identical(other.step, step) || other.step == step)&&(identical(other.id, id) || other.id == id)&&(identical(other.parentId, parentId) || other.parentId == parentId)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.address, address) || other.address == address)&&(identical(other.genrer, genrer) || other.genrer == genrer)&&(identical(other.relationType, relationType) || other.relationType == relationType)&&(identical(other.strapQr, strapQr) || other.strapQr == strapQr)&&(identical(other.strapCode, strapCode) || other.strapCode == strapCode)&&(identical(other.watchQr, watchQr) || other.watchQr == watchQr)&&(identical(other.watchCode, watchCode) || other.watchCode == watchCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isSuccess, isSuccess) || other.isSuccess == isSuccess)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.lastSignature, lastSignature) || other.lastSignature == lastSignature));
} }
@override @override
int get hashCode => Object.hash(runtimeType,step,firstName,lastName,bornAt,address,strapQr,watchQr,watchCode,loading,errorMessage); int get hashCode => Object.hashAll([runtimeType,step,id,parentId,firstName,lastName,bornAt,address,genrer,relationType,strapQr,strapCode,watchQr,watchCode,isLoading,errorMessage,isSuccess,pin,isSigning,lastSignature]);
@override @override
String toString() { String toString() {
return 'DeviceSetupViewState(step: $step, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, address: $address, strapQr: $strapQr, watchQr: $watchQr, watchCode: $watchCode, loading: $loading, errorMessage: $errorMessage)'; return 'DeviceSetupViewState(step: $step, id: $id, parentId: $parentId, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, address: $address, genrer: $genrer, relationType: $relationType, strapQr: $strapQr, strapCode: $strapCode, watchQr: $watchQr, watchCode: $watchCode, isLoading: $isLoading, errorMessage: $errorMessage, isSuccess: $isSuccess, pin: $pin, isSigning: $isSigning, lastSignature: $lastSignature)';
} }
@@ -45,7 +45,7 @@ abstract mixin class $DeviceSetupViewStateCopyWith<$Res> {
factory $DeviceSetupViewStateCopyWith(DeviceSetupViewState value, $Res Function(DeviceSetupViewState) _then) = _$DeviceSetupViewStateCopyWithImpl; factory $DeviceSetupViewStateCopyWith(DeviceSetupViewState value, $Res Function(DeviceSetupViewState) _then) = _$DeviceSetupViewStateCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
AddKidStep step, String firstName, String lastName, DateTime? bornAt, String address, String strapQr, String watchQr, String watchCode, bool loading, String errorMessage AddKidStep step, String id, String parentId, String firstName, String lastName, DateTime? bornAt, String address, String genrer, String relationType, String strapQr, String strapCode, String watchQr, String watchCode, bool isLoading, String errorMessage, bool isSuccess, String pin, bool isSigning, String lastSignature
}); });
@@ -62,18 +62,27 @@ class _$DeviceSetupViewStateCopyWithImpl<$Res>
/// Create a copy of DeviceSetupViewState /// Create a copy of DeviceSetupViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? step = null,Object? firstName = null,Object? lastName = null,Object? bornAt = freezed,Object? address = null,Object? strapQr = null,Object? watchQr = null,Object? watchCode = null,Object? loading = null,Object? errorMessage = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? step = null,Object? id = null,Object? parentId = null,Object? firstName = null,Object? lastName = null,Object? bornAt = freezed,Object? address = null,Object? genrer = null,Object? relationType = null,Object? strapQr = null,Object? strapCode = null,Object? watchQr = null,Object? watchCode = null,Object? isLoading = null,Object? errorMessage = null,Object? isSuccess = null,Object? pin = null,Object? isSigning = null,Object? lastSignature = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable
as AddKidStep,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as AddKidStep,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,parentId: null == parentId ? _self.parentId : parentId // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as DateTime?,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as DateTime?,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,genrer: null == genrer ? _self.genrer : genrer // ignore: cast_nullable_to_non_nullable
as String,relationType: null == relationType ? _self.relationType : relationType // ignore: cast_nullable_to_non_nullable
as String,strapQr: null == strapQr ? _self.strapQr : strapQr // ignore: cast_nullable_to_non_nullable as String,strapQr: null == strapQr ? _self.strapQr : strapQr // ignore: cast_nullable_to_non_nullable
as String,strapCode: null == strapCode ? _self.strapCode : strapCode // ignore: cast_nullable_to_non_nullable
as String,watchQr: null == watchQr ? _self.watchQr : watchQr // ignore: cast_nullable_to_non_nullable as String,watchQr: null == watchQr ? _self.watchQr : watchQr // ignore: cast_nullable_to_non_nullable
as String,watchCode: null == watchCode ? _self.watchCode : watchCode // ignore: cast_nullable_to_non_nullable as String,watchCode: null == watchCode ? _self.watchCode : watchCode // ignore: cast_nullable_to_non_nullable
as String,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isSuccess: null == isSuccess ? _self.isSuccess : isSuccess // ignore: cast_nullable_to_non_nullable
as bool,pin: null == pin ? _self.pin : pin // ignore: cast_nullable_to_non_nullable
as String,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,lastSignature: null == lastSignature ? _self.lastSignature : lastSignature // ignore: cast_nullable_to_non_nullable
as String, as String,
)); ));
} }
@@ -159,10 +168,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( AddKidStep step, String firstName, String lastName, DateTime? bornAt, String address, String strapQr, String watchQr, String watchCode, bool loading, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( AddKidStep step, String id, String parentId, String firstName, String lastName, DateTime? bornAt, String address, String genrer, String relationType, String strapQr, String strapCode, String watchQr, String watchCode, bool isLoading, String errorMessage, bool isSuccess, String pin, bool isSigning, String lastSignature)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _AddKidFlowState() when $default != null: case _AddKidFlowState() when $default != null:
return $default(_that.step,_that.firstName,_that.lastName,_that.bornAt,_that.address,_that.strapQr,_that.watchQr,_that.watchCode,_that.loading,_that.errorMessage);case _: return $default(_that.step,_that.id,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.address,_that.genrer,_that.relationType,_that.strapQr,_that.strapCode,_that.watchQr,_that.watchCode,_that.isLoading,_that.errorMessage,_that.isSuccess,_that.pin,_that.isSigning,_that.lastSignature);case _:
return orElse(); return orElse();
} }
@@ -180,10 +189,10 @@ return $default(_that.step,_that.firstName,_that.lastName,_that.bornAt,_that.add
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( AddKidStep step, String firstName, String lastName, DateTime? bornAt, String address, String strapQr, String watchQr, String watchCode, bool loading, String errorMessage) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( AddKidStep step, String id, String parentId, String firstName, String lastName, DateTime? bornAt, String address, String genrer, String relationType, String strapQr, String strapCode, String watchQr, String watchCode, bool isLoading, String errorMessage, bool isSuccess, String pin, bool isSigning, String lastSignature) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _AddKidFlowState(): case _AddKidFlowState():
return $default(_that.step,_that.firstName,_that.lastName,_that.bornAt,_that.address,_that.strapQr,_that.watchQr,_that.watchCode,_that.loading,_that.errorMessage);case _: return $default(_that.step,_that.id,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.address,_that.genrer,_that.relationType,_that.strapQr,_that.strapCode,_that.watchQr,_that.watchCode,_that.isLoading,_that.errorMessage,_that.isSuccess,_that.pin,_that.isSigning,_that.lastSignature);case _:
throw StateError('Unexpected subclass'); throw StateError('Unexpected subclass');
} }
@@ -200,10 +209,10 @@ return $default(_that.step,_that.firstName,_that.lastName,_that.bornAt,_that.add
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( AddKidStep step, String firstName, String lastName, DateTime? bornAt, String address, String strapQr, String watchQr, String watchCode, bool loading, String errorMessage)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( AddKidStep step, String id, String parentId, String firstName, String lastName, DateTime? bornAt, String address, String genrer, String relationType, String strapQr, String strapCode, String watchQr, String watchCode, bool isLoading, String errorMessage, bool isSuccess, String pin, bool isSigning, String lastSignature)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _AddKidFlowState() when $default != null: case _AddKidFlowState() when $default != null:
return $default(_that.step,_that.firstName,_that.lastName,_that.bornAt,_that.address,_that.strapQr,_that.watchQr,_that.watchCode,_that.loading,_that.errorMessage);case _: return $default(_that.step,_that.id,_that.parentId,_that.firstName,_that.lastName,_that.bornAt,_that.address,_that.genrer,_that.relationType,_that.strapQr,_that.strapCode,_that.watchQr,_that.watchCode,_that.isLoading,_that.errorMessage,_that.isSuccess,_that.pin,_that.isSigning,_that.lastSignature);case _:
return null; return null;
} }
@@ -215,19 +224,28 @@ return $default(_that.step,_that.firstName,_that.lastName,_that.bornAt,_that.add
class _AddKidFlowState implements DeviceSetupViewState { class _AddKidFlowState implements DeviceSetupViewState {
const _AddKidFlowState({this.step = AddKidStep.intro, this.firstName = '', this.lastName = '', this.bornAt, this.address = '', this.strapQr = '', this.watchQr = '', this.watchCode = '', this.loading = false, this.errorMessage = ''}); const _AddKidFlowState({this.step = AddKidStep.intro, this.id = '', this.parentId = '', this.firstName = '', this.lastName = '', this.bornAt, this.address = '', this.genrer = '', this.relationType = '', this.strapQr = '', this.strapCode = '', this.watchQr = '', this.watchCode = '', this.isLoading = false, this.errorMessage = '', this.isSuccess = false, this.pin = '', this.isSigning = false, this.lastSignature = ''});
@override@JsonKey() final AddKidStep step; @override@JsonKey() final AddKidStep step;
@override@JsonKey() final String id;
@override@JsonKey() final String parentId;
@override@JsonKey() final String firstName; @override@JsonKey() final String firstName;
@override@JsonKey() final String lastName; @override@JsonKey() final String lastName;
@override final DateTime? bornAt; @override final DateTime? bornAt;
@override@JsonKey() final String address; @override@JsonKey() final String address;
@override@JsonKey() final String genrer;
@override@JsonKey() final String relationType;
@override@JsonKey() final String strapQr; @override@JsonKey() final String strapQr;
@override@JsonKey() final String strapCode;
@override@JsonKey() final String watchQr; @override@JsonKey() final String watchQr;
@override@JsonKey() final String watchCode; @override@JsonKey() final String watchCode;
@override@JsonKey() final bool loading; @override@JsonKey() final bool isLoading;
@override@JsonKey() final String errorMessage; @override@JsonKey() final String errorMessage;
@override@JsonKey() final bool isSuccess;
@override@JsonKey() final String pin;
@override@JsonKey() final bool isSigning;
@override@JsonKey() final String lastSignature;
/// Create a copy of DeviceSetupViewState /// Create a copy of DeviceSetupViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -239,16 +257,16 @@ _$AddKidFlowStateCopyWith<_AddKidFlowState> get copyWith => __$AddKidFlowStateCo
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddKidFlowState&&(identical(other.step, step) || other.step == step)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.address, address) || other.address == address)&&(identical(other.strapQr, strapQr) || other.strapQr == strapQr)&&(identical(other.watchQr, watchQr) || other.watchQr == watchQr)&&(identical(other.watchCode, watchCode) || other.watchCode == watchCode)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddKidFlowState&&(identical(other.step, step) || other.step == step)&&(identical(other.id, id) || other.id == id)&&(identical(other.parentId, parentId) || other.parentId == parentId)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.address, address) || other.address == address)&&(identical(other.genrer, genrer) || other.genrer == genrer)&&(identical(other.relationType, relationType) || other.relationType == relationType)&&(identical(other.strapQr, strapQr) || other.strapQr == strapQr)&&(identical(other.strapCode, strapCode) || other.strapCode == strapCode)&&(identical(other.watchQr, watchQr) || other.watchQr == watchQr)&&(identical(other.watchCode, watchCode) || other.watchCode == watchCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isSuccess, isSuccess) || other.isSuccess == isSuccess)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.lastSignature, lastSignature) || other.lastSignature == lastSignature));
} }
@override @override
int get hashCode => Object.hash(runtimeType,step,firstName,lastName,bornAt,address,strapQr,watchQr,watchCode,loading,errorMessage); int get hashCode => Object.hashAll([runtimeType,step,id,parentId,firstName,lastName,bornAt,address,genrer,relationType,strapQr,strapCode,watchQr,watchCode,isLoading,errorMessage,isSuccess,pin,isSigning,lastSignature]);
@override @override
String toString() { String toString() {
return 'DeviceSetupViewState(step: $step, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, address: $address, strapQr: $strapQr, watchQr: $watchQr, watchCode: $watchCode, loading: $loading, errorMessage: $errorMessage)'; return 'DeviceSetupViewState(step: $step, id: $id, parentId: $parentId, firstName: $firstName, lastName: $lastName, bornAt: $bornAt, address: $address, genrer: $genrer, relationType: $relationType, strapQr: $strapQr, strapCode: $strapCode, watchQr: $watchQr, watchCode: $watchCode, isLoading: $isLoading, errorMessage: $errorMessage, isSuccess: $isSuccess, pin: $pin, isSigning: $isSigning, lastSignature: $lastSignature)';
} }
@@ -259,7 +277,7 @@ abstract mixin class _$AddKidFlowStateCopyWith<$Res> implements $DeviceSetupView
factory _$AddKidFlowStateCopyWith(_AddKidFlowState value, $Res Function(_AddKidFlowState) _then) = __$AddKidFlowStateCopyWithImpl; factory _$AddKidFlowStateCopyWith(_AddKidFlowState value, $Res Function(_AddKidFlowState) _then) = __$AddKidFlowStateCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
AddKidStep step, String firstName, String lastName, DateTime? bornAt, String address, String strapQr, String watchQr, String watchCode, bool loading, String errorMessage AddKidStep step, String id, String parentId, String firstName, String lastName, DateTime? bornAt, String address, String genrer, String relationType, String strapQr, String strapCode, String watchQr, String watchCode, bool isLoading, String errorMessage, bool isSuccess, String pin, bool isSigning, String lastSignature
}); });
@@ -276,18 +294,27 @@ class __$AddKidFlowStateCopyWithImpl<$Res>
/// Create a copy of DeviceSetupViewState /// Create a copy of DeviceSetupViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? step = null,Object? firstName = null,Object? lastName = null,Object? bornAt = freezed,Object? address = null,Object? strapQr = null,Object? watchQr = null,Object? watchCode = null,Object? loading = null,Object? errorMessage = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? step = null,Object? id = null,Object? parentId = null,Object? firstName = null,Object? lastName = null,Object? bornAt = freezed,Object? address = null,Object? genrer = null,Object? relationType = null,Object? strapQr = null,Object? strapCode = null,Object? watchQr = null,Object? watchCode = null,Object? isLoading = null,Object? errorMessage = null,Object? isSuccess = null,Object? pin = null,Object? isSigning = null,Object? lastSignature = null,}) {
return _then(_AddKidFlowState( return _then(_AddKidFlowState(
step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable
as AddKidStep,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable as AddKidStep,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,parentId: null == parentId ? _self.parentId : parentId // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable as String,bornAt: freezed == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as DateTime?,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as DateTime?,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,genrer: null == genrer ? _self.genrer : genrer // ignore: cast_nullable_to_non_nullable
as String,relationType: null == relationType ? _self.relationType : relationType // ignore: cast_nullable_to_non_nullable
as String,strapQr: null == strapQr ? _self.strapQr : strapQr // ignore: cast_nullable_to_non_nullable as String,strapQr: null == strapQr ? _self.strapQr : strapQr // ignore: cast_nullable_to_non_nullable
as String,strapCode: null == strapCode ? _self.strapCode : strapCode // ignore: cast_nullable_to_non_nullable
as String,watchQr: null == watchQr ? _self.watchQr : watchQr // ignore: cast_nullable_to_non_nullable as String,watchQr: null == watchQr ? _self.watchQr : watchQr // ignore: cast_nullable_to_non_nullable
as String,watchCode: null == watchCode ? _self.watchCode : watchCode // ignore: cast_nullable_to_non_nullable as String,watchCode: null == watchCode ? _self.watchCode : watchCode // ignore: cast_nullable_to_non_nullable
as String,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isSuccess: null == isSuccess ? _self.isSuccess : isSuccess // ignore: cast_nullable_to_non_nullable
as bool,pin: null == pin ? _self.pin : pin // ignore: cast_nullable_to_non_nullable
as String,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,lastSignature: null == lastSignature ? _self.lastSignature : lastSignature // ignore: cast_nullable_to_non_nullable
as String, as String,
)); ));
} }

View File

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

View File

@@ -9,66 +9,111 @@ class ProfileStepScreen extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider); const Map<String, String> genrer = <String, String>{
'F': 'Femenino',
'M': 'Masculino',
'O': 'Otro',
};
const Map<String, String> relationship = <String, String>{
'FATHER': 'Padre',
'MOTHER': 'Madre',
'OTHER': 'Otro',
};
final theme = ref.watch(themePortProvider);
final state = ref.watch(deviceSetupViewModelProvider);
final vm = ref.read(deviceSetupViewModelProvider.notifier); final vm = ref.read(deviceSetupViewModelProvider.notifier);
return Column( return SingleChildScrollView(
mainAxisSize: MainAxisSize.max, child: Column(
crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max,
children: [ crossAxisAlignment: CrossAxisAlignment.center,
const SizedBox(height: 30), children: [
Text( const SizedBox(height: 30),
context.translate(I18n.deviceSetup_intro_step_1), Text(
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), context.translate(I18n.deviceSetup_intro_step_1),
), style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
const SizedBox(height: 30), ),
Text( const SizedBox(height: 30),
context.translate(I18n.deviceSetup_accountData_info), Text(
style: TextStyle(fontSize: 18), context.translate(I18n.deviceSetup_accountData_info),
textAlign: TextAlign.center, style: const TextStyle(fontSize: 18),
), textAlign: TextAlign.center,
SizedBox(height: 20), ),
const SizedBox(height: 20),
Text(
context.translate(I18n.deviceSetup_startWithOneKid_info),
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.only(left: 24, right: 24),
child: Column(
children: [
CustomTextField(
label: context.translate(I18n.firstNameLabel),
hint: context.translate(I18n.firstNameHint),
controller: vm.firstNameController,
),
const SizedBox(height: 8),
CustomTextField(
label: context.translate(I18n.lastNameLabel),
hint: context.translate(I18n.lastNameHint),
controller: vm.lastNameController,
),
const SizedBox(height: 8),
Text( GestureDetector(
context.translate(I18n.deviceSetup_startWithOneKid_info), onTap: () => vm.pickBornAt(context),
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500), child: AbsorbPointer(
textAlign: TextAlign.center, child: CustomTextField(
), label: context.translate(I18n.birthDateLabel),
SizedBox(height: 20), hint: context.translate(I18n.birthDateHint),
Padding( controller: vm.bornAtController,
padding: const EdgeInsets.only(left: 24, right: 24), readOnly: true,
child: Column( keyboardType: TextInputType.none,
children: [ ),
CustomTextField(
label: context.translate(I18n.firstNameLabel),
hint: context.translate(I18n.firstNameHint),
controller: vm.firstNameController,
),
const SizedBox(height: 8),
CustomTextField(
label: context.translate(I18n.lastNameLabel),
hint: context.translate(I18n.lastNameHint),
controller: vm.lastNameController,
),
const SizedBox(height: 8),
GestureDetector(
onTap: () => vm.pickBornAt(context),
child: AbsorbPointer(
child: CustomTextField(
label: context.translate(I18n.birthDateLabel),
hint: context.translate(I18n.birthDateHint),
controller: vm.bornAtController,
readOnly: true,
keyboardType: TextInputType.none,
), ),
), ),
),
], const SizedBox(height: 8),
CustomDropdown(
items: genrer.values.map(Text.new).toList(growable: false),
values: genrer.keys.toList(growable: false),
value: state.genrer.isEmpty ? null : state.genrer,
hint: 'Género',
onChanged: (v) => vm.onGenrerChanged(v as String?),
),
const SizedBox(height: 8),
CustomDropdown(
items: relationship.values
.map(Text.new)
.toList(growable: false),
values: relationship.keys.toList(growable: false),
value: state.relationType.isEmpty ? null : state.relationType,
hint: 'Relación',
onChanged: (v) => vm.onRelationTypeChanged(v as String?),
),
const SizedBox(height: 8),
CustomTextField(
label: 'Dirección',
hint: 'Dirección',
controller: vm.addressController,
),
const SizedBox(height: 8),
],
),
), ),
), ],
], ),
); );
} }
} }

View File

@@ -0,0 +1,212 @@
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

@@ -26,126 +26,127 @@ class ScanStrapAndWatchStepScreen extends ConsumerWidget {
final vm = ref.read(deviceSetupViewModelProvider.notifier); final vm = ref.read(deviceSetupViewModelProvider.notifier);
final state = ref.watch(deviceSetupViewModelProvider); final state = ref.watch(deviceSetupViewModelProvider);
return Column( return SingleChildScrollView(
mainAxisSize: MainAxisSize.max, child: Column(
crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max,
children: [ crossAxisAlignment: CrossAxisAlignment.center,
const SizedBox(height: 30), children: [
const SizedBox(height: 30),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 65), padding: const EdgeInsets.symmetric(horizontal: 65),
child: Text( child: Text(
context.translate(I18n.deviceSetup_linkInfo_title), context.translate(I18n.deviceSetup_linkInfo_title),
style: const TextStyle( style: const TextStyle(
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
height: 1.2, height: 1.2,
),
textAlign: TextAlign.center,
), ),
textAlign: TextAlign.center,
), ),
),
const SizedBox(height: 18), const SizedBox(height: 18),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 100), padding: const EdgeInsets.symmetric(horizontal: 100),
child: ScanLinkStepsIndicator( child: ScanLinkStepsIndicator(
step: step, step: step,
activeColor: activeColor, activeColor: activeColor,
inactiveCircleColor: inactiveCircleColor, inactiveCircleColor: inactiveCircleColor,
inactiveLineColor: inactiveLineColor, inactiveLineColor: inactiveLineColor,
textPrimary: textPrimary, textPrimary: textPrimary,
),
), ),
),
const SizedBox(height: 12), const SizedBox(height: 12),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 35), padding: const EdgeInsets.symmetric(horizontal: 35),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text.rich( child: Text.rich(
TextSpan( TextSpan(
children: [ children: [
TextSpan( TextSpan(
text: context.translate( text: context.translate(
I18n.deviceSetup_linkInfo_item1_prefix, I18n.deviceSetup_linkInfo_item1_prefix,
),
), ),
), TextSpan(
TextSpan( text: context.translate(
text: context.translate( I18n.deviceSetup_linkInfo_item1_boldWord,
I18n.deviceSetup_linkInfo_item1_boldWord, ),
style: TextStyle(fontWeight: FontWeight.w800),
), ),
style: TextStyle(fontWeight: FontWeight.w800), ],
), ),
], style: const TextStyle(fontSize: 18),
), ),
style: const TextStyle(fontSize: 18),
), ),
), ),
), Expanded(
Expanded( child: Align(
child: Align( alignment: Alignment.centerRight,
alignment: Alignment.centerRight, child: Text.rich(
child: Text.rich( TextSpan(
TextSpan( children: [
children: [ TextSpan(
TextSpan( text: context.translate(
text: context.translate( I18n.deviceSetup_linkInfo_item2_prefix,
I18n.deviceSetup_linkInfo_item2_prefix, ),
), ),
), TextSpan(
TextSpan( text: context.translate(
text: context.translate( I18n.deviceSetup_linkInfo_item2_boldWord,
I18n.deviceSetup_linkInfo_item2_boldWord, ),
style: TextStyle(fontWeight: FontWeight.w800),
), ),
style: TextStyle(fontWeight: FontWeight.w800), ],
), ),
], style: const TextStyle(fontSize: 18),
), ),
style: const TextStyle(fontSize: 18),
), ),
), ),
), ],
],
),
),
const SizedBox(height: 28),
InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () async {
final result = await Navigator.of(context).push<String>(
MaterialPageRoute(builder: (_) => const QrScannerScreen()),
);
if (result == null || result.isEmpty) return;
vm.onQrScanned(step: step, qr: result);
},
child: Container(
width: 170,
height: 170,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade500, width: 1),
borderRadius: BorderRadius.circular(16),
), ),
child: Center( ),
child: SvgPicture.asset(
"assets/images/ui/qr.svg", const SizedBox(height: 28),
width: 90,
height: 90, InkWell(
fit: BoxFit.contain, borderRadius: BorderRadius.circular(16),
onTap: () async {
final result = await Navigator.of(context).push<String>(
MaterialPageRoute(builder: (_) => const QrScannerScreen()),
);
if (result == null || result.isEmpty) return;
vm.onQrScanned(step: step, qr: result);
},
child: Container(
width: 170,
height: 170,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade500, width: 1),
borderRadius: BorderRadius.circular(16),
),
child: Center(
child: SvgPicture.asset(
"assets/images/ui/qr.svg",
width: 90,
height: 90,
fit: BoxFit.contain,
),
), ),
), ),
), ),
),
const SizedBox(height: 22), const SizedBox(height: 22),
if (step == ScanLinkStep.watch) ...[ // if (step == ScanLinkStep.watch) ...[
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column( child: Column(
@@ -158,43 +159,52 @@ class ScanStrapAndWatchStepScreen extends ConsumerWidget {
const SizedBox(height: 12), const SizedBox(height: 12),
Row( Row(
children: [ children: [
Expanded(child: CustomTextField(hint: "XXXXXXXXXX")),
const SizedBox(width: 12),
Expanded( Expanded(
child: PrimaryButton( child: CustomTextField(
onPressed: () {}, hint: "XXXXXXXXXX",
text: context.translate( controller: ScanLinkStep.strap == step
I18n.deviceSetup_watchCode_continueWithCode, ? vm.strapCodeController
), : vm.watchCodeController,
size: 14, // controller: vm.codeController,
color: theme.getColorFor(ThemeCode.buttonSecondary),
), ),
), ),
const SizedBox(width: 12),
// Expanded(
// child: PrimaryButton(
// onPressed: () {},
// text: context.translate(
// I18n.deviceSetup_watchCode_continueWithCode,
// ),
// size: 14,
// color: theme.getColorFor(ThemeCode.buttonSecondary),
// ),
// ),
], ],
), ),
], ],
), ),
), ),
// ],
const SizedBox(height: 10),
Column(
children: [
Text(
context.translate(I18n.deviceSetup_linkTroubleshoot_title),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
CustomTextButton(
onPressed: () {},
text: context.translate(I18n.deviceSetup_contactUs),
weight: FontWeight.w800,
size: 18,
),
],
),
], ],
),
const SizedBox(height: 10),
Column(
children: [
Text(
context.translate(I18n.deviceSetup_linkTroubleshoot_title),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18),
),
CustomTextButton(
onPressed: () {},
text: context.translate(I18n.deviceSetup_contactUs),
weight: FontWeight.w800,
size: 18,
),
],
),
],
); );
} }
} }

View File

@@ -1,3 +1,4 @@
import 'package:auth/src/features/device_setup/presentation/state/device_setup_view_model.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -9,6 +10,8 @@ class SuccessStepScreen extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider); final theme = ref.watch(themePortProvider);
final state = ref.watch(deviceSetupViewModelProvider);
final vm = ref.read(deviceSetupViewModelProvider.notifier);
return Column( return Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@@ -31,19 +34,19 @@ class SuccessStepScreen extends ConsumerWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Text( Text(
'Julián Alcalá', '${state.firstName} ${state.lastName}',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500), style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
SizedBox(height: 40), SizedBox(height: 40),
Text( Text(
'Reloj: SaveWatch Plus 2', 'Reloj: ${state.watchCode}',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),
Text( Text(
'ID del reloj: 1106652524', 'ID de la tarjeta: ${state.strapCode} ',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontSize: 16), style: TextStyle(fontSize: 16),
), ),

View File

@@ -56,7 +56,7 @@ class RequestLinkPhoneScreen extends ConsumerWidget {
children: [ children: [
CountryPrefixPicker( CountryPrefixPicker(
headerText: context.translate(I18n.selectYourCountry), headerText: context.translate(I18n.selectYourCountry),
initialCountryCode: viewState.dialCode, initialSelection: viewState.dialCode,
onChanged: (country) { onChanged: (country) {
viewModel.updateDialCode( viewModel.updateDialCode(
country.dialCode ?? viewState.dialCode, country.dialCode ?? viewState.dialCode,

View File

@@ -82,6 +82,7 @@ class LoginScreen extends ConsumerWidget {
} }
final user = await vm.getUserInfo(); final user = await vm.getUserInfo();
// await ref.watch(scaWalletsUseCaseProvider).scaWallets();
if (!context.mounted) return; if (!context.mounted) return;
if (user == null) { if (user == null) {
@@ -122,7 +123,7 @@ class LoginScreen extends ConsumerWidget {
if (!context.mounted) return; if (!context.mounted) return;
if (verified == true) { if (verified == true) {
navigationContract.goTo(AppRoutes.dashboardHome); navigationContract.goTo(AppRoutes.scaTreezor);
} }
} }
@@ -156,10 +157,10 @@ class LoginScreen extends ConsumerWidget {
onSignIn: () => _onLogIn(context, ref), onSignIn: () => _onLogIn(context, ref),
), ),
SizedBox(height: 30), SizedBox(height: 30),
_OrContinueWith(theme: theme), // _OrContinueWith(theme: theme),
SizedBox(height: 24), // SizedBox(height: 24),
_SocialButtons(theme: theme), // _SocialButtons(theme: theme),
SizedBox(height: 30), // SizedBox(height: 30),
_Footer(navigationContract: navigationContract), _Footer(navigationContract: navigationContract),
], ],
), ),

View File

@@ -46,7 +46,7 @@ class TwoFactorBottomSheetView extends StatelessWidget {
color: theme.getColorFor(ThemeCode.backgroundPrimary), color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
), ),
padding: EdgeInsets.fromLTRB(24, 12, 24, 24 + bottomInset), padding: EdgeInsets.fromLTRB(12, 12, 12, 24 + bottomInset),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [

View File

@@ -31,7 +31,7 @@ class OnboardingScreen extends ConsumerWidget {
void goToNext() { void goToNext() {
if (isLast) { if (isLast) {
navigationContract.goTo(AppRoutes.linkPhone); navigationContract.goTo(AppRoutes.login);
} else { } else {
pageController.nextPage( pageController.nextPage(
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
@@ -104,7 +104,7 @@ class OnboardingScreen extends ConsumerWidget {
? const SizedBox.shrink() ? const SizedBox.shrink()
: TextButton( : TextButton(
onPressed: () => onPressed: () =>
navigationContract.goTo(AppRoutes.linkPhone), navigationContract.goTo(AppRoutes.login),
child: Text( child: Text(
context.translate(I18n.skip), context.translate(I18n.skip),
style: TextStyle( style: TextStyle(

View File

@@ -23,20 +23,28 @@ class RequestRecoveryScreen extends ConsumerWidget {
return Scaffold( return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Container( body: Container(
margin: EdgeInsets.all(SizeUtils.getByScreen(small: 30, big: 30, xl: 20)), margin: EdgeInsets.all(
SizeUtils.getByScreen(small: 30, big: 30, xl: 20),
),
child: Center( child: Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( Text(
context.translate(I18n.recoverPasswordTitle), context.translate(I18n.recoverPasswordTitle),
style: TextStyle(fontWeight: FontWeight.bold, fontSize: SizeUtils.getByScreen(small: 29, big: 29, xl: 26)), style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: SizeUtils.getByScreen(small: 29, big: 29, xl: 26),
),
), ),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 32)), SizedBox(height: SizeUtils.getByScreen(small: 24, big: 32)),
Text( Text(
context.translate(I18n.recoverPasswordSubtitle), context.translate(I18n.recoverPasswordSubtitle),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(letterSpacing: 0, fontSize: SizeUtils.getByScreen(small: 18, big: 18, xl: 16)), style: TextStyle(
letterSpacing: 0,
fontSize: SizeUtils.getByScreen(small: 18, big: 18, xl: 16),
),
), ),
SizedBox(height: SizeUtils.getByScreen(small: 56, big: 48)), SizedBox(height: SizeUtils.getByScreen(small: 56, big: 48)),
CustomTextField( CustomTextField(
@@ -44,7 +52,9 @@ class RequestRecoveryScreen extends ConsumerWidget {
hint: context.translate(I18n.email), hint: context.translate(I18n.email),
controller: viewModel.emailController, controller: viewModel.emailController,
), ),
SizedBox(height: SizeUtils.getByScreen(small: 40, big: 40, xl: 28)), SizedBox(
height: SizeUtils.getByScreen(small: 40, big: 40, xl: 28),
),
Align( Align(
alignment: Alignment.bottomLeft, alignment: Alignment.bottomLeft,
child: Text( child: Text(
@@ -57,7 +67,7 @@ class RequestRecoveryScreen extends ConsumerWidget {
children: [ children: [
CountryPrefixPicker( CountryPrefixPicker(
headerText: context.translate(I18n.selectYourCountry), headerText: context.translate(I18n.selectYourCountry),
initialCountryCode: viewState.dialCode, initialSelection: viewState.dialCode,
onChanged: (country) { onChanged: (country) {
viewModel.updateDialCode( viewModel.updateDialCode(
country.dialCode ?? viewState.dialCode, country.dialCode ?? viewState.dialCode,
@@ -65,7 +75,9 @@ class RequestRecoveryScreen extends ConsumerWidget {
}, },
width: 80, width: 80,
), ),
SizedBox(width: SizeUtils.getByScreen(small: 10, big: 10, xl: 6)), SizedBox(
width: SizeUtils.getByScreen(small: 10, big: 10, xl: 6),
),
Expanded( Expanded(
child: CustomTextField( child: CustomTextField(
hint: context.translate(I18n.phoneNumber), hint: context.translate(I18n.phoneNumber),
@@ -75,18 +87,20 @@ class RequestRecoveryScreen extends ConsumerWidget {
), ),
], ],
), ),
SizedBox(height: SizeUtils.getByScreen(small: 40, big: 40, xl: 28)), SizedBox(
height: SizeUtils.getByScreen(small: 40, big: 40, xl: 28),
),
if (viewState.errorMessage.isNotEmpty) ...[ if (viewState.errorMessage.isNotEmpty) ...[
Text( Text(
context.translate(viewState.errorMessage), context.translate(viewState.errorMessage),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1), color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12, fontSize: 12,
),
), ),
SizedBox(height: 40), ),
], SizedBox(height: 40),
],
Row( Row(
children: [ children: [
Expanded( Expanded(
@@ -96,17 +110,23 @@ class RequestRecoveryScreen extends ConsumerWidget {
size: SizeUtils.getByScreen(small: 16, big: 16, xl: 14), size: SizeUtils.getByScreen(small: 16, big: 16, xl: 14),
), ),
), ),
SizedBox(width: SizeUtils.getByScreen(small: 20, big: 20, xl: 10)), SizedBox(
width: SizeUtils.getByScreen(small: 20, big: 20, xl: 10),
),
Expanded( Expanded(
child: PrimaryButton( child: PrimaryButton(
onPressed: () async { onPressed: () async {
await viewModel.requestRecovery(); await viewModel.requestRecovery();
final updatedState = ref.read(recoverPasswordViewModelProvider); final updatedState = ref.read(
recoverPasswordViewModelProvider,
);
if (updatedState.recoveryRequested) { if (updatedState.recoveryRequested) {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => SentScreen(navigationContract: navigationContract), builder: (_) => SentScreen(
navigationContract: navigationContract,
),
), ),
); );
} }

View File

@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
import 'sca_treezor_screen.dart';
class SCATreezorBuilder {
const SCATreezorBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: SCATreezorScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,358 @@
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 'sca_treezor_view_model.dart';
import 'sca_treezor_view_state.dart';
class SCATreezorScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const SCATreezorScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
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(
child: Padding(
padding: const EdgeInsets.all(16),
child: state.isLoading
? const CircularProgressIndicator()
: state.isProvisioned
? _ConnectionBody(
state: state,
vm: vm,
navigationContract: navigationContract,
)
: _ProvisioningBody(state: state, vm: vm),
),
),
);
}
}
class _ProvisioningBody extends StatelessWidget {
final SCATreezorViewState state;
final SCATreezorViewModel vm;
const _ProvisioningBody({required this.state, required this.vm});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 16),
if (state.isProvisioning) ...[
const CircularProgressIndicator(),
const SizedBox(height: 8),
const Text('Provisionando...'),
] else ...[
ElevatedButton(
onPressed: vm.provisionWallet,
child: const Text('Provisionar (manual)'),
),
],
],
);
}
}
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

@@ -0,0 +1,334 @@
import 'dart:async';
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_shared/sf_shared.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'sca_treezor_view_state.dart';
final scaTreezorViewModelProvider =
NotifierProvider.autoDispose<SCATreezorViewModel, SCATreezorViewState>(
SCATreezorViewModel.new,
);
class SCATreezorViewModel extends Notifier<SCATreezorViewState> {
static const _kIsProvisionedKey = 'treezor.isProvisioned';
static const _kIsFirstConnectionDoneKey = 'treezor.isFirstConnectionDone';
static const int _pinLength = 6;
late final ScaWalletsUseCase _scaWalletsUseCase;
late final SendJWSSesionUseCase _sendJWSSesionUseCase;
late final TreezorWalletProvisioningService _provisioningService;
late final TreezorWalletConnectionService _connectionService;
late final TreezorWalletSignatureService _signatureService;
late final TextEditingController codeController;
bool _started = false;
bool _syncingController = false;
@override
SCATreezorViewState build() {
Future.microtask(_initAsync);
return const SCATreezorViewState(isLoading: true);
}
Future<void> _initAsync() async {
_scaWalletsUseCase = ref.read(scaWalletsUseCaseProvider);
_sendJWSSesionUseCase = ref.read(sendJWSSesionUseCaseProvider);
_provisioningService = GetIt.I<TreezorWalletProvisioningService>();
_connectionService = GetIt.I<TreezorWalletConnectionService>();
_signatureService = GetIt.I<TreezorWalletSignatureService>();
codeController = TextEditingController();
codeController.addListener(_onCodeChanged);
ref.onDispose(_disposeControllers);
if (!_started) {
_started = true;
await _bootstrap();
}
}
void _disposeControllers() {
codeController.removeListener(_onCodeChanged);
codeController.dispose();
}
void _onCodeChanged() {
if (_syncingController) return;
state = state.copyWith(
activationCode: codeController.text,
errorMessage: '',
success: false,
);
}
Future<void> _bootstrap() async {
final prefs = await SharedPreferences.getInstance();
final alreadyProvisioned = prefs.getBool(_kIsProvisionedKey) ?? false;
final firstConnectionDone =
prefs.getBool(_kIsFirstConnectionDoneKey) ?? false;
if (alreadyProvisioned) {
state = state.copyWith(
isLoading: false,
isProvisioned: true,
isFirstConnectionDone: firstConnectionDone,
errorMessage: '',
success: true,
);
return;
}
await _loadActivationCode();
final code = state.activationCode.trim();
if (code.isEmpty) {
state = state.copyWith(isLoading: false);
return;
}
await provisionWallet();
}
Future<void> _loadActivationCode() async {
state = state.copyWith(errorMessage: '', isLoading: true, success: false);
try {
final ScaWalletsResponseEntity response = await _scaWalletsUseCase
.scaWallets();
final rawCode = response.item.activationCode;
final sanitized = _sanitizeActivationCode(rawCode);
_syncingController = true;
codeController.text = sanitized;
_syncingController = false;
state = state.copyWith(
activationCode: sanitized,
errorMessage: '',
isCreated: response.isCreated,
isLoading: false,
walletId: response.item.id,
walletStatus: response.item.status,
);
} catch (e) {
state = state.copyWith(
errorMessage: '❌ Error obteniendo activationCode: $e',
isLoading: false,
);
}
}
Future<void> provisionWallet() async {
if (state.isProvisioning) return;
final activationCode = state.activationCode.trim();
if (activationCode.isEmpty) {
state = state.copyWith(errorMessage: '⚠️ Activation code vacío');
return;
}
state = state.copyWith(
errorMessage: '',
isProvisioning: true,
success: false,
);
try {
await _provisioningService.provisionWallet(
activationCode: activationCode,
);
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kIsProvisionedKey, true);
await prefs.setBool(_kIsFirstConnectionDoneKey, false);
state = state.copyWith(
isProvisioning: false,
isProvisioned: true,
isFirstConnectionDone: false,
success: true,
isLoading: false,
errorMessage: '',
);
} catch (e) {
state = state.copyWith(
isProvisioning: false,
success: false,
errorMessage: '❌ Error en provisioning: $e',
);
}
}
void onDigitPressed(String digit) {
if (state.isConnecting) return;
if (state.pin.length >= _pinLength) return;
state = state.copyWith(
pin: '${state.pin}$digit',
errorMessage: '',
success: false,
);
}
void onBackspacePressed() {
if (state.isConnecting) return;
if (state.pin.isEmpty) return;
state = state.copyWith(
pin: state.pin.substring(0, state.pin.length - 1),
errorMessage: '',
success: false,
);
}
void clearPin() {
if (state.isConnecting) return;
state = state.copyWith(pin: '', errorMessage: '', success: false);
}
bool get canSubmitPin => state.pin.length == _pinLength;
Future<bool> connectWithPin() async {
if (state.isConnecting) return false;
if (!state.isProvisioned) {
state = state.copyWith(errorMessage: '⚠️ No está provisionado aún');
return false;
}
if (!canSubmitPin) {
state = state.copyWith(
errorMessage: '⚠️ El PIN debe tener $_pinLength dígitos',
);
return false;
}
state = state.copyWith(
isConnecting: true,
errorMessage: '',
success: false,
);
final prefs = await SharedPreferences.getInstance();
final firstConnectionDone =
prefs.getBool(_kIsFirstConnectionDoneKey) ?? false;
try {
if (!firstConnectionDone) {
await _connectionService.connectFirstTime(newPin: state.pin);
await prefs.setBool(_kIsFirstConnectionDoneKey, true);
state = state.copyWith(isFirstConnectionDone: true);
} else {
await _connectionService.connectWithPin(loginPin: state.pin);
}
state = state.copyWith(
isConnecting: false,
isConnected: true,
success: true,
errorMessage: '',
);
return true;
} catch (e) {
state = state.copyWith(
isConnecting: false,
isConnected: false,
success: false,
errorMessage: '❌ Error conectando wallet: $e',
pin: '',
);
return false;
}
}
Future<void> retry() async {
state = const SCATreezorViewState(isLoading: true);
await _bootstrap();
}
Future<void> signJwtSca() async {
if (state.isSigning) return;
if (state.isConnected == false) {
state = state.copyWith(errorMessage: '⚠️ Conecta la wallet primero');
return;
}
if (!canSubmitPin) {
state = state.copyWith(errorMessage: '⚠️ PIN de $_pinLength dígitos');
return;
}
state = state.copyWith(
errorMessage: '',
isSigning: true,
success: false,
lastSignature: '',
);
try {
final signature = await _signatureService.generateJwsWithPin(
message: 'JWT sesion',
input: '',
pin: state.pin,
);
await _sendJWSSesionUseCase.sendJWSSesion(jws: signature);
state = state.copyWith(
isSigning: false,
success: true,
lastSignature: signature,
errorMessage: '',
pin: '',
);
} catch (e) {
state = state.copyWith(
isSigning: false,
success: false,
errorMessage: '❌ Error firmando JWT SCA: $e',
pin: '',
);
}
}
Future<void> connectAndSignJwtSca() async {
if (state.isConnecting || state.isSigning) return;
final connected = await connectWithPin();
if (!connected) return;
await signJwtSca();
}
String _sanitizeActivationCode(String code) {
if (code.startsWith('0x')) return code.substring(2);
return code;
}
Future<void> clearLocalFlags() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_kIsProvisionedKey);
await prefs.remove(_kIsFirstConnectionDoneKey);
state = state.copyWith(
isProvisioned: false,
isFirstConnectionDone: false,
isConnected: false,
pin: '',
success: false,
errorMessage: '',
);
}
}

View File

@@ -0,0 +1,31 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'sca_treezor_view_state.freezed.dart';
@freezed
abstract class SCATreezorViewState with _$SCATreezorViewState {
const SCATreezorViewState._();
const factory SCATreezorViewState({
@Default('') String activationCode,
@Default('') String errorMessage,
@Default(false) bool isCreated,
@Default(false) bool isLoading,
@Default(false) bool isProvisioning,
@Default(false) bool isProvisioned,
@Default(false) bool isFirstConnectionDone,
@Default(false) bool isConnecting,
@Default(false) bool isConnected,
@Default('') String pin,
@Default(false) bool isSigning,
@Default('') String lastSignature,
@Default(false) bool success,
@Default('') String walletId,
@Default('') String walletStatus,
}) = _SCATreezorViewState;
bool get hasError => errorMessage.isNotEmpty;
}

View File

@@ -0,0 +1,313 @@
// 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 'sca_treezor_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SCATreezorViewState {
String get activationCode; String get errorMessage; bool get isCreated; bool get isLoading; bool get isProvisioning; bool get isProvisioned; bool get isFirstConnectionDone; bool get isConnecting; bool get isConnected; String get pin; bool get isSigning; String get lastSignature; bool get success; String get walletId; String get walletStatus;
/// Create a copy of SCATreezorViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SCATreezorViewStateCopyWith<SCATreezorViewState> get copyWith => _$SCATreezorViewStateCopyWithImpl<SCATreezorViewState>(this as SCATreezorViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SCATreezorViewState&&(identical(other.activationCode, activationCode) || other.activationCode == activationCode)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isProvisioning, isProvisioning) || other.isProvisioning == isProvisioning)&&(identical(other.isProvisioned, isProvisioned) || other.isProvisioned == isProvisioned)&&(identical(other.isFirstConnectionDone, isFirstConnectionDone) || other.isFirstConnectionDone == isFirstConnectionDone)&&(identical(other.isConnecting, isConnecting) || other.isConnecting == isConnecting)&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.lastSignature, lastSignature) || other.lastSignature == lastSignature)&&(identical(other.success, success) || other.success == success)&&(identical(other.walletId, walletId) || other.walletId == walletId)&&(identical(other.walletStatus, walletStatus) || other.walletStatus == walletStatus));
}
@override
int get hashCode => Object.hash(runtimeType,activationCode,errorMessage,isCreated,isLoading,isProvisioning,isProvisioned,isFirstConnectionDone,isConnecting,isConnected,pin,isSigning,lastSignature,success,walletId,walletStatus);
@override
String toString() {
return 'SCATreezorViewState(activationCode: $activationCode, errorMessage: $errorMessage, isCreated: $isCreated, isLoading: $isLoading, isProvisioning: $isProvisioning, isProvisioned: $isProvisioned, isFirstConnectionDone: $isFirstConnectionDone, isConnecting: $isConnecting, isConnected: $isConnected, pin: $pin, isSigning: $isSigning, lastSignature: $lastSignature, success: $success, walletId: $walletId, walletStatus: $walletStatus)';
}
}
/// @nodoc
abstract mixin class $SCATreezorViewStateCopyWith<$Res> {
factory $SCATreezorViewStateCopyWith(SCATreezorViewState value, $Res Function(SCATreezorViewState) _then) = _$SCATreezorViewStateCopyWithImpl;
@useResult
$Res call({
String activationCode, String errorMessage, bool isCreated, bool isLoading, bool isProvisioning, bool isProvisioned, bool isFirstConnectionDone, bool isConnecting, bool isConnected, String pin, bool isSigning, String lastSignature, bool success, String walletId, String walletStatus
});
}
/// @nodoc
class _$SCATreezorViewStateCopyWithImpl<$Res>
implements $SCATreezorViewStateCopyWith<$Res> {
_$SCATreezorViewStateCopyWithImpl(this._self, this._then);
final SCATreezorViewState _self;
final $Res Function(SCATreezorViewState) _then;
/// Create a copy of SCATreezorViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? activationCode = null,Object? errorMessage = null,Object? isCreated = null,Object? isLoading = null,Object? isProvisioning = null,Object? isProvisioned = null,Object? isFirstConnectionDone = null,Object? isConnecting = null,Object? isConnected = null,Object? pin = null,Object? isSigning = null,Object? lastSignature = null,Object? success = null,Object? walletId = null,Object? walletStatus = null,}) {
return _then(_self.copyWith(
activationCode: null == activationCode ? _self.activationCode : activationCode // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isProvisioning: null == isProvisioning ? _self.isProvisioning : isProvisioning // ignore: cast_nullable_to_non_nullable
as bool,isProvisioned: null == isProvisioned ? _self.isProvisioned : isProvisioned // ignore: cast_nullable_to_non_nullable
as bool,isFirstConnectionDone: null == isFirstConnectionDone ? _self.isFirstConnectionDone : isFirstConnectionDone // ignore: cast_nullable_to_non_nullable
as bool,isConnecting: null == isConnecting ? _self.isConnecting : isConnecting // ignore: cast_nullable_to_non_nullable
as bool,isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
as bool,pin: null == pin ? _self.pin : pin // ignore: cast_nullable_to_non_nullable
as String,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,lastSignature: null == lastSignature ? _self.lastSignature : lastSignature // ignore: cast_nullable_to_non_nullable
as String,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool,walletId: null == walletId ? _self.walletId : walletId // ignore: cast_nullable_to_non_nullable
as String,walletStatus: null == walletStatus ? _self.walletStatus : walletStatus // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [SCATreezorViewState].
extension SCATreezorViewStatePatterns on SCATreezorViewState {
/// 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( _SCATreezorViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SCATreezorViewState() 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( _SCATreezorViewState value) $default,){
final _that = this;
switch (_that) {
case _SCATreezorViewState():
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( _SCATreezorViewState value)? $default,){
final _that = this;
switch (_that) {
case _SCATreezorViewState() 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 activationCode, String errorMessage, bool isCreated, bool isLoading, bool isProvisioning, bool isProvisioned, bool isFirstConnectionDone, bool isConnecting, bool isConnected, String pin, bool isSigning, String lastSignature, bool success, String walletId, String walletStatus)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SCATreezorViewState() when $default != null:
return $default(_that.activationCode,_that.errorMessage,_that.isCreated,_that.isLoading,_that.isProvisioning,_that.isProvisioned,_that.isFirstConnectionDone,_that.isConnecting,_that.isConnected,_that.pin,_that.isSigning,_that.lastSignature,_that.success,_that.walletId,_that.walletStatus);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 activationCode, String errorMessage, bool isCreated, bool isLoading, bool isProvisioning, bool isProvisioned, bool isFirstConnectionDone, bool isConnecting, bool isConnected, String pin, bool isSigning, String lastSignature, bool success, String walletId, String walletStatus) $default,) {final _that = this;
switch (_that) {
case _SCATreezorViewState():
return $default(_that.activationCode,_that.errorMessage,_that.isCreated,_that.isLoading,_that.isProvisioning,_that.isProvisioned,_that.isFirstConnectionDone,_that.isConnecting,_that.isConnected,_that.pin,_that.isSigning,_that.lastSignature,_that.success,_that.walletId,_that.walletStatus);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 activationCode, String errorMessage, bool isCreated, bool isLoading, bool isProvisioning, bool isProvisioned, bool isFirstConnectionDone, bool isConnecting, bool isConnected, String pin, bool isSigning, String lastSignature, bool success, String walletId, String walletStatus)? $default,) {final _that = this;
switch (_that) {
case _SCATreezorViewState() when $default != null:
return $default(_that.activationCode,_that.errorMessage,_that.isCreated,_that.isLoading,_that.isProvisioning,_that.isProvisioned,_that.isFirstConnectionDone,_that.isConnecting,_that.isConnected,_that.pin,_that.isSigning,_that.lastSignature,_that.success,_that.walletId,_that.walletStatus);case _:
return null;
}
}
}
/// @nodoc
class _SCATreezorViewState extends SCATreezorViewState {
const _SCATreezorViewState({this.activationCode = '', this.errorMessage = '', this.isCreated = false, this.isLoading = false, this.isProvisioning = false, this.isProvisioned = false, this.isFirstConnectionDone = false, this.isConnecting = false, this.isConnected = false, this.pin = '', this.isSigning = false, this.lastSignature = '', this.success = false, this.walletId = '', this.walletStatus = ''}): super._();
@override@JsonKey() final String activationCode;
@override@JsonKey() final String errorMessage;
@override@JsonKey() final bool isCreated;
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isProvisioning;
@override@JsonKey() final bool isProvisioned;
@override@JsonKey() final bool isFirstConnectionDone;
@override@JsonKey() final bool isConnecting;
@override@JsonKey() final bool isConnected;
@override@JsonKey() final String pin;
@override@JsonKey() final bool isSigning;
@override@JsonKey() final String lastSignature;
@override@JsonKey() final bool success;
@override@JsonKey() final String walletId;
@override@JsonKey() final String walletStatus;
/// Create a copy of SCATreezorViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SCATreezorViewStateCopyWith<_SCATreezorViewState> get copyWith => __$SCATreezorViewStateCopyWithImpl<_SCATreezorViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SCATreezorViewState&&(identical(other.activationCode, activationCode) || other.activationCode == activationCode)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isProvisioning, isProvisioning) || other.isProvisioning == isProvisioning)&&(identical(other.isProvisioned, isProvisioned) || other.isProvisioned == isProvisioned)&&(identical(other.isFirstConnectionDone, isFirstConnectionDone) || other.isFirstConnectionDone == isFirstConnectionDone)&&(identical(other.isConnecting, isConnecting) || other.isConnecting == isConnecting)&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.lastSignature, lastSignature) || other.lastSignature == lastSignature)&&(identical(other.success, success) || other.success == success)&&(identical(other.walletId, walletId) || other.walletId == walletId)&&(identical(other.walletStatus, walletStatus) || other.walletStatus == walletStatus));
}
@override
int get hashCode => Object.hash(runtimeType,activationCode,errorMessage,isCreated,isLoading,isProvisioning,isProvisioned,isFirstConnectionDone,isConnecting,isConnected,pin,isSigning,lastSignature,success,walletId,walletStatus);
@override
String toString() {
return 'SCATreezorViewState(activationCode: $activationCode, errorMessage: $errorMessage, isCreated: $isCreated, isLoading: $isLoading, isProvisioning: $isProvisioning, isProvisioned: $isProvisioned, isFirstConnectionDone: $isFirstConnectionDone, isConnecting: $isConnecting, isConnected: $isConnected, pin: $pin, isSigning: $isSigning, lastSignature: $lastSignature, success: $success, walletId: $walletId, walletStatus: $walletStatus)';
}
}
/// @nodoc
abstract mixin class _$SCATreezorViewStateCopyWith<$Res> implements $SCATreezorViewStateCopyWith<$Res> {
factory _$SCATreezorViewStateCopyWith(_SCATreezorViewState value, $Res Function(_SCATreezorViewState) _then) = __$SCATreezorViewStateCopyWithImpl;
@override @useResult
$Res call({
String activationCode, String errorMessage, bool isCreated, bool isLoading, bool isProvisioning, bool isProvisioned, bool isFirstConnectionDone, bool isConnecting, bool isConnected, String pin, bool isSigning, String lastSignature, bool success, String walletId, String walletStatus
});
}
/// @nodoc
class __$SCATreezorViewStateCopyWithImpl<$Res>
implements _$SCATreezorViewStateCopyWith<$Res> {
__$SCATreezorViewStateCopyWithImpl(this._self, this._then);
final _SCATreezorViewState _self;
final $Res Function(_SCATreezorViewState) _then;
/// Create a copy of SCATreezorViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? activationCode = null,Object? errorMessage = null,Object? isCreated = null,Object? isLoading = null,Object? isProvisioning = null,Object? isProvisioned = null,Object? isFirstConnectionDone = null,Object? isConnecting = null,Object? isConnected = null,Object? pin = null,Object? isSigning = null,Object? lastSignature = null,Object? success = null,Object? walletId = null,Object? walletStatus = null,}) {
return _then(_SCATreezorViewState(
activationCode: null == activationCode ? _self.activationCode : activationCode // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isProvisioning: null == isProvisioning ? _self.isProvisioning : isProvisioning // ignore: cast_nullable_to_non_nullable
as bool,isProvisioned: null == isProvisioned ? _self.isProvisioned : isProvisioned // ignore: cast_nullable_to_non_nullable
as bool,isFirstConnectionDone: null == isFirstConnectionDone ? _self.isFirstConnectionDone : isFirstConnectionDone // ignore: cast_nullable_to_non_nullable
as bool,isConnecting: null == isConnecting ? _self.isConnecting : isConnecting // ignore: cast_nullable_to_non_nullable
as bool,isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
as bool,pin: null == pin ? _self.pin : pin // ignore: cast_nullable_to_non_nullable
as String,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,lastSignature: null == lastSignature ? _self.lastSignature : lastSignature // ignore: cast_nullable_to_non_nullable
as String,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool,walletId: null == walletId ? _self.walletId : walletId // ignore: cast_nullable_to_non_nullable
as String,walletStatus: null == walletStatus ? _self.walletStatus : walletStatus // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -122,7 +122,7 @@ List<SignUpStepConfig> signUpSteps(BuildContext context) => [
? null ? null
: state.address.country, : state.address.country,
onAddressCountryChanged: (CountryCode value) { onAddressCountryChanged: (CountryCode value) {
vm.setAddressCountry(name: value.name ?? ''); vm.setAddressCountry(name: value.name ?? '', code: value.code ?? '');
}, },
addressCountryLabel: context.translate(I18n.addressCountryLabel), addressCountryLabel: context.translate(I18n.addressCountryLabel),
addressCountryController: vm.addressCountryController, addressCountryController: vm.addressCountryController,

View File

@@ -10,6 +10,7 @@ abstract class AddressViewState with _$AddressViewState {
@Default('') String province, @Default('') String province,
@Default('') String state, @Default('') String state,
@Default('España') String country, @Default('España') String country,
@Default('ES') String countryCode,
int? postCode, int? postCode,
}) = _AddressViewState; }) = _AddressViewState;
} }

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$AddressViewState { mixin _$AddressViewState {
String get street; String get city; String get province; String get state; String get country; int? get postCode; String get street; String get city; String get province; String get state; String get country; String get countryCode; int? get postCode;
/// Create a copy of AddressViewState /// Create a copy of AddressViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $AddressViewStateCopyWith<AddressViewState> get copyWith => _$AddressViewStateCo
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AddressViewState&&(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)); return identical(this, other) || (other.runtimeType == runtimeType&&other is AddressViewState&&(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.countryCode, countryCode) || other.countryCode == countryCode)&&(identical(other.postCode, postCode) || other.postCode == postCode));
} }
@override @override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); int get hashCode => Object.hash(runtimeType,street,city,province,state,country,countryCode,postCode);
@override @override
String toString() { String toString() {
return 'AddressViewState(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; return 'AddressViewState(street: $street, city: $city, province: $province, state: $state, country: $country, countryCode: $countryCode, postCode: $postCode)';
} }
@@ -45,7 +45,7 @@ abstract mixin class $AddressViewStateCopyWith<$Res> {
factory $AddressViewStateCopyWith(AddressViewState value, $Res Function(AddressViewState) _then) = _$AddressViewStateCopyWithImpl; factory $AddressViewStateCopyWith(AddressViewState value, $Res Function(AddressViewState) _then) = _$AddressViewStateCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String street, String city, String province, String state, String country, int? postCode String street, String city, String province, String state, String country, String countryCode, int? postCode
}); });
@@ -62,13 +62,14 @@ class _$AddressViewStateCopyWithImpl<$Res>
/// Create a copy of AddressViewState /// Create a copy of AddressViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? countryCode = null,Object? postCode = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable 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,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,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,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,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,countryCode: null == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
as String,postCode: freezed == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable as String,postCode: freezed == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int?, as int?,
)); ));
@@ -155,10 +156,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@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; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country, String countryCode, int? postCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _AddressViewState() when $default != null: case _AddressViewState() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.countryCode,_that.postCode);case _:
return orElse(); return orElse();
} }
@@ -176,10 +177,10 @@ return $default(_that.street,_that.city,_that.province,_that.state,_that.country
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country, int? postCode) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country, String countryCode, int? postCode) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _AddressViewState(): case _AddressViewState():
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.countryCode,_that.postCode);case _:
throw StateError('Unexpected subclass'); throw StateError('Unexpected subclass');
} }
@@ -196,10 +197,10 @@ return $default(_that.street,_that.city,_that.province,_that.state,_that.country
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String street, String city, String province, String state, String country, int? postCode)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String street, String city, String province, String state, String country, String countryCode, int? postCode)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _AddressViewState() when $default != null: case _AddressViewState() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _: return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.countryCode,_that.postCode);case _:
return null; return null;
} }
@@ -211,7 +212,7 @@ return $default(_that.street,_that.city,_that.province,_that.state,_that.country
class _AddressViewState implements AddressViewState { class _AddressViewState implements AddressViewState {
const _AddressViewState({this.street = '', this.city = '', this.province = '', this.state = '', this.country = 'España', this.postCode}); const _AddressViewState({this.street = '', this.city = '', this.province = '', this.state = '', this.country = 'España', this.countryCode = 'ES', this.postCode});
@override@JsonKey() final String street; @override@JsonKey() final String street;
@@ -219,6 +220,7 @@ class _AddressViewState implements AddressViewState {
@override@JsonKey() final String province; @override@JsonKey() final String province;
@override@JsonKey() final String state; @override@JsonKey() final String state;
@override@JsonKey() final String country; @override@JsonKey() final String country;
@override@JsonKey() final String countryCode;
@override final int? postCode; @override final int? postCode;
/// Create a copy of AddressViewState /// Create a copy of AddressViewState
@@ -231,16 +233,16 @@ _$AddressViewStateCopyWith<_AddressViewState> get copyWith => __$AddressViewStat
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddressViewState&&(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)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddressViewState&&(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.countryCode, countryCode) || other.countryCode == countryCode)&&(identical(other.postCode, postCode) || other.postCode == postCode));
} }
@override @override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode); int get hashCode => Object.hash(runtimeType,street,city,province,state,country,countryCode,postCode);
@override @override
String toString() { String toString() {
return 'AddressViewState(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)'; return 'AddressViewState(street: $street, city: $city, province: $province, state: $state, country: $country, countryCode: $countryCode, postCode: $postCode)';
} }
@@ -251,7 +253,7 @@ abstract mixin class _$AddressViewStateCopyWith<$Res> implements $AddressViewSta
factory _$AddressViewStateCopyWith(_AddressViewState value, $Res Function(_AddressViewState) _then) = __$AddressViewStateCopyWithImpl; factory _$AddressViewStateCopyWith(_AddressViewState value, $Res Function(_AddressViewState) _then) = __$AddressViewStateCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String street, String city, String province, String state, String country, int? postCode String street, String city, String province, String state, String country, String countryCode, int? postCode
}); });
@@ -268,13 +270,14 @@ class __$AddressViewStateCopyWithImpl<$Res>
/// Create a copy of AddressViewState /// Create a copy of AddressViewState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? countryCode = null,Object? postCode = freezed,}) {
return _then(_AddressViewState( return _then(_AddressViewState(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable 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,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,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,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,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,countryCode: null == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
as String,postCode: freezed == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable as String,postCode: freezed == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int?, as int?,
)); ));

View File

@@ -15,6 +15,7 @@ import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_state.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_localizations/sf_localizations.dart'; import 'package:sf_localizations/sf_localizations.dart';
import 'package:sealed_countries/sealed_countries.dart';
final signUpViewModelProvider = final signUpViewModelProvider =
NotifierProvider.autoDispose<SignUpViewModel, SignUpViewState>( NotifierProvider.autoDispose<SignUpViewModel, SignUpViewState>(
@@ -195,9 +196,11 @@ class SignUpViewModel extends Notifier<SignUpViewState> {
relationshipController.text = v; relationshipController.text = v;
} }
void setAddressCountry({required String name}) { void setAddressCountry({required String name, required String code}) {
addressCountryController.text = name; addressCountryController.text = name;
state = state.copyWith(address: state.address.copyWith(country: name)); state = state.copyWith(
address: state.address.copyWith(country: name, countryCode: code),
);
} }
void setAcceptTerms(bool value) { void setAcceptTerms(bool value) {
@@ -534,12 +537,15 @@ class SignUpViewModel extends Notifier<SignUpViewState> {
} }
AddressEntity _toAddressEntity(AddressViewState a) { AddressEntity _toAddressEntity(AddressViewState a) {
final country = WorldCountry.fromCodeShort(a.countryCode.toUpperCase());
return AddressEntity( return AddressEntity(
street: a.street.trim(), street: a.street.trim(),
city: a.city.trim(), city: a.city.trim(),
province: a.province.trim(), province: a.province.trim(),
state: a.state.trim(), state: a.state.trim(),
country: a.country.trim(), // country: a.country.trim(),
country: country.name.common,
postCode: a.postCode ?? 0, postCode: a.postCode ?? 0,
); );
} }

View File

@@ -1,4 +1,3 @@
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'address_view_state.dart'; import 'address_view_state.dart';

View File

@@ -26,6 +26,10 @@ dependencies:
path: ../../packages/sf_infrastructure path: ../../packages/sf_infrastructure
utils: utils:
path: ../../packages/utils path: ../../packages/utils
sf_shared:
path: ../../packages/sf_shared
sca_treezor:
path: ../../packages/sca_treezor
#dependencies go here #dependencies go here
flutter_svg: ^2.2.1 flutter_svg: ^2.2.1
get_it: ^9.0.5 get_it: ^9.0.5
@@ -42,6 +46,9 @@ dependencies:
dio_cookie_manager: ^3.3.0 dio_cookie_manager: ^3.3.0
cookie_jar: ^4.0.8 cookie_jar: ^4.0.8
path_provider: ^2.1.5 path_provider: ^2.1.5
shared_preferences: ^2.5.4
l10n_countries: ^1.3.1
sealed_countries: ^2.8.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -1,9 +1,11 @@
# melos_managed_dependency_overrides: dashboard_shell,design_system,home,notifications,profile,sf_shared,navigation,utils,sf_localizations,fonts,sf_infrastructure # melos_managed_dependency_overrides: dashboard_shell,design_system,home,notifications,profile,sf_shared,navigation,utils,sf_localizations,fonts,sf_infrastructure,flutter_treezor_entrust_sdk_bridge,sca_treezor
dependency_overrides: dependency_overrides:
dashboard_shell: dashboard_shell:
path: ../dashboard_shell path: ../dashboard_shell
design_system: design_system:
path: ../../packages/design_system path: ../../packages/design_system
flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge
fonts: fonts:
path: ../../packages/fonts path: ../../packages/fonts
home: home:
@@ -14,6 +16,8 @@ dependency_overrides:
path: ../notifications path: ../notifications
profile: profile:
path: ../profile path: ../profile
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure: sf_infrastructure:
path: ../../packages/sf_infrastructure path: ../../packages/sf_infrastructure
sf_localizations: sf_localizations:

View File

@@ -1,9 +1,11 @@
# melos_managed_dependency_overrides: auth,design_system,home,notifications,profile,sf_shared,navigation,utils,sf_localizations,fonts,sf_infrastructure # melos_managed_dependency_overrides: auth,design_system,home,notifications,profile,sf_shared,navigation,utils,sf_localizations,fonts,sf_infrastructure,flutter_treezor_entrust_sdk_bridge,sca_treezor
dependency_overrides: dependency_overrides:
auth: auth:
path: ../auth path: ../auth
design_system: design_system:
path: ../../packages/design_system path: ../../packages/design_system
flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge
fonts: fonts:
path: ../../packages/fonts path: ../../packages/fonts
home: home:
@@ -14,6 +16,8 @@ dependency_overrides:
path: ../notifications path: ../notifications
profile: profile:
path: ../profile path: ../profile
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure: sf_infrastructure:
path: ../../packages/sf_infrastructure path: ../../packages/sf_infrastructure
sf_localizations: sf_localizations:

View File

@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: auth,dashboard_shell,design_system,notifications,profile,sf_shared,navigation,utils,sf_localizations,fonts,sf_infrastructure # melos_managed_dependency_overrides: auth,dashboard_shell,design_system,notifications,profile,sf_shared,navigation,utils,sf_localizations,fonts,sf_infrastructure,flutter_treezor_entrust_sdk_bridge,sca_treezor
dependency_overrides: dependency_overrides:
auth: auth:
path: ../auth path: ../auth
@@ -6,6 +6,8 @@ dependency_overrides:
path: ../dashboard_shell path: ../dashboard_shell
design_system: design_system:
path: ../../packages/design_system path: ../../packages/design_system
flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge
fonts: fonts:
path: ../../packages/fonts path: ../../packages/fonts
navigation: navigation:
@@ -14,6 +16,8 @@ dependency_overrides:
path: ../notifications path: ../notifications
profile: profile:
path: ../profile path: ../profile
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure: sf_infrastructure:
path: ../../packages/sf_infrastructure path: ../../packages/sf_infrastructure
sf_localizations: sf_localizations:

View File

@@ -1,9 +1,15 @@
# melos_managed_dependency_overrides: design_system,sf_shared,utils,fonts # melos_managed_dependency_overrides: design_system,sf_shared,utils,fonts,sf_infrastructure,flutter_treezor_entrust_sdk_bridge,sca_treezor
dependency_overrides: dependency_overrides:
design_system: design_system:
path: ../../packages/design_system path: ../../packages/design_system
flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge
fonts: fonts:
path: ../../packages/fonts path: ../../packages/fonts
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure:
path: ../../packages/sf_infrastructure
sf_shared: sf_shared:
path: ../../packages/sf_shared path: ../../packages/sf_shared
utils: utils:

View File

@@ -1,11 +1,17 @@
# melos_managed_dependency_overrides: design_system,notifications,sf_shared,utils,fonts # melos_managed_dependency_overrides: design_system,notifications,sf_shared,utils,fonts,sf_infrastructure,flutter_treezor_entrust_sdk_bridge,sca_treezor
dependency_overrides: dependency_overrides:
design_system: design_system:
path: ../../packages/design_system path: ../../packages/design_system
flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge
fonts: fonts:
path: ../../packages/fonts path: ../../packages/fonts
notifications: notifications:
path: ../notifications path: ../notifications
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure:
path: ../../packages/sf_infrastructure
sf_shared: sf_shared:
path: ../../packages/sf_shared path: ../../packages/sf_shared
utils: utils:

View File

@@ -6,7 +6,7 @@ class CountryPrefixPicker extends StatelessWidget {
super.key, super.key,
required this.onChanged, required this.onChanged,
required this.headerText, required this.headerText,
this.initialCountryCode = '+34', this.initialSelection = '+34',
this.radius = 12, this.radius = 12,
this.width = 90, this.width = 90,
this.height = 55, this.height = 55,
@@ -15,7 +15,7 @@ class CountryPrefixPicker extends StatelessWidget {
}); });
final ValueChanged<CountryCode> onChanged; final ValueChanged<CountryCode> onChanged;
final String initialCountryCode; final String initialSelection;
final String headerText; final String headerText;
final double radius; final double radius;
final double width; final double width;
@@ -33,18 +33,19 @@ class CountryPrefixPicker extends StatelessWidget {
showOnlyCountryWhenClosed: true, showOnlyCountryWhenClosed: true,
showDropDownButton: true, showDropDownButton: true,
headerText: headerText, headerText: headerText,
onChanged: onChanged, initialSelection: initialSelection,
initialSelection: initialCountryCode,
showFlag: true, showFlag: true,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
// onInit: (country) {
// if (country != null) onChanged(country);
// },
onChanged: onChanged,
builder: (CountryCode? country) { builder: (CountryCode? country) {
if (country == null) { if (country == null) return const SizedBox.shrink();
return const SizedBox.shrink();
}
return InputDecorator( return InputDecorator(
decoration: InputDecoration( decoration: InputDecoration(
isDense: false,
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
horizontal: 12, horizontal: 12,
vertical: 16, vertical: 16,
@@ -71,6 +72,15 @@ class CountryPrefixPicker extends StatelessWidget {
height: 24, height: 24,
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
// ✅ si quieres mostrar el dialCode también:
// Expanded(
// child: Text(
// country.dialCode ?? '',
// style: const TextStyle(fontWeight: FontWeight.w600),
// overflow: TextOverflow.ellipsis,
// ),
// ),
const Icon(Icons.arrow_drop_down, size: 24), const Icon(Icons.arrow_drop_down, size: 24),
], ],
), ),

View File

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

View File

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

View File

@@ -1 +1 @@
2b14702afabf7dabc6ee436857156114 d3de74b51e78f073e96d4f8f7e37e091

View File

@@ -1 +1 @@
238e2d82008afe02e0766c6273b14984c94b9375 7bc6215d7d14b8165f428c6cffca46f51c260d44

View File

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

View File

@@ -21,39 +21,41 @@ class AntelopError {
); );
} }
static AntelopError handleError(Object? error) { static AntelopError handleError(Object? error) {
// Check if the error is a Map<Object?, Object?> (likely from Swift) // Check if the error is a Map<Object?, Object?> (likely from Swift)
if (error is Map<Object?, Object?>) { if (error is Map<Object?, Object?>) {
try { try {
// Convert the map to a map with String keys, since AntelopError expects a Map<String, dynamic> // Convert the map to a map with String keys, since AntelopError expects a Map<String, dynamic>
final map = Map<String, dynamic>.from(error.cast<String, dynamic>()); final map = Map<String, dynamic>.from(error.cast<String, dynamic>());
return AntelopError.fromMap(map); return AntelopError.fromMap(map);
} catch (e) { } catch (e) {
throw FormatException('Failed to convert Map<Object?, Object?> to AntelopError: $e'); throw FormatException(
'Failed to convert Map<Object?, Object?> to AntelopError: $e');
}
} }
}
// Check if the error is a Map<String, dynamic> and convert it to AntelopError // Check if the error is a Map<String, dynamic> and convert it to AntelopError
if (error is Map<String, dynamic>) { if (error is Map<String, dynamic>) {
try { try {
return AntelopError.fromMap(error); return AntelopError.fromMap(error);
} catch (e) { } catch (e) {
throw FormatException('Failed to convert Map<String, dynamic> to AntelopError: $e'); throw FormatException(
'Failed to convert Map<String, dynamic> to AntelopError: $e');
}
} }
}
// Check if it's already an instance of AntelopError, return it directly // Check if it's already an instance of AntelopError, return it directly
if (error is AntelopError) { if (error is AntelopError) {
return error; return error;
} }
// If the error is neither a Map nor an AntelopError, throw an error // If the error is neither a Map nor an AntelopError, throw an error
throw FormatException('Unhandled error type: ${error.runtimeType}'); throw FormatException('Unhandled error type: ${error.runtimeType}');
} }
// Optionally, override toString for easier logging/debugging // Optionally, override toString for easier logging/debugging
@override @override
String toString() { String toString() {
return 'AntelopError(message: $message, reason: $reason, code: $code, type: $type)'; return 'AntelopError(message: $message, reason: $reason, code: $code, type: $type)';
} }
} }

View File

@@ -3,16 +3,20 @@ import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
class CustomerAuthenticatedSignatureModule { class CustomerAuthenticatedSignatureModule {
static const _channel = MethodChannel('CustomerAuthenticatedSignature'); static const _channel = MethodChannel('CustomerAuthenticatedSignature');
static const _eventChannel = EventChannel('customer_authenticated_signature_callback'); static const _eventChannel =
EventChannel('customer_authenticated_signature_callback');
CustomerAuthenticatedSignatureModule(String patternName, String message, String input) { CustomerAuthenticatedSignatureModule(
// Initialize the module when an instance is created String patternName, String message, String input) {
_initialize(patternName, message, input); // Initialize the module when an instance is created
_initialize(patternName, message, input);
} }
// Constructor to initialize the listener // Constructor to initialize the listener
void initDefaultCustomerAuthenticatedProcessCallback(DefaultCustomerAuthenticatedProcessCallback defaultCustomerAuthenticatedProcessCallback) { void initDefaultCustomerAuthenticatedProcessCallback(
eventStream.listen((event){ DefaultCustomerAuthenticatedProcessCallback
defaultCustomerAuthenticatedProcessCallback) {
eventStream.listen((event) {
final eventName = event['event'] as String; final eventName = event['event'] as String;
final params = event['params'] as Map<String, dynamic>?; final params = event['params'] as Map<String, dynamic>?;
@@ -33,7 +37,8 @@ class CustomerAuthenticatedSignatureModule {
} }
break; break;
case 'onAuthenticationDeclined': case 'onAuthenticationDeclined':
defaultCustomerAuthenticatedProcessCallback.onAuthenticationDeclined?.call(); defaultCustomerAuthenticatedProcessCallback.onAuthenticationDeclined
?.call();
break; break;
default: default:
print('Unknown event received: $eventName'); print('Unknown event received: $eventName');
@@ -41,25 +46,31 @@ class CustomerAuthenticatedSignatureModule {
}); });
} }
void initCustomCustomerAuthenticatedProcessCallback(CustomCustomerAuthenticatedProcessCallback customCustomerAuthenticatedProcessCallback) { void initCustomCustomerAuthenticatedProcessCallback(
eventStream.listen((event){ CustomCustomerAuthenticatedProcessCallback
customCustomerAuthenticatedProcessCallback) {
eventStream.listen((event) {
final eventName = event['event'] as String; final eventName = event['event'] as String;
final params = event['params'] != null final params = event['params'] != null
? Map<String, dynamic>.from(event['params'] as Map) // Safely cast ? Map<String, dynamic>.from(event['params'] as Map) // Safely cast
: null; : null;
switch (eventName) { switch (eventName) {
case 'onCustomerCredentialsRequired': case 'onCustomerCredentialsRequired':
customCustomerAuthenticatedProcessCallback.onCustomerCredentialsRequired?.call(); customCustomerAuthenticatedProcessCallback
.onCustomerCredentialsRequired
?.call();
break; break;
case 'onCustomerCredentialsInvalid': case 'onCustomerCredentialsInvalid':
if (params != null) { if (params != null) {
final reason = params['reason'] as String; final reason = params['reason'] as String;
customCustomerAuthenticatedProcessCallback.onCustomerCredentialsInvalid?.call(reason); customCustomerAuthenticatedProcessCallback
.onCustomerCredentialsInvalid
?.call(reason);
} }
break; break;
case 'onProcessStart': case 'onProcessStart':
customCustomerAuthenticatedProcessCallback.onProcessStart?.call(); customCustomerAuthenticatedProcessCallback.onProcessStart?.call();
break; break;
case 'onProcessSuccess': case 'onProcessSuccess':
customCustomerAuthenticatedProcessCallback.onProcessSuccess?.call(); customCustomerAuthenticatedProcessCallback.onProcessSuccess?.call();
@@ -71,7 +82,8 @@ class CustomerAuthenticatedSignatureModule {
} }
break; break;
case 'onAuthenticationDeclined': case 'onAuthenticationDeclined':
customCustomerAuthenticatedProcessCallback.onAuthenticationDeclined?.call(); customCustomerAuthenticatedProcessCallback.onAuthenticationDeclined
?.call();
break; break;
default: default:
print('Unknown event received: $eventName'); print('Unknown event received: $eventName');
@@ -80,7 +92,8 @@ class CustomerAuthenticatedSignatureModule {
} }
// Initialize // Initialize
Future<void> _initialize(String patternName, String message, String input) async { Future<void> _initialize(
String patternName, String message, String input) async {
try { try {
await _channel.invokeMethod('_initialize', { await _channel.invokeMethod('_initialize', {
'patternName': patternName, 'patternName': patternName,
@@ -93,9 +106,12 @@ class CustomerAuthenticatedSignatureModule {
} }
// Sign with default authentication // Sign with default authentication
Future<void> signWithDefaultAuthentication(DefaultCustomerAuthenticatedProcessCallback defaultCustomerAuthenticatedProcessCallback) async { Future<void> signWithDefaultAuthentication(
DefaultCustomerAuthenticatedProcessCallback
defaultCustomerAuthenticatedProcessCallback) async {
try { try {
initDefaultCustomerAuthenticatedProcessCallback(defaultCustomerAuthenticatedProcessCallback); initDefaultCustomerAuthenticatedProcessCallback(
defaultCustomerAuthenticatedProcessCallback);
await _channel.invokeMethod('signWithDefaultAuthentication'); await _channel.invokeMethod('signWithDefaultAuthentication');
} catch (e) { } catch (e) {
print("signWithDefaultAuthentication error: $e"); print("signWithDefaultAuthentication error: $e");
@@ -103,8 +119,11 @@ class CustomerAuthenticatedSignatureModule {
} }
// Sign with custom authentication // Sign with custom authentication
Future<void> signWithCustomAuthentication(CustomCustomerAuthenticatedProcessCallback customCustomerAuthenticatedProcessCallback) async { Future<void> signWithCustomAuthentication(
initCustomCustomerAuthenticatedProcessCallback(customCustomerAuthenticatedProcessCallback); CustomCustomerAuthenticatedProcessCallback
customCustomerAuthenticatedProcessCallback) async {
initCustomCustomerAuthenticatedProcessCallback(
customCustomerAuthenticatedProcessCallback);
try { try {
await _channel.invokeMethod('signWithCustomAuthentication'); await _channel.invokeMethod('signWithCustomAuthentication');
} catch (e) { } catch (e) {
@@ -138,7 +157,8 @@ class CustomerAuthenticatedSignatureModule {
// Get pattern name // Get pattern name
Future<String?> getAuthenticationPatternName() async { Future<String?> getAuthenticationPatternName() async {
try { try {
return await _channel.invokeMethod<String>('getAuthenticationPatternName'); return await _channel
.invokeMethod<String>('getAuthenticationPatternName');
} catch (e) { } catch (e) {
print("getAuthenticationPatternName error: $e"); print("getAuthenticationPatternName error: $e");
return null; return null;
@@ -204,14 +224,16 @@ class CustomerAuthenticatedSignatureModule {
} }
} }
// Event listener to handle events from the native code // Event listener to handle events from the native code
Stream<Map<String, dynamic>> get eventStream { Stream<Map<String, dynamic>> get eventStream {
return _eventChannel.receiveBroadcastStream().map((event) { return _eventChannel.receiveBroadcastStream().map((event) {
final eventMap = Map<String, dynamic>.from(event); final eventMap = Map<String, dynamic>.from(event);
// Check for error data and parse it as an AntelopError // Check for error data and parse it as an AntelopError
if (eventMap.containsKey('error') && eventMap['error'] is Map<String, dynamic>) { if (eventMap.containsKey('error') &&
eventMap['error'] = AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error'])); eventMap['error'] is Map<String, dynamic>) {
eventMap['error'] =
AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error']));
} }
return eventMap; return eventMap;
@@ -226,12 +248,11 @@ class DefaultCustomerAuthenticatedProcessCallback {
final void Function()? onAuthenticationDeclined; final void Function()? onAuthenticationDeclined;
// Constructor // Constructor
DefaultCustomerAuthenticatedProcessCallback({ DefaultCustomerAuthenticatedProcessCallback(
this.onProcessStart, {this.onProcessStart,
this.onProcessSuccess, this.onProcessSuccess,
this.onError, this.onError,
this.onAuthenticationDeclined this.onAuthenticationDeclined});
});
} }
class CustomCustomerAuthenticatedProcessCallback { class CustomCustomerAuthenticatedProcessCallback {
@@ -243,12 +264,11 @@ class CustomCustomerAuthenticatedProcessCallback {
final void Function()? onAuthenticationDeclined; final void Function()? onAuthenticationDeclined;
// Constructor // Constructor
CustomCustomerAuthenticatedProcessCallback({ CustomCustomerAuthenticatedProcessCallback(
this.onCustomerCredentialsRequired, {this.onCustomerCredentialsRequired,
this.onCustomerCredentialsInvalid, this.onCustomerCredentialsInvalid,
this.onProcessStart, this.onProcessStart,
this.onProcessSuccess, this.onProcessSuccess,
this.onError, this.onError,
this.onAuthenticationDeclined this.onAuthenticationDeclined});
});
} }

View File

@@ -3,29 +3,29 @@ import 'package:flutter/services.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart'; import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
class WalletManagerModule { class WalletManagerModule {
static const _channel = MethodChannel('WalletManager');
static const _eventChannel = EventChannel('wallet_manager_callback');
static const _channel = MethodChannel('WalletManager'); WalletManagerModule() {
static const _eventChannel = EventChannel('wallet_manager_callback');
WalletManagerModule() {
// Initialize the module when an instance is created // Initialize the module when an instance is created
// _initialize(walletManagerCallbacks); // _initialize(walletManagerCallbacks);
} }
// Constructor to initialize the listener // Constructor to initialize the listener
void initWalletManagerCallbacks(WalletManagerCallbacks walletManagerCallbacks) { void initWalletManagerCallbacks(
eventStream.listen((event){ WalletManagerCallbacks walletManagerCallbacks) {
eventStream.listen((event) {
final eventName = event['event'] as String; final eventName = event['event'] as String;
final params = event['params'] != null final params = event['params'] != null
? Map<String, dynamic>.from(event['params'] as Map) // Safely cast ? Map<String, dynamic>.from(event['params'] as Map) // Safely cast
: null; : null;
switch (eventName) { switch (eventName) {
case 'onProvisioningRequired': case 'onProvisioningRequired':
walletManagerCallbacks.onProvisioningRequired?.call(); walletManagerCallbacks.onProvisioningRequired?.call();
break; break;
case 'onCredentialsRequired': case 'onCredentialsRequired':
if (params != null) { if (params != null) {
final reason = params['reason'] as String; final reason = params['reason'] as String;
final error = params['error'] as AntelopError?; final error = params['error'] as AntelopError?;
walletManagerCallbacks.onCredentialsRequired?.call(reason, error); walletManagerCallbacks.onCredentialsRequired?.call(reason, error);
@@ -46,7 +46,7 @@ WalletManagerModule() {
}); });
} }
Future<void> initialize(WalletManagerCallbacks walletManagerCallbacks) async { Future<void> initialize(WalletManagerCallbacks walletManagerCallbacks) async {
try { try {
await _channel.invokeMethod('_initialize'); await _channel.invokeMethod('_initialize');
initWalletManagerCallbacks(walletManagerCallbacks); initWalletManagerCallbacks(walletManagerCallbacks);
@@ -55,7 +55,7 @@ Future<void> initialize(WalletManagerCallbacks walletManagerCallbacks) async {
} }
} }
Future<void> connect(String? loginCreds, String? newCreds) async { Future<void> connect(String? loginCreds, String? newCreds) async {
try { try {
await _channel.invokeMethod('connect', { await _channel.invokeMethod('connect', {
'loginCreds': loginCreds, 'loginCreds': loginCreds,
@@ -105,14 +105,16 @@ Future<void> initialize(WalletManagerCallbacks walletManagerCallbacks) async {
// Check credentials for the wallet // Check credentials for the wallet
Future<void> checkCredentials(String credentials) async { Future<void> checkCredentials(String credentials) async {
try { try {
await _channel.invokeMethod('checkCredentials', {'credentials': credentials}); await _channel
.invokeMethod('checkCredentials', {'credentials': credentials});
} on PlatformException catch (e) { } on PlatformException catch (e) {
throw 'Failed to check credentials: ${e.message}'; throw 'Failed to check credentials: ${e.message}';
} }
} }
// Change the wallet credentials // Change the wallet credentials
Future<void> changeCredentials(String currentCredentials, String newCredentials) async { Future<void> changeCredentials(
String currentCredentials, String newCredentials) async {
try { try {
await _channel.invokeMethod('changeCredentials', { await _channel.invokeMethod('changeCredentials', {
'currentCredentials': currentCredentials, 'currentCredentials': currentCredentials,
@@ -126,9 +128,8 @@ Future<void> initialize(WalletManagerCallbacks walletManagerCallbacks) async {
// Activated Method authentification // Activated Method authentification
Future<void> activateAuthenticationMethod(String methodToActivated) async { Future<void> activateAuthenticationMethod(String methodToActivated) async {
try { try {
await _channel.invokeMethod('activateAuthenticationMethod', { await _channel.invokeMethod('activateAuthenticationMethod',
'methodToActivated': methodToActivated {'methodToActivated': methodToActivated});
});
} on PlatformException catch (e) { } on PlatformException catch (e) {
throw 'Failed to activate method: ${e.message}'; throw 'Failed to activate method: ${e.message}';
} }
@@ -143,15 +144,16 @@ Future<void> initialize(WalletManagerCallbacks walletManagerCallbacks) async {
} }
} }
// Event listener to handle events from the native code // Event listener to handle events from the native code
Stream<Map<String, dynamic>> get eventStream { Stream<Map<String, dynamic>> get eventStream {
return _eventChannel.receiveBroadcastStream().map((event) { return _eventChannel.receiveBroadcastStream().map((event) {
final eventMap = Map<String, dynamic>.from(event); final eventMap = Map<String, dynamic>.from(event);
// Check for error data and parse it as an AntelopError // Check for error data and parse it as an AntelopError
if (eventMap.containsKey('error') && eventMap['error'] is Map<String, dynamic>) { if (eventMap.containsKey('error') &&
eventMap['error'] = AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error'])); eventMap['error'] is Map<String, dynamic>) {
eventMap['error'] =
AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error']));
} }
return eventMap; return eventMap;
@@ -161,18 +163,15 @@ Future<void> initialize(WalletManagerCallbacks walletManagerCallbacks) async {
class WalletManagerCallbacks { class WalletManagerCallbacks {
final void Function()? onProvisioningRequired; final void Function()? onProvisioningRequired;
final void Function(String reason, AntelopError? error)? onCredentialsRequired; final void Function(String reason, AntelopError? error)?
onCredentialsRequired;
final void Function(AntelopError error)? onConnectionError; final void Function(AntelopError error)? onConnectionError;
final void Function()? onConnectionSuccess; final void Function()? onConnectionSuccess;
// Constructor // Constructor
WalletManagerCallbacks({ WalletManagerCallbacks(
this.onProvisioningRequired, {this.onProvisioningRequired,
this.onCredentialsRequired, this.onCredentialsRequired,
this.onConnectionError, this.onConnectionError,
this.onConnectionSuccess this.onConnectionSuccess});
});
} }

View File

@@ -8,15 +8,15 @@ class WalletProvisioningModule {
WalletProvisioningModule() { WalletProvisioningModule() {
// Initialize the module when an instance is created // Initialize the module when an instance is created
//_initialize(callbacks); //_initialize(callbacks);
} }
// Initialize event callbacks // Initialize event callbacks
void initWalletProvisioningCallbacks(WalletProvisioningCallbacks callbacks) { void initWalletProvisioningCallbacks(WalletProvisioningCallbacks callbacks) {
eventStream.listen((event) { eventStream.listen((event) {
final eventName = event['event'] as String; final eventName = event['event'] as String;
final params = event['params'] != null final params = event['params'] != null
? Map<String, dynamic>.from(event['params'] as Map) // Safely cast ? Map<String, dynamic>.from(event['params'] as Map) // Safely cast
: null; : null;
switch (eventName) { switch (eventName) {
case 'onInitializationSuccess': case 'onInitializationSuccess':
@@ -31,7 +31,8 @@ class WalletProvisioningModule {
callbacks.onPermissionNotGranted?.call(permissions); callbacks.onPermissionNotGranted?.call(permissions);
break; break;
case 'onDeviceEligible': case 'onDeviceEligible':
final fingerprintAllowed = params?['fingerprintAllowed'] as bool? ?? false; final fingerprintAllowed =
params?['fingerprintAllowed'] as bool? ?? false;
callbacks.onDeviceEligible?.call(fingerprintAllowed); callbacks.onDeviceEligible?.call(fingerprintAllowed);
break; break;
case 'onDeviceNotEligible': case 'onDeviceNotEligible':
@@ -69,7 +70,7 @@ class WalletProvisioningModule {
} }
} }
// Method to initialize Wallet Provisioning // Method to initialize Wallet Provisioning
Future<void> initialize(WalletProvisioningCallbacks callbacks) async { Future<void> initialize(WalletProvisioningCallbacks callbacks) async {
try { try {
await _initialize(callbacks); await _initialize(callbacks);
@@ -116,8 +117,10 @@ class WalletProvisioningModule {
final eventMap = Map<String, dynamic>.from(event); final eventMap = Map<String, dynamic>.from(event);
// Parse the error if present // Parse the error if present
if (eventMap.containsKey('error') && eventMap['error'] is Map<String, dynamic>) { if (eventMap.containsKey('error') &&
eventMap['error'] = AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error'])); eventMap['error'] is Map<String, dynamic>) {
eventMap['error'] =
AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error']));
} }
return eventMap; return eventMap;
}); });
@@ -136,21 +139,21 @@ class WalletProvisioningCallbacks {
final void Function(AntelopError error)? onInitializationError; final void Function(AntelopError error)? onInitializationError;
final void Function(List<String> permissions)? onPermissionNotGranted; final void Function(List<String> permissions)? onPermissionNotGranted;
final void Function(bool fingerprintAllowed)? onDeviceEligible; final void Function(bool fingerprintAllowed)? onDeviceEligible;
final void Function(String reason, String? denialReference)? onDeviceNotEligible; final void Function(String reason, String? denialReference)?
onDeviceNotEligible;
final void Function(AntelopError error)? onCheckEligibilityError; final void Function(AntelopError error)? onCheckEligibilityError;
final void Function()? onProvisioningPending; final void Function()? onProvisioningPending;
final void Function()? onProvisioningSuccess; final void Function()? onProvisioningSuccess;
final void Function(AntelopError error)? onProvisioningError; final void Function(AntelopError error)? onProvisioningError;
WalletProvisioningCallbacks({ WalletProvisioningCallbacks(
this.onInitializationSuccess, {this.onInitializationSuccess,
this.onInitializationError, this.onInitializationError,
this.onPermissionNotGranted, this.onPermissionNotGranted,
this.onDeviceEligible, this.onDeviceEligible,
this.onDeviceNotEligible, this.onDeviceNotEligible,
this.onCheckEligibilityError, this.onCheckEligibilityError,
this.onProvisioningPending, this.onProvisioningPending,
this.onProvisioningSuccess, this.onProvisioningSuccess,
this.onProvisioningError this.onProvisioningError});
});
} }

View File

@@ -1,6 +1,7 @@
class AppRoutes { class AppRoutes {
static const splash = '/splash'; static const splash = '/splash';
static const login = '/login'; static const login = '/login';
static const scaTreezor = '/sca_treezor';
static const signup = '/signup'; static const signup = '/signup';
static const onboarding = '/onboarding'; static const onboarding = '/onboarding';
static const linkPhone = '/request_link_phone'; static const linkPhone = '/request_link_phone';

31
packages/sca_treezor/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

View File

@@ -0,0 +1 @@
TODO: Add your license here.

View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@@ -0,0 +1,4 @@
export 'sca_treezor_module.dart';
export 'src/treezor_wallet_provisioning_service.dart';
export 'src/treezor_wallet_connection_service.dart';
export 'src/treezor_wallet_signature_service.dart';

View File

@@ -0,0 +1,29 @@
import 'package:get_it/get_it.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/flutter_treezor_entrust_sdk_bridge.dart';
import 'package:sca_treezor/sca_treezor.dart';
final GetIt getIt = GetIt.instance;
void scaTreezorModule() {
// Base SDK bridge
getIt.registerLazySingleton<FlutterTreezorEntrustSdkBridge>(
() => FlutterTreezorEntrustSdkBridge(),
);
// Services
getIt.registerLazySingleton<TreezorWalletProvisioningService>(
() => TreezorWalletProvisioningService(
getIt<FlutterTreezorEntrustSdkBridge>(),
),
);
getIt.registerLazySingleton<TreezorWalletConnectionService>(
() =>
TreezorWalletConnectionService(getIt<FlutterTreezorEntrustSdkBridge>()),
);
getIt.registerLazySingleton<TreezorWalletSignatureService>(
() =>
TreezorWalletSignatureService(getIt<FlutterTreezorEntrustSdkBridge>()),
);
}

View File

@@ -0,0 +1,69 @@
import 'dart:async';
import 'package:flutter_treezor_entrust_sdk_bridge/flutter_treezor_entrust_sdk_bridge.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/wallet/wallet_manager_module.dart';
class TreezorWalletConnectionService {
final FlutterTreezorEntrustSdkBridge _bridge;
TreezorWalletConnectionService(this._bridge);
Future<void> connectWallet({String? loginPin, String? newPin}) async {
if (loginPin == null && newPin == null) {
throw ArgumentError('Debes enviar loginPin o newPin');
}
final WalletManagerModule walletManager = _bridge.getWalletManagerModule();
final completer = Completer<void>();
var finished = false;
void completeOnce([Object? error, StackTrace? st]) {
if (finished) return;
finished = true;
if (error == null) {
completer.complete();
} else {
completer.completeError(error, st);
}
}
await walletManager.initialize(
WalletManagerCallbacks(
onProvisioningRequired: () {
completeOnce(
Exception('Wallet no provisionado. Ejecuta provisioning primero.'),
);
},
onCredentialsRequired: (reason, error) {
completeOnce(
Exception('Credenciales requeridas: $reason | error=$error'),
);
},
onConnectionError: (AntelopError error) {
completeOnce(error);
},
onConnectionSuccess: () {
completeOnce();
},
),
);
await walletManager.connect(loginPin, newPin);
return completer.future;
}
Future<void> connectFirstTime({required String newPin}) {
return connectWallet(newPin: newPin);
}
Future<void> connectWithPin({required String loginPin}) {
return connectWallet(loginPin: loginPin);
}
Future<void> logout() async {
final walletManager = _bridge.getWalletManagerModule();
await walletManager.logout();
}
}

View File

@@ -0,0 +1,58 @@
import 'dart:async';
import 'package:flutter_treezor_entrust_sdk_bridge/flutter_treezor_entrust_sdk_bridge.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/wallet/wallet_provisioning_module.dart';
class TreezorWalletProvisioningService {
final FlutterTreezorEntrustSdkBridge _bridge;
TreezorWalletProvisioningService(this._bridge);
Future<void> provisionWallet({
required String activationCode,
bool rootedDeviceForbidden = true,
}) async {
final WalletProvisioningModule provisioning = _bridge
.getWalletProvisioningModule();
final completer = Completer<void>();
await provisioning.initialize(
WalletProvisioningCallbacks(
onInitializationSuccess: () async {
await provisioning.checkEligibility(rootedDeviceForbidden);
},
onInitializationError: (AntelopError error) {
completer.completeError(error);
},
onPermissionNotGranted: (permissions) {
completer.completeError(
Exception('Permisos no concedidos: $permissions'),
);
},
onDeviceEligible: (fingerprintAllowed) async {
await provisioning.launch(activationCode);
},
onDeviceNotEligible: (reason, denialReference) {
completer.completeError(
Exception(
'Dispositivo NO elegible: $reason | ref=$denialReference',
),
);
},
onCheckEligibilityError: (AntelopError error) {
completer.completeError(error);
},
onProvisioningPending: () {},
onProvisioningSuccess: () {
completer.complete();
},
onProvisioningError: (AntelopError error) {
completer.completeError(error);
},
),
);
return completer.future;
}
}

View File

@@ -0,0 +1,74 @@
import 'dart:async';
import 'package:flutter_treezor_entrust_sdk_bridge/flutter_treezor_entrust_sdk_bridge.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/signature/customer_authenticated_signature_module.dart';
class TreezorWalletSignatureService {
final FlutterTreezorEntrustSdkBridge _bridge;
TreezorWalletSignatureService(this._bridge);
/// Firma usando autenticación con PIN.
/// Devuelve un JWS (String) que luego se manda al backend como "SCA proof".
Future<String> generateJwsWithPin({
required String message,
required String input,
required String pin,
}) async {
// Es SOLO: NONE | PIN | BIOMETRY
const patternName = 'PIN';
final CustomerAuthenticatedSignatureModule module = _bridge
.getCustomerAuthenticatedSignatureModule(patternName, message, input);
final completer = Completer<String>();
var finished = false;
void completeOnce({Object? error, String? value}) {
if (finished) return;
finished = true;
if (error != null) {
completer.completeError(error);
} else {
completer.complete(value ?? '');
}
}
await module.signWithCustomAuthentication(
CustomCustomerAuthenticatedProcessCallback(
onCustomerCredentialsRequired: () {
module.setPinCustomerCredentials(pin);
},
onCustomerCredentialsInvalid: (reason) {
completeOnce(error: Exception('PIN inválido: $reason'));
},
onProcessSuccess: () async {
try {
final result = await module.getResult();
if (result == null || result.isEmpty) {
completeOnce(error: Exception('Firma vacía'));
return;
}
completeOnce(value: result);
} catch (e) {
completeOnce(error: e);
}
},
onError: (AntelopError? error) {
completeOnce(error: error ?? Exception('Error desconocido'));
},
onAuthenticationDeclined: () {
completeOnce(error: Exception('Autenticación rechazada'));
},
),
);
try {
return await completer.future;
} finally {
await module.clean();
}
}
}

View File

@@ -0,0 +1,58 @@
name: sca_treezor
description: "A new Flutter package project."
version: 0.0.1
homepage:
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: ^3.9.2
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge
get_it: ^9.0.5
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,4 @@
# melos_managed_dependency_overrides: flutter_treezor_entrust_sdk_bridge
dependency_overrides:
flutter_treezor_entrust_sdk_bridge:
path: ../flutter_treezor_entrust_sdk_bridge

View File

@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@@ -1,9 +1,15 @@
export 'src/models/kid.dart'; export 'src/data/models/kid.dart';
export 'src/models/task.dart'; export 'src/data/models/task.dart';
export 'src/models/savings_goal.dart'; export 'src/data/models/savings_goal.dart';
export 'src/widgets/line_graph.dart'; export 'src/widgets/line_graph.dart';
export 'src/widgets/deposit_block.dart'; export 'src/widgets/deposit_block.dart';
export 'src/screens/connection_error_screen.dart'; export 'src/screens/connection_error_screen.dart';
export 'src/screens/server_error_screen.dart'; export 'src/screens/server_error_screen.dart';
export 'src/screens/no_plan_error_screen.dart'; export 'src/screens/no_plan_error_screen.dart';
export 'src/widgets/wallet_balance_block.dart'; export 'src/widgets/wallet_balance_block.dart';
export 'src/providers/sca_wallets_use_case_provider.dart';
export 'src/providers/treezor_wallet_setup_provider.dart';
export 'src/domain/entities/sca_wallet_entity.dart';
export 'src/domain/use_cases/sca_wallets_use_case.dart';
export 'src/domain/use_cases/send_jws_sesion_use_case.dart';
export 'src/providers/send_jws_sesion_use_case_provider.dart';

View File

@@ -0,0 +1,11 @@
import '../models/sca_wallet_model.dart';
abstract class TreezorLocalDatasource {
Future<bool> isScaWalletsDone();
Future<ScaWalletsResponseModel?> getCachedScaWallets();
Future<void> saveScaWallets({required ScaWalletsResponseModel model});
Future<void> clearScaWallets();
}

View File

@@ -0,0 +1,44 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/sca_wallet_model.dart';
import 'treezor_local_data_source.dart';
class TreezorLocalDatasourceImpl implements TreezorLocalDatasource {
static const _doneKey = 'treezor_sca_wallets_done';
static const _cacheKey = 'treezor_sca_wallets_cache';
@override
Future<bool> isScaWalletsDone() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_doneKey) ?? false;
}
@override
Future<ScaWalletsResponseModel?> getCachedScaWallets() async {
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString(_cacheKey);
if (raw == null || raw.isEmpty) return null;
final decoded = jsonDecode(raw) as Map<String, dynamic>;
return ScaWalletsResponseModel.fromJson(decoded);
}
@override
Future<void> saveScaWallets({required ScaWalletsResponseModel model}) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_doneKey, true);
await prefs.setString(_cacheKey, jsonEncode(model.toJson()));
}
@override
Future<void> clearScaWallets() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_doneKey);
await prefs.remove(_cacheKey);
}
}

View File

@@ -0,0 +1,7 @@
import 'package:sf_shared/src/data/models/sca_wallet_model.dart';
abstract class TreezorRemoteDatasource {
Future<ScaWalletsResponseModel> scaWallets();
Future<bool> sendJWSSesion({required String jws});
}

View File

@@ -0,0 +1,99 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_shared/src/data/models/sca_wallet_model.dart';
import 'treezor_remote_data_source.dart';
class TreezorRemoteDatasourceImpl implements TreezorRemoteDatasource {
TreezorRemoteDatasourceImpl(this._repository);
final QuestiaRepository _repository;
@override
Future<ScaWalletsResponseModel> scaWallets() async {
try {
final response = await _repository.post<Map<String, dynamic>>(
'/treezor/sca-wallets',
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /treezor/sca-wallets');
}
final model = ScaWalletsResponseModel.fromJson(
Map<String, dynamic>.from(data),
);
return model;
} on DioException catch (error) {
final fallback = error.response?.data;
throw _mapDioError(
error,
defaultMessage: fallback is String
? fallback
: 'Error to provision / activate wallet',
);
}
}
@override
Future<bool> sendJWSSesion({required String jws}) async {
try {
final response = await _repository.post<void>(
'/payment-profiles/jwt',
body: <String, dynamic>{'jws': jws},
);
final statusCode = response.statusCode ?? 0;
return statusCode >= 200 && statusCode < 300;
} on DioException catch (error) {
final fallback = error.response?.data;
throw _mapDioError(
error,
defaultMessage: fallback is String
? fallback
: 'Error generating JWT in /payment-profiles/jwt',
);
}
}
}
Exception _mapDioError(DioException error, {required String defaultMessage}) {
final apiMsg = _extractApiMessage(error.response?.data);
final msg = apiMsg ?? error.message ?? defaultMessage;
return Exception(msg);
}
String? _extractApiMessage(Object? data) {
if (data == null) return null;
if (data is Map) {
final errorObj = data['error'];
if (errorObj is Map && errorObj['message'] is String) {
return (errorObj['message'] as String).trim();
}
if (data['message'] is String) {
return (data['message'] as String).trim();
}
return null;
}
if (data is String) {
final raw = data.trim();
if (raw.isEmpty) return null;
try {
final decoded = jsonDecode(raw);
return _extractApiMessage(decoded);
} catch (_) {
return raw;
}
}
return null;
}

View File

@@ -0,0 +1,40 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:sf_shared/src/domain/entities/sca_wallet_entity.dart';
part 'sca_wallet_model.freezed.dart';
part 'sca_wallet_model.g.dart';
@freezed
abstract class ScaWalletsResponseModel with _$ScaWalletsResponseModel {
const factory ScaWalletsResponseModel({
required bool isCreated,
required ScaWalletItemModel item,
}) = _ScaWalletsResponseModel;
factory ScaWalletsResponseModel.fromJson(Map<String, dynamic> json) =>
_$ScaWalletsResponseModelFromJson(json);
}
@freezed
abstract class ScaWalletItemModel with _$ScaWalletItemModel {
const factory ScaWalletItemModel({
required String id,
required String status,
required String activationCode,
}) = _ScaWalletItemModel;
factory ScaWalletItemModel.fromJson(Map<String, dynamic> json) =>
_$ScaWalletItemModelFromJson(json);
}
extension ScaWalletsResponseModelMapper on ScaWalletsResponseModel {
ScaWalletsResponseEntity toEntity() {
return ScaWalletsResponseEntity(
isCreated: isCreated,
item: ScaWalletItemEntity(
id: item.id,
status: item.status,
activationCode: item.activationCode,
),
);
}
}

View File

@@ -0,0 +1,567 @@
// 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 'sca_wallet_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ScaWalletsResponseModel {
bool get isCreated; ScaWalletItemModel get item;
/// Create a copy of ScaWalletsResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ScaWalletsResponseModelCopyWith<ScaWalletsResponseModel> get copyWith => _$ScaWalletsResponseModelCopyWithImpl<ScaWalletsResponseModel>(this as ScaWalletsResponseModel, _$identity);
/// Serializes this ScaWalletsResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ScaWalletsResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ScaWalletsResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class $ScaWalletsResponseModelCopyWith<$Res> {
factory $ScaWalletsResponseModelCopyWith(ScaWalletsResponseModel value, $Res Function(ScaWalletsResponseModel) _then) = _$ScaWalletsResponseModelCopyWithImpl;
@useResult
$Res call({
bool isCreated, ScaWalletItemModel item
});
$ScaWalletItemModelCopyWith<$Res> get item;
}
/// @nodoc
class _$ScaWalletsResponseModelCopyWithImpl<$Res>
implements $ScaWalletsResponseModelCopyWith<$Res> {
_$ScaWalletsResponseModelCopyWithImpl(this._self, this._then);
final ScaWalletsResponseModel _self;
final $Res Function(ScaWalletsResponseModel) _then;
/// Create a copy of ScaWalletsResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_self.copyWith(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ScaWalletItemModel,
));
}
/// Create a copy of ScaWalletsResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ScaWalletItemModelCopyWith<$Res> get item {
return $ScaWalletItemModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [ScaWalletsResponseModel].
extension ScaWalletsResponseModelPatterns on ScaWalletsResponseModel {
/// 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( _ScaWalletsResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ScaWalletsResponseModel() 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( _ScaWalletsResponseModel value) $default,){
final _that = this;
switch (_that) {
case _ScaWalletsResponseModel():
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( _ScaWalletsResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _ScaWalletsResponseModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isCreated, ScaWalletItemModel item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ScaWalletsResponseModel() when $default != null:
return $default(_that.isCreated,_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( bool isCreated, ScaWalletItemModel item) $default,) {final _that = this;
switch (_that) {
case _ScaWalletsResponseModel():
return $default(_that.isCreated,_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( bool isCreated, ScaWalletItemModel item)? $default,) {final _that = this;
switch (_that) {
case _ScaWalletsResponseModel() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _ScaWalletsResponseModel implements ScaWalletsResponseModel {
const _ScaWalletsResponseModel({required this.isCreated, required this.item});
factory _ScaWalletsResponseModel.fromJson(Map<String, dynamic> json) => _$ScaWalletsResponseModelFromJson(json);
@override final bool isCreated;
@override final ScaWalletItemModel item;
/// Create a copy of ScaWalletsResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ScaWalletsResponseModelCopyWith<_ScaWalletsResponseModel> get copyWith => __$ScaWalletsResponseModelCopyWithImpl<_ScaWalletsResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ScaWalletsResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ScaWalletsResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ScaWalletsResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class _$ScaWalletsResponseModelCopyWith<$Res> implements $ScaWalletsResponseModelCopyWith<$Res> {
factory _$ScaWalletsResponseModelCopyWith(_ScaWalletsResponseModel value, $Res Function(_ScaWalletsResponseModel) _then) = __$ScaWalletsResponseModelCopyWithImpl;
@override @useResult
$Res call({
bool isCreated, ScaWalletItemModel item
});
@override $ScaWalletItemModelCopyWith<$Res> get item;
}
/// @nodoc
class __$ScaWalletsResponseModelCopyWithImpl<$Res>
implements _$ScaWalletsResponseModelCopyWith<$Res> {
__$ScaWalletsResponseModelCopyWithImpl(this._self, this._then);
final _ScaWalletsResponseModel _self;
final $Res Function(_ScaWalletsResponseModel) _then;
/// Create a copy of ScaWalletsResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_ScaWalletsResponseModel(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ScaWalletItemModel,
));
}
/// Create a copy of ScaWalletsResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ScaWalletItemModelCopyWith<$Res> get item {
return $ScaWalletItemModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$ScaWalletItemModel {
String get id; String get status; String get activationCode;
/// Create a copy of ScaWalletItemModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ScaWalletItemModelCopyWith<ScaWalletItemModel> get copyWith => _$ScaWalletItemModelCopyWithImpl<ScaWalletItemModel>(this as ScaWalletItemModel, _$identity);
/// Serializes this ScaWalletItemModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ScaWalletItemModel&&(identical(other.id, id) || other.id == id)&&(identical(other.status, status) || other.status == status)&&(identical(other.activationCode, activationCode) || other.activationCode == activationCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,status,activationCode);
@override
String toString() {
return 'ScaWalletItemModel(id: $id, status: $status, activationCode: $activationCode)';
}
}
/// @nodoc
abstract mixin class $ScaWalletItemModelCopyWith<$Res> {
factory $ScaWalletItemModelCopyWith(ScaWalletItemModel value, $Res Function(ScaWalletItemModel) _then) = _$ScaWalletItemModelCopyWithImpl;
@useResult
$Res call({
String id, String status, String activationCode
});
}
/// @nodoc
class _$ScaWalletItemModelCopyWithImpl<$Res>
implements $ScaWalletItemModelCopyWith<$Res> {
_$ScaWalletItemModelCopyWithImpl(this._self, this._then);
final ScaWalletItemModel _self;
final $Res Function(ScaWalletItemModel) _then;
/// Create a copy of ScaWalletItemModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? status = null,Object? activationCode = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,activationCode: null == activationCode ? _self.activationCode : activationCode // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [ScaWalletItemModel].
extension ScaWalletItemModelPatterns on ScaWalletItemModel {
/// 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( _ScaWalletItemModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ScaWalletItemModel() 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( _ScaWalletItemModel value) $default,){
final _that = this;
switch (_that) {
case _ScaWalletItemModel():
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( _ScaWalletItemModel value)? $default,){
final _that = this;
switch (_that) {
case _ScaWalletItemModel() 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 status, String activationCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ScaWalletItemModel() when $default != null:
return $default(_that.id,_that.status,_that.activationCode);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 status, String activationCode) $default,) {final _that = this;
switch (_that) {
case _ScaWalletItemModel():
return $default(_that.id,_that.status,_that.activationCode);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 status, String activationCode)? $default,) {final _that = this;
switch (_that) {
case _ScaWalletItemModel() when $default != null:
return $default(_that.id,_that.status,_that.activationCode);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _ScaWalletItemModel implements ScaWalletItemModel {
const _ScaWalletItemModel({required this.id, required this.status, required this.activationCode});
factory _ScaWalletItemModel.fromJson(Map<String, dynamic> json) => _$ScaWalletItemModelFromJson(json);
@override final String id;
@override final String status;
@override final String activationCode;
/// Create a copy of ScaWalletItemModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ScaWalletItemModelCopyWith<_ScaWalletItemModel> get copyWith => __$ScaWalletItemModelCopyWithImpl<_ScaWalletItemModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ScaWalletItemModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ScaWalletItemModel&&(identical(other.id, id) || other.id == id)&&(identical(other.status, status) || other.status == status)&&(identical(other.activationCode, activationCode) || other.activationCode == activationCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,status,activationCode);
@override
String toString() {
return 'ScaWalletItemModel(id: $id, status: $status, activationCode: $activationCode)';
}
}
/// @nodoc
abstract mixin class _$ScaWalletItemModelCopyWith<$Res> implements $ScaWalletItemModelCopyWith<$Res> {
factory _$ScaWalletItemModelCopyWith(_ScaWalletItemModel value, $Res Function(_ScaWalletItemModel) _then) = __$ScaWalletItemModelCopyWithImpl;
@override @useResult
$Res call({
String id, String status, String activationCode
});
}
/// @nodoc
class __$ScaWalletItemModelCopyWithImpl<$Res>
implements _$ScaWalletItemModelCopyWith<$Res> {
__$ScaWalletItemModelCopyWithImpl(this._self, this._then);
final _ScaWalletItemModel _self;
final $Res Function(_ScaWalletItemModel) _then;
/// Create a copy of ScaWalletItemModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? status = null,Object? activationCode = null,}) {
return _then(_ScaWalletItemModel(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,activationCode: null == activationCode ? _self.activationCode : activationCode // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,32 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sca_wallet_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_ScaWalletsResponseModel _$ScaWalletsResponseModelFromJson(
Map<String, dynamic> json,
) => _ScaWalletsResponseModel(
isCreated: json['isCreated'] as bool,
item: ScaWalletItemModel.fromJson(json['item'] as Map<String, dynamic>),
);
Map<String, dynamic> _$ScaWalletsResponseModelToJson(
_ScaWalletsResponseModel instance,
) => <String, dynamic>{'isCreated': instance.isCreated, 'item': instance.item};
_ScaWalletItemModel _$ScaWalletItemModelFromJson(Map<String, dynamic> json) =>
_ScaWalletItemModel(
id: json['id'] as String,
status: json['status'] as String,
activationCode: json['activationCode'] as String,
);
Map<String, dynamic> _$ScaWalletItemModelToJson(_ScaWalletItemModel instance) =>
<String, dynamic>{
'id': instance.id,
'status': instance.status,
'activationCode': instance.activationCode,
};

View File

@@ -0,0 +1,58 @@
import 'package:sf_shared/src/data/models/sca_wallet_model.dart';
import 'package:sf_shared/src/domain/entities/sca_wallet_entity.dart';
import 'package:sf_shared/src/domain/repositories/treezor_repository.dart';
import '../datasource/treezor_local_data_source.dart';
import '../datasource/treezor_remote_data_source.dart';
class TreezorRepositoryImpl implements TreezorRepository {
TreezorRepositoryImpl(this._remote, this._local);
final TreezorRemoteDatasource _remote;
final TreezorLocalDatasource _local;
Future<ScaWalletsResponseEntity>? _inFlight;
@override
Future<ScaWalletsResponseEntity> scaWallets({bool forceRefresh = false}) {
if (!forceRefresh && _inFlight != null) {
return _inFlight!;
}
final future = _scaWalletsInternal(forceRefresh: forceRefresh);
_inFlight = future;
return future.whenComplete(() {
_inFlight = null;
});
}
Future<ScaWalletsResponseEntity> _scaWalletsInternal({
required bool forceRefresh,
}) async {
if (!forceRefresh) {
final done = await _local.isScaWalletsDone();
if (done) {
final cached = await _local.getCachedScaWallets();
if (cached != null) return cached.toEntity();
}
}
final model = await _remote.scaWallets();
await _local.saveScaWallets(model: model);
return model.toEntity();
}
@override
Future<void> resetScaWallets() {
return _local.clearScaWallets();
}
@override
Future<bool> sendJWSSesion({required String jws}) {
return _remote.sendJWSSesion(jws: jws);
}
}

View File

@@ -0,0 +1,20 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'sca_wallet_entity.freezed.dart';
@freezed
abstract class ScaWalletsResponseEntity with _$ScaWalletsResponseEntity {
const factory ScaWalletsResponseEntity({
required bool isCreated,
required ScaWalletItemEntity item,
}) = _ScaWalletsResponseEntity;
}
@freezed
abstract class ScaWalletItemEntity with _$ScaWalletItemEntity {
const factory ScaWalletItemEntity({
required String id,
required String status,
required String activationCode,
}) = _ScaWalletItemEntity;
}

View File

@@ -0,0 +1,555 @@
// 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 'sca_wallet_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ScaWalletsResponseEntity {
bool get isCreated; ScaWalletItemEntity get item;
/// Create a copy of ScaWalletsResponseEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ScaWalletsResponseEntityCopyWith<ScaWalletsResponseEntity> get copyWith => _$ScaWalletsResponseEntityCopyWithImpl<ScaWalletsResponseEntity>(this as ScaWalletsResponseEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ScaWalletsResponseEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ScaWalletsResponseEntity(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class $ScaWalletsResponseEntityCopyWith<$Res> {
factory $ScaWalletsResponseEntityCopyWith(ScaWalletsResponseEntity value, $Res Function(ScaWalletsResponseEntity) _then) = _$ScaWalletsResponseEntityCopyWithImpl;
@useResult
$Res call({
bool isCreated, ScaWalletItemEntity item
});
$ScaWalletItemEntityCopyWith<$Res> get item;
}
/// @nodoc
class _$ScaWalletsResponseEntityCopyWithImpl<$Res>
implements $ScaWalletsResponseEntityCopyWith<$Res> {
_$ScaWalletsResponseEntityCopyWithImpl(this._self, this._then);
final ScaWalletsResponseEntity _self;
final $Res Function(ScaWalletsResponseEntity) _then;
/// Create a copy of ScaWalletsResponseEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_self.copyWith(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ScaWalletItemEntity,
));
}
/// Create a copy of ScaWalletsResponseEntity
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ScaWalletItemEntityCopyWith<$Res> get item {
return $ScaWalletItemEntityCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [ScaWalletsResponseEntity].
extension ScaWalletsResponseEntityPatterns on ScaWalletsResponseEntity {
/// 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( _ScaWalletsResponseEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ScaWalletsResponseEntity() 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( _ScaWalletsResponseEntity value) $default,){
final _that = this;
switch (_that) {
case _ScaWalletsResponseEntity():
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( _ScaWalletsResponseEntity value)? $default,){
final _that = this;
switch (_that) {
case _ScaWalletsResponseEntity() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isCreated, ScaWalletItemEntity item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ScaWalletsResponseEntity() when $default != null:
return $default(_that.isCreated,_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( bool isCreated, ScaWalletItemEntity item) $default,) {final _that = this;
switch (_that) {
case _ScaWalletsResponseEntity():
return $default(_that.isCreated,_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( bool isCreated, ScaWalletItemEntity item)? $default,) {final _that = this;
switch (_that) {
case _ScaWalletsResponseEntity() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return null;
}
}
}
/// @nodoc
class _ScaWalletsResponseEntity implements ScaWalletsResponseEntity {
const _ScaWalletsResponseEntity({required this.isCreated, required this.item});
@override final bool isCreated;
@override final ScaWalletItemEntity item;
/// Create a copy of ScaWalletsResponseEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ScaWalletsResponseEntityCopyWith<_ScaWalletsResponseEntity> get copyWith => __$ScaWalletsResponseEntityCopyWithImpl<_ScaWalletsResponseEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ScaWalletsResponseEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'ScaWalletsResponseEntity(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class _$ScaWalletsResponseEntityCopyWith<$Res> implements $ScaWalletsResponseEntityCopyWith<$Res> {
factory _$ScaWalletsResponseEntityCopyWith(_ScaWalletsResponseEntity value, $Res Function(_ScaWalletsResponseEntity) _then) = __$ScaWalletsResponseEntityCopyWithImpl;
@override @useResult
$Res call({
bool isCreated, ScaWalletItemEntity item
});
@override $ScaWalletItemEntityCopyWith<$Res> get item;
}
/// @nodoc
class __$ScaWalletsResponseEntityCopyWithImpl<$Res>
implements _$ScaWalletsResponseEntityCopyWith<$Res> {
__$ScaWalletsResponseEntityCopyWithImpl(this._self, this._then);
final _ScaWalletsResponseEntity _self;
final $Res Function(_ScaWalletsResponseEntity) _then;
/// Create a copy of ScaWalletsResponseEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_ScaWalletsResponseEntity(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as ScaWalletItemEntity,
));
}
/// Create a copy of ScaWalletsResponseEntity
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ScaWalletItemEntityCopyWith<$Res> get item {
return $ScaWalletItemEntityCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$ScaWalletItemEntity {
String get id; String get status; String get activationCode;
/// Create a copy of ScaWalletItemEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ScaWalletItemEntityCopyWith<ScaWalletItemEntity> get copyWith => _$ScaWalletItemEntityCopyWithImpl<ScaWalletItemEntity>(this as ScaWalletItemEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ScaWalletItemEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.status, status) || other.status == status)&&(identical(other.activationCode, activationCode) || other.activationCode == activationCode));
}
@override
int get hashCode => Object.hash(runtimeType,id,status,activationCode);
@override
String toString() {
return 'ScaWalletItemEntity(id: $id, status: $status, activationCode: $activationCode)';
}
}
/// @nodoc
abstract mixin class $ScaWalletItemEntityCopyWith<$Res> {
factory $ScaWalletItemEntityCopyWith(ScaWalletItemEntity value, $Res Function(ScaWalletItemEntity) _then) = _$ScaWalletItemEntityCopyWithImpl;
@useResult
$Res call({
String id, String status, String activationCode
});
}
/// @nodoc
class _$ScaWalletItemEntityCopyWithImpl<$Res>
implements $ScaWalletItemEntityCopyWith<$Res> {
_$ScaWalletItemEntityCopyWithImpl(this._self, this._then);
final ScaWalletItemEntity _self;
final $Res Function(ScaWalletItemEntity) _then;
/// Create a copy of ScaWalletItemEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? status = null,Object? activationCode = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,activationCode: null == activationCode ? _self.activationCode : activationCode // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [ScaWalletItemEntity].
extension ScaWalletItemEntityPatterns on ScaWalletItemEntity {
/// 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( _ScaWalletItemEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ScaWalletItemEntity() 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( _ScaWalletItemEntity value) $default,){
final _that = this;
switch (_that) {
case _ScaWalletItemEntity():
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( _ScaWalletItemEntity value)? $default,){
final _that = this;
switch (_that) {
case _ScaWalletItemEntity() 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 status, String activationCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ScaWalletItemEntity() when $default != null:
return $default(_that.id,_that.status,_that.activationCode);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 status, String activationCode) $default,) {final _that = this;
switch (_that) {
case _ScaWalletItemEntity():
return $default(_that.id,_that.status,_that.activationCode);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 status, String activationCode)? $default,) {final _that = this;
switch (_that) {
case _ScaWalletItemEntity() when $default != null:
return $default(_that.id,_that.status,_that.activationCode);case _:
return null;
}
}
}
/// @nodoc
class _ScaWalletItemEntity implements ScaWalletItemEntity {
const _ScaWalletItemEntity({required this.id, required this.status, required this.activationCode});
@override final String id;
@override final String status;
@override final String activationCode;
/// Create a copy of ScaWalletItemEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ScaWalletItemEntityCopyWith<_ScaWalletItemEntity> get copyWith => __$ScaWalletItemEntityCopyWithImpl<_ScaWalletItemEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ScaWalletItemEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.status, status) || other.status == status)&&(identical(other.activationCode, activationCode) || other.activationCode == activationCode));
}
@override
int get hashCode => Object.hash(runtimeType,id,status,activationCode);
@override
String toString() {
return 'ScaWalletItemEntity(id: $id, status: $status, activationCode: $activationCode)';
}
}
/// @nodoc
abstract mixin class _$ScaWalletItemEntityCopyWith<$Res> implements $ScaWalletItemEntityCopyWith<$Res> {
factory _$ScaWalletItemEntityCopyWith(_ScaWalletItemEntity value, $Res Function(_ScaWalletItemEntity) _then) = __$ScaWalletItemEntityCopyWithImpl;
@override @useResult
$Res call({
String id, String status, String activationCode
});
}
/// @nodoc
class __$ScaWalletItemEntityCopyWithImpl<$Res>
implements _$ScaWalletItemEntityCopyWith<$Res> {
__$ScaWalletItemEntityCopyWithImpl(this._self, this._then);
final _ScaWalletItemEntity _self;
final $Res Function(_ScaWalletItemEntity) _then;
/// Create a copy of ScaWalletItemEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? status = null,Object? activationCode = null,}) {
return _then(_ScaWalletItemEntity(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as String,activationCode: null == activationCode ? _self.activationCode : activationCode // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,9 @@
import '../entities/sca_wallet_entity.dart';
abstract class TreezorRepository {
Future<ScaWalletsResponseEntity> scaWallets({bool forceRefresh});
Future<void> resetScaWallets();
Future<bool> sendJWSSesion({required String jws});
}

View File

@@ -0,0 +1,7 @@
import '../entities/sca_wallet_entity.dart';
abstract class ScaWalletsUseCase {
Future<ScaWalletsResponseEntity> scaWallets({bool forceRefresh});
Future<void> resetScaWallets();
}

View File

@@ -0,0 +1,19 @@
import '../entities/sca_wallet_entity.dart';
import '../repositories/treezor_repository.dart';
import 'sca_wallets_use_case.dart';
class ScaWalletsUseCaseImpl implements ScaWalletsUseCase {
const ScaWalletsUseCaseImpl(this._repository);
final TreezorRepository _repository;
@override
Future<ScaWalletsResponseEntity> scaWallets({bool forceRefresh = false}) {
return _repository.scaWallets(forceRefresh: forceRefresh);
}
@override
Future<void> resetScaWallets() {
return _repository.resetScaWallets();
}
}

View File

@@ -0,0 +1,3 @@
abstract class SendJWSSesionUseCase {
Future<bool> sendJWSSesion({required String jws});
}

View File

@@ -0,0 +1,14 @@
import 'package:sf_shared/src/domain/use_cases/send_jws_sesion_use_case.dart';
import '../repositories/treezor_repository.dart';
class SendJWSSesionImpl implements SendJWSSesionUseCase {
const SendJWSSesionImpl(this._repository);
final TreezorRepository _repository;
@override
Future<bool> sendJWSSesion({required String jws}) {
return _repository.sendJWSSesion(jws: jws);
}
}

View File

@@ -0,0 +1,90 @@
import 'package:sca_treezor/sca_treezor.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sf_shared/src/domain/use_cases/sca_wallets_use_case.dart';
class TreezorWalletSetupUseCase {
TreezorWalletSetupUseCase(
this._scaWalletsUseCase,
this._provisioning,
this._connection,
);
final ScaWalletsUseCase _scaWalletsUseCase;
final TreezorWalletProvisioningService _provisioning;
final TreezorWalletConnectionService _connection;
static const _walletProvisionedKey = 'treezor_wallet_provisioned';
Future<bool> _isProvisioned() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_walletProvisionedKey) ?? false;
}
Future<void> _setProvisioned(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_walletProvisionedKey, value);
}
/// ✅ Esto es lo que llamarás después del 2FA
///
/// - Si NO está provisionado:
/// - pide activationCode (backend)
/// - provisiona wallet
/// - pide PIN nuevo
/// - conecta creando PIN
/// - Si ya está provisionado:
/// - pide PIN existente
/// - conecta con PIN
Future<void> ensureWalletReady({
required Future<String> Function() askNewPin,
required Future<String> Function() askLoginPin,
}) async {
final provisioned = await _isProvisioned();
if (!provisioned) {
// 1) pedir activationCode del backend
final response = await _scaWalletsUseCase.scaWallets(forceRefresh: true);
// ⚠️ AJUSTA esta línea según tu entity real:
// Ej: response.activationCode, response.item.activationCode, response.wallet.activationCode, etc.
final activationCode = response.item.activationCode;
if (activationCode.trim().isEmpty) {
throw Exception('ActivationCode vacío en la respuesta del backend');
}
// 2) provisionar en el dispositivo
await _provisioning.provisionWallet(
activationCode: activationCode,
rootedDeviceForbidden: true,
);
// 3) crear PIN + conectar
final newPin = await askNewPin();
await _connection.connectFirstTime(newPin: newPin);
// 4) marcar como provisionado
await _setProvisioned(true);
return;
}
// Ya provisionado → conectar con PIN existente
try {
final loginPin = await askLoginPin();
await _connection.connectWithPin(loginPin: loginPin);
} catch (e) {
// Fallback: si el SDK dice provisioning requerido, reseteamos flag
final msg = e.toString().toLowerCase();
if (msg.contains('provisioning')) {
await _setProvisioned(false);
rethrow;
}
rethrow;
}
}
Future<void> reset() async {
await _setProvisioned(false);
}
}

View File

@@ -0,0 +1,10 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_shared/src/domain/use_cases/sca_wallets_use_case.dart';
import 'package:sf_shared/src/domain/use_cases/sca_wallets_use_case_impl.dart';
import 'package:sf_shared/src/providers/treezor_repository_provider.dart';
final scaWalletsUseCaseProvider = Provider<ScaWalletsUseCase>((ref) {
final repository = ref.watch(treezorRepositoryProvider);
return ScaWalletsUseCaseImpl(repository);
});

Some files were not shown because too many files have changed in this diff Show More