Compare commits

...

94 Commits

Author SHA1 Message Date
c80b7a9a5f fix 2026-04-14 21:36:15 +02:00
db153bb38d removed "+" symbol from sign up email 2026-04-14 14:09:05 +02:00
50c4529cba fix 2026-04-14 12:49:43 +02:00
b6deb4b371 renew card 2026-03-24 20:20:54 +01:00
47c7821b0c feat: add card PIN management and card renewal to child wallet
- Add set PIN / change PIN multi-step flow (4-digit card PIN + 6-digit SCA PIN) with Treezor PCI DSS SCA proof generation
  - Add unblock PIN for blocked cards after failed attempts
  - Add renew card with SCA proof (same as wallet creation)
  - Show menu options conditionally based on hasCardPin and isPinBlocked flags
  - Make ScaPinView configurable with pinLength parameter (default 6)
  - Add hasCardPin to ChildProfileEntity and isPinBlocked to WalletCardEntity
  - Add EN/ES localizations for all new screens and messages
2026-03-24 13:47:21 +01:00
a07e9c23ca create wallet witch pci work in progress 2026-03-24 13:46:53 +01:00
5111d5d65f feat: add call history screen
- Add call history screen with list of incoming/outgoing calls
  - Implement GET /devices/identificator/{id}/call-histories endpoint
  - Add CallHistoryResponseModel with freezed
  - Add Riverpod provider for CallHistoryDatasource
  - Add route, builder, and menu button in device management
2026-03-22 05:50:20 +01:00
ced0895063 feat: merge health feature and add measure command
- Add REQUEST_HEART_RATE command with measure button in health screen
  - Add ref.mounted checks and fix early return in measure()
  - Remove unused SET_LANGUAGE from DeviceCommand enum
2026-03-22 05:15:22 +01:00
34e7a7c60f feat: merge remote-call feature and fix remote connection
- Implement photos API (GET /devices/identificator/{id}/photos)
  - Fix deviceId empty in commands (set before async load)
  - Fix missing await in call() method
  - Add ref.mounted checks on all async operations
  - Reload photos after REQUEST_PHOTO command
  - Add CountryPrefixPicker to spy call dialog
  - Add loading state and topSnackbar feedback on call
  - Handle empty photos list in gallery
  - Fix Expanded overflow in remote camera screen
  - Change keyboard to phone type in spy call
  - Remove unnecessary use cases
  - Add GetPicturesResponseModel with freezed
2026-03-22 04:57:38 +01:00
c89f1c666e feat: add volume control and merge sound mode feature
- Add volume control screen with sliders for media, ringtone, and alarm
- Update device settings via PUT /devices with CSV (same as language)
- Extract DeviceCsvBuilder to legacy_shared (shared between language and volume)
- Create Riverpod provider for DeviceUpdateDatasource
- Extract VolumeThumbShape to separate widget file
- Merge sound mode feature (SET_SOUND_MODE command, pending backend whitelist)
- Fix sound screen overflow with SingleChildScrollView
2026-03-22 04:01:09 +01:00
33c2403aef fix: improve language feature and fix merge issues
- Change language update from POST /commands to PUT /devices with CSV
- Add CSV escape for JSON fields (doubled quotes)
- Move device payload construction to datasource layer
- Add loading indicator on save button
- Fix 401 redirect to legacy login
- Remove debug print from commands datasource
2026-03-22 03:16:07 +01:00
0088d146f0 feat: enhance location map with route history, animations, follow mode, and fix API models
- Fix position address model nullability (province field missing from API)
  - Fix health query order to sortDirection to match backend API
  - Add pagination to health chart queries to prevent backend timeout
  - Align GetDevicesResponseModel with full backend schema
  - Add route history with gradient polyline, direction arrows, and clustering
  - Add animated map movements
  - Add follow mode with auto-refresh
  - Add share location via Google Maps link
  - Add fit bounds on history load
  - Add expandable action buttons panel
  - Add location list bottom sheet with type filters
  - Add whitelist sync alongside secondary contacts
  - Add loading state to linked devices screen
  - Refactor location_map.dart: extract RouteHistoryLayer, MapActionsPanel
  - Migrate setState to LocationMapViewModel
2026-03-22 02:30:21 +01:00
94c042d403 manual health measurement command 2026-03-20 15:35:12 +01:00
48cb23379c remote photo command 2026-03-20 15:14:40 +01:00
e526dce2c9 change language options and fix command 2026-03-20 10:41:03 +01:00
cacc2460f1 Merge branch 'fusion-app' into feature/language 2026-03-20 09:34:47 +01:00
dd53db6795 set language 2026-03-20 09:33:57 +01:00
435a9c04f9 bump build to 5 2026-03-18 21:00:03 +01:00
8e3a27e0d3 feat: add route history, map controls, and geofence/FP management
- Position history with polyline trail and date range picker
  - Map style selector (standard, voyager, light, dark, satellite) persisted via SharedPreferences
  - Geofence and frequent place CRUD with info cards
  - Device banner with swipeable carousel
  - Refresh position button
  - Widget extraction: map controls, info cards, device banner, modal overlay
2026-03-18 19:48:30 +01:00
48d2430c9c remote call command 2026-03-18 17:09:03 +01:00
cf0c55eafe Merge branch 'feature/linked-devices' into fusion-app 2026-03-18 15:09:35 +01:00
03c6633504 fix delete device sizing 2026-03-18 15:09:13 +01:00
b8184f02ec Merge remote-tracking branch 'origin/fusion-app' into fusion-app 2026-03-18 14:56:51 +01:00
c12d1924c4 splash screen fix 2026-03-18 13:52:07 +01:00
869f33f1f1 Merge remote-tracking branch 'origin/feature/change-password' into fusion-app 2026-03-18 13:21:52 +01:00
a07246130e fix change password fields and validation 2026-03-18 13:21:16 +01:00
67aafafd1e hide interceptor for legacy app 2026-03-18 13:15:00 +01:00
c929e1e2d7 beneficiary validation and development api origin fix 2026-03-18 13:13:36 +01:00
990266ba95 delete device command and device setup flow 2026-03-18 11:49:05 +01:00
fa36037aac personal data dial code 2026-03-16 17:45:00 +01:00
c9e2adf692 contacts dial codes 2026-03-16 16:22:45 +01:00
995b69eb65 Merge remote-tracking branch 'origin/fusion-app' into legacy 2026-03-16 15:25:16 +01:00
88269c40f8 Add iOS privacy keys for staging/dev flavors, bump build to 4, hide SF Pay button 2026-03-16 15:09:26 +01:00
f1226b4c18 Merge remote-tracking branch 'origin/fusion-app' into legacy
# Conflicts:
#	apps/mobile_app/lib/save_family_app.dart
#	packages/sf_localizations/assets/l10n/de.json
#	packages/sf_localizations/assets/l10n/en.json
#	packages/sf_localizations/assets/l10n/es.json
#	packages/sf_localizations/assets/l10n/fr.json
#	packages/sf_localizations/assets/l10n/it.json
#	packages/sf_localizations/assets/l10n/pt.json
#	packages/sf_localizations/lib/src/generated/i18n.dart
2026-03-16 13:26:08 +01:00
b636550619 navigation fixes 2026-03-16 13:10:55 +01:00
797d236547 fix translation texts 2026-03-16 12:56:17 +01:00
90447ce9a0 Merge branch 'feature/remote-management' into legacy
# Conflicts:
#	apps/mobile_app/lib/save_family_app.dart
#	packages/sf_localizations/assets/l10n/de.json
#	packages/sf_localizations/assets/l10n/en.json
#	packages/sf_localizations/assets/l10n/es.json
#	packages/sf_localizations/assets/l10n/fr.json
#	packages/sf_localizations/assets/l10n/it.json
#	packages/sf_localizations/assets/l10n/pt.json
#	packages/sf_localizations/lib/src/generated/i18n.dart
2026-03-16 12:54:23 +01:00
22ef648b41 Merge remote-tracking branch 'origin/fusion-app' into legacy
# Conflicts:
#	apps/mobile_app/lib/navigation/app_router.dart
#	packages/sf_localizations/assets/l10n/en.json
#	packages/sf_localizations/assets/l10n/es.json
#	packages/sf_localizations/lib/src/generated/i18n.dart
2026-03-16 12:50:54 +01:00
ae4bc7824a Merge remote-tracking branch 'origin/feature/remote-management' into feature/remote-management 2026-03-16 12:47:18 +01:00
4eb4ac81ce Merge origin/feature/remote-management
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 12:46:58 +01:00
01d9d4241b remote management commands 2026-03-16 12:46:13 +01:00
907eaf8d33 commented some tabs to deploy in stores ,persistence login and heartbeaat 2026-03-16 12:40:24 +01:00
440bbcac66 device management features, settings module and contact sync
Device management:
  - Activity meter with steps charts and history
  - Apps usage with daily breakdown and top apps
  - Health monitoring (heart rate, oxygen, blood pressure)
  - Scheduled activities with timeline and CRUD
  - Contacts sync to device via contact-lists
  - Locate device, rewards refactor

  Settings (new module):
  - Block phone
  - SOS contacts
  - WiFi networks
  - Alarm refactor with full CRUD
  - Settings menu with feature stubs

  Account:
  - Personal data and account settings refactor

  Shared:
  - 100+ i18n keys in 6 languages
  - New routes in app_router
  - WeekDayChips, TimeRangeSelector shared widgets
  - Legacy dashboard shell simplified
2026-03-16 08:37:52 +01:00
904cfee2a9 comment some widgets 2026-03-13 09:44:59 +01:00
69b3cf358a add edit profile screens (child/parent) with SCA, paginated transactions, and reactive state refresh 2026-03-12 22:42:38 +01:00
ec4e42b408 settings ui 2026-03-12 17:40:06 +01:00
9bf06f2480 contacts endpoints and state fix 2026-03-12 17:11:26 +01:00
903e1991e1 delete account endpoint 2026-03-12 11:12:13 +01:00
f9d8f59195 personal data ui fix and endpoint 2026-03-11 17:35:00 +01:00
ffc0a1f103 legacy device setup screen flow from account settings 2026-03-11 13:11:11 +01:00
53cadd8499 linked devices state and models fix 2026-03-11 11:56:39 +01:00
b4bb90d357 change password state fix and endpoint 2026-03-10 17:35:48 +01:00
5ee7852ee7 rewards ui, state and command 2026-03-10 16:08:33 +01:00
2b3169b3e5 find device command 2026-03-10 12:26:00 +01:00
3cbf936583 fix command endpoint 2026-03-10 12:24:22 +01:00
bb666e8ab6 remove duplicated providers, send command basic endpoint 2026-03-09 17:47:24 +01:00
76c7eb606f fix 2026-03-09 15:51:10 +01:00
e4000d57fc activation key in legacy device setup 2026-03-09 15:32:53 +01:00
fd8574e631 get device position and staging config fixed 2026-03-09 11:45:04 +01:00
644d1c2abe Legacy modules refactor: auth, control panel, location module, and session persistence 2026-03-09 02:28:31 +01:00
01de94876b legacy login refactor 2026-03-06 00:44:12 +01:00
74309006b1 fixes 2026-03-05 15:42:34 +01:00
e8d8e4a022 pubspec overrides fixes 2026-03-05 12:57:04 +01:00
cf03bbc1b5 Merge commit '014ef1ad2615fe2981f18ed28adcde17edda4f1e' into fusion-app 2026-03-05 12:53:39 +01:00
46b062bc48 splash gif switched by lottie animation 2026-03-05 10:12:55 +01:00
014ef1ad26 Added translations, fixed icons, created menu buttons, refactor screens 2026-03-04 18:00:55 +01:00
88275c4ae6 confetti animation 2026-03-03 12:15:50 +01:00
5a68dfb3df Delete account 2026-02-26 17:01:19 +01:00
8445af3f5a Change password 2026-02-23 09:46:41 +01:00
471aa1067c Added selected device provider, applied page_layout to account screens 2026-02-18 16:31:50 +01:00
d8afa49897 Added regcode qr dialog 2026-02-17 16:34:19 +01:00
5f1e125cff Added map data and device selection 2026-02-17 12:11:50 +01:00
f76a88a53c Fix translations and icons
Added page layout to customer service screens
2026-02-16 11:20:33 +01:00
e581895246 Refactor UserEntity to legacy_shared package 2026-02-16 08:54:15 +01:00
5186c21e9a Merge branch 'customer-service' into legacy
# Conflicts:
#	apps/mobile_app/lib/navigation/app_router.dart
#	modules/legacy/melos_legacy.iml
#	packages/navigation/lib/app_routes.dart
#	packages/sf_localizations/assets/l10n/en.json
#	packages/sf_localizations/assets/l10n/es.json
#	packages/sf_localizations/lib/src/generated/i18n.dart
2026-02-13 09:34:45 +01:00
4f5e69c6fc Merge branch 'functions' into legacy
# Conflicts:
#	apps/mobile_app/lib/navigation/app_router.dart
#	apps/mobile_app/pubspec.yaml
#	modules/legacy/melos_legacy.iml
#	packages/design_system/fonts/config.json
2026-02-13 09:20:45 +01:00
e4c0a23ded Added call watch dialog 2026-02-12 17:27:55 +01:00
a53cfb24ea Added locate device screen, state and endpoints 2026-02-12 15:48:11 +01:00
f8ff70c35d fixed remote connection dialogs and edit contact screen 2026-02-11 11:57:45 +01:00
46b7ba4a1d added remote_connection screens and state 2026-02-10 17:33:18 +01:00
da90ed94ea create legacy_shared package and added page_layout and logged_user_provider 2026-02-10 17:30:58 +01:00
3283494cb1 fix contacts endpoint and entity 2026-02-10 17:25:23 +01:00
6049ce0bee added edit_contact screen and functions icons 2026-02-09 09:38:58 +01:00
1cd18b002c added functions module and functions and contacts screens, state and use cases 2026-02-05 17:51:52 +01:00
16a5ed2e80 added account settings texts to I18n 2026-02-04 16:32:33 +01:00
54818ead25 added delete_app_user and update_device use cases 2026-02-04 12:45:11 +01:00
3955fbf4bc added app_users screen and states 2026-02-03 10:57:57 +01:00
d515d0596f added personal_data endpoints, providers and states 2026-02-02 15:14:44 +01:00
880dd85d2b added account_settings, linked_devices and edit_linked_device screens and state 2026-01-29 17:45:14 +01:00
cd578352f9 Added customer service and contact screens and state 2026-01-28 17:36:01 +01:00
12edcd0940 added legacy and legacy_dashboard_shell modules 2026-01-27 12:45:25 +01:00
60a49060a1 added hub module 2026-01-23 14:52:08 +01:00
337a00e98f Merge branch 'app-bundle-and-splash' into legacy 2026-01-18 14:01:53 +01:00
59d566e9a6 onboarding copy 2026-01-17 22:20:34 +01:00
916 changed files with 103646 additions and 3651 deletions

View File

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

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-02-27 12:35:56.235180","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-03-13 09:52:54.635963","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}

10
.idea/modules.xml generated
View File

@@ -2,19 +2,29 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/modules/legacy/modules/account/melos_account.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/account/melos_account.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/activity/melos_activity.iml" filepath="$PROJECT_DIR$/modules/activity/melos_activity.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/auth/melos_auth.iml" filepath="$PROJECT_DIR$/modules/auth/melos_auth.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/modules/control_panel/melos_control_panel.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/control_panel/melos_control_panel.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/modules/customer_service/melos_customer_service.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/customer_service/melos_customer_service.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/dashboard_shell/melos_dashboard_shell.iml" filepath="$PROJECT_DIR$/modules/dashboard_shell/melos_dashboard_shell.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/design_system/melos_design_system.iml" filepath="$PROJECT_DIR$/packages/design_system/melos_design_system.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/modules/device_management/melos_device_management.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/device_management/melos_device_management.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/flutter_treezor_entrust_sdk_bridge/melos_flutter_treezor_entrust_sdk_bridge.iml" filepath="$PROJECT_DIR$/packages/flutter_treezor_entrust_sdk_bridge/melos_flutter_treezor_entrust_sdk_bridge.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/flutter_treezor_entrust_sdk_bridge/example/melos_flutter_treezor_entrust_sdk_bridge_example.iml" filepath="$PROJECT_DIR$/packages/flutter_treezor_entrust_sdk_bridge/example/melos_flutter_treezor_entrust_sdk_bridge_example.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/fonts/melos_fonts.iml" filepath="$PROJECT_DIR$/packages/fonts/melos_fonts.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/home/melos_home.iml" filepath="$PROJECT_DIR$/modules/home/melos_home.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/melos_legacy.iml" filepath="$PROJECT_DIR$/modules/legacy/melos_legacy.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/modules/legacy_auth/melos_legacy_auth.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/legacy_auth/melos_legacy_auth.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/modules/legacy_dashboard_shell/melos_legacy_dashboard_shell.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/legacy_dashboard_shell/melos_legacy_dashboard_shell.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/packages/legacy_shared/melos_legacy_shared.iml" filepath="$PROJECT_DIR$/modules/legacy/packages/legacy_shared/melos_legacy_shared.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/legacy/modules/location/melos_location.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/location/melos_location.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$/packages/payments/melos_payments.iml" filepath="$PROJECT_DIR$/packages/payments/melos_payments.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$/modules/legacy/modules/settings/melos_settings.iml" filepath="$PROJECT_DIR$/modules/legacy/modules/settings/melos_settings.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_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; 'legacy_auth'" type="FlutterTestConfigType" factoryName="Flutter Test">
<option name="testDir" value="$PROJECT_DIR$/modules/legacy/modules/legacy_auth/test" />
<method v="2" />
</configuration>
</component>

View File

@@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<application
android:label="@string/app_name"

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,6 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
# permission_handler: enable contacts permission
PERMISSION_CONTACTS=1

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,19 @@
<svg width="43" height="55" viewBox="0 0 43 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_973)">
<path d="M22.339 0C23.8812 0.269461 25.4552 0.434132 26.9498 0.838323C31.1949 1.96108 34.6769 4.20659 37.4434 7.41018C39.4944 9.79042 40.9094 12.5 41.5931 15.494C42.7856 20.7335 41.9906 25.7186 38.9379 30.2545C33.6752 38.0988 28.3012 45.8982 22.9749 53.7126C22.8954 53.8323 22.816 53.9521 22.7365 54.0718C21.8143 55.3293 20.2721 55.3293 19.3817 54.0569C18.38 52.6347 17.4261 51.1976 16.4562 49.7605C12.068 43.3084 7.64795 36.8713 3.29152 30.4042C0.429637 26.1527 -0.572024 21.512 0.334241 16.5569C1.81288 8.36826 8.20443 2.15569 16.8378 0.404192C17.8077 0.209581 18.8093 0.134731 19.7951 0C20.6377 0 21.4963 0 22.339 0Z" fill="#F9B03C"/>
<path d="M28.8576 25.8982L31.5923 28.473C32.3396 29.1766 32.3396 30.3293 31.5923 31.0479C30.8451 31.7515 29.6208 31.7515 28.8576 31.0479L26.123 28.473C25.3757 27.7694 25.3757 26.6167 26.123 25.8982C26.8702 25.1796 28.0945 25.1796 28.8576 25.8982Z" fill="white"/>
<path d="M12.926 20.7485C12.926 21.7515 12.0675 22.5599 11.0022 22.5599H7.13867C6.07341 22.5599 5.21484 21.7515 5.21484 20.7485C5.21484 19.7455 6.07341 18.9371 7.13867 18.9371H11.0022C12.0675 18.9371 12.926 19.7455 12.926 20.7485Z" fill="white"/>
<path d="M12.4651 15.5987L9.73037 13.0388C8.9831 12.3352 8.9831 11.1825 9.73037 10.464C10.4776 9.76038 11.7019 9.76038 12.4651 10.464L15.1998 13.0388C15.947 13.7424 15.947 14.8951 15.1998 15.6137C14.4366 16.3173 13.2123 16.3173 12.4651 15.5987Z" fill="white"/>
<path d="M12.4651 31.0329C11.7178 31.7365 10.4935 31.7365 9.73037 31.0329C8.9831 30.3293 8.9831 29.1766 9.73037 28.458L12.4651 25.8832C13.2123 25.1796 14.4366 25.1796 15.1998 25.8832C15.947 26.5868 15.947 27.7394 15.1998 28.458L12.4651 31.0329Z" fill="white"/>
<path d="M20.6533 13.473C19.5881 13.473 18.7295 12.6646 18.7295 11.6616V8.0239C18.7295 7.02091 19.5881 6.21252 20.6533 6.21252C21.7186 6.21252 22.5771 7.02091 22.5771 8.0239V11.6616C22.593 12.6646 21.7186 13.473 20.6533 13.473Z" fill="white"/>
<path d="M20.6533 35.2993C19.5881 35.2993 18.7295 34.4909 18.7295 33.4879V29.8502C18.7295 28.8472 19.5881 28.0388 20.6533 28.0388C21.7186 28.0388 22.5771 28.8472 22.5771 29.8502V33.4879C22.593 34.4909 21.7186 35.2993 20.6533 35.2993Z" fill="white"/>
<path d="M28.8576 15.5988C28.1104 16.3024 26.8861 16.3024 26.123 15.5988C25.3757 14.8952 25.3757 13.7425 26.123 13.0239L28.8576 10.4491C29.6049 9.74549 30.8292 9.74549 31.5923 10.4491C32.3396 11.1527 32.3396 12.3054 31.5923 13.0239L28.8576 15.5988Z" fill="white"/>
<path d="M34.1839 22.5748H30.3203C29.255 22.5748 28.3965 21.7664 28.3965 20.7634C28.3965 19.7604 29.255 18.952 30.3203 18.952H34.1839C35.2491 18.952 36.1077 19.7604 36.1077 20.7634C36.1077 21.7514 35.2491 22.5748 34.1839 22.5748Z" fill="white"/>
<path d="M20.1288 15.7634C20.256 15.6587 20.3991 15.5539 20.574 15.5389C21.0033 15.509 21.21 16.003 21.2895 16.4072C21.4485 17.2155 21.5916 18.0389 21.7506 18.8473C21.7824 19.0269 21.8301 19.2215 21.9891 19.3263C22.1322 19.4161 22.3389 19.4012 22.5138 19.3712C23.5472 19.2664 24.6284 19.476 25.5187 19.985C25.8049 20.1497 26.0752 20.3593 26.1229 20.6736C26.1706 21.1227 25.6777 21.4371 25.2166 21.5868C24.4058 21.8563 23.5154 21.9161 22.6727 21.7664C22.9589 22.2904 23.1815 22.8593 23.3564 23.4281C23.5631 24.1467 23.5154 25.1197 22.784 25.3892C22.2276 25.5838 21.6234 25.2245 21.2577 24.7904C20.892 24.3563 20.6376 23.8323 20.1765 23.473C19.5406 24.1916 18.5389 24.5359 17.5849 24.7904C16.9013 24.97 16.0268 25.0299 15.6452 24.4611C15.4544 24.1616 15.8519 23.2335 16.3766 22.8443C16.9649 22.4102 17.2829 21.991 17.887 21.6916C16.5674 21.2275 15.8996 21.0479 15.0569 19.985C14.8185 19.6856 14.6595 19.2066 14.9933 19.0119C15.1046 18.9521 15.2318 18.9521 15.3431 18.9521C16.4879 18.9221 17.7439 19.3114 18.5071 18.488C19.2067 17.7096 19.2067 16.5419 20.1288 15.7634Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_1_973">
<rect width="42.1333" height="55" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,12 @@
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M108.055 98.4048L117.431 107.744C119.993 110.297 119.993 114.478 117.431 117.084C114.869 119.636 110.672 119.636 108.055 117.084L98.6799 107.744C96.1179 105.192 96.1179 101.011 98.6799 98.4048C101.242 95.7983 105.439 95.7983 108.055 98.4048Z" fill="#588EA5"/>
<path d="M53.4371 79.7255C53.4371 83.3636 50.4936 86.2958 46.8414 86.2958H33.5956C29.9435 86.2958 27 83.3636 27 79.7255C27 76.0874 29.9435 73.1552 33.5956 73.1552H46.8414C50.4936 73.1552 53.4371 76.0874 53.4371 79.7255Z" fill="#588EA5"/>
<path d="M51.8563 61.0462L42.4807 51.7609C39.9187 49.2087 39.9187 45.0276 42.4807 42.4212C45.0426 39.8691 49.2398 39.8691 51.8563 42.4212L61.2319 51.7609C63.7938 54.313 63.7938 58.4941 61.2319 61.1005C58.6155 63.6526 54.4182 63.6526 51.8563 61.0462Z" fill="#588EA5"/>
<path d="M51.8563 117.03C49.2943 119.582 45.0971 119.582 42.4807 117.03C39.9187 114.478 39.9187 110.297 42.4807 107.69L51.8563 98.3505C54.4182 95.7984 58.6155 95.7984 61.2319 98.3505C63.7938 100.903 63.7938 105.084 61.2319 107.69L51.8563 117.03Z" fill="#588EA5"/>
<path d="M79.9287 53.3356C76.2765 53.3356 73.333 50.4034 73.333 46.7653V33.5703C73.333 29.9322 76.2765 27 79.9287 27C83.5808 27 86.5243 29.9322 86.5243 33.5703V46.7653C86.5788 50.4034 83.5808 53.3356 79.9287 53.3356Z" fill="#588EA5"/>
<path d="M79.9287 132.505C76.2765 132.505 73.333 129.573 73.333 125.935V112.74C73.333 109.102 76.2765 106.17 79.9287 106.17C83.5808 106.17 86.5243 109.102 86.5243 112.74V125.935C86.5788 129.573 83.5808 132.505 79.9287 132.505Z" fill="#588EA5"/>
<path d="M108.055 61.0462C105.494 63.5983 101.296 63.5983 98.6799 61.0462C96.1179 58.4941 96.1179 54.313 98.6799 51.7066L108.055 42.367C110.617 39.8149 114.815 39.8149 117.431 42.367C119.993 44.9191 119.993 49.1002 117.431 51.7066L108.055 61.0462Z" fill="#588EA5"/>
<path d="M126.316 86.3501H113.07C109.418 86.3501 106.475 83.4179 106.475 79.7798C106.475 76.1417 109.418 73.2095 113.07 73.2095H126.316C129.968 73.2095 132.912 76.1417 132.912 79.7798C132.912 83.3636 129.968 86.3501 126.316 86.3501Z" fill="#588EA5"/>
<path d="M78.1299 61.6435C78.5659 61.2634 79.0565 60.8833 79.6561 60.829C81.1279 60.7204 81.8365 62.5124 82.109 63.9785C82.6541 66.9107 83.1447 69.8972 83.6898 72.8294C83.7988 73.481 83.9624 74.1869 84.5075 74.567C84.998 74.8928 85.7067 74.8385 86.3063 74.7299C89.8494 74.3498 93.556 75.11 96.6085 76.9562C97.5897 77.5535 98.5164 78.3137 98.6799 79.454C98.8434 81.083 97.1536 82.2233 95.5729 82.7663C92.7929 83.7437 89.7404 83.9609 86.8514 83.4179C87.8325 85.3184 88.5957 87.3818 89.1953 89.4452C89.9039 92.0517 89.7404 95.5812 87.2329 96.5586C85.3251 97.2645 83.2537 95.9613 82 94.3866C80.7463 92.8119 79.8742 90.9114 78.2934 89.6082C76.113 92.2146 72.6789 93.4635 69.4083 94.3866C67.0644 95.0382 64.0664 95.2554 62.7582 93.192C62.1041 92.106 63.4668 88.7394 65.2656 87.3275C67.2825 85.7528 68.3727 84.2324 70.444 83.1464C65.9197 81.4631 63.6303 80.8115 60.7413 76.9562C59.9237 75.8702 59.3786 74.1326 60.5233 73.4267C60.9049 73.2095 61.3409 73.2095 61.7225 73.2095C65.6472 73.1009 69.9534 74.5127 72.5699 71.5262C74.9683 68.7026 74.9683 64.4672 78.1299 61.6435Z" fill="#588EA5"/>
<circle cx="80" cy="80" r="78" stroke="#588EA5" stroke-width="4"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

View File

@@ -2,4 +2,4 @@
"env": "development",
"apiBaseUrl": "https://api-neki-b2b.neki.es/gateway/api/",
"apiOrigin": "https://neki-b2b.neki.es"
}
}

View File

@@ -1,5 +1,5 @@
{
"env": "staging",
"apiBaseUrl": "https://api-neki-b2b.neki.es/gateway/api/",
"apiOrigin": "https://neki-b2b.neki.es"
}
"apiBaseUrl": "https://api-platform.pre.savefamilygps.net/gateway/api/",
"apiOrigin": "https://platform.pre.savefamilygps.net"
}

View File

@@ -1,5 +1,7 @@
PODS:
- Flutter (1.0.0)
- flutter_contacts (0.0.1):
- Flutter
- flutter_native_splash (2.4.3):
- Flutter
- flutter_treezor_entrust_sdk_bridge (0.0.1):
@@ -23,6 +25,7 @@ PODS:
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_treezor_entrust_sdk_bridge (from `.symlinks/plugins/flutter_treezor_entrust_sdk_bridge/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
@@ -35,6 +38,8 @@ DEPENDENCIES:
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_contacts:
:path: ".symlinks/plugins/flutter_contacts/ios"
flutter_native_splash:
:path: ".symlinks/plugins/flutter_native_splash/ios"
flutter_treezor_entrust_sdk_bridge:
@@ -54,6 +59,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
flutter_treezor_entrust_sdk_bridge: 4c2c94fb74ab57576e8d49f5f2a4b214e41141fe
mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e

View File

@@ -543,7 +543,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-development;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Runner/Runner-development.entitlements";
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -727,7 +727,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-development;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Runner/Runner-development.entitlements";
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -751,7 +751,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-development;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Runner/Runner-development.entitlements";
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -1100,7 +1100,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-staging;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Runner/Runner-staging.entitlements";
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -1124,7 +1124,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -1148,7 +1148,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-staging;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Runner/Runner-staging.entitlements";
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -1171,7 +1171,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -1194,7 +1194,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-staging;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Runner/Runner-staging.entitlements";
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
@@ -1217,7 +1217,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-production;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";

View File

@@ -47,8 +47,14 @@
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>NSContactsUsageDescription</key>
<string>Necesitamos acceso a tus contactos para seleccionar números de teléfono.</string>
<key>NSCameraUsageDescription</key>
<string>Necesitamos la cámara para escanear códigos QR</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Usamos tu ubicación para verificar la seguridad de las transacciones.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Usamos tu ubicación para verificar la seguridad de las transacciones.</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSFaceIDUsageDescription</key>

View File

@@ -47,8 +47,14 @@
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>NSContactsUsageDescription</key>
<string>Necesitamos acceso a tus contactos para seleccionar números de teléfono.</string>
<key>NSCameraUsageDescription</key>
<string>Necesitamos la cámara para escanear códigos QR</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Usamos tu ubicación para verificar la seguridad de las transacciones.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Usamos tu ubicación para verificar la seguridad de las transacciones.</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSFaceIDUsageDescription</key>

View File

@@ -47,6 +47,8 @@
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>NSContactsUsageDescription</key>
<string>Necesitamos acceso a tus contactos para seleccionar números de teléfono.</string>
<key>NSCameraUsageDescription</key>
<string>Necesitamos la cámara para escanear códigos QR</string>
<key>NSLocationWhenInUseUsageDescription</key>

View File

@@ -32,6 +32,9 @@ Future<void> initApp(EnvironmentEnum env) async {
log: env.isDevelopment || kDebugMode,
onTokenExpired: () => appRouter.go(AppRoutes.scaTreezor),
onUnauthorized: () async {
final currentLocation =
appRouter.routerDelegate.currentConfiguration.uri.path;
if (currentLocation == AppRoutes.splash) return;
try {
await GetIt.I<TreezorWalletConnectionService>().logout();
} catch (_) {}

View File

@@ -1,6 +1,13 @@
import 'package:account/account.dart';
import 'package:activity/activity.dart';
import 'package:auth/auth.dart';
import 'package:legacy_auth/legacy_auth.dart';
import 'package:customer_service/customer_service.dart';
import 'package:dashboard_shell/dashboard_builder.dart';
import 'package:device_management/device_management.dart';
import 'package:control_panel/control_panel.dart';
import 'package:legacy_dashboard_shell/legacy_dashboard_builder.dart';
import 'package:location/location.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
@@ -9,10 +16,30 @@ import 'package:navigation/navigation.dart';
import 'package:notifications/notifications.dart';
import 'package:payments/payments.dart';
import 'package:profile/profile.dart';
import 'package:settings/settings.dart';
import 'package:splash/splash.dart';
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();
final _legacyControlPanelNavKey =
GlobalKey<NavigatorState>(debugLabel: 'legacyControlPanel');
final _legacyDeviceMgmtNavKey =
GlobalKey<NavigatorState>(debugLabel: 'legacyDeviceMgmt');
final _legacyLocationNavKey =
GlobalKey<NavigatorState>(debugLabel: 'legacyLocation');
final _legacyChatNavKey = GlobalKey<NavigatorState>(debugLabel: 'legacyChat');
final _legacySettingsNavKey =
GlobalKey<NavigatorState>(debugLabel: 'legacySettings');
final _dashboardHomeNavKey =
GlobalKey<NavigatorState>(debugLabel: 'dashboardHome');
final _dashboardActivityNavKey =
GlobalKey<NavigatorState>(debugLabel: 'dashboardActivity');
final _dashboardNotificationsNavKey =
GlobalKey<NavigatorState>(debugLabel: 'dashboardNotifications');
final _dashboardProfileNavKey =
GlobalKey<NavigatorState>(debugLabel: 'dashboardProfile');
late final GoRouter appRouter;
void configureAppRouter() {
@@ -26,6 +53,245 @@ void configureAppRouter() {
name: 'splash',
pageBuilder: SplashBuilder().buildPage,
),
StatefulShellRoute.indexedStack(
builder: (context, state, navShell) {
return LegacyDashboardBuilder().build(context, navShell);
},
branches: [
StatefulShellBranch(
navigatorKey: _legacyControlPanelNavKey,
routes: [
GoRoute(
path: AppRoutes.controlPanel,
name: 'control_panel',
pageBuilder: const ControlPanelBuilder().buildPage,
routes: [
GoRoute(
path: 'customer_service',
name: 'customer_service',
pageBuilder: CustomerServiceBuilder().buildPage,
),
GoRoute(
path: 'account_settings',
name: 'account_settings',
pageBuilder: AccountSettingsBuilder().buildPage,
routes: [
GoRoute(
path: 'personal_data',
name: 'personal_data',
pageBuilder: PersonalDataBuilder().buildPage,
),
GoRoute(
path: 'change_password',
name: 'change_password',
pageBuilder: ChangePasswordBuilder().buildPage,
),
GoRoute(
path: 'linked_devices',
name: 'linked_devices',
pageBuilder: LinkedDevicesBuilder().buildPage,
),
GoRoute(
path: 'app_users',
name: 'app_users',
pageBuilder: AppUsersBuilder().buildPage,
),
GoRoute(
path: 'delete_account',
name: 'delete_account',
pageBuilder: DeleteAccountBuilder().buildPage,
),
],
),
],
),
],
),
StatefulShellBranch(
navigatorKey: _legacyDeviceMgmtNavKey,
routes: [
GoRoute(
path: AppRoutes.deviceManagement,
name: 'device_management',
pageBuilder: DeviceManagementBuilder().buildPage,
routes: [
GoRoute(
path: 'scheduled_activities',
name: 'scheduled_activities',
pageBuilder: const ScheduledActivitiesBuilder().buildPage,
),
GoRoute(
path: 'contacts',
name: 'contacts',
pageBuilder: const ContactsBuilder().buildPage,
routes: [
GoRoute(
path: 'edit/:contactId',
name: 'edit_contact',
pageBuilder: const EditContactBuilder().buildPage,
),
],
),
GoRoute(
path: 'health',
name: 'health',
pageBuilder: const HealthBuilder().buildPage,
),
GoRoute(
path: 'remote_connection',
name: 'remote_connection',
pageBuilder: RemoteConnectionBuilder().buildPage,
),
GoRoute(
path: 'locate_device',
name: 'locate_device',
pageBuilder: LocateDeviceBuilder().buildPage,
),
GoRoute(
path: 'rewards',
name: 'rewards',
pageBuilder: RewardsBuilder().buildPage,
),
GoRoute(
path: 'activity_meter',
name: 'activity_meter',
pageBuilder: const ActivityMeterBuilder().buildPage,
),
GoRoute(
path: 'apps_use',
name: 'apps_use',
pageBuilder: const AppsUseBuilder().buildPage,
),
GoRoute(
path: 'volume_control',
name: 'volume_control',
pageBuilder: const VolumeControlBuilder().buildPage,
),
GoRoute(
path: 'call_history',
name: 'call_history',
pageBuilder: const CallHistoryBuilder().buildPage,
),
],
),
],
),
StatefulShellBranch(
navigatorKey: _legacyLocationNavKey,
routes: [
GoRoute(
path: AppRoutes.legacyLocation,
name: 'legacy_location',
pageBuilder: const LocationBuilder().buildPage,
),
],
),
// TODO: Añadir branch para Chat (tab 4)
StatefulShellBranch(
navigatorKey: _legacyChatNavKey,
routes: [
GoRoute(
path: '${AppRoutes.legacyDashboard}/chat',
name: 'legacy_chat',
pageBuilder: (context, state) => MaterialPage<void>(
key: state.pageKey,
child: const Scaffold(
body: Center(child: Text('Chat - Coming soon')),
),
),
),
],
),
StatefulShellBranch(
navigatorKey: _legacySettingsNavKey,
routes: [
GoRoute(
path: AppRoutes.settings,
name: 'settings',
pageBuilder: SettingsBuilder().buildPage,
routes: [
GoRoute(
path: 'alarm',
name: 'alarm',
pageBuilder: AlarmBuilder().buildPage,
),
GoRoute(
path: 'remote_management',
name: 'remote_management',
pageBuilder: RemoteManagementBuilder().buildPage,
),
GoRoute(
path: 'sos_agenda',
name: 'sos_agenda',
pageBuilder: SosContactsBuilder().buildPage,
),
GoRoute(
path: 'sound',
name: 'sound',
pageBuilder: SoundBuilder().buildPage,
),
GoRoute(
path: 'sync_clock',
name: 'sync_clock',
pageBuilder: SyncClockBuilder().buildPage,
),
GoRoute(
path: 'app_store',
name: 'app_store',
pageBuilder: AppStoreBuilder().buildPage,
),
GoRoute(
path: 'battery',
name: 'battery',
pageBuilder: BatteryBuilder().buildPage,
),
GoRoute(
path: 'block_phone',
name: 'block_phone',
pageBuilder: BlockPhoneBuilder().buildPage,
),
GoRoute(
path: 'disable_functions',
name: 'disable_functions',
pageBuilder: DisableFunctionsBuilder().buildPage,
),
GoRoute(
path: 'language',
name: 'language',
pageBuilder: LanguageBuilder().buildPage,
),
GoRoute(
path: 'legacy_notifications',
name: 'legacy_notifications',
pageBuilder: LegacyNotificationsBuilder().buildPage,
),
GoRoute(
path: 'remote_on_off',
name: 'remote_on_off',
pageBuilder: RemoteOnOffBuilder().buildPage,
),
GoRoute(
path: 'sms_alert',
name: 'sms_alert',
pageBuilder: SmsAlertBuilder().buildPage,
),
GoRoute(
path: 'timezone',
name: 'timezone',
pageBuilder: TimezoneBuilder().buildPage,
),
GoRoute(
path: 'wifi_settings',
name: 'wifi_settings',
pageBuilder: WifiSettingsBuilder().buildPage,
),
],
),
],
),
],
),
GoRoute(
path: AppRoutes.login,
name: 'login',
@@ -67,6 +333,41 @@ void configureAppRouter() {
name: 'device_setup',
pageBuilder: DeviceSetupBuilder().buildPage,
),
GoRoute(
path: AppRoutes.legacyLogin,
name: 'legacy_login',
pageBuilder: LegacyLoginBuilder().buildPage,
),
GoRoute(
path: AppRoutes.legacySignup,
name: 'legacy_signup',
pageBuilder: LegacySignupBuilder().buildPage,
),
GoRoute(
path: AppRoutes.legacyOnboarding,
name: 'legacy_onboarding',
pageBuilder: LegacyOnboardingBuilder().buildPage,
),
GoRoute(
path: AppRoutes.legacyLinkPhone,
name: 'legacy_request_link_phone',
pageBuilder: LegacyRequestLinkPhoneBuilder().buildPage,
),
GoRoute(
path: AppRoutes.legacyPhoneCode,
name: 'legacy_verify_link_phone_code',
pageBuilder: LegacyVerifyLinkPhoneCodeBuilder().buildPage,
),
GoRoute(
path: AppRoutes.legacyRecoverPassword,
name: 'legacy_recover_password',
pageBuilder: LegacyRequestRecoveryBuilder().buildPage,
),
GoRoute(
path: AppRoutes.legacyDeviceSetup,
name: 'legacy_device_setup',
pageBuilder: LegacyDeviceSetupBuilder().buildPage,
),
GoRoute(
path: AppRoutes.hipayWebView,
name: 'hipay_webview',
@@ -78,6 +379,7 @@ void configureAppRouter() {
},
branches: [
StatefulShellBranch(
navigatorKey: _dashboardHomeNavKey,
routes: [
GoRoute(
path: AppRoutes.dashboardHome,
@@ -119,6 +421,30 @@ void configureAppRouter() {
name: 'home_extract',
pageBuilder: const ExtractBuilder().buildPage,
),
GoRoute(
path: 'edit',
name: 'home_edit_child_profile',
pageBuilder:
const EditChildProfileBuilder().buildPage,
),
GoRoute(
path: 'set-pin',
name: 'home_set_card_pin',
pageBuilder:
const SetCardPinBuilder().buildPage,
),
GoRoute(
path: 'change-pin',
name: 'home_change_card_pin',
pageBuilder:
const ChangeCardPinBuilder().buildPage,
),
GoRoute(
path: 'renew-card',
name: 'home_renew_card',
pageBuilder:
const RenewCardBuilder().buildPage,
),
],
),
],
@@ -126,6 +452,7 @@ void configureAppRouter() {
],
),
StatefulShellBranch(
navigatorKey: _dashboardActivityNavKey,
routes: [
GoRoute(
path: AppRoutes.dashboardActivity,
@@ -135,6 +462,7 @@ void configureAppRouter() {
],
),
StatefulShellBranch(
navigatorKey: _dashboardNotificationsNavKey,
routes: [
GoRoute(
path: AppRoutes.dashboardNotifications,
@@ -144,6 +472,7 @@ void configureAppRouter() {
],
),
StatefulShellBranch(
navigatorKey: _dashboardProfileNavKey,
routes: [
GoRoute(
path: AppRoutes.dashboardProfile,
@@ -160,6 +489,12 @@ void configureAppRouter() {
name: 'profile_settings',
pageBuilder: const ProfileSettingsBuilder().buildPage,
routes: [
GoRoute(
path: 'edit-personal-data',
name: 'profile_edit_personal_data',
pageBuilder:
const EditPersonalDataBuilder().buildPage,
),
GoRoute(
path: 'payment-methods',
name: 'profile_payment_methods',

View File

@@ -0,0 +1,45 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
class LegacyHeartbeatService {
LegacyHeartbeatService({
required QuestiaRepository repository,
required void Function() onUnauthorized,
}) : _repository = repository,
_onUnauthorized = onUnauthorized;
final QuestiaRepository _repository;
final void Function() _onUnauthorized;
Timer? _timer;
static const _interval = Duration(minutes: 3);
void start() {
if (_timer != null) return;
_beat();
_timer = Timer.periodic(_interval, (_) => _beat());
debugPrint('[LegacyHeartbeat] started');
}
void stop() {
_timer?.cancel();
_timer = null;
debugPrint('[LegacyHeartbeat] stopped');
}
Future<void> _beat() async {
try {
await _repository.get<dynamic>('/auth/me');
debugPrint('[LegacyHeartbeat] /auth/me => OK');
} catch (e) {
debugPrint('[LegacyHeartbeat] error: $e');
if (e is DioException && e.response?.statusCode == 401) {
stop();
_onUnauthorized();
}
}
}
}

View File

@@ -6,7 +6,9 @@ import 'package:sf_app_platform/navigation/app_router.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_app_platform/providers/app_state_provider.dart';
import 'package:sf_app_platform/providers/permissions/permissions_provider.dart';
import 'package:sf_app_platform/providers/legacy_heartbeat_service.dart';
import 'package:sf_app_platform/providers/wallet_heartbeat_service.dart';
import 'package:get_it/get_it.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
@@ -23,6 +25,7 @@ class SaveFamilyApp extends ConsumerStatefulWidget {
class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
with WidgetsBindingObserver {
late final WalletHeartbeatService walletHeartbeat;
late final LegacyHeartbeatService legacyHeartbeat;
@override
void initState() {
@@ -33,13 +36,34 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
sessionLocal: SessionLocalDatasourceImpl(),
onError: () => appRouter.go(AppRoutes.scaTreezor),
);
onBeforeSessionCleared = walletHeartbeat.stop;
walletHeartbeat.start();
legacyHeartbeat = LegacyHeartbeatService(
repository: GetIt.I<QuestiaRepository>(),
onUnauthorized: () {
clearSessionData();
appRouter.go(AppRoutes.login);
},
);
onBeforeSessionCleared = () {
walletHeartbeat.stop();
legacyHeartbeat.stop();
};
appRouter.routerDelegate.addListener(_onRouteChanged);
}
void _onRouteChanged() {
final location = appRouter.routerDelegate.currentConfiguration.uri.path;
if (location.startsWith(AppRoutes.legacyDashboard)) {
legacyHeartbeat.start();
} else {
legacyHeartbeat.stop();
}
}
@override
void dispose() {
appRouter.routerDelegate.removeListener(_onRouteChanged);
walletHeartbeat.stop();
legacyHeartbeat.stop();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@@ -49,10 +73,12 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
debugPrint('State: $state');
ref.read(appLifecycleStateProvider.notifier).setState(state);
if (state == AppLifecycleState.resumed) {
walletHeartbeat.start();
// walletHeartbeat.start();
_onRouteChanged();
ref.read(permissionsProvider.notifier).checkPermissions();
} else if (state == AppLifecycleState.paused) {
walletHeartbeat.stop();
// walletHeartbeat.stop();
legacyHeartbeat.stop();
}
super.didChangeAppLifecycleState(state);
}

View File

@@ -0,0 +1 @@
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/

View File

@@ -0,0 +1 @@
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/share_plus-10.1.4/

View File

@@ -0,0 +1 @@
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/

View File

@@ -0,0 +1 @@
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/

View File

@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@@ -0,0 +1,24 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@@ -9,6 +9,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "85.0.0"
account:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/account"
relative: true
source: path
version: "1.0.0+1"
activity:
dependency: "direct main"
description:
@@ -24,14 +31,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.7.1"
animated_splash_screen:
dependency: transitive
description:
name: animated_splash_screen
sha256: f45634db6ec4e8cf034c53e03f3bd83898a16fe3c9286bf5510b6831dfcf2124
url: "https://pub.dev"
source: hosted
version: "1.3.0"
ansicolor:
dependency: transitive
description:
@@ -44,10 +43,10 @@ packages:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
url: "https://pub.dev"
source: hosted
version: "4.0.7"
version: "4.0.9"
args:
dependency: transitive
description:
@@ -139,10 +138,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d
sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9"
url: "https://pub.dev"
source: hosted
version: "8.12.0"
version: "8.12.4"
characters:
dependency: transitive
description:
@@ -187,10 +186,10 @@ packages:
dependency: transitive
description:
name: code_builder
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243"
sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
url: "https://pub.dev"
source: hosted
version: "4.11.0"
version: "4.11.1"
collection:
dependency: transitive
description:
@@ -199,6 +198,21 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
confetti:
dependency: transitive
description:
name: confetti
sha256: "979aafde2428c53947892c95eb244466c109c129b7eee9011f0a66caaca52267"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
control_panel:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/control_panel"
relative: true
source: path
version: "1.0.0+1"
convert:
dependency: transitive
description:
@@ -211,10 +225,10 @@ packages:
dependency: transitive
description:
name: cookie_jar
sha256: a6ac027d3ed6ed756bfce8f3ff60cb479e266f3b0fdabd6242b804b6765e52de
sha256: "963da02c1ef64cb5ac20de948c9e5940aa351f1e34a12b1d327c83d85b7e8fff"
url: "https://pub.dev"
source: hosted
version: "4.0.8"
version: "4.0.9"
country_code_picker:
dependency: "direct main"
description:
@@ -231,6 +245,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.15.0"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
url: "https://pub.dev"
source: hosted
version: "0.3.5+2"
crypto:
dependency: transitive
description:
@@ -255,6 +277,29 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
customer_service:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/customer_service"
relative: true
source: path
version: "1.0.0+1"
dart_earcut:
dependency: transitive
description:
name: dart_earcut
sha256: e485001bfc05dcbc437d7bfb666316182e3522d4c3f9668048e004d0eb2ce43b
url: "https://pub.dev"
source: hosted
version: "1.2.0"
dart_polylabel2:
dependency: transitive
description:
name: dart_polylabel2
sha256: "7eeab15ce72894e4bdba6a8765712231fc81be0bd95247de4ad9966abc57adc6"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
dart_style:
dependency: transitive
description:
@@ -277,6 +322,13 @@ packages:
relative: true
source: path
version: "0.0.1"
device_management:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/device_management"
relative: true
source: path
version: "1.0.0+1"
diacritic:
dependency: transitive
description:
@@ -289,34 +341,34 @@ packages:
dependency: transitive
description:
name: dio
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c
url: "https://pub.dev"
source: hosted
version: "5.9.0"
version: "5.9.2"
dio_cookie_manager:
dependency: transitive
description:
name: dio_cookie_manager
sha256: d39c16abcc711c871b7b29bd51c6b5f3059ef39503916c6a9df7e22c4fc595e0
sha256: "0db1a7b997a0455e488ac35744c68eed3f2a4280d3ab531835a65641b0a08744"
url: "https://pub.dev"
source: hosted
version: "3.3.0"
version: "3.4.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
equatable:
dependency: transitive
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
version: "2.0.8"
fake_async:
dependency: transitive
description:
@@ -329,10 +381,10 @@ packages:
dependency: transitive
description:
name: ffi
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "2.2.0"
file:
dependency: transitive
description:
@@ -362,6 +414,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_contacts:
dependency: transitive
description:
name: flutter_contacts
sha256: "388d32cd33f16640ee169570128c933b45f3259bddbfae7a100bb49e5ffea9ae"
url: "https://pub.dev"
source: hosted
version: "1.1.9+2"
flutter_launcher_icons:
dependency: "direct dev"
description:
@@ -383,6 +443,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_map:
dependency: transitive
description:
name: flutter_map
sha256: "391e7dc95cc3f5190748210a69d4cfeb5d8f84dcdfa9c3235d0a9d7742ccb3f8"
url: "https://pub.dev"
source: hosted
version: "8.2.2"
flutter_native_splash:
dependency: "direct main"
description:
@@ -395,10 +463,10 @@ packages:
dependency: "direct main"
description:
name: flutter_riverpod
sha256: "9e2d6907f12cc7d23a846847615941bddee8709bf2bfd274acdf5e80bcf22fde"
sha256: e2026c72738a925a60db30258ff1f29974e40716749f3c9850aabf34ffc1a14c
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.2.1"
flutter_svg:
dependency: "direct main"
description:
@@ -459,10 +527,10 @@ packages:
dependency: "direct main"
description:
name: get_it
sha256: "84792561b731b6463d053e9761a5236da967c369da10b134b8585a5e18429956"
sha256: "568d62f0e68666fb5d95519743b3c24a34c7f19d834b0658c46e26d778461f66"
url: "https://pub.dev"
source: hosted
version: "9.0.5"
version: "9.2.1"
glob:
dependency: transitive
description:
@@ -475,10 +543,10 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: c92d18e1fe994cb06d48aa786c46b142a5633067e8297cff6b5a3ac742620104
sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896"
url: "https://pub.dev"
source: hosted
version: "17.0.0"
version: "17.1.0"
go_router_builder:
dependency: "direct main"
description:
@@ -514,10 +582,10 @@ packages:
dependency: transitive
description:
name: http
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "1.6.0"
http_multi_server:
dependency: transitive
description:
@@ -538,10 +606,10 @@ packages:
dependency: transitive
description:
name: image
sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c"
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
url: "https://pub.dev"
source: hosted
version: "4.7.2"
version: "4.8.0"
intl:
dependency: transitive
description:
@@ -606,6 +674,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
latlong2:
dependency: transitive
description:
name: latlong2
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
url: "https://pub.dev"
source: hosted
version: "0.9.1"
leak_tracker:
dependency: transitive
description:
@@ -630,6 +706,27 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
legacy_auth:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/legacy_auth"
relative: true
source: path
version: "0.0.1"
legacy_dashboard_shell:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/legacy_dashboard_shell"
relative: true
source: path
version: "0.0.1"
legacy_shared:
dependency: "direct overridden"
description:
path: "../../modules/legacy/packages/legacy_shared"
relative: true
source: path
version: "0.0.1"
lints:
dependency: transitive
description:
@@ -638,6 +735,29 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.1"
lists:
dependency: transitive
description:
name: lists
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
location:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/location"
relative: true
source: path
version: "1.0.0+1"
logger:
dependency: transitive
description:
name: logger
sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3
url: "https://pub.dev"
source: hosted
version: "2.6.2"
logging:
dependency: transitive
description:
@@ -678,6 +798,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.16.0"
mgrs_dart:
dependency: transitive
description:
name: mgrs_dart
sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
url: "https://pub.dev"
source: hosted
version: "2.0.0"
mime:
dependency: transitive
description:
@@ -690,10 +818,10 @@ packages:
dependency: transitive
description:
name: mobile_scanner
sha256: c6184bf2913dd66be244108c9c27ca04b01caf726321c44b0e7a7a1e32d41044
sha256: c92c26bf2231695b6d3477c8dcf435f51e28f87b1745966b1fe4c47a286171ce
url: "https://pub.dev"
source: hosted
version: "7.1.4"
version: "7.2.0"
navigation:
dependency: "direct main"
description:
@@ -724,14 +852,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
page_transition:
dependency: transitive
description:
name: page_transition
sha256: "9d2a780d7d68b53ae82fbcc43e06a16195e6775e9aae40e55dc0cbb593460f9d"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path:
dependency: transitive
description:
@@ -855,10 +975,10 @@ packages:
dependency: transitive
description:
name: petitparser
sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.1"
version: "7.0.2"
platform:
dependency: transitive
description:
@@ -887,10 +1007,10 @@ packages:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
version: "6.5.0"
profile:
dependency: "direct main"
description:
@@ -898,6 +1018,14 @@ packages:
relative: true
source: path
version: "0.0.1"
proj4dart:
dependency: transitive
description:
name: proj4dart
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
url: "https://pub.dev"
source: hosted
version: "2.1.0"
pub_semver:
dependency: transitive
description:
@@ -914,14 +1042,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.0"
qr:
dependency: transitive
description:
name: qr
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
qr_flutter:
dependency: transitive
description:
name: qr_flutter
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
riverpod:
dependency: transitive
description:
name: riverpod
sha256: c406de02bff19d920b832bddfb8283548bfa05ce41c59afba57ce643e116aa59
sha256: "8c22216be8ad3ef2b44af3a329693558c98eca7b8bd4ef495c92db0bba279f83"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.2.1"
sca_treezor:
dependency: "direct main"
description:
@@ -953,6 +1097,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.5.0"
settings:
dependency: "direct main"
description:
path: "../../modules/legacy/modules/settings"
relative: true
source: path
version: "1.0.0+1"
sf_infrastructure:
dependency: "direct main"
description:
@@ -974,6 +1125,22 @@ packages:
relative: true
source: path
version: "0.0.1"
share_plus:
dependency: transitive
description:
name: share_plus
sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da
url: "https://pub.dev"
source: hosted
version: "10.1.4"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b
url: "https://pub.dev"
source: hosted
version: "5.0.2"
shared_preferences:
dependency: transitive
description:
@@ -986,10 +1153,10 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41"
url: "https://pub.dev"
source: hosted
version: "2.4.18"
version: "2.4.21"
shared_preferences_foundation:
dependency: transitive
description:
@@ -1103,10 +1270,10 @@ packages:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.1"
version: "1.10.2"
splash:
dependency: "direct main"
description:
@@ -1210,6 +1377,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
unicode:
dependency: transitive
description:
name: unicode
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
universal_io:
dependency: transitive
description:
@@ -1293,10 +1468,10 @@ packages:
dependency: transitive
description:
name: uuid
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
url: "https://pub.dev"
source: hosted
version: "4.5.2"
version: "4.5.3"
vector_graphics:
dependency: transitive
description:
@@ -1317,10 +1492,10 @@ packages:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc
sha256: "5a88dd14c0954a5398af544651c7fb51b457a2a556949bfb25369b210ef73a74"
url: "https://pub.dev"
source: hosted
version: "1.1.19"
version: "1.2.0"
vector_math:
dependency: transitive
description:
@@ -1341,10 +1516,10 @@ packages:
dependency: transitive
description:
name: watcher
sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a"
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
url: "https://pub.dev"
source: hosted
version: "1.1.4"
version: "1.2.1"
web:
dependency: transitive
description:
@@ -1389,10 +1564,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_android
sha256: eeeb3fcd5f0ff9f8446c9f4bbc18a99b809e40297528a3395597d03aafb9f510
sha256: "2a03df01df2fd30b075d1e7f24c28aee593f2e5d5ac4c3c4283c5eda63717b24"
url: "https://pub.dev"
source: hosted
version: "4.10.11"
version: "4.10.13"
webview_flutter_platform_interface:
dependency: transitive
description:
@@ -1405,10 +1580,26 @@ packages:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: "0412b657a2828fb301e73509909e6ec02b77cd2b441ae9f77125d482b3ddf0e7"
sha256: fc0af89d403e1c053f03d023d97550412fa79f35332e2939514c82e6fe633198
url: "https://pub.dev"
source: hosted
version: "3.23.6"
version: "3.23.8"
win32:
dependency: transitive
description:
name: win32
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
url: "https://pub.dev"
source: hosted
version: "5.15.0"
wkt_parser:
dependency: transitive
description:
name: wkt_parser
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
xdg_directories:
dependency: transitive
description:

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
version: 1.0.0+5
environment:
sdk: ^3.9.2
@@ -49,8 +49,24 @@ dependencies:
path: ../../modules/notifications
dashboard_shell:
path: ../../modules/dashboard_shell
legacy_dashboard_shell:
path: ../../modules/legacy/modules/legacy_dashboard_shell
control_panel:
path: ../../modules/legacy/modules/control_panel
customer_service:
path: ../../modules/legacy/modules/customer_service
splash:
path: ../../modules/splash
account:
path: ../../modules/legacy/modules/account
device_management:
path: ../../modules/legacy/modules/device_management
location:
path: ../../modules/legacy/modules/location
legacy_auth:
path: ../../modules/legacy/modules/legacy_auth
settings:
path: ../../modules/legacy/modules/settings
#packages dependencies go here
navigation:
path: ../../packages/navigation

View File

@@ -1,19 +1,37 @@
# 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,payments,activity
# melos_managed_dependency_overrides: account,activity,auth,customer_service,dashboard_shell,design_system,flutter_treezor_entrust_sdk_bridge,fonts,home,legacy_dashboard_shell,legacy_shared,navigation,notifications,payments,profile,sca_treezor,sf_infrastructure,sf_localizations,sf_shared,splash,utils,control_panel,device_management,legacy_auth,location
# melos_managed_dependency_overrides: settings
# melos_managed_dependency_overrides: account,activity,auth,customer_service,dashboard_shell,design_system,flutter_treezor_entrust_sdk_bridge,fonts,home,legacy_dashboard_shell,legacy_shared,navigation,notifications,payments,profile,sca_treezor,sf_infrastructure,sf_localizations,sf_shared,splash,utils,control_panel,device_management
dependency_overrides:
account:
path: ../../modules/legacy/modules/account
activity:
path: ../../modules/activity
auth:
path: ../../modules/auth
control_panel:
path: ../../modules/legacy/modules/control_panel
customer_service:
path: ../../modules/legacy/modules/customer_service
dashboard_shell:
path: ../../modules/dashboard_shell
design_system:
path: ../../packages/design_system
device_management:
path: ../../modules/legacy/modules/device_management
flutter_treezor_entrust_sdk_bridge:
path: ../../packages/flutter_treezor_entrust_sdk_bridge
fonts:
path: ../../packages/fonts
home:
path: ../../modules/home
legacy_auth:
path: ../../modules/legacy/modules/legacy_auth
legacy_dashboard_shell:
path: ../../modules/legacy/modules/legacy_dashboard_shell
legacy_shared:
path: ../../modules/legacy/packages/legacy_shared
location:
path: ../../modules/legacy/modules/location
navigation:
path: ../../packages/navigation
notifications:
@@ -24,6 +42,8 @@ dependency_overrides:
path: ../../modules/profile
sca_treezor:
path: ../../packages/sca_treezor
settings:
path: ../../modules/legacy/modules/settings
sf_infrastructure:
path: ../../packages/sf_infrastructure
sf_localizations:

View File

@@ -4,9 +4,36 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/splash/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/splash/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/splash/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/hub/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/hub/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/hub/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/modules/dashboard_shell/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/modules/dashboard_shell/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/modules/dashboard_shell/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/modules/hub/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/modules/hub/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/modules/hub/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/legacy/build" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/.pub" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/flutter_secure_storage_linux/build" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/.pub" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/build" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
</module>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"version":2,"entries":[{"package":"design_system","rootUri":"../../../packages/design_system/","packageUri":"lib/"},{"package":"flutter_treezor_entrust_sdk_bridge","rootUri":"../../../packages/flutter_treezor_entrust_sdk_bridge/","packageUri":"lib/"},{"package":"fonts","rootUri":"../../../packages/fonts/","packageUri":"lib/"},{"package":"get_it","rootUri":"file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/get_it-9.2.0/","packageUri":"lib/","config":{"name":"get_it","issueTracker":"https://github.com/fluttercommunity/get_it/issues","version":"0.0.1","materialIconCodePoint":"0xe189"}},{"package":"sca_treezor","rootUri":"../../../packages/sca_treezor/","packageUri":"lib/"},{"package":"sf_infrastructure","rootUri":"../../../packages/sf_infrastructure/","packageUri":"lib/"},{"package":"sf_localizations","rootUri":"../../../packages/sf_localizations/","packageUri":"lib/"},{"package":"sf_shared","rootUri":"../../../packages/sf_shared/","packageUri":"lib/"},{"package":"shared_preferences","rootUri":"file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences-2.5.4/","packageUri":"lib/","config":{"name":"shared_preferences","issueTracker":"https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22","version":"1.0.0","materialIconCodePoint":"0xe683"}},{"package":"utils","rootUri":"../../../packages/utils/","packageUri":"lib/"},{"package":"activity","rootUri":"../","packageUri":"lib/"}]}

View File

@@ -121,6 +121,12 @@
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "confetti",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/confetti-0.7.0",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "convert",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/convert-3.1.2",

View File

@@ -104,6 +104,7 @@
"name": "design_system",
"version": "0.0.1",
"dependencies": [
"confetti",
"country_code_picker",
"flutter",
"flutter_riverpod",
@@ -356,6 +357,14 @@
"meta"
]
},
{
"name": "confetti",
"version": "0.7.0",
"dependencies": [
"flutter",
"vector_math"
]
},
{
"name": "lottie",
"version": "3.3.2",

View File

@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/apps/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.5.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/apps/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.22/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.5.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"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},{"name":"shared_preferences_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false}],"windows":[{"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},{"name":"shared_preferences_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false}],"web":[{"name":"shared_preferences_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_treezor_entrust_sdk_bridge","dependencies":[]},{"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":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2026-02-28 20:18:36.684471","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_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/save-family-app/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.5.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"/Users/juliandalcalaf/Desktop/save-family-app/sf-app-platform/packages/flutter_treezor_entrust_sdk_bridge/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.22/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.5.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"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},{"name":"shared_preferences_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false}],"windows":[{"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},{"name":"shared_preferences_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false}],"web":[{"name":"shared_preferences_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_treezor_entrust_sdk_bridge","dependencies":[]},{"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":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2026-03-18 14:45:38.408085","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}

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,5 +1,6 @@
export 'src/presentation/activity_screen.dart';
export 'src/widgets/activity_list.dart';
export 'src/widgets/pagination_bar.dart';
export 'src/widgets/transaction_tile.dart';
export 'src/activity_builder.dart';
export 'src/providers/activity_providers.dart';

View File

@@ -17,7 +17,7 @@ class GetWalletTransactionsUseCaseImpl implements GetWalletTransactionsUseCase {
required String walletId,
Map<String, dynamic>? queryParameters,
}) {
return _repository.getWalletTransactions(
return _repository.getWalletOperations(
walletId: walletId,
queryParameters: queryParameters,
);

View File

@@ -5,6 +5,7 @@ import 'package:sf_shared/sf_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:activity/src/presentation/state/activity_view_model.dart';
import 'package:activity/src/presentation/state/activity_view_state.dart';
import 'package:activity/src/widgets/pagination_bar.dart';
import 'package:activity/src/widgets/transaction_tile.dart';
class ActivityScreen extends ConsumerStatefulWidget {
@@ -173,7 +174,7 @@ class _ActivityScreenState extends ConsumerState<ActivityScreen> {
return _buildError(context, theme, viewState.errorMessage);
}
if (viewState.transactions.isEmpty) {
if (viewState.transactionPages.isEmpty) {
return Center(
child: Text(
context.translate(I18n.activityNoTransactions),
@@ -185,38 +186,48 @@ class _ActivityScreenState extends ConsumerState<ActivityScreen> {
);
}
final walletId = viewState.selectedTab!.walletId;
final balanceAsync = ref.watch(walletBalanceProvider(walletId));
final currentTransactions = viewState.transactionPages[viewState.currentPage];
return RefreshIndicator(
onRefresh: () =>
ref.read(activityViewModelProvider.notifier).loadTabs(),
child: ListView.builder(
child: ListView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 24),
itemCount: viewState.transactions.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: balanceAsync.when(
loading: () =>
const Center(child: AppLoadingIndicator(size: 48)),
error: (_, __) => const SizedBox.shrink(),
data: (balance) => WalletBalanceBlock(
availableBalance: balance.availableBalance,
allocatedBalance: balance.allocatedBalance,
totalBalance: balance.totalBalance,
),
),
);
}
final transaction = viewState.transactions[index - 1];
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TransactionTile(transaction: transaction),
);
},
children: [
// TODO: WalletBalanceBlock temporarily hidden
// final walletId = viewState.selectedTab!.walletId;
// final balanceAsync = ref.watch(walletBalanceProvider(walletId));
// Padding(
// padding: const EdgeInsets.only(bottom: 16),
// child: balanceAsync.when(
// loading: () =>
// const Center(child: AppLoadingIndicator(size: 48)),
// error: (_, __) => const SizedBox.shrink(),
// data: (balance) => WalletBalanceBlock(
// availableBalance: balance.availableBalance,
// allocatedBalance: balance.allocatedBalance,
// totalBalance: balance.totalBalance,
// ),
// ),
// ),
...currentTransactions.map(
(tx) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TransactionTile(transaction: tx),
),
),
PaginationBar(
currentPage: viewState.currentPage,
totalPages: viewState.transactionPages.length,
hasMore: viewState.nextCursor != null,
isLoadingMore: viewState.isLoadingMore,
onPageChanged: (page) =>
ref.read(activityViewModelProvider.notifier).setPage(page),
onLoadMore: () =>
ref.read(activityViewModelProvider.notifier).loadMore(),
),
],
),
);
}

View File

@@ -73,20 +73,27 @@ class ActivityViewModel extends Notifier<ActivityViewState> {
final tab = state.selectedTab;
if (tab == null) return;
state = state.copyWith(isLoadingTransactions: true, errorMessage: '');
state = state.copyWith(
isLoadingTransactions: true,
errorMessage: '',
transactionPages: [],
nextCursor: null,
currentPage: 0,
);
try {
final query = TransactionsQuery(
walletId: tab.walletId,
dateFilter: state.selectedDateFilter,
);
final transactions =
final response =
await ref.read(walletTransactionsProvider(query).future);
if (!ref.mounted) return;
state = state.copyWith(
isLoadingTransactions: false,
transactions: transactions,
transactionPages: [response.items],
nextCursor: response.nextCursor,
);
} catch (e) {
if (!ref.mounted) return;
@@ -97,6 +104,38 @@ class ActivityViewModel extends Notifier<ActivityViewState> {
}
}
Future<void> loadMore() async {
final tab = state.selectedTab;
if (tab == null || state.isLoadingMore || state.nextCursor == null) return;
state = state.copyWith(isLoadingMore: true);
try {
final query = TransactionsQuery(
walletId: tab.walletId,
dateFilter: state.selectedDateFilter,
cursor: state.nextCursor,
);
final response =
await ref.read(walletTransactionsProvider(query).future);
if (!ref.mounted) return;
state = state.copyWith(
isLoadingMore: false,
transactionPages: [...state.transactionPages, response.items],
nextCursor: response.nextCursor,
currentPage: state.transactionPages.length,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoadingMore: false);
}
}
void setPage(int page) {
state = state.copyWith(currentPage: page);
}
void selectWallet(int index) {
if (index == state.selectedWalletIndex) return;
state = state.copyWith(selectedWalletIndex: index);

View File

@@ -14,7 +14,10 @@ abstract class ActivityViewState with _$ActivityViewState {
@Default([]) List<WalletTab> tabs,
@Default(0) int selectedWalletIndex,
@Default(DateFilter.today) DateFilter selectedDateFilter,
@Default([]) List<WalletTransactionEntity> transactions,
@Default([]) List<List<WalletTransactionEntity>> transactionPages,
String? nextCursor,
@Default(false) bool isLoadingMore,
@Default(0) int currentPage,
@Default('') String errorMessage,
}) = _ActivityViewState;

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ActivityViewState {
bool get isLoading; bool get isLoadingTransactions; List<WalletTab> get tabs; int get selectedWalletIndex; DateFilter get selectedDateFilter; List<WalletTransactionEntity> get transactions; String get errorMessage;
bool get isLoading; bool get isLoadingTransactions; List<WalletTab> get tabs; int get selectedWalletIndex; DateFilter get selectedDateFilter; List<List<WalletTransactionEntity>> get transactionPages; String? get nextCursor; bool get isLoadingMore; int get currentPage; String get errorMessage;
/// Create a copy of ActivityViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $ActivityViewStateCopyWith<ActivityViewState> get copyWith => _$ActivityViewStat
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ActivityViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other.tabs, tabs)&&(identical(other.selectedWalletIndex, selectedWalletIndex) || other.selectedWalletIndex == selectedWalletIndex)&&(identical(other.selectedDateFilter, selectedDateFilter) || other.selectedDateFilter == selectedDateFilter)&&const DeepCollectionEquality().equals(other.transactions, transactions)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ActivityViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other.tabs, tabs)&&(identical(other.selectedWalletIndex, selectedWalletIndex) || other.selectedWalletIndex == selectedWalletIndex)&&(identical(other.selectedDateFilter, selectedDateFilter) || other.selectedDateFilter == selectedDateFilter)&&const DeepCollectionEquality().equals(other.transactionPages, transactionPages)&&(identical(other.nextCursor, nextCursor) || other.nextCursor == nextCursor)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.currentPage, currentPage) || other.currentPage == currentPage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isLoadingTransactions,const DeepCollectionEquality().hash(tabs),selectedWalletIndex,selectedDateFilter,const DeepCollectionEquality().hash(transactions),errorMessage);
int get hashCode => Object.hash(runtimeType,isLoading,isLoadingTransactions,const DeepCollectionEquality().hash(tabs),selectedWalletIndex,selectedDateFilter,const DeepCollectionEquality().hash(transactionPages),nextCursor,isLoadingMore,currentPage,errorMessage);
@override
String toString() {
return 'ActivityViewState(isLoading: $isLoading, isLoadingTransactions: $isLoadingTransactions, tabs: $tabs, selectedWalletIndex: $selectedWalletIndex, selectedDateFilter: $selectedDateFilter, transactions: $transactions, errorMessage: $errorMessage)';
return 'ActivityViewState(isLoading: $isLoading, isLoadingTransactions: $isLoadingTransactions, tabs: $tabs, selectedWalletIndex: $selectedWalletIndex, selectedDateFilter: $selectedDateFilter, transactionPages: $transactionPages, nextCursor: $nextCursor, isLoadingMore: $isLoadingMore, currentPage: $currentPage, errorMessage: $errorMessage)';
}
@@ -45,7 +45,7 @@ abstract mixin class $ActivityViewStateCopyWith<$Res> {
factory $ActivityViewStateCopyWith(ActivityViewState value, $Res Function(ActivityViewState) _then) = _$ActivityViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<WalletTransactionEntity> transactions, String errorMessage
bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage, String errorMessage
});
@@ -62,15 +62,18 @@ class _$ActivityViewStateCopyWithImpl<$Res>
/// Create a copy of ActivityViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isLoadingTransactions = null,Object? tabs = null,Object? selectedWalletIndex = null,Object? selectedDateFilter = null,Object? transactions = null,Object? errorMessage = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isLoadingTransactions = null,Object? tabs = null,Object? selectedWalletIndex = null,Object? selectedDateFilter = null,Object? transactionPages = null,Object? nextCursor = freezed,Object? isLoadingMore = null,Object? currentPage = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isLoadingTransactions: null == isLoadingTransactions ? _self.isLoadingTransactions : isLoadingTransactions // ignore: cast_nullable_to_non_nullable
as bool,tabs: null == tabs ? _self.tabs : tabs // ignore: cast_nullable_to_non_nullable
as List<WalletTab>,selectedWalletIndex: null == selectedWalletIndex ? _self.selectedWalletIndex : selectedWalletIndex // ignore: cast_nullable_to_non_nullable
as int,selectedDateFilter: null == selectedDateFilter ? _self.selectedDateFilter : selectedDateFilter // ignore: cast_nullable_to_non_nullable
as DateFilter,transactions: null == transactions ? _self.transactions : transactions // ignore: cast_nullable_to_non_nullable
as List<WalletTransactionEntity>,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as DateFilter,transactionPages: null == transactionPages ? _self.transactionPages : transactionPages // ignore: cast_nullable_to_non_nullable
as List<List<WalletTransactionEntity>>,nextCursor: freezed == nextCursor ? _self.nextCursor : nextCursor // ignore: cast_nullable_to_non_nullable
as String?,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
as bool,currentPage: null == currentPage ? _self.currentPage : currentPage // ignore: cast_nullable_to_non_nullable
as int,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
@@ -156,10 +159,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<WalletTransactionEntity> transactions, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ActivityViewState() when $default != null:
return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.selectedWalletIndex,_that.selectedDateFilter,_that.transactions,_that.errorMessage);case _:
return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.selectedWalletIndex,_that.selectedDateFilter,_that.transactionPages,_that.nextCursor,_that.isLoadingMore,_that.currentPage,_that.errorMessage);case _:
return orElse();
}
@@ -177,10 +180,10 @@ return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.sel
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<WalletTransactionEntity> transactions, String errorMessage) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _ActivityViewState():
return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.selectedWalletIndex,_that.selectedDateFilter,_that.transactions,_that.errorMessage);case _:
return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.selectedWalletIndex,_that.selectedDateFilter,_that.transactionPages,_that.nextCursor,_that.isLoadingMore,_that.currentPage,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
@@ -197,10 +200,10 @@ return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.sel
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<WalletTransactionEntity> transactions, String errorMessage)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _ActivityViewState() when $default != null:
return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.selectedWalletIndex,_that.selectedDateFilter,_that.transactions,_that.errorMessage);case _:
return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.selectedWalletIndex,_that.selectedDateFilter,_that.transactionPages,_that.nextCursor,_that.isLoadingMore,_that.currentPage,_that.errorMessage);case _:
return null;
}
@@ -212,7 +215,7 @@ return $default(_that.isLoading,_that.isLoadingTransactions,_that.tabs,_that.sel
class _ActivityViewState extends ActivityViewState {
const _ActivityViewState({this.isLoading = false, this.isLoadingTransactions = false, final List<WalletTab> tabs = const [], this.selectedWalletIndex = 0, this.selectedDateFilter = DateFilter.today, final List<WalletTransactionEntity> transactions = const [], this.errorMessage = ''}): _tabs = tabs,_transactions = transactions,super._();
const _ActivityViewState({this.isLoading = false, this.isLoadingTransactions = false, final List<WalletTab> tabs = const [], this.selectedWalletIndex = 0, this.selectedDateFilter = DateFilter.today, final List<List<WalletTransactionEntity>> transactionPages = const [], this.nextCursor, this.isLoadingMore = false, this.currentPage = 0, this.errorMessage = ''}): _tabs = tabs,_transactionPages = transactionPages,super._();
@override@JsonKey() final bool isLoading;
@@ -226,13 +229,16 @@ class _ActivityViewState extends ActivityViewState {
@override@JsonKey() final int selectedWalletIndex;
@override@JsonKey() final DateFilter selectedDateFilter;
final List<WalletTransactionEntity> _transactions;
@override@JsonKey() List<WalletTransactionEntity> get transactions {
if (_transactions is EqualUnmodifiableListView) return _transactions;
final List<List<WalletTransactionEntity>> _transactionPages;
@override@JsonKey() List<List<WalletTransactionEntity>> get transactionPages {
if (_transactionPages is EqualUnmodifiableListView) return _transactionPages;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_transactions);
return EqualUnmodifiableListView(_transactionPages);
}
@override final String? nextCursor;
@override@JsonKey() final bool isLoadingMore;
@override@JsonKey() final int currentPage;
@override@JsonKey() final String errorMessage;
/// Create a copy of ActivityViewState
@@ -245,16 +251,16 @@ _$ActivityViewStateCopyWith<_ActivityViewState> get copyWith => __$ActivityViewS
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActivityViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other._tabs, _tabs)&&(identical(other.selectedWalletIndex, selectedWalletIndex) || other.selectedWalletIndex == selectedWalletIndex)&&(identical(other.selectedDateFilter, selectedDateFilter) || other.selectedDateFilter == selectedDateFilter)&&const DeepCollectionEquality().equals(other._transactions, _transactions)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActivityViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other._tabs, _tabs)&&(identical(other.selectedWalletIndex, selectedWalletIndex) || other.selectedWalletIndex == selectedWalletIndex)&&(identical(other.selectedDateFilter, selectedDateFilter) || other.selectedDateFilter == selectedDateFilter)&&const DeepCollectionEquality().equals(other._transactionPages, _transactionPages)&&(identical(other.nextCursor, nextCursor) || other.nextCursor == nextCursor)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.currentPage, currentPage) || other.currentPage == currentPage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isLoadingTransactions,const DeepCollectionEquality().hash(_tabs),selectedWalletIndex,selectedDateFilter,const DeepCollectionEquality().hash(_transactions),errorMessage);
int get hashCode => Object.hash(runtimeType,isLoading,isLoadingTransactions,const DeepCollectionEquality().hash(_tabs),selectedWalletIndex,selectedDateFilter,const DeepCollectionEquality().hash(_transactionPages),nextCursor,isLoadingMore,currentPage,errorMessage);
@override
String toString() {
return 'ActivityViewState(isLoading: $isLoading, isLoadingTransactions: $isLoadingTransactions, tabs: $tabs, selectedWalletIndex: $selectedWalletIndex, selectedDateFilter: $selectedDateFilter, transactions: $transactions, errorMessage: $errorMessage)';
return 'ActivityViewState(isLoading: $isLoading, isLoadingTransactions: $isLoadingTransactions, tabs: $tabs, selectedWalletIndex: $selectedWalletIndex, selectedDateFilter: $selectedDateFilter, transactionPages: $transactionPages, nextCursor: $nextCursor, isLoadingMore: $isLoadingMore, currentPage: $currentPage, errorMessage: $errorMessage)';
}
@@ -265,7 +271,7 @@ abstract mixin class _$ActivityViewStateCopyWith<$Res> implements $ActivityViewS
factory _$ActivityViewStateCopyWith(_ActivityViewState value, $Res Function(_ActivityViewState) _then) = __$ActivityViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<WalletTransactionEntity> transactions, String errorMessage
bool isLoading, bool isLoadingTransactions, List<WalletTab> tabs, int selectedWalletIndex, DateFilter selectedDateFilter, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage, String errorMessage
});
@@ -282,15 +288,18 @@ class __$ActivityViewStateCopyWithImpl<$Res>
/// Create a copy of ActivityViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isLoadingTransactions = null,Object? tabs = null,Object? selectedWalletIndex = null,Object? selectedDateFilter = null,Object? transactions = null,Object? errorMessage = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isLoadingTransactions = null,Object? tabs = null,Object? selectedWalletIndex = null,Object? selectedDateFilter = null,Object? transactionPages = null,Object? nextCursor = freezed,Object? isLoadingMore = null,Object? currentPage = null,Object? errorMessage = null,}) {
return _then(_ActivityViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isLoadingTransactions: null == isLoadingTransactions ? _self.isLoadingTransactions : isLoadingTransactions // ignore: cast_nullable_to_non_nullable
as bool,tabs: null == tabs ? _self._tabs : tabs // ignore: cast_nullable_to_non_nullable
as List<WalletTab>,selectedWalletIndex: null == selectedWalletIndex ? _self.selectedWalletIndex : selectedWalletIndex // ignore: cast_nullable_to_non_nullable
as int,selectedDateFilter: null == selectedDateFilter ? _self.selectedDateFilter : selectedDateFilter // ignore: cast_nullable_to_non_nullable
as DateFilter,transactions: null == transactions ? _self._transactions : transactions // ignore: cast_nullable_to_non_nullable
as List<WalletTransactionEntity>,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as DateFilter,transactionPages: null == transactionPages ? _self._transactionPages : transactionPages // ignore: cast_nullable_to_non_nullable
as List<List<WalletTransactionEntity>>,nextCursor: freezed == nextCursor ? _self.nextCursor : nextCursor // ignore: cast_nullable_to_non_nullable
as String?,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
as bool,currentPage: null == currentPage ? _self.currentPage : currentPage // ignore: cast_nullable_to_non_nullable
as int,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}

View File

@@ -0,0 +1,103 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class PaginationBar extends ConsumerWidget {
final int currentPage;
final int totalPages;
final bool hasMore;
final bool isLoadingMore;
final ValueChanged<int> onPageChanged;
final VoidCallback onLoadMore;
const PaginationBar({
super.key,
required this.currentPage,
required this.totalPages,
required this.hasMore,
required this.isLoadingMore,
required this.onPageChanged,
required this.onLoadMore,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final textColor = theme.getColorFor(ThemeCode.textPrimary);
final primaryColor = theme.getColorFor(ThemeCode.buttonPrimary);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: currentPage > 0
? () => onPageChanged(currentPage - 1)
: null,
icon: Icon(Icons.chevron_left, color: currentPage > 0 ? primaryColor : Colors.grey),
iconSize: 28,
constraints: const BoxConstraints(minWidth: 36, minHeight: 36),
padding: EdgeInsets.zero,
),
Flexible(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(totalPages, (index) {
final isSelected = index == currentPage;
return GestureDetector(
onTap: () => onPageChanged(index),
child: Container(
width: 32,
height: 32,
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: isSelected ? primaryColor : Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: isSelected ? null : Border.all(color: Colors.grey.shade300),
),
alignment: Alignment.center,
child: Text(
'${index + 1}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isSelected ? Colors.white : textColor,
),
),
),
);
}),
),
),
),
if (hasMore)
isLoadingMore
? const Padding(
padding: EdgeInsets.only(left: 8),
child: AppLoadingIndicator(size: 20),
)
: IconButton(
onPressed: onLoadMore,
icon: Icon(Icons.add_circle_outline, color: primaryColor),
iconSize: 28,
constraints: const BoxConstraints(minWidth: 36, minHeight: 36),
padding: EdgeInsets.zero,
tooltip: 'Load more',
),
IconButton(
onPressed: currentPage < totalPages - 1
? () => onPageChanged(currentPage + 1)
: null,
icon: Icon(Icons.chevron_right, color: currentPage < totalPages - 1 ? primaryColor : Colors.grey),
iconSize: 28,
constraints: const BoxConstraints(minWidth: 36, minHeight: 36),
padding: EdgeInsets.zero,
),
],
),
);
}
}

View File

@@ -1,6 +1,7 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
@@ -12,9 +13,18 @@ class TransactionTile extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final color = _color(transaction.transactionType);
final icon = _icon(transaction.transactionType);
final label = context.translate(_i18nKey(transaction.transactionType));
final isDeclined = transaction.status == 'DECLINED';
final isCredit = transaction.direction == 'CREDIT';
final color = isDeclined ? Colors.grey : _color(transaction.operationType);
final icon = _icon(transaction.operationType);
final label = transaction.merchantName ??
context.translate(_i18nKey(transaction.operationType));
final amountPrefix = isCredit ? '+' : '-';
final amountColor = isDeclined
? Colors.grey
: isCredit
? Colors.green
: Colors.red;
return Container(
padding: const EdgeInsets.all(16),
@@ -41,19 +51,27 @@ class TransactionTile extends ConsumerWidget {
),
),
Text(
'${transaction.amount} ${transaction.currency}',
'$amountPrefix${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: theme.getColorFor(ThemeCode.textPrimary),
color: amountColor,
decoration: isDeclined ? TextDecoration.lineThrough : null,
),
),
],
),
if (transaction.name.isNotEmpty) ...[
if (transaction.messageToUser != null &&
transaction.messageToUser!.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
transaction.name,
transaction.messageToUser!,
style: const TextStyle(fontSize: 13, color: Colors.red),
),
] else if (transaction.label.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
transaction.label,
style: TextStyle(
fontSize: 14,
color: theme.getColorFor(ThemeCode.textPrimary),
@@ -61,12 +79,65 @@ class TransactionTile extends ConsumerWidget {
),
],
const SizedBox(height: 4),
Text(
transaction.executionDate,
style: TextStyle(
fontSize: 12,
color: theme.getColorFor(ThemeCode.textPrimary),
),
Wrap(
spacing: 8,
runSpacing: 4,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
_formatDate(transaction.createdDate, context),
style: TextStyle(
fontSize: 12,
color: theme.getColorFor(ThemeCode.textPrimary),
),
),
if (transaction.merchantCity != null &&
transaction.merchantCity!.isNotEmpty)
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.location_on, size: 12,
color: theme.getColorFor(ThemeCode.textPrimary),
),
const SizedBox(width: 2),
Text(
transaction.merchantCity!,
style: TextStyle(
fontSize: 12,
color: theme.getColorFor(ThemeCode.textPrimary),
),
),
],
),
if (transaction.maskedPan != null &&
transaction.maskedPan!.isNotEmpty)
Text(
transaction.maskedPan!,
style: TextStyle(
fontSize: 12,
color: theme.getColorFor(ThemeCode.textPrimary),
),
),
if (isDeclined)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: Colors.red.withAlpha(0x1A),
borderRadius: BorderRadius.circular(4),
),
child: Text(
transaction.status,
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: Colors.red,
),
),
),
],
),
],
),
@@ -74,82 +145,51 @@ class TransactionTile extends ConsumerWidget {
}
static IconData _icon(TransactionType type) => switch (type) {
TransactionType.payin ||
TransactionType.payinAcquiring ||
TransactionType.checkPayin => Icons.arrow_downward,
TransactionType.payout ||
TransactionType.payoutSctInstantEmit => Icons.arrow_upward,
TransactionType.payinRefund ||
TransactionType.payoutRefund ||
TransactionType.payinRefundAcquiring ||
TransactionType.checkRefund => Icons.replay,
TransactionType.transfer ||
TransactionType.sctr ||
TransactionType.sctrInst ||
TransactionType.creditInternationalTransfer => Icons.swap_horiz,
TransactionType.cardTopup => Icons.arrow_downward,
TransactionType.cardTransaction => Icons.credit_card,
TransactionType.sdde || TransactionType.sddr => Icons.account_balance,
TransactionType.creditTransferReturned ||
TransactionType.payinSctInstantRecall ||
TransactionType.payinSctInstantEmitRecall ||
TransactionType.sctrRecall ||
TransactionType.sddrReversal => Icons.undo,
TransactionType.bankTransfer ||
TransactionType.instantBankTransfer => Icons.account_balance,
TransactionType.walletTransfer => Icons.swap_horiz,
TransactionType.bankDirectDebit => Icons.account_balance_wallet,
TransactionType.check => Icons.receipt_long,
TransactionType.creditNote => Icons.replay,
TransactionType.fees => Icons.monetization_on,
TransactionType.unknown => Icons.help_outline,
};
static Color _color(TransactionType type) => switch (type) {
TransactionType.payin ||
TransactionType.payinAcquiring ||
TransactionType.checkPayin => Colors.green,
TransactionType.payout ||
TransactionType.payoutSctInstantEmit => Colors.red,
TransactionType.payinRefund ||
TransactionType.payoutRefund ||
TransactionType.payinRefundAcquiring ||
TransactionType.checkRefund ||
TransactionType.creditTransferReturned ||
TransactionType.payinSctInstantRecall ||
TransactionType.payinSctInstantEmitRecall ||
TransactionType.sctrRecall ||
TransactionType.sddrReversal => Colors.orange,
TransactionType.transfer ||
TransactionType.sctr ||
TransactionType.sctrInst ||
TransactionType.creditInternationalTransfer ||
TransactionType.sdde ||
TransactionType.sddr => Colors.blue,
TransactionType.cardTopup => Colors.green,
TransactionType.cardTransaction => Colors.purple,
TransactionType.bankTransfer ||
TransactionType.instantBankTransfer ||
TransactionType.walletTransfer => Colors.blue,
TransactionType.bankDirectDebit => Colors.red,
TransactionType.check => Colors.teal,
TransactionType.creditNote => Colors.orange,
TransactionType.fees => Colors.red,
TransactionType.unknown => Colors.grey,
};
static String _i18nKey(TransactionType type) => switch (type) {
TransactionType.payin => I18n.transactionPayin,
TransactionType.payout => I18n.transactionPayout,
TransactionType.transfer => I18n.transactionTransfer,
TransactionType.payinRefund => I18n.transactionPayinRefund,
TransactionType.payoutRefund => I18n.transactionPayoutRefund,
TransactionType.bankDirectDebit => I18n.transactionBankDirectDebit,
TransactionType.bankTransfer => I18n.transactionBankTransfer,
TransactionType.cardTopup => I18n.transactionCardTopup,
TransactionType.cardTransaction => I18n.transactionCardPayment,
TransactionType.payinAcquiring => I18n.transactionPayinAcquiring,
TransactionType.payinRefundAcquiring =>
I18n.transactionPayinRefundAcquiring,
TransactionType.sctrInst => I18n.transactionSctrInst,
TransactionType.payinSctInstantRecall =>
I18n.transactionPayinSctInstantRecall,
TransactionType.payoutSctInstantEmit =>
I18n.transactionPayoutSctInstantEmit,
TransactionType.payinSctInstantEmitRecall =>
I18n.transactionPayinSctInstantEmitRecall,
TransactionType.creditTransferReturned =>
I18n.transactionCreditTransferReturned,
TransactionType.checkPayin => I18n.transactionCheckPayin,
TransactionType.sdde => I18n.transactionSdde,
TransactionType.sddr => I18n.transactionSddr,
TransactionType.sddrReversal => I18n.transactionSddrReversal,
TransactionType.sctrRecall => I18n.transactionSctrRecall,
TransactionType.checkRefund => I18n.transactionCheckRefund,
TransactionType.sctr => I18n.transactionSctr,
TransactionType.creditInternationalTransfer =>
I18n.transactionCreditInternationalTransfer,
TransactionType.check => I18n.transactionCheck,
TransactionType.creditNote => I18n.transactionCreditNote,
TransactionType.fees => I18n.transactionFees,
TransactionType.instantBankTransfer => I18n.transactionInstantBankTransfer,
TransactionType.walletTransfer => I18n.transactionWalletTransfer,
TransactionType.unknown => I18n.transactionUnknown,
};
static String _formatDate(String raw, BuildContext context) {
try {
final date = DateTime.parse(raw).toLocal();
final locale = Localizations.localeOf(context).languageCode;
return DateFormat('d MMM yyyy · HH:mm', locale).format(date);
} catch (_) {
return raw;
}
}
}

View File

@@ -161,6 +161,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
confetti:
dependency: transitive
description:
name: confetti
sha256: "979aafde2428c53947892c95eb244466c109c129b7eee9011f0a66caaca52267"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
convert:
dependency: transitive
description:

View File

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

View File

@@ -14,17 +14,17 @@ class OnboardingPage {
const List<OnboardingPage> onboardingPages = <OnboardingPage>[
OnboardingPage(
image: 'assets/shared/images/bienvenida_paso1.svg',
image: 'assets/shared/images/real_time_device_location.svg',
title: I18n.onboardingTitle1,
subtitle: I18n.onboardingSubtitle1,
),
OnboardingPage(
image: 'assets/shared/images/bienvenida_paso2.svg',
image: 'assets/shared/images/safety_zone.svg',
title: I18n.onboardingTitle2,
subtitle: I18n.onboardingSubtitle2,
),
OnboardingPage(
image: 'assets/shared/images/bienvenida_paso3.svg',
image: 'assets/shared/images/chat.svg',
title: I18n.onboardingTitle3,
subtitle: I18n.onboardingSubtitle3,
),

View File

@@ -5,6 +5,7 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fonts/fonts.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
@@ -41,7 +42,7 @@ class OnboardingScreen extends ConsumerWidget {
}
return Scaffold(
backgroundColor: Colors.white,
backgroundColor: Color.fromRGBO(247, 247, 247, 1),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
@@ -64,7 +65,7 @@ class OnboardingScreen extends ConsumerWidget {
StepIndicator(
current: state.cardIndex + 1,
total: onboardingPages.length,
color: const Color(0xFF4A4A4A),
color: Color.fromRGBO(88, 142, 165, 1),
),
const SizedBox(height: 38),
Container(
@@ -74,24 +75,30 @@ class OnboardingScreen extends ConsumerWidget {
onPressed: goToNext,
style: TextButton.styleFrom(
backgroundColor: isLast
? const Color(0xFF329E95)
: const Color(0xFF333333),
foregroundColor: Colors.white,
? const Color.fromRGBO(88, 142, 165, 1)
: const Color.fromRGBO(255, 255, 255, 1),
foregroundColor: isLast
? const Color.fromRGBO(255, 255, 255, 1)
: const Color.fromRGBO(88, 142, 165, 1),
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 24,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
side: const BorderSide(
color: Color.fromRGBO(88, 142, 165, 1),
width: 3,
),
),
),
child: Text(
isLast
? context.translate(I18n.start)
: context.translate(I18n.next),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
style: AppFonts.stolzlStyle(
size: 18,
weight: FontWeight.w600,
),
),
),
@@ -107,11 +114,11 @@ class OnboardingScreen extends ConsumerWidget {
navigationContract.goTo(AppRoutes.login),
child: Text(
context.translate(I18n.skip),
style: TextStyle(
color: Color(0xFF333333),
style: AppFonts.stolzlStyle(
color: Color.fromRGBO(88, 142, 165, 1),
decoration: TextDecoration.underline,
fontWeight: FontWeight.w500,
fontSize: 18,
weight: FontWeight.w600,
size: 18,
),
),
),

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fonts/fonts.dart';
import 'package:sf_localizations/sf_localizations.dart';
class OnboardingContent extends StatelessWidget {
@@ -21,28 +22,35 @@ class OnboardingContent extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(flex: 3, child: SvgPicture.asset(image)),
SvgPicture.asset(image, height: 250),
const SizedBox(height: 48),
Text(
context.translate(title),
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 28,
style: AppFonts.stolzlStyle(
size: 28,
weight: FontWeight.w700,
color: const Color(0xFF4A4A4A),
height: 1.4,
letterSpacing: 0.3,
color: Color(0xFF4A4A4A),
),
// style: const TextStyle(
// fontWeight: FontWeight.w700,
// fontSize: 28,
// height: 1.4,
// letterSpacing: 0.3,
// color: Color(0xFF4A4A4A),
// ),
),
const SizedBox(height: 16),
Text(
context.translate(subtitle),
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 18,
style: AppFonts.stolzlStyle(
size: 18,
height: 1.4,
letterSpacing: 0.3,
color: Color(0xFF9B9B9B),
color: const Color(0xFF333333),
),
),
],

View File

@@ -15,6 +15,7 @@ class ScaPinView extends StatelessWidget {
final VoidCallback onSubmit;
final String clearPinText;
final String? errorMessage;
final int pinLength;
const ScaPinView({
super.key,
@@ -30,6 +31,7 @@ class ScaPinView extends StatelessWidget {
required this.onSubmit,
required this.clearPinText,
this.errorMessage,
this.pinLength = 6,
});
@override
@@ -49,7 +51,7 @@ class ScaPinView extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 44),
_PinDots(length: pin.length, max: 6),
_PinDots(length: pin.length, max: pinLength),
const SizedBox(width: 12),
SizedBox(
width: 44,

View File

@@ -80,7 +80,7 @@ class AccountCreatedScreen extends ConsumerWidget {
PrimaryButton(
onPressed: () => navigationContract.goTo(AppRoutes.login),
text: context.translate(I18n.accountCreatedContinue),
color: theme.getColorFor(ThemeCode.buttonPrimary),
color: Color.fromRGBO(88, 142, 165, 1),
),
const Spacer(flex: 8),
],

View File

@@ -495,7 +495,6 @@ class SignUpViewModel extends Notifier<SignUpViewState> {
String _emailErrorFor(String value) {
final email = value.trim();
if (email.isEmpty) return I18n.errorEmailRequired;
if (email.contains('+')) return I18n.errorEmailPlusNotAllowed;
if (!_isValidEmail(email)) return I18n.errorEmailInvalid;
return '';
}

View File

@@ -90,7 +90,7 @@ class SignUpLayout extends StatelessWidget {
onPressed: onNextPressed,
text: "Siguiente",
size: 16,
color: theme.getColorFor(ThemeCode.buttonSecondary),
color: Color.fromRGBO(88, 142, 165, 1),
),
),
],

View File

@@ -13,8 +13,8 @@ dependencies:
flutter:
sdk: flutter
#modules dependencies go here
dashboard_shell:
path: ../../modules/dashboard_shell
home:
path: ../../modules/home
#packages dependencies go here
design_system:
path: ../../packages/design_system
@@ -32,6 +32,8 @@ dependencies:
path: ../../packages/sca_treezor
payments:
path: ../../packages/payments
fonts:
path: ../../packages/fonts
#dependencies go here
flutter_svg: ^2.2.1
get_it: ^9.0.5
@@ -51,6 +53,8 @@ dependencies:
shared_preferences: ^2.5.4
l10n_countries: ^1.3.1
sealed_countries: ^2.8.0
flutter_map: ^8.2.2
latlong2: ^0.9.1
dev_dependencies:
flutter_test:

View File

@@ -1,9 +1,7 @@
# 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,payments,activity
# melos_managed_dependency_overrides: activity,design_system,flutter_treezor_entrust_sdk_bridge,fonts,home,navigation,notifications,payments,sca_treezor,sf_infrastructure,sf_localizations,sf_shared,utils
dependency_overrides:
activity:
path: ../activity
dashboard_shell:
path: ../dashboard_shell
design_system:
path: ../../packages/design_system
flutter_treezor_entrust_sdk_bridge:
@@ -18,8 +16,6 @@ dependency_overrides:
path: ../notifications
payments:
path: ../../packages/payments
profile:
path: ../profile
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure:

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,4 +1,4 @@
# 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,payments,activity
# melos_managed_dependency_overrides: activity,auth,design_system,flutter_treezor_entrust_sdk_bridge,fonts,home,navigation,notifications,payments,profile,sca_treezor,sf_infrastructure,sf_localizations,sf_shared,utils
dependency_overrides:
activity:
path: ../activity

View File

@@ -7,3 +7,7 @@ export 'src/features/lock_card/lock_card_builder.dart';
export 'src/features/limits/limits_builder.dart';
export 'src/features/goals/goals_builder.dart';
export 'src/features/extract/extract_builder.dart';
export 'src/features/edit_child_profile/edit_child_profile_builder.dart';
export 'src/features/card_pin/card_pin_builder.dart';
export 'src/features/card_pin/card_pin_view_state.dart';
export 'src/features/renew_card/renew_card_builder.dart';

View File

@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:navigation/navigation.dart';
import 'card_pin_screen.dart';
import 'card_pin_view_state.dart';
class SetCardPinBuilder {
const SetCardPinBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final childWalletId = state.pathParameters['childWalletId'] ?? '';
final navigationContract = GetIt.I<NavigationContract>();
return MaterialPage(
key: state.pageKey,
child: CardPinScreen(
childId: childWalletId,
navigation: navigationContract,
mode: CardPinMode.set,
),
);
}
}
class ChangeCardPinBuilder {
const ChangeCardPinBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final childWalletId = state.pathParameters['childWalletId'] ?? '';
final navigationContract = GetIt.I<NavigationContract>();
return MaterialPage(
key: state.pageKey,
child: CardPinScreen(
childId: childWalletId,
navigation: navigationContract,
mode: CardPinMode.change,
),
);
}
}

View File

@@ -0,0 +1,136 @@
import 'package:auth/auth.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'card_pin_view_model.dart';
import 'card_pin_view_state.dart';
class CardPinScreen extends ConsumerWidget {
final String childId;
final NavigationContract navigation;
final CardPinMode mode;
const CardPinScreen({
super.key,
required this.childId,
required this.navigation,
required this.mode,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final arg = (childId, mode);
final viewState = ref.watch(cardPinViewModelProvider(arg));
final viewModel = ref.read(cardPinViewModelProvider(arg).notifier);
ref.listen(cardPinViewModelProvider(arg), (prev, next) {
if (next.success && !(prev?.success ?? false)) {
showTopSnackbar(
context,
message: context.translate(I18n.cardPinSuccess),
type: MessageType.success,
);
navigation.goBack();
return;
}
if (next.errorMessage.isNotEmpty &&
next.errorMessage != (prev?.errorMessage ?? '')) {
if (next.errorMessage == 'cardPinMismatch') {
showTopSnackbar(
context,
message: context.translate(I18n.cardPinMismatch),
type: MessageType.error,
);
} else {
showTopSnackbar(
context,
message: context.translate(I18n.cardPinError),
type: MessageType.error,
);
}
}
});
if (viewState.isLoading) {
return const Scaffold(body: Center(child: AppLoadingIndicator()));
}
if (viewState.errorMessage.isNotEmpty && viewState.childProfile == null) {
return Scaffold(
body: Center(child: Text('Error: ${viewState.errorMessage}')),
);
}
final pinLength = viewState.step == CardPinStep.scaPin ? 6 : 4;
final stepTitle = _stepTitle(context, viewState.step);
final currentPin = switch (viewState.step) {
CardPinStep.currentPin => viewState.currentPin,
CardPinStep.newPin => viewState.newPin,
CardPinStep.confirmPin => viewState.confirmPin,
CardPinStep.scaPin => viewState.scaPin,
};
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
appBar: AppBar(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: theme.getColorFor(ThemeCode.textPrimary),
),
onPressed: () {
if (viewModel.isFirstStep) {
navigation.goBack();
} else {
viewModel.goBack();
}
},
),
),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: ScaPinView(
title: stepTitle,
pin: currentPin,
pinLength: pinLength,
isProcessing: viewState.isSigning || viewState.isSubmitting,
processingText: context.translate(I18n.scaSigning),
canSubmit: viewModel.canSubmitStep,
submitText: viewState.step == CardPinStep.scaPin
? context.translate(I18n.scaConnect)
: context.translate(I18n.cardPinNext),
clearPinText: context.translate(I18n.scaClearPin),
onDigitPressed: viewModel.onDigitPressed,
onBackspacePressed: viewModel.onBackspacePressed,
onClearPin: viewModel.onClearPin,
onSubmit: viewModel.onStepSubmit,
),
),
),
SafeArea(
child: TextButton(
onPressed: () => navigation.goBack(),
child: Text(context.translate(I18n.cancel)),
),
),
],
),
);
}
String _stepTitle(BuildContext context, CardPinStep step) {
return switch (step) {
CardPinStep.currentPin => context.translate(I18n.cardPinCurrentStep),
CardPinStep.newPin => context.translate(I18n.cardPinNewStep),
CardPinStep.confirmPin => context.translate(I18n.cardPinConfirmStep),
CardPinStep.scaPin => context.translate(I18n.cardPinScaStep),
};
}
}

View File

@@ -0,0 +1,257 @@
import 'dart:convert';
import 'package:flutter/foundation.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 '../child_wallet/child_data_provider.dart';
import 'card_pin_view_state.dart';
final cardPinViewModelProvider = NotifierProvider.autoDispose
.family<CardPinViewModel, CardPinViewState, (String, CardPinMode)>(
CardPinViewModel.new,
);
class CardPinViewModel extends Notifier<CardPinViewState> {
final (String, CardPinMode) arg;
CardPinViewModel(this.arg);
String get childId => arg.$1;
CardPinMode get mode => arg.$2;
late final TreezorWalletConnectionService _connectionService;
late final TreezorWalletSignatureService _signatureService;
@override
CardPinViewState build() {
debugPrint('[CardPIN] build() mode=$mode childId=$childId');
_connectionService = GetIt.I<TreezorWalletConnectionService>();
_signatureService = GetIt.I<TreezorWalletSignatureService>();
ref.listen(childDataProvider(childId), (prev, next) {
state = state.copyWith(
isLoading: next.isLoading,
childProfile: next.childProfile,
childWallet: next.childWallet,
device: next.device,
errorMessage: next.errorMessage,
);
if (next.childProfile != null &&
prev?.childProfile == null &&
state.cardId.isEmpty) {
_loadCard(next.childProfile!.walletId);
}
});
final data = ref.read(childDataProvider(childId));
final initialStep =
mode == CardPinMode.change ? CardPinStep.currentPin : CardPinStep.newPin;
final initialState = CardPinViewState(
isLoading: data.isLoading,
childProfile: data.childProfile,
childWallet: data.childWallet,
device: data.device,
errorMessage: data.errorMessage,
step: initialStep,
);
if (data.childProfile != null) {
Future.microtask(() => _loadCard(data.childProfile!.walletId));
}
return initialState;
}
Future<void> _loadCard(String walletId) async {
debugPrint('[CardPIN] _loadCard walletId=$walletId');
try {
final card =
await ref.read(treezorRepositoryProvider).getCard(walletId: walletId);
if (!ref.mounted) return;
debugPrint('[CardPIN] _loadCard cardId=${card.cardId} status=${card.status}');
state = state.copyWith(cardId: card.cardId.toString());
} catch (e) {
debugPrint('[CardPIN] _loadCard error: $e');
}
}
String _currentStepPin() => switch (state.step) {
CardPinStep.currentPin => state.currentPin,
CardPinStep.newPin => state.newPin,
CardPinStep.confirmPin => state.confirmPin,
CardPinStep.scaPin => state.scaPin,
};
int get _currentStepMaxLength =>
state.step == CardPinStep.scaPin ? 6 : 4;
void onDigitPressed(String digit) {
final current = _currentStepPin();
if (current.length >= _currentStepMaxLength) return;
final updated = current + digit;
state = _updateStepPin(updated).copyWith(errorMessage: '');
}
void onBackspacePressed() {
final current = _currentStepPin();
if (current.isEmpty) return;
state = _updateStepPin(current.substring(0, current.length - 1));
}
void onClearPin() {
state = _updateStepPin('');
}
CardPinViewState _updateStepPin(String value) => switch (state.step) {
CardPinStep.currentPin => state.copyWith(currentPin: value),
CardPinStep.newPin => state.copyWith(newPin: value),
CardPinStep.confirmPin => state.copyWith(confirmPin: value),
CardPinStep.scaPin => state.copyWith(scaPin: value),
};
bool get canSubmitStep => _currentStepPin().length == _currentStepMaxLength;
void onStepSubmit() {
debugPrint('[CardPIN] onStepSubmit step=${state.step}');
switch (state.step) {
case CardPinStep.currentPin:
debugPrint('[CardPIN] currentPin entered, advancing to newPin');
state = state.copyWith(step: CardPinStep.newPin, errorMessage: '');
case CardPinStep.newPin:
debugPrint('[CardPIN] newPin entered, advancing to confirmPin');
state = state.copyWith(step: CardPinStep.confirmPin, errorMessage: '');
case CardPinStep.confirmPin:
if (state.confirmPin != state.newPin) {
debugPrint('[CardPIN] confirmPin MISMATCH');
state = state.copyWith(
confirmPin: '',
errorMessage: 'cardPinMismatch',
);
return;
}
debugPrint('[CardPIN] confirmPin matches, advancing to scaPin');
state = state.copyWith(step: CardPinStep.scaPin, errorMessage: '');
case CardPinStep.scaPin:
debugPrint('[CardPIN] scaPin entered, submitting...');
_submit();
}
}
void goBack() {
switch (state.step) {
case CardPinStep.currentPin:
break;
case CardPinStep.newPin:
if (mode == CardPinMode.change) {
state = state.copyWith(step: CardPinStep.currentPin, newPin: '');
}
case CardPinStep.confirmPin:
state = state.copyWith(step: CardPinStep.newPin, confirmPin: '');
case CardPinStep.scaPin:
state = state.copyWith(step: CardPinStep.confirmPin, scaPin: '');
}
}
bool get isFirstStep =>
(mode == CardPinMode.set && state.step == CardPinStep.newPin) ||
(mode == CardPinMode.change && state.step == CardPinStep.currentPin);
Future<void> _submit() async {
final cardId = state.cardId;
final walletId = state.childProfile?.walletId;
final userId = state.childProfile?.treezorUserId;
debugPrint('[CardPIN] _submit mode=$mode cardId=$cardId walletId=$walletId userId=$userId');
if (cardId.isEmpty || walletId == null) {
debugPrint('[CardPIN] _submit aborted: cardId or walletId missing');
return;
}
state = state.copyWith(isSigning: true, errorMessage: '');
try {
debugPrint('[CardPIN] connecting with SCA PIN...');
await _connectionService.connectWithPin(loginPin: state.scaPin);
if (!ref.mounted) return;
debugPrint('[CardPIN] SCA connection successful');
final String scaProof;
if (mode == CardPinMode.set) {
final url =
'https://savefamily.preprod.secure.treezor.co/cards/$cardId/setPIN';
final scaInput = jsonEncode({
'url': url,
'body': {
'newPIN': state.newPin,
'confirmPIN': state.confirmPin,
'userId': userId,
},
});
debugPrint('[CardPIN] setPIN scaInput: $scaInput');
scaProof = await _signatureService.generateJwsWithPin(
message: '',
input: scaInput,
pin: state.scaPin,
);
debugPrint('[CardPIN] setPIN scaProof: $scaProof');
} else {
final url =
'https://savefamily.preprod.secure.treezor.co/cards/$cardId/ChangePIN';
final scaInput = jsonEncode({
'url': url,
'body': {
'currentPIN': state.currentPin,
'newPIN': state.newPin,
'confirmPIN': state.confirmPin,
'userId': userId,
},
});
debugPrint('[CardPIN] changePIN scaInput: $scaInput');
scaProof = await _signatureService.generateJwsWithPin(
message: '',
input: scaInput,
pin: state.scaPin,
);
debugPrint('[CardPIN] changePIN scaProof: $scaProof');
}
if (!ref.mounted) return;
state = state.copyWith(isSigning: false, isSubmitting: true, scaPin: '');
if (mode == CardPinMode.set) {
debugPrint('[CardPIN] calling backend setCardPin walletId=$walletId');
await ref.read(treezorRepositoryProvider).setCardPin(
walletId: walletId,
newPin: state.newPin,
confirmPin: state.confirmPin,
scaProof: scaProof,
);
debugPrint('[CardPIN] setCardPin SUCCESS');
} else {
debugPrint('[CardPIN] calling backend changeCardPin walletId=$walletId');
await ref.read(treezorRepositoryProvider).changeCardPin(
walletId: walletId,
currentPin: state.currentPin,
newPin: state.newPin,
confirmPin: state.confirmPin,
scaProof: scaProof,
);
debugPrint('[CardPIN] changeCardPin SUCCESS');
}
if (!ref.mounted) return;
state = state.copyWith(isSubmitting: false, success: true);
} catch (e) {
debugPrint('[CardPIN] _submit ERROR: $e');
if (!ref.mounted) return;
state = state.copyWith(
isSigning: false,
isSubmitting: false,
scaPin: '',
errorMessage: e.toString(),
);
}
}
}

View File

@@ -0,0 +1,28 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:sf_shared/sf_shared.dart';
part 'card_pin_view_state.freezed.dart';
enum CardPinMode { set, change }
enum CardPinStep { currentPin, newPin, confirmPin, scaPin }
@freezed
abstract class CardPinViewState with _$CardPinViewState {
const factory CardPinViewState({
@Default(true) bool isLoading,
ChildProfileEntity? childProfile,
ChildWalletEntity? childWallet,
DeviceEntity? device,
@Default('') String cardId,
@Default('') String currentPin,
@Default('') String newPin,
@Default('') String confirmPin,
@Default('') String scaPin,
@Default(CardPinStep.newPin) CardPinStep step,
@Default(false) bool isSigning,
@Default(false) bool isSubmitting,
@Default(false) bool success,
@Default('') String errorMessage,
}) = _CardPinViewState;
}

View File

@@ -0,0 +1,382 @@
// 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 'card_pin_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$CardPinViewState {
bool get isLoading; ChildProfileEntity? get childProfile; ChildWalletEntity? get childWallet; DeviceEntity? get device; String get cardId; String get currentPin; String get newPin; String get confirmPin; String get scaPin; CardPinStep get step; bool get isSigning; bool get isSubmitting; bool get success; String get errorMessage;
/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CardPinViewStateCopyWith<CardPinViewState> get copyWith => _$CardPinViewStateCopyWithImpl<CardPinViewState>(this as CardPinViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CardPinViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardId, cardId) || other.cardId == cardId)&&(identical(other.currentPin, currentPin) || other.currentPin == currentPin)&&(identical(other.newPin, newPin) || other.newPin == newPin)&&(identical(other.confirmPin, confirmPin) || other.confirmPin == confirmPin)&&(identical(other.scaPin, scaPin) || other.scaPin == scaPin)&&(identical(other.step, step) || other.step == step)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.success, success) || other.success == success)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,childProfile,childWallet,device,cardId,currentPin,newPin,confirmPin,scaPin,step,isSigning,isSubmitting,success,errorMessage);
@override
String toString() {
return 'CardPinViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardId: $cardId, currentPin: $currentPin, newPin: $newPin, confirmPin: $confirmPin, scaPin: $scaPin, step: $step, isSigning: $isSigning, isSubmitting: $isSubmitting, success: $success, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class $CardPinViewStateCopyWith<$Res> {
factory $CardPinViewStateCopyWith(CardPinViewState value, $Res Function(CardPinViewState) _then) = _$CardPinViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String currentPin, String newPin, String confirmPin, String scaPin, CardPinStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage
});
$ChildProfileEntityCopyWith<$Res>? get childProfile;$ChildWalletEntityCopyWith<$Res>? get childWallet;$DeviceEntityCopyWith<$Res>? get device;
}
/// @nodoc
class _$CardPinViewStateCopyWithImpl<$Res>
implements $CardPinViewStateCopyWith<$Res> {
_$CardPinViewStateCopyWithImpl(this._self, this._then);
final CardPinViewState _self;
final $Res Function(CardPinViewState) _then;
/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardId = null,Object? currentPin = null,Object? newPin = null,Object? confirmPin = null,Object? scaPin = null,Object? step = null,Object? isSigning = null,Object? isSubmitting = null,Object? success = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,childProfile: freezed == childProfile ? _self.childProfile : childProfile // ignore: cast_nullable_to_non_nullable
as ChildProfileEntity?,childWallet: freezed == childWallet ? _self.childWallet : childWallet // ignore: cast_nullable_to_non_nullable
as ChildWalletEntity?,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable
as DeviceEntity?,cardId: null == cardId ? _self.cardId : cardId // ignore: cast_nullable_to_non_nullable
as String,currentPin: null == currentPin ? _self.currentPin : currentPin // ignore: cast_nullable_to_non_nullable
as String,newPin: null == newPin ? _self.newPin : newPin // ignore: cast_nullable_to_non_nullable
as String,confirmPin: null == confirmPin ? _self.confirmPin : confirmPin // ignore: cast_nullable_to_non_nullable
as String,scaPin: null == scaPin ? _self.scaPin : scaPin // ignore: cast_nullable_to_non_nullable
as String,step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable
as CardPinStep,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileEntityCopyWith<$Res>? get childProfile {
if (_self.childProfile == null) {
return null;
}
return $ChildProfileEntityCopyWith<$Res>(_self.childProfile!, (value) {
return _then(_self.copyWith(childProfile: value));
});
}/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildWalletEntityCopyWith<$Res>? get childWallet {
if (_self.childWallet == null) {
return null;
}
return $ChildWalletEntityCopyWith<$Res>(_self.childWallet!, (value) {
return _then(_self.copyWith(childWallet: value));
});
}/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DeviceEntityCopyWith<$Res>? get device {
if (_self.device == null) {
return null;
}
return $DeviceEntityCopyWith<$Res>(_self.device!, (value) {
return _then(_self.copyWith(device: value));
});
}
}
/// Adds pattern-matching-related methods to [CardPinViewState].
extension CardPinViewStatePatterns on CardPinViewState {
/// 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( _CardPinViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CardPinViewState() 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( _CardPinViewState value) $default,){
final _that = this;
switch (_that) {
case _CardPinViewState():
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( _CardPinViewState value)? $default,){
final _that = this;
switch (_that) {
case _CardPinViewState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String currentPin, String newPin, String confirmPin, String scaPin, CardPinStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CardPinViewState() when $default != null:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.currentPin,_that.newPin,_that.confirmPin,_that.scaPin,_that.step,_that.isSigning,_that.isSubmitting,_that.success,_that.errorMessage);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String currentPin, String newPin, String confirmPin, String scaPin, CardPinStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _CardPinViewState():
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.currentPin,_that.newPin,_that.confirmPin,_that.scaPin,_that.step,_that.isSigning,_that.isSubmitting,_that.success,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String currentPin, String newPin, String confirmPin, String scaPin, CardPinStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _CardPinViewState() when $default != null:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.currentPin,_that.newPin,_that.confirmPin,_that.scaPin,_that.step,_that.isSigning,_that.isSubmitting,_that.success,_that.errorMessage);case _:
return null;
}
}
}
/// @nodoc
class _CardPinViewState implements CardPinViewState {
const _CardPinViewState({this.isLoading = true, this.childProfile, this.childWallet, this.device, this.cardId = '', this.currentPin = '', this.newPin = '', this.confirmPin = '', this.scaPin = '', this.step = CardPinStep.newPin, this.isSigning = false, this.isSubmitting = false, this.success = false, this.errorMessage = ''});
@override@JsonKey() final bool isLoading;
@override final ChildProfileEntity? childProfile;
@override final ChildWalletEntity? childWallet;
@override final DeviceEntity? device;
@override@JsonKey() final String cardId;
@override@JsonKey() final String currentPin;
@override@JsonKey() final String newPin;
@override@JsonKey() final String confirmPin;
@override@JsonKey() final String scaPin;
@override@JsonKey() final CardPinStep step;
@override@JsonKey() final bool isSigning;
@override@JsonKey() final bool isSubmitting;
@override@JsonKey() final bool success;
@override@JsonKey() final String errorMessage;
/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CardPinViewStateCopyWith<_CardPinViewState> get copyWith => __$CardPinViewStateCopyWithImpl<_CardPinViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CardPinViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardId, cardId) || other.cardId == cardId)&&(identical(other.currentPin, currentPin) || other.currentPin == currentPin)&&(identical(other.newPin, newPin) || other.newPin == newPin)&&(identical(other.confirmPin, confirmPin) || other.confirmPin == confirmPin)&&(identical(other.scaPin, scaPin) || other.scaPin == scaPin)&&(identical(other.step, step) || other.step == step)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.success, success) || other.success == success)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,childProfile,childWallet,device,cardId,currentPin,newPin,confirmPin,scaPin,step,isSigning,isSubmitting,success,errorMessage);
@override
String toString() {
return 'CardPinViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardId: $cardId, currentPin: $currentPin, newPin: $newPin, confirmPin: $confirmPin, scaPin: $scaPin, step: $step, isSigning: $isSigning, isSubmitting: $isSubmitting, success: $success, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class _$CardPinViewStateCopyWith<$Res> implements $CardPinViewStateCopyWith<$Res> {
factory _$CardPinViewStateCopyWith(_CardPinViewState value, $Res Function(_CardPinViewState) _then) = __$CardPinViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String currentPin, String newPin, String confirmPin, String scaPin, CardPinStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage
});
@override $ChildProfileEntityCopyWith<$Res>? get childProfile;@override $ChildWalletEntityCopyWith<$Res>? get childWallet;@override $DeviceEntityCopyWith<$Res>? get device;
}
/// @nodoc
class __$CardPinViewStateCopyWithImpl<$Res>
implements _$CardPinViewStateCopyWith<$Res> {
__$CardPinViewStateCopyWithImpl(this._self, this._then);
final _CardPinViewState _self;
final $Res Function(_CardPinViewState) _then;
/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardId = null,Object? currentPin = null,Object? newPin = null,Object? confirmPin = null,Object? scaPin = null,Object? step = null,Object? isSigning = null,Object? isSubmitting = null,Object? success = null,Object? errorMessage = null,}) {
return _then(_CardPinViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,childProfile: freezed == childProfile ? _self.childProfile : childProfile // ignore: cast_nullable_to_non_nullable
as ChildProfileEntity?,childWallet: freezed == childWallet ? _self.childWallet : childWallet // ignore: cast_nullable_to_non_nullable
as ChildWalletEntity?,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable
as DeviceEntity?,cardId: null == cardId ? _self.cardId : cardId // ignore: cast_nullable_to_non_nullable
as String,currentPin: null == currentPin ? _self.currentPin : currentPin // ignore: cast_nullable_to_non_nullable
as String,newPin: null == newPin ? _self.newPin : newPin // ignore: cast_nullable_to_non_nullable
as String,confirmPin: null == confirmPin ? _self.confirmPin : confirmPin // ignore: cast_nullable_to_non_nullable
as String,scaPin: null == scaPin ? _self.scaPin : scaPin // ignore: cast_nullable_to_non_nullable
as String,step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable
as CardPinStep,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileEntityCopyWith<$Res>? get childProfile {
if (_self.childProfile == null) {
return null;
}
return $ChildProfileEntityCopyWith<$Res>(_self.childProfile!, (value) {
return _then(_self.copyWith(childProfile: value));
});
}/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildWalletEntityCopyWith<$Res>? get childWallet {
if (_self.childWallet == null) {
return null;
}
return $ChildWalletEntityCopyWith<$Res>(_self.childWallet!, (value) {
return _then(_self.copyWith(childWallet: value));
});
}/// Create a copy of CardPinViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DeviceEntityCopyWith<$Res>? get device {
if (_self.device == null) {
return null;
}
return $DeviceEntityCopyWith<$Res>(_self.device!, (value) {
return _then(_self.copyWith(device: value));
});
}
}
// dart format on

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_shared/sf_shared.dart';
@@ -15,11 +14,12 @@ class ChildDataNotifier extends Notifier<ChildDataState> {
final String childId;
ChildDataNotifier(this.childId);
late final TreezorRepository _treezorRepository;
late final UserRepository _userRepository;
late TreezorRepository _treezorRepository;
late UserRepository _userRepository;
@override
ChildDataState build() {
ref.watch(walletRefreshProvider);
final link = ref.keepAlive();
final timer = Timer(const Duration(minutes: 5), link.close);
ref.onDispose(timer.cancel);
@@ -56,9 +56,7 @@ class ChildDataNotifier extends Notifier<ChildDataState> {
device = await _userRepository.getDeviceByIdentificator(
identificator: childProfile.deviceIdentificator,
);
} catch (e) {
debugPrint('Error fetching device for child $childId: $e');
}
} catch (_) {}
if (!ref.mounted) return;

View File

@@ -5,13 +5,15 @@ import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:sf_shared/sf_shared.dart';
import '../../card_colors.dart';
import '../../presentation/state/home_view_model.dart';
import '../deposit/deposit_success_provider.dart';
import 'child_wallet_view_model.dart';
import 'wallet_actions_bar.dart';
class ChildWalletScreen extends ConsumerWidget {
class ChildWalletScreen extends ConsumerStatefulWidget {
final String childId;
final NavigationContract navigation;
@@ -22,16 +24,21 @@ class ChildWalletScreen extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewState = ref.watch(childWalletViewModelProvider(childId));
ConsumerState<ChildWalletScreen> createState() => _ChildWalletScreenState();
}
ref.listen(childWalletViewModelProvider(childId), (prev, next) {
class _ChildWalletScreenState extends ConsumerState<ChildWalletScreen> {
@override
Widget build(BuildContext context) {
final theme = ref.watch(themePortProvider);
final viewState = ref.watch(childWalletViewModelProvider(widget.childId));
final depositSuccess = ref.watch(depositSuccessProvider(widget.childId));
ref.listen(childWalletViewModelProvider(widget.childId), (prev, next) {
if (next.cardStatusSuccess && !(prev?.cardStatusSuccess ?? false)) {
ref.read(homeViewModelProvider.notifier).updateChildCardStatus(
childId,
next.cardStatus,
);
ref
.read(homeViewModelProvider.notifier)
.updateChildCardStatus(widget.childId, next.cardStatus);
showTopSnackbar(
context,
message: context.translate(I18n.cardStatusSuccess),
@@ -50,7 +57,7 @@ class ChildWalletScreen extends ConsumerWidget {
if (viewState.showPin) {
final viewModel = ref.read(
childWalletViewModelProvider(childId).notifier,
childWalletViewModelProvider(widget.childId).notifier,
);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
@@ -117,104 +124,184 @@ class ChildWalletScreen extends ConsumerWidget {
cardStatus: viewState.cardStatus,
);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: Stack(
children: [
DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(24),
bottomLeft: Radius.circular(24),
),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: locked ? theme.getDisabledCardColors() : cardColors,
),
),
child: SizedBox(width: double.infinity, height: 420),
),
SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 50, horizontal: 20),
child: Column(
spacing: 24,
children: [
Row(
spacing: 7,
children: [
IconButton(
onPressed: () => navigation.goBack(),
icon: Icon(
Icons.arrow_back_ios_new_outlined,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
size: 24,
),
),
_buildGenderAvatar(device?.carrierGenre, 50),
Text(
childName,
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
],
return ConfettiOverlay(
play: depositSuccess,
onPlayed: () =>
ref.read(depositSuccessProvider(widget.childId).notifier).reset(),
child: Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: Stack(
children: [
DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(24),
bottomLeft: Radius.circular(24),
),
Column(
spacing: 16,
children: [
MoneyText(
text: "${availableBalance.toString()}",
size: 60,
secondarySize: 24,
color: theme.getColorFor(ThemeCode.textSecondary),
),
Text(
context.translate(I18n.childWalletAvailableBalance),
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
),
LinearProgressIndicator(
value: 0.7,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
backgroundColor: theme
.getColorFor(ThemeCode.backgroundPrimary)
.withAlpha(0x4C),
minHeight: 10,
borderRadius: BorderRadius.all(Radius.circular(5)),
),
if (CardStatus.fromString(viewState.cardStatus) == CardStatus.lost ||
CardStatus.fromString(viewState.cardStatus) == CardStatus.stolen)
TextButton(
style: ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.all(0)),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: locked ? theme.getDisabledCardColors() : cardColors,
),
),
child: SizedBox(width: double.infinity, height: 420),
),
SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 50, horizontal: 20),
child: Column(
spacing: 24,
children: [
Row(
spacing: 7,
children: [
IconButton(
onPressed: () => widget.navigation.goBack(),
icon: Icon(
Icons.arrow_back_ios_new_outlined,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
size: 24,
),
onPressed: viewState.isUpdatingCard
? null
: () => _showDeleteConfirmation(context, ref),
child: Row(
spacing: 10,
children: [
Icon(
Icons.delete_outline,
size: 24,
color: theme.getColorFor(ThemeCode.textSecondary),
),
_buildGenderAvatar(device?.carrierGenre, 50),
Expanded(
child: Text(
childName,
style: TextStyle(
color: theme.getColorFor(
ThemeCode.backgroundPrimary,
),
Text(
context.translate(I18n.deleteDevice),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: theme.getColorFor(ThemeCode.textSecondary),
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
IconButton(
onPressed: () => widget.navigation.pushTo(
AppRoutes.renewCard(widget.childId),
),
icon: Icon(
Icons.credit_card_outlined,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
size: 24,
),
),
PopupMenuButton<String>(
icon: Icon(
Icons.more_vert,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
onSelected: (value) {
if (value == 'edit') {
widget.navigation.pushTo(
AppRoutes.editChildProfile(widget.childId),
);
} else if (value == 'delete') {
_showDeleteConfirmation();
} else if (value == 'set_pin') {
widget.navigation.pushTo(
AppRoutes.setCardPin(widget.childId),
);
} else if (value == 'change_pin') {
widget.navigation.pushTo(
AppRoutes.changeCardPin(widget.childId),
);
} else if (value == 'unblock_pin') {
_showUnblockPinConfirmation();
}
},
itemBuilder: (_) => [
PopupMenuItem(
value: 'edit',
child: Row(
spacing: 8,
children: [
Icon(Icons.edit_outlined),
Text(
context.translate(I18n.editChildProfile),
),
],
),
),
if (!viewState.isPinBlocked && !(viewState.childProfile?.hasCardPin ?? false))
PopupMenuItem(
value: 'set_pin',
child: Row(
spacing: 8,
children: [
Icon(Icons.pin_outlined),
Text(
context.translate(I18n.cardPinSet),
),
],
),
),
],
if (!viewState.isPinBlocked && (viewState.childProfile?.hasCardPin ?? false))
PopupMenuItem(
value: 'change_pin',
child: Row(
spacing: 8,
children: [
Icon(Icons.lock_reset_outlined),
Text(
context.translate(I18n.cardPinChange),
),
],
),
),
if (viewState.isPinBlocked)
PopupMenuItem(
value: 'unblock_pin',
child: Row(
spacing: 8,
children: [
Icon(Icons.lock_open_outlined),
Text(
context.translate(I18n.cardPinUnblock),
),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
spacing: 8,
children: [
Icon(Icons.delete_outline, color: Colors.red),
Text(
context.translate(I18n.deleteDevice),
style: TextStyle(color: Colors.red),
),
],
),
),
],
),
],
),
Column(
spacing: 16,
children: [
MoneyText(
text: "${availableBalance.toString()}",
size: 60,
secondarySize: 24,
color: theme.getColorFor(ThemeCode.textSecondary),
),
Text(
context.translate(I18n.childWalletAvailableBalance),
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
)
else
),
LinearProgressIndicator(
value: 0.7,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
backgroundColor: theme
.getColorFor(ThemeCode.backgroundPrimary)
.withAlpha(0x4C),
minHeight: 10,
borderRadius: BorderRadius.all(Radius.circular(5)),
),
TextButton(
style: ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.all(0)),
@@ -231,100 +318,125 @@ class ChildWalletScreen extends ConsumerWidget {
),
Text(
locked
? context.translate(I18n.childWalletUnlockCard)
? context.translate(
I18n.childWalletUnlockCard,
)
: context.translate(I18n.childWalletLockCard),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: theme.getColorFor(ThemeCode.textSecondary),
color: theme.getColorFor(
ThemeCode.textSecondary,
),
),
),
],
),
),
],
),
Column(
spacing: 16,
children: [
if (!locked)
WalletActionsBar(childId: childId, navigation: navigation),
Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.translate(
I18n.childWalletRecentTransactions,
),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 16),
if (viewState.isLoadingTransactions)
const Center(
child: Padding(
padding: EdgeInsets.all(24),
child: AppLoadingIndicator(size: 48),
],
),
Column(
spacing: 16,
children: [
if (!locked)
WalletActionsBar(
childId: widget.childId,
navigation: widget.navigation,
),
Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.translate(
I18n.childWalletRecentTransactions,
),
)
else if (viewState.transactions.isEmpty)
Padding(
padding: const EdgeInsets.all(24),
child: Center(
child: Text(
context.translate(
I18n.activityNoTransactions,
),
style: TextStyle(
fontSize: 14,
color: theme.getColorFor(
ThemeCode.textPrimary,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 16),
if (viewState.isLoadingTransactions)
const Center(
child: Padding(
padding: EdgeInsets.all(24),
child: AppLoadingIndicator(size: 48),
),
)
else if (viewState.transactionPages.isEmpty)
Padding(
padding: const EdgeInsets.all(24),
child: Center(
child: Text(
context.translate(
I18n.activityNoTransactions,
),
style: TextStyle(
fontSize: 14,
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
),
),
)
else ...[
...viewState.transactionPages[viewState.currentPage].map(
(tx) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TransactionTile(transaction: tx),
),
),
)
else
...viewState.transactions.map(
(tx) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: TransactionTile(transaction: tx),
PaginationBar(
currentPage: viewState.currentPage,
totalPages: viewState.transactionPages.length,
hasMore: viewState.nextCursor != null,
isLoadingMore: viewState.isLoadingMore,
onPageChanged: (page) => ref
.read(
childWalletViewModelProvider(
widget.childId,
).notifier,
)
.setPage(page),
onLoadMore: () => ref
.read(
childWalletViewModelProvider(
widget.childId,
).notifier,
)
.loadMore(),
),
),
],
],
],
),
),
),
],
),
],
],
),
],
),
),
),
],
],
),
),
);
}
Widget _buildGenderAvatar(String? carrierGenre, double size) {
final IconData icon;
final Color color;
switch (carrierGenre) {
case 'M':
icon = Icons.face;
color = const Color(0xFF64B5F6);
case 'F':
icon = Icons.face_3;
color = const Color(0xFFF48FB1);
default:
icon = Icons.face_2;
color = const Color(0xFF90A4AE);
}
return CircleAvatar(
radius: size / 2,
@@ -344,47 +456,154 @@ class ChildWalletScreen extends ConsumerWidget {
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
builder: (_) => _CardStatusSheet(childId: childId),
builder: (_) => _CardStatusSheet(childId: widget.childId),
);
}
void _showDeleteConfirmation(BuildContext context, WidgetRef ref) {
Future<void> _showDeleteConfirmation() async {
final theme = ref.read(themePortProvider);
final userRepo = ref.read(userRepositoryProvider);
final navigator = Navigator.of(context, rootNavigator: true);
final checkingText = context.translate(I18n.deleteDeviceChecking);
final notAllowedTitle = context.translate(I18n.deleteDeviceNotAllowedTitle);
final nonZeroText = context.translate(
I18n.deleteDeviceWalletNonZeroBalance,
);
final acceptText = context.translate(I18n.accept);
final confirmTitle = context.translate(I18n.deleteDeviceConfirmTitle);
final confirmMessage = context.translate(I18n.deleteDeviceConfirmMessage);
final cancelText = context.translate(I18n.cancel);
final deleteText = context.translate(I18n.deleteDevice);
final successText = context.translate(I18n.deleteDeviceSuccess);
final bgColor = theme.getColorFor(ThemeCode.backgroundPrimary);
final shape = RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
);
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
backgroundColor: bgColor,
shape: shape,
content: Row(
spacing: 16,
children: [const AppLoadingIndicator(size: 24), Text(checkingText)],
),
title: Text(context.translate(I18n.deleteDeviceConfirmTitle)),
content: Text(context.translate(I18n.deleteDeviceConfirmMessage)),
),
);
try {
final deletability = await userRepo.checkChildProfileDeletability(
childProfileId: widget.childId,
);
if (!mounted) return;
navigator.pop();
if (!deletability.isDeletable) {
if (!mounted) return;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
backgroundColor: bgColor,
shape: shape,
title: Text(notAllowedTitle),
content: Text(nonZeroText),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: Text(acceptText),
),
],
),
);
return;
}
if (!mounted) return;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
backgroundColor: bgColor,
shape: shape,
title: Text(confirmTitle),
content: Text(confirmMessage),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: Text(cancelText),
),
TextButton(
onPressed: () async {
Navigator.of(ctx).pop();
final viewModel = ref.read(
childWalletViewModelProvider(widget.childId).notifier,
);
final success = await viewModel.deleteDevice();
if (success && mounted) {
ref
.read(homeViewModelProvider.notifier)
.removeChild(widget.childId);
showTopSnackbar(
context,
message: successText,
type: MessageType.success,
);
widget.navigation.goBack();
}
},
child: Text(deleteText, style: TextStyle(color: Colors.red)),
),
],
),
);
} catch (e) {
if (!mounted) return;
navigator.pop();
showTopSnackbar(context, message: e.toString(), type: MessageType.error);
}
}
Future<void> _showUnblockPinConfirmation() async {
final theme = ref.read(themePortProvider);
final confirmTitle = context.translate(I18n.cardPinUnblock);
final confirmMessage = context.translate(I18n.cardPinUnblockConfirm);
final cancelText = context.translate(I18n.cancel);
final confirmText = context.translate(I18n.accept);
final successText = context.translate(I18n.cardPinUnblockSuccess);
final bgColor = theme.getColorFor(ThemeCode.backgroundPrimary);
final shape = RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
);
showDialog(
context: context,
builder: (ctx) => AlertDialog(
backgroundColor: bgColor,
shape: shape,
title: Text(confirmTitle),
content: Text(confirmMessage),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(context.translate(I18n.cancel)),
onPressed: () => Navigator.of(ctx).pop(),
child: Text(cancelText),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
Navigator.of(ctx).pop();
final viewModel = ref.read(
childWalletViewModelProvider(childId).notifier,
childWalletViewModelProvider(widget.childId).notifier,
);
final success = await viewModel.deleteDevice();
if (success && context.mounted) {
ref.read(homeViewModelProvider.notifier).removeChild(childId);
final success = await viewModel.unblockCardPin();
if (success && mounted) {
showTopSnackbar(
context,
message: context.translate(I18n.deleteDeviceSuccess),
message: successText,
type: MessageType.success,
);
navigation.goBack();
}
},
child: Text(
context.translate(I18n.deleteDevice),
style: TextStyle(color: Colors.red),
),
child: Text(confirmText),
),
],
),
@@ -417,7 +636,11 @@ class _CardStatusSheetState extends ConsumerState<_CardStatusSheet> {
final viewState = ref.watch(childWalletViewModelProvider(widget.childId));
final currentStatus = viewState.cardStatus;
final statuses = CardStatus.fromString(currentStatus) == CardStatus.unlock
? [CardStatus.lock.value, CardStatus.lost.value, CardStatus.stolen.value]
? [
CardStatus.lock.value,
CardStatus.lost.value,
CardStatus.stolen.value,
]
: [CardStatus.unlock.value];
_selected ??= statuses.first;
@@ -431,13 +654,19 @@ class _CardStatusSheetState extends ConsumerState<_CardStatusSheet> {
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
),
const SizedBox(height: 20),
...statuses.map(
(status) => RadioListTile<String>(
title: Text(context.translate(_labelKey(status))),
value: status,
groupValue: _selected,
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
onChanged: (v) => setState(() => _selected = v!),
RadioGroup<String>(
groupValue: _selected!,
onChanged: (v) => setState(() => _selected = v),
child: Column(
children: statuses
.map(
(status) => RadioListTile<String>(
title: Text(context.translate(_labelKey(status))),
value: status,
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
)
.toList(),
),
),
const SizedBox(height: 16),

View File

@@ -5,7 +5,6 @@ import 'package:get_it/get_it.dart';
import 'package:sca_treezor/sca_treezor.dart';
import 'package:sf_shared/sf_shared.dart';
import '../../card_colors.dart';
import 'child_data_provider.dart';
import 'child_wallet_view_state.dart';
@@ -19,8 +18,8 @@ class ChildWalletViewModel extends Notifier<ChildWalletViewState> {
final String childId;
ChildWalletViewModel(this.childId);
late final TreezorWalletConnectionService _connectionService;
late final TreezorWalletSignatureService _signatureService;
late TreezorWalletConnectionService _connectionService;
late TreezorWalletSignatureService _signatureService;
@override
ChildWalletViewState build() {
@@ -43,6 +42,8 @@ class ChildWalletViewModel extends Notifier<ChildWalletViewState> {
}
});
ref.watch(walletRefreshProvider);
final data = ref.read(childDataProvider(childId));
final initialState = ChildWalletViewState(
isLoading: data.isLoading,
@@ -66,19 +67,53 @@ class ChildWalletViewModel extends Notifier<ChildWalletViewState> {
state = state.copyWith(isLoadingTransactions: true);
try {
final query = TransactionsQuery(walletId: walletId);
final transactions =
await ref.read(walletTransactionsProvider(query).future);
final response = await ref.read(
walletTransactionsProvider(query).future,
);
if (!ref.mounted) return;
state = state.copyWith(
isLoadingTransactions: false,
transactions: transactions,
transactionPages: [response.items],
nextCursor: response.nextCursor,
currentPage: 0,
);
} catch (_) {
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoadingTransactions: false);
}
}
Future<void> loadMore() async {
final walletId = state.childProfile?.walletId;
if (walletId == null || state.isLoadingMore || state.nextCursor == null) return;
state = state.copyWith(isLoadingMore: true);
try {
final query = TransactionsQuery(
walletId: walletId,
cursor: state.nextCursor,
);
final response = await ref.read(
walletTransactionsProvider(query).future,
);
if (!ref.mounted) return;
state = state.copyWith(
isLoadingMore: false,
transactionPages: [...state.transactionPages, response.items],
nextCursor: response.nextCursor,
currentPage: state.transactionPages.length,
);
} catch (_) {
if (!ref.mounted) return;
state = state.copyWith(isLoadingMore: false);
}
}
void setPage(int page) {
state = state.copyWith(currentPage: page);
}
Future<void> _loadCard(String walletId) async {
try {
final card = await ref
@@ -89,6 +124,7 @@ class ChildWalletViewModel extends Notifier<ChildWalletViewState> {
cardId: card.cardId.toString(),
cardStatus: card.status,
locked: CardStatus.fromString(card.status).isLocked,
isPinBlocked: card.isPinBlocked,
);
} catch (_) {}
}
@@ -181,6 +217,12 @@ class ChildWalletViewModel extends Notifier<ChildWalletViewState> {
}
}
Future<ChildProfileDeletabilityEntity> checkDeletability() async {
return ref
.read(userRepositoryProvider)
.checkChildProfileDeletability(childProfileId: childId);
}
Future<bool> deleteDevice() async {
final deviceId = state.device?.id;
if (deviceId == null || deviceId.isEmpty) return false;
@@ -195,7 +237,33 @@ class ChildWalletViewModel extends Notifier<ChildWalletViewState> {
return true;
} catch (e) {
if (!ref.mounted) return false;
state = state.copyWith(isUpdatingCard: false, cardStatusError: e.toString());
state = state.copyWith(
isUpdatingCard: false,
cardStatusError: e.toString(),
);
return false;
}
}
Future<bool> unblockCardPin() async {
final walletId = state.childProfile?.walletId;
if (walletId == null) return false;
state = state.copyWith(isUpdatingCard: true, cardStatusError: '');
try {
await ref.read(treezorRepositoryProvider).unblockCardPin(
walletId: walletId,
);
if (!ref.mounted) return false;
state = state.copyWith(isUpdatingCard: false);
return true;
} catch (e) {
if (!ref.mounted) return false;
state = state.copyWith(
isUpdatingCard: false,
cardStatusError: e.toString(),
);
return false;
}
}

View File

@@ -13,6 +13,7 @@ abstract class ChildWalletViewState with _$ChildWalletViewState {
@Default('') String cardId,
@Default('') String cardStatus,
@Default(false) bool locked,
@Default(false) bool isPinBlocked,
@Default('') String errorMessage,
@Default(false) bool isUpdatingCard,
@Default('') String cardStatusError,
@@ -22,6 +23,9 @@ abstract class ChildWalletViewState with _$ChildWalletViewState {
@Default('') String pin,
@Default(false) bool isSigning,
@Default(false) bool isLoadingTransactions,
@Default([]) List<WalletTransactionEntity> transactions,
@Default([]) List<List<WalletTransactionEntity>> transactionPages,
String? nextCursor,
@Default(false) bool isLoadingMore,
@Default(0) int currentPage,
}) = _ChildWalletViewState;
}

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ChildWalletViewState {
bool get isLoading; ChildProfileEntity? get childProfile; ChildWalletEntity? get childWallet; DeviceEntity? get device; String get cardId; String get cardStatus; bool get locked; String get errorMessage; bool get isUpdatingCard; String get cardStatusError; bool get cardStatusSuccess; bool get showPin; String get selectedStatus; String get pin; bool get isSigning; bool get isLoadingTransactions; List<WalletTransactionEntity> get transactions;
bool get isLoading; ChildProfileEntity? get childProfile; ChildWalletEntity? get childWallet; DeviceEntity? get device; String get cardId; String get cardStatus; bool get locked; bool get isPinBlocked; String get errorMessage; bool get isUpdatingCard; String get cardStatusError; bool get cardStatusSuccess; bool get showPin; String get selectedStatus; String get pin; bool get isSigning; bool get isLoadingTransactions; List<List<WalletTransactionEntity>> get transactionPages; String? get nextCursor; bool get isLoadingMore; int get currentPage;
/// Create a copy of ChildWalletViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $ChildWalletViewStateCopyWith<ChildWalletViewState> get copyWith => _$ChildWalle
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChildWalletViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardId, cardId) || other.cardId == cardId)&&(identical(other.cardStatus, cardStatus) || other.cardStatus == cardStatus)&&(identical(other.locked, locked) || other.locked == locked)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isUpdatingCard, isUpdatingCard) || other.isUpdatingCard == isUpdatingCard)&&(identical(other.cardStatusError, cardStatusError) || other.cardStatusError == cardStatusError)&&(identical(other.cardStatusSuccess, cardStatusSuccess) || other.cardStatusSuccess == cardStatusSuccess)&&(identical(other.showPin, showPin) || other.showPin == showPin)&&(identical(other.selectedStatus, selectedStatus) || other.selectedStatus == selectedStatus)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other.transactions, transactions));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChildWalletViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardId, cardId) || other.cardId == cardId)&&(identical(other.cardStatus, cardStatus) || other.cardStatus == cardStatus)&&(identical(other.locked, locked) || other.locked == locked)&&(identical(other.isPinBlocked, isPinBlocked) || other.isPinBlocked == isPinBlocked)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isUpdatingCard, isUpdatingCard) || other.isUpdatingCard == isUpdatingCard)&&(identical(other.cardStatusError, cardStatusError) || other.cardStatusError == cardStatusError)&&(identical(other.cardStatusSuccess, cardStatusSuccess) || other.cardStatusSuccess == cardStatusSuccess)&&(identical(other.showPin, showPin) || other.showPin == showPin)&&(identical(other.selectedStatus, selectedStatus) || other.selectedStatus == selectedStatus)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other.transactionPages, transactionPages)&&(identical(other.nextCursor, nextCursor) || other.nextCursor == nextCursor)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.currentPage, currentPage) || other.currentPage == currentPage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,childProfile,childWallet,device,cardId,cardStatus,locked,errorMessage,isUpdatingCard,cardStatusError,cardStatusSuccess,showPin,selectedStatus,pin,isSigning,isLoadingTransactions,const DeepCollectionEquality().hash(transactions));
int get hashCode => Object.hashAll([runtimeType,isLoading,childProfile,childWallet,device,cardId,cardStatus,locked,isPinBlocked,errorMessage,isUpdatingCard,cardStatusError,cardStatusSuccess,showPin,selectedStatus,pin,isSigning,isLoadingTransactions,const DeepCollectionEquality().hash(transactionPages),nextCursor,isLoadingMore,currentPage]);
@override
String toString() {
return 'ChildWalletViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardId: $cardId, cardStatus: $cardStatus, locked: $locked, errorMessage: $errorMessage, isUpdatingCard: $isUpdatingCard, cardStatusError: $cardStatusError, cardStatusSuccess: $cardStatusSuccess, showPin: $showPin, selectedStatus: $selectedStatus, pin: $pin, isSigning: $isSigning, isLoadingTransactions: $isLoadingTransactions, transactions: $transactions)';
return 'ChildWalletViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardId: $cardId, cardStatus: $cardStatus, locked: $locked, isPinBlocked: $isPinBlocked, errorMessage: $errorMessage, isUpdatingCard: $isUpdatingCard, cardStatusError: $cardStatusError, cardStatusSuccess: $cardStatusSuccess, showPin: $showPin, selectedStatus: $selectedStatus, pin: $pin, isSigning: $isSigning, isLoadingTransactions: $isLoadingTransactions, transactionPages: $transactionPages, nextCursor: $nextCursor, isLoadingMore: $isLoadingMore, currentPage: $currentPage)';
}
@@ -45,7 +45,7 @@ abstract mixin class $ChildWalletViewStateCopyWith<$Res> {
factory $ChildWalletViewStateCopyWith(ChildWalletViewState value, $Res Function(ChildWalletViewState) _then) = _$ChildWalletViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<WalletTransactionEntity> transactions
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, bool isPinBlocked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage
});
@@ -62,7 +62,7 @@ class _$ChildWalletViewStateCopyWithImpl<$Res>
/// Create a copy of ChildWalletViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardId = null,Object? cardStatus = null,Object? locked = null,Object? errorMessage = null,Object? isUpdatingCard = null,Object? cardStatusError = null,Object? cardStatusSuccess = null,Object? showPin = null,Object? selectedStatus = null,Object? pin = null,Object? isSigning = null,Object? isLoadingTransactions = null,Object? transactions = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardId = null,Object? cardStatus = null,Object? locked = null,Object? isPinBlocked = null,Object? errorMessage = null,Object? isUpdatingCard = null,Object? cardStatusError = null,Object? cardStatusSuccess = null,Object? showPin = null,Object? selectedStatus = null,Object? pin = null,Object? isSigning = null,Object? isLoadingTransactions = null,Object? transactionPages = null,Object? nextCursor = freezed,Object? isLoadingMore = null,Object? currentPage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,childProfile: freezed == childProfile ? _self.childProfile : childProfile // ignore: cast_nullable_to_non_nullable
@@ -71,6 +71,7 @@ as ChildWalletEntity?,device: freezed == device ? _self.device : device // ignor
as DeviceEntity?,cardId: null == cardId ? _self.cardId : cardId // ignore: cast_nullable_to_non_nullable
as String,cardStatus: null == cardStatus ? _self.cardStatus : cardStatus // ignore: cast_nullable_to_non_nullable
as String,locked: null == locked ? _self.locked : locked // ignore: cast_nullable_to_non_nullable
as bool,isPinBlocked: null == isPinBlocked ? _self.isPinBlocked : isPinBlocked // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isUpdatingCard: null == isUpdatingCard ? _self.isUpdatingCard : isUpdatingCard // ignore: cast_nullable_to_non_nullable
as bool,cardStatusError: null == cardStatusError ? _self.cardStatusError : cardStatusError // ignore: cast_nullable_to_non_nullable
@@ -80,8 +81,11 @@ as bool,selectedStatus: null == selectedStatus ? _self.selectedStatus : selected
as String,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,isLoadingTransactions: null == isLoadingTransactions ? _self.isLoadingTransactions : isLoadingTransactions // ignore: cast_nullable_to_non_nullable
as bool,transactions: null == transactions ? _self.transactions : transactions // ignore: cast_nullable_to_non_nullable
as List<WalletTransactionEntity>,
as bool,transactionPages: null == transactionPages ? _self.transactionPages : transactionPages // ignore: cast_nullable_to_non_nullable
as List<List<WalletTransactionEntity>>,nextCursor: freezed == nextCursor ? _self.nextCursor : nextCursor // ignore: cast_nullable_to_non_nullable
as String?,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
as bool,currentPage: null == currentPage ? _self.currentPage : currentPage // ignore: cast_nullable_to_non_nullable
as int,
));
}
/// Create a copy of ChildWalletViewState
@@ -202,10 +206,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<WalletTransactionEntity> transactions)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, bool isPinBlocked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ChildWalletViewState() when $default != null:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.cardStatus,_that.locked,_that.errorMessage,_that.isUpdatingCard,_that.cardStatusError,_that.cardStatusSuccess,_that.showPin,_that.selectedStatus,_that.pin,_that.isSigning,_that.isLoadingTransactions,_that.transactions);case _:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.cardStatus,_that.locked,_that.isPinBlocked,_that.errorMessage,_that.isUpdatingCard,_that.cardStatusError,_that.cardStatusSuccess,_that.showPin,_that.selectedStatus,_that.pin,_that.isSigning,_that.isLoadingTransactions,_that.transactionPages,_that.nextCursor,_that.isLoadingMore,_that.currentPage);case _:
return orElse();
}
@@ -223,10 +227,10 @@ return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.devic
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<WalletTransactionEntity> transactions) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, bool isPinBlocked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage) $default,) {final _that = this;
switch (_that) {
case _ChildWalletViewState():
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.cardStatus,_that.locked,_that.errorMessage,_that.isUpdatingCard,_that.cardStatusError,_that.cardStatusSuccess,_that.showPin,_that.selectedStatus,_that.pin,_that.isSigning,_that.isLoadingTransactions,_that.transactions);case _:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.cardStatus,_that.locked,_that.isPinBlocked,_that.errorMessage,_that.isUpdatingCard,_that.cardStatusError,_that.cardStatusSuccess,_that.showPin,_that.selectedStatus,_that.pin,_that.isSigning,_that.isLoadingTransactions,_that.transactionPages,_that.nextCursor,_that.isLoadingMore,_that.currentPage);case _:
throw StateError('Unexpected subclass');
}
@@ -243,10 +247,10 @@ return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.devic
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<WalletTransactionEntity> transactions)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, bool isPinBlocked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage)? $default,) {final _that = this;
switch (_that) {
case _ChildWalletViewState() when $default != null:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.cardStatus,_that.locked,_that.errorMessage,_that.isUpdatingCard,_that.cardStatusError,_that.cardStatusSuccess,_that.showPin,_that.selectedStatus,_that.pin,_that.isSigning,_that.isLoadingTransactions,_that.transactions);case _:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardId,_that.cardStatus,_that.locked,_that.isPinBlocked,_that.errorMessage,_that.isUpdatingCard,_that.cardStatusError,_that.cardStatusSuccess,_that.showPin,_that.selectedStatus,_that.pin,_that.isSigning,_that.isLoadingTransactions,_that.transactionPages,_that.nextCursor,_that.isLoadingMore,_that.currentPage);case _:
return null;
}
@@ -258,7 +262,7 @@ return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.devic
class _ChildWalletViewState implements ChildWalletViewState {
const _ChildWalletViewState({this.isLoading = true, this.childProfile, this.childWallet, this.device, this.cardId = '', this.cardStatus = '', this.locked = false, this.errorMessage = '', this.isUpdatingCard = false, this.cardStatusError = '', this.cardStatusSuccess = false, this.showPin = false, this.selectedStatus = '', this.pin = '', this.isSigning = false, this.isLoadingTransactions = false, final List<WalletTransactionEntity> transactions = const []}): _transactions = transactions;
const _ChildWalletViewState({this.isLoading = true, this.childProfile, this.childWallet, this.device, this.cardId = '', this.cardStatus = '', this.locked = false, this.isPinBlocked = false, this.errorMessage = '', this.isUpdatingCard = false, this.cardStatusError = '', this.cardStatusSuccess = false, this.showPin = false, this.selectedStatus = '', this.pin = '', this.isSigning = false, this.isLoadingTransactions = false, final List<List<WalletTransactionEntity>> transactionPages = const [], this.nextCursor, this.isLoadingMore = false, this.currentPage = 0}): _transactionPages = transactionPages;
@override@JsonKey() final bool isLoading;
@@ -268,6 +272,7 @@ class _ChildWalletViewState implements ChildWalletViewState {
@override@JsonKey() final String cardId;
@override@JsonKey() final String cardStatus;
@override@JsonKey() final bool locked;
@override@JsonKey() final bool isPinBlocked;
@override@JsonKey() final String errorMessage;
@override@JsonKey() final bool isUpdatingCard;
@override@JsonKey() final String cardStatusError;
@@ -277,13 +282,16 @@ class _ChildWalletViewState implements ChildWalletViewState {
@override@JsonKey() final String pin;
@override@JsonKey() final bool isSigning;
@override@JsonKey() final bool isLoadingTransactions;
final List<WalletTransactionEntity> _transactions;
@override@JsonKey() List<WalletTransactionEntity> get transactions {
if (_transactions is EqualUnmodifiableListView) return _transactions;
final List<List<WalletTransactionEntity>> _transactionPages;
@override@JsonKey() List<List<WalletTransactionEntity>> get transactionPages {
if (_transactionPages is EqualUnmodifiableListView) return _transactionPages;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_transactions);
return EqualUnmodifiableListView(_transactionPages);
}
@override final String? nextCursor;
@override@JsonKey() final bool isLoadingMore;
@override@JsonKey() final int currentPage;
/// Create a copy of ChildWalletViewState
/// with the given fields replaced by the non-null parameter values.
@@ -295,16 +303,16 @@ _$ChildWalletViewStateCopyWith<_ChildWalletViewState> get copyWith => __$ChildWa
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChildWalletViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardId, cardId) || other.cardId == cardId)&&(identical(other.cardStatus, cardStatus) || other.cardStatus == cardStatus)&&(identical(other.locked, locked) || other.locked == locked)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isUpdatingCard, isUpdatingCard) || other.isUpdatingCard == isUpdatingCard)&&(identical(other.cardStatusError, cardStatusError) || other.cardStatusError == cardStatusError)&&(identical(other.cardStatusSuccess, cardStatusSuccess) || other.cardStatusSuccess == cardStatusSuccess)&&(identical(other.showPin, showPin) || other.showPin == showPin)&&(identical(other.selectedStatus, selectedStatus) || other.selectedStatus == selectedStatus)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other._transactions, _transactions));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChildWalletViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardId, cardId) || other.cardId == cardId)&&(identical(other.cardStatus, cardStatus) || other.cardStatus == cardStatus)&&(identical(other.locked, locked) || other.locked == locked)&&(identical(other.isPinBlocked, isPinBlocked) || other.isPinBlocked == isPinBlocked)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isUpdatingCard, isUpdatingCard) || other.isUpdatingCard == isUpdatingCard)&&(identical(other.cardStatusError, cardStatusError) || other.cardStatusError == cardStatusError)&&(identical(other.cardStatusSuccess, cardStatusSuccess) || other.cardStatusSuccess == cardStatusSuccess)&&(identical(other.showPin, showPin) || other.showPin == showPin)&&(identical(other.selectedStatus, selectedStatus) || other.selectedStatus == selectedStatus)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isLoadingTransactions, isLoadingTransactions) || other.isLoadingTransactions == isLoadingTransactions)&&const DeepCollectionEquality().equals(other._transactionPages, _transactionPages)&&(identical(other.nextCursor, nextCursor) || other.nextCursor == nextCursor)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.currentPage, currentPage) || other.currentPage == currentPage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,childProfile,childWallet,device,cardId,cardStatus,locked,errorMessage,isUpdatingCard,cardStatusError,cardStatusSuccess,showPin,selectedStatus,pin,isSigning,isLoadingTransactions,const DeepCollectionEquality().hash(_transactions));
int get hashCode => Object.hashAll([runtimeType,isLoading,childProfile,childWallet,device,cardId,cardStatus,locked,isPinBlocked,errorMessage,isUpdatingCard,cardStatusError,cardStatusSuccess,showPin,selectedStatus,pin,isSigning,isLoadingTransactions,const DeepCollectionEquality().hash(_transactionPages),nextCursor,isLoadingMore,currentPage]);
@override
String toString() {
return 'ChildWalletViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardId: $cardId, cardStatus: $cardStatus, locked: $locked, errorMessage: $errorMessage, isUpdatingCard: $isUpdatingCard, cardStatusError: $cardStatusError, cardStatusSuccess: $cardStatusSuccess, showPin: $showPin, selectedStatus: $selectedStatus, pin: $pin, isSigning: $isSigning, isLoadingTransactions: $isLoadingTransactions, transactions: $transactions)';
return 'ChildWalletViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardId: $cardId, cardStatus: $cardStatus, locked: $locked, isPinBlocked: $isPinBlocked, errorMessage: $errorMessage, isUpdatingCard: $isUpdatingCard, cardStatusError: $cardStatusError, cardStatusSuccess: $cardStatusSuccess, showPin: $showPin, selectedStatus: $selectedStatus, pin: $pin, isSigning: $isSigning, isLoadingTransactions: $isLoadingTransactions, transactionPages: $transactionPages, nextCursor: $nextCursor, isLoadingMore: $isLoadingMore, currentPage: $currentPage)';
}
@@ -315,7 +323,7 @@ abstract mixin class _$ChildWalletViewStateCopyWith<$Res> implements $ChildWalle
factory _$ChildWalletViewStateCopyWith(_ChildWalletViewState value, $Res Function(_ChildWalletViewState) _then) = __$ChildWalletViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<WalletTransactionEntity> transactions
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardId, String cardStatus, bool locked, bool isPinBlocked, String errorMessage, bool isUpdatingCard, String cardStatusError, bool cardStatusSuccess, bool showPin, String selectedStatus, String pin, bool isSigning, bool isLoadingTransactions, List<List<WalletTransactionEntity>> transactionPages, String? nextCursor, bool isLoadingMore, int currentPage
});
@@ -332,7 +340,7 @@ class __$ChildWalletViewStateCopyWithImpl<$Res>
/// Create a copy of ChildWalletViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardId = null,Object? cardStatus = null,Object? locked = null,Object? errorMessage = null,Object? isUpdatingCard = null,Object? cardStatusError = null,Object? cardStatusSuccess = null,Object? showPin = null,Object? selectedStatus = null,Object? pin = null,Object? isSigning = null,Object? isLoadingTransactions = null,Object? transactions = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardId = null,Object? cardStatus = null,Object? locked = null,Object? isPinBlocked = null,Object? errorMessage = null,Object? isUpdatingCard = null,Object? cardStatusError = null,Object? cardStatusSuccess = null,Object? showPin = null,Object? selectedStatus = null,Object? pin = null,Object? isSigning = null,Object? isLoadingTransactions = null,Object? transactionPages = null,Object? nextCursor = freezed,Object? isLoadingMore = null,Object? currentPage = null,}) {
return _then(_ChildWalletViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,childProfile: freezed == childProfile ? _self.childProfile : childProfile // ignore: cast_nullable_to_non_nullable
@@ -341,6 +349,7 @@ as ChildWalletEntity?,device: freezed == device ? _self.device : device // ignor
as DeviceEntity?,cardId: null == cardId ? _self.cardId : cardId // ignore: cast_nullable_to_non_nullable
as String,cardStatus: null == cardStatus ? _self.cardStatus : cardStatus // ignore: cast_nullable_to_non_nullable
as String,locked: null == locked ? _self.locked : locked // ignore: cast_nullable_to_non_nullable
as bool,isPinBlocked: null == isPinBlocked ? _self.isPinBlocked : isPinBlocked // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isUpdatingCard: null == isUpdatingCard ? _self.isUpdatingCard : isUpdatingCard // ignore: cast_nullable_to_non_nullable
as bool,cardStatusError: null == cardStatusError ? _self.cardStatusError : cardStatusError // ignore: cast_nullable_to_non_nullable
@@ -350,8 +359,11 @@ as bool,selectedStatus: null == selectedStatus ? _self.selectedStatus : selected
as String,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,isLoadingTransactions: null == isLoadingTransactions ? _self.isLoadingTransactions : isLoadingTransactions // ignore: cast_nullable_to_non_nullable
as bool,transactions: null == transactions ? _self._transactions : transactions // ignore: cast_nullable_to_non_nullable
as List<WalletTransactionEntity>,
as bool,transactionPages: null == transactionPages ? _self._transactionPages : transactionPages // ignore: cast_nullable_to_non_nullable
as List<List<WalletTransactionEntity>>,nextCursor: freezed == nextCursor ? _self.nextCursor : nextCursor // ignore: cast_nullable_to_non_nullable
as String?,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
as bool,currentPage: null == currentPage ? _self.currentPage : currentPage // ignore: cast_nullable_to_non_nullable
as int,
));
}

View File

@@ -7,6 +7,7 @@ import 'package:sf_localizations/sf_localizations.dart';
import '../../card_colors.dart';
import '../child_wallet/child_data_provider.dart';
import 'deposit_success_provider.dart';
import 'deposit_view_model.dart';
class DepositScreen extends ConsumerWidget {
@@ -33,6 +34,7 @@ class DepositScreen extends ConsumerWidget {
message: context.translate(I18n.walletMoveSuccess),
type: MessageType.success,
);
ref.read(depositSuccessProvider(childId).notifier).trigger();
navigation.goBack();
}
if (next.errorMessage.isNotEmpty &&
@@ -122,106 +124,6 @@ class DepositScreen extends ConsumerWidget {
),
],
),
// Container(
// decoration: BoxDecoration(
// color: theme.getColorFor(ThemeCode.backgroundPrimary),
// borderRadius: const BorderRadius.all(Radius.circular(20)),
// ),
// padding: const EdgeInsets.all(10),
// child: Column(
// spacing: 10,
// children: [
// Text(
// context.translate(I18n.depositReason),
// style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
// ),
// Text(context.translate(I18n.watchInfo)),
// CheckboxListTile(
// contentPadding: EdgeInsets.zero,
// title: Text(context.translate(I18n.depositReasonWeekly)),
// controlAffinity: ListTileControlAffinity.leading,
// value: viewState.reason == 'weekly',
// onChanged: (_) => viewModel.selectReason('weekly'),
// activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
// ),
// CheckboxListTile(
// contentPadding: EdgeInsets.zero,
// title: Text(context.translate(I18n.depositReasonGoalMet)),
// controlAffinity: ListTileControlAffinity.leading,
// value: viewState.reason == 'goal',
// onChanged: (_) => viewModel.selectReason('goal'),
// activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
// ),
// CheckboxListTile(
// contentPadding: EdgeInsets.zero,
// title: Text(context.translate(I18n.depositReasonExtraordinary)),
// controlAffinity: ListTileControlAffinity.leading,
// value: viewState.reason == 'extraordinary',
// onChanged: (_) => viewModel.selectReason('extraordinary'),
// activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
// ),
// CheckboxListTile(
// contentPadding: EdgeInsets.zero,
// title: Text(context.translate(I18n.depositReasonOther)),
// controlAffinity: ListTileControlAffinity.leading,
// value: viewState.reason == 'other',
// onChanged: (_) => viewModel.selectReason('other'),
// activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
// ),
// CustomTextField(
// controller: viewModel.messageController,
// lines: 3,
// length: 150,
// label: context.translate(I18n.depositMessageLabel, args: {'name': childName}),
// hint: context.translate(I18n.allowanceMessageHint),
// ),
// Align(
// alignment: Alignment.topLeft,
// child: Text(context.translate(I18n.allowanceMaxChars, args: {'count': '150'})),
// ),
// ],
// ),
// ),
// Container(
// decoration: BoxDecoration(
// color: theme.getColorFor(ThemeCode.backgroundPrimary),
// borderRadius: const BorderRadius.all(Radius.circular(20)),
// ),
// padding: const EdgeInsets.all(10),
// child: Column(
// spacing: 10,
// children: [
// Text(
// context.translate(I18n.depositWhenSend),
// style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
// ),
// Text(context.translate(I18n.watchInfo)),
// CheckboxListTile(
// contentPadding: EdgeInsets.zero,
// title: Text(context.translate(I18n.depositNow)),
// controlAffinity: ListTileControlAffinity.leading,
// value: !viewState.program,
// onChanged: (_) {
// if (viewState.program) viewModel.toggleProgram();
// },
// activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
// ),
// CheckboxListTile(
// contentPadding: EdgeInsets.zero,
// title: Text(context.translate(I18n.depositSchedule)),
// controlAffinity: ListTileControlAffinity.leading,
// value: viewState.program,
// onChanged: (_) {
// if (!viewState.program) viewModel.toggleProgram();
// },
// activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
// ),
// if (viewState.program) CustomTextField(),
// ],
// ),
// ),
],
);
}

View File

@@ -0,0 +1,22 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
final depositSuccessProvider =
NotifierProvider.family<DepositSuccessNotifier, bool, String>(
DepositSuccessNotifier.new,
);
class DepositSuccessNotifier extends Notifier<bool> {
final String childId;
DepositSuccessNotifier(this.childId);
@override
bool build() => false;
void trigger() {
state = true;
}
void reset() {
state = false;
}
}

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_shared/sf_shared.dart';
import '../../presentation/state/home_view_model.dart';
import '../child_wallet/child_data_provider.dart';
import 'deposit_view_state.dart';
@@ -100,8 +99,7 @@ class DepositViewModel extends Notifier<DepositViewState> {
);
if (!ref.mounted) return;
ref.read(childDataProvider(childId).notifier).load();
ref.read(homeViewModelProvider.notifier).refreshChildWallet(childId);
ref.read(walletRefreshProvider.notifier).refresh();
await ref.read(parentWalletBalanceProvider.notifier).refresh();
if (!ref.mounted) return;
state = state.copyWith(isSubmitting: false, success: true);

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:navigation/navigation.dart';
import 'presentation/edit_child_profile_screen.dart';
class EditChildProfileBuilder {
const EditChildProfileBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final childWalletId = state.pathParameters['childWalletId'] ?? '';
final navigationContract = GetIt.I<NavigationContract>();
return MaterialPage(
key: state.pageKey,
child: EditChildProfileScreen(
childId: childWalletId,
navigation: navigationContract,
),
);
}
}

View File

@@ -0,0 +1,191 @@
import 'package:auth/auth.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'edit_child_profile_view_model.dart';
class EditChildProfileScreen extends ConsumerWidget {
final String childId;
final NavigationContract navigation;
const EditChildProfileScreen({
super.key,
required this.childId,
required this.navigation,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewState = ref.watch(editChildProfileViewModelProvider(childId));
final viewModel =
ref.read(editChildProfileViewModelProvider(childId).notifier);
ref.listen(editChildProfileViewModelProvider(childId), (prev, next) {
if (next.saveSuccess && !(prev?.saveSuccess ?? false)) {
showTopSnackbar(
context,
message: context.translate(I18n.editChildProfileSaveSuccess),
type: MessageType.success,
);
navigation.goBack();
}
if (next.errorMessage.isNotEmpty &&
!next.showPin &&
next.errorMessage != (prev?.errorMessage ?? '')) {
showTopSnackbar(
context,
message: next.errorMessage,
type: MessageType.error,
);
}
});
if (viewState.isLoading) {
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: const Center(child: AppLoadingIndicator()),
);
}
if (viewState.showPin) {
return _buildPinScaffold(context, theme, viewState, viewModel);
}
return _buildFormScaffold(context, theme, viewState, viewModel);
}
Widget _buildPinScaffold(
BuildContext context,
ThemePort theme,
EditChildProfileViewState viewState,
EditChildProfileViewModel viewModel,
) {
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
appBar: AppBar(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: theme.getColorFor(ThemeCode.textPrimary),
),
onPressed: viewModel.cancelPin,
),
),
body: SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: ScaPinView(
title: context.translate(I18n.scaPinEnter),
pin: viewState.pin,
isProcessing: viewState.isSigning || viewState.isSaving,
processingText: context.translate(I18n.scaSigning),
canSubmit: viewModel.canSubmitPin,
submitText: context.translate(I18n.scaConnect),
clearPinText: context.translate(I18n.scaClearPin),
onDigitPressed: viewModel.onDigitPressed,
onBackspacePressed: viewModel.onBackspacePressed,
onClearPin: viewModel.onClearPin,
onSubmit: () => viewModel.onPinSubmit(),
),
),
),
if (viewState.errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
viewState.errorMessage,
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
),
),
TextButton(
onPressed: viewModel.cancelPin,
child: Text(context.translate(I18n.cancel)),
),
],
),
),
);
}
Widget _buildFormScaffold(
BuildContext context,
ThemePort theme,
EditChildProfileViewState viewState,
EditChildProfileViewModel viewModel,
) {
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
appBar: AppBar(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: theme.getColorFor(ThemeCode.textPrimary),
),
onPressed: () => navigation.goBack(),
),
title: Text(
context.translate(I18n.editChildProfileTitle),
style: TextStyle(color: theme.getColorFor(ThemeCode.textPrimary)),
),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Expanded(
child: ListView(
children: [
TextFormField(
initialValue: viewState.firstName,
decoration: InputDecoration(
labelText: context.translate(I18n.firstNameLabel),
border: const OutlineInputBorder(),
),
onChanged: viewModel.setFirstName,
),
const SizedBox(height: 16),
TextFormField(
initialValue: viewState.lastName,
decoration: InputDecoration(
labelText: context.translate(I18n.lastNameLabel),
border: const OutlineInputBorder(),
),
onChanged: viewModel.setLastName,
),
const SizedBox(height: 16),
TextFormField(
initialValue: viewState.address,
decoration: InputDecoration(
labelText: context.translate(I18n.streetLabel),
border: const OutlineInputBorder(),
),
onChanged: viewModel.setAddress,
),
],
),
),
PrimaryButton(
onPressed: () => viewModel.requestPin(),
text: context.translate(I18n.profileSettingsSave),
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
TextButton(
onPressed: () => navigation.goBack(),
child: Text(context.translate(I18n.cancel)),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,155 @@
import 'dart:convert';
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 '../../child_wallet/child_data_provider.dart';
export 'edit_child_profile_view_state.dart';
import 'edit_child_profile_view_state.dart';
final editChildProfileViewModelProvider = NotifierProvider.autoDispose
.family<EditChildProfileViewModel, EditChildProfileViewState, String>(
EditChildProfileViewModel.new,
);
class EditChildProfileViewModel extends Notifier<EditChildProfileViewState> {
final String childId;
EditChildProfileViewModel(this.childId);
late TreezorWalletConnectionService _connectionService;
late TreezorWalletSignatureService _signatureService;
@override
EditChildProfileViewState build() {
_connectionService = GetIt.I<TreezorWalletConnectionService>();
_signatureService = GetIt.I<TreezorWalletSignatureService>();
Future.microtask(() => _loadChildProfile());
return const EditChildProfileViewState();
}
Future<void> _loadChildProfile() async {
state = state.copyWith(isLoading: true, errorMessage: '');
try {
final childData = ref.read(childDataProvider(childId));
var childProfile = childData.childProfile;
if (childProfile == null) {
final profiles =
await ref.read(userRepositoryProvider).getChildProfiles();
childProfile = profiles.where((p) => p.id == childId).firstOrNull;
if (childProfile == null) {
state = state.copyWith(
isLoading: false,
errorMessage: 'Child profile not found',
);
return;
}
if (!ref.mounted) return;
}
state = state.copyWith(
isLoading: false,
firstName: childProfile.firstName,
lastName: childProfile.lastName,
address: childProfile.address,
childProfileId: childProfile.id,
treezorUserId: childProfile.treezorUserId,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
}
}
void setFirstName(String value) =>
state = state.copyWith(firstName: value, saveSuccess: false);
void setLastName(String value) =>
state = state.copyWith(lastName: value, saveSuccess: false);
void setAddress(String value) =>
state = state.copyWith(address: value, saveSuccess: false);
void requestPin() {
state = state.copyWith(showPin: true, pin: '', errorMessage: '');
}
void cancelPin() {
state = state.copyWith(showPin: false, pin: '');
}
void onDigitPressed(String digit) {
if (state.pin.length >= 6) return;
state = state.copyWith(pin: state.pin + digit, errorMessage: '');
}
void onBackspacePressed() {
if (state.pin.isEmpty) return;
state = state.copyWith(pin: state.pin.substring(0, state.pin.length - 1));
}
void onClearPin() {
state = state.copyWith(pin: '');
}
bool get canSubmitPin => state.pin.length == 6;
Future<void> onPinSubmit() async {
state = state.copyWith(isSigning: true, errorMessage: '');
try {
await _connectionService.connectWithPin(loginPin: state.pin);
if (!ref.mounted) return;
final scaProof = await _generateScaProof();
if (!ref.mounted) return;
state = state.copyWith(isSigning: false, isSaving: true, pin: '');
await ref.read(userRepositoryProvider).updateChildProfile(
childProfileId: state.childProfileId,
scaProof: scaProof,
firstName: state.firstName,
lastName: state.lastName,
address: state.address,
);
if (!ref.mounted) return;
ref.read(walletRefreshProvider.notifier).refresh();
state = state.copyWith(
isSaving: false,
saveSuccess: true,
showPin: false,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isSigning: false,
isSaving: false,
pin: '',
errorMessage: e.toString(),
);
}
}
Future<String> _generateScaProof() async {
final url =
'https://savefamily.sandbox.treezor.co/v1/users/${state.treezorUserId}';
final scaBody = <String, dynamic>{
'firstName': state.firstName,
'lastName': state.lastName,
};
return _signatureService.generateJwsWithPin(
message: '',
input: jsonEncode({'url': url, 'body': scaBody}),
pin: state.pin,
);
}
}

View File

@@ -0,0 +1,21 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'edit_child_profile_view_state.freezed.dart';
@freezed
abstract class EditChildProfileViewState with _$EditChildProfileViewState {
const factory EditChildProfileViewState({
@Default(true) bool isLoading,
@Default(false) bool isSaving,
@Default(false) bool isSigning,
@Default(false) bool showPin,
@Default('') String pin,
@Default('') String firstName,
@Default('') String lastName,
@Default('') String address,
@Default('') String childProfileId,
@Default('') String treezorUserId,
@Default('') String errorMessage,
@Default(false) bool saveSuccess,
}) = _EditChildProfileViewState;
}

View File

@@ -0,0 +1,304 @@
// 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 'edit_child_profile_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$EditChildProfileViewState {
bool get isLoading; bool get isSaving; bool get isSigning; bool get showPin; String get pin; String get firstName; String get lastName; String get address; String get childProfileId; String get treezorUserId; String get errorMessage; bool get saveSuccess;
/// Create a copy of EditChildProfileViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$EditChildProfileViewStateCopyWith<EditChildProfileViewState> get copyWith => _$EditChildProfileViewStateCopyWithImpl<EditChildProfileViewState>(this as EditChildProfileViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is EditChildProfileViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.showPin, showPin) || other.showPin == showPin)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.address, address) || other.address == address)&&(identical(other.childProfileId, childProfileId) || other.childProfileId == childProfileId)&&(identical(other.treezorUserId, treezorUserId) || other.treezorUserId == treezorUserId)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isSaving,isSigning,showPin,pin,firstName,lastName,address,childProfileId,treezorUserId,errorMessage,saveSuccess);
@override
String toString() {
return 'EditChildProfileViewState(isLoading: $isLoading, isSaving: $isSaving, isSigning: $isSigning, showPin: $showPin, pin: $pin, firstName: $firstName, lastName: $lastName, address: $address, childProfileId: $childProfileId, treezorUserId: $treezorUserId, errorMessage: $errorMessage, saveSuccess: $saveSuccess)';
}
}
/// @nodoc
abstract mixin class $EditChildProfileViewStateCopyWith<$Res> {
factory $EditChildProfileViewStateCopyWith(EditChildProfileViewState value, $Res Function(EditChildProfileViewState) _then) = _$EditChildProfileViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, bool isSaving, bool isSigning, bool showPin, String pin, String firstName, String lastName, String address, String childProfileId, String treezorUserId, String errorMessage, bool saveSuccess
});
}
/// @nodoc
class _$EditChildProfileViewStateCopyWithImpl<$Res>
implements $EditChildProfileViewStateCopyWith<$Res> {
_$EditChildProfileViewStateCopyWithImpl(this._self, this._then);
final EditChildProfileViewState _self;
final $Res Function(EditChildProfileViewState) _then;
/// Create a copy of EditChildProfileViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isSaving = null,Object? isSigning = null,Object? showPin = null,Object? pin = null,Object? firstName = null,Object? lastName = null,Object? address = null,Object? childProfileId = null,Object? treezorUserId = null,Object? errorMessage = null,Object? saveSuccess = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
as bool,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,showPin: null == showPin ? _self.showPin : showPin // ignore: cast_nullable_to_non_nullable
as bool,pin: null == pin ? _self.pin : pin // 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,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,childProfileId: null == childProfileId ? _self.childProfileId : childProfileId // ignore: cast_nullable_to_non_nullable
as String,treezorUserId: null == treezorUserId ? _self.treezorUserId : treezorUserId // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [EditChildProfileViewState].
extension EditChildProfileViewStatePatterns on EditChildProfileViewState {
/// 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( _EditChildProfileViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _EditChildProfileViewState() 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( _EditChildProfileViewState value) $default,){
final _that = this;
switch (_that) {
case _EditChildProfileViewState():
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( _EditChildProfileViewState value)? $default,){
final _that = this;
switch (_that) {
case _EditChildProfileViewState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, bool isSaving, bool isSigning, bool showPin, String pin, String firstName, String lastName, String address, String childProfileId, String treezorUserId, String errorMessage, bool saveSuccess)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _EditChildProfileViewState() when $default != null:
return $default(_that.isLoading,_that.isSaving,_that.isSigning,_that.showPin,_that.pin,_that.firstName,_that.lastName,_that.address,_that.childProfileId,_that.treezorUserId,_that.errorMessage,_that.saveSuccess);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, bool isSaving, bool isSigning, bool showPin, String pin, String firstName, String lastName, String address, String childProfileId, String treezorUserId, String errorMessage, bool saveSuccess) $default,) {final _that = this;
switch (_that) {
case _EditChildProfileViewState():
return $default(_that.isLoading,_that.isSaving,_that.isSigning,_that.showPin,_that.pin,_that.firstName,_that.lastName,_that.address,_that.childProfileId,_that.treezorUserId,_that.errorMessage,_that.saveSuccess);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, bool isSaving, bool isSigning, bool showPin, String pin, String firstName, String lastName, String address, String childProfileId, String treezorUserId, String errorMessage, bool saveSuccess)? $default,) {final _that = this;
switch (_that) {
case _EditChildProfileViewState() when $default != null:
return $default(_that.isLoading,_that.isSaving,_that.isSigning,_that.showPin,_that.pin,_that.firstName,_that.lastName,_that.address,_that.childProfileId,_that.treezorUserId,_that.errorMessage,_that.saveSuccess);case _:
return null;
}
}
}
/// @nodoc
class _EditChildProfileViewState implements EditChildProfileViewState {
const _EditChildProfileViewState({this.isLoading = true, this.isSaving = false, this.isSigning = false, this.showPin = false, this.pin = '', this.firstName = '', this.lastName = '', this.address = '', this.childProfileId = '', this.treezorUserId = '', this.errorMessage = '', this.saveSuccess = false});
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isSaving;
@override@JsonKey() final bool isSigning;
@override@JsonKey() final bool showPin;
@override@JsonKey() final String pin;
@override@JsonKey() final String firstName;
@override@JsonKey() final String lastName;
@override@JsonKey() final String address;
@override@JsonKey() final String childProfileId;
@override@JsonKey() final String treezorUserId;
@override@JsonKey() final String errorMessage;
@override@JsonKey() final bool saveSuccess;
/// Create a copy of EditChildProfileViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$EditChildProfileViewStateCopyWith<_EditChildProfileViewState> get copyWith => __$EditChildProfileViewStateCopyWithImpl<_EditChildProfileViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _EditChildProfileViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.showPin, showPin) || other.showPin == showPin)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.address, address) || other.address == address)&&(identical(other.childProfileId, childProfileId) || other.childProfileId == childProfileId)&&(identical(other.treezorUserId, treezorUserId) || other.treezorUserId == treezorUserId)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isSaving,isSigning,showPin,pin,firstName,lastName,address,childProfileId,treezorUserId,errorMessage,saveSuccess);
@override
String toString() {
return 'EditChildProfileViewState(isLoading: $isLoading, isSaving: $isSaving, isSigning: $isSigning, showPin: $showPin, pin: $pin, firstName: $firstName, lastName: $lastName, address: $address, childProfileId: $childProfileId, treezorUserId: $treezorUserId, errorMessage: $errorMessage, saveSuccess: $saveSuccess)';
}
}
/// @nodoc
abstract mixin class _$EditChildProfileViewStateCopyWith<$Res> implements $EditChildProfileViewStateCopyWith<$Res> {
factory _$EditChildProfileViewStateCopyWith(_EditChildProfileViewState value, $Res Function(_EditChildProfileViewState) _then) = __$EditChildProfileViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, bool isSaving, bool isSigning, bool showPin, String pin, String firstName, String lastName, String address, String childProfileId, String treezorUserId, String errorMessage, bool saveSuccess
});
}
/// @nodoc
class __$EditChildProfileViewStateCopyWithImpl<$Res>
implements _$EditChildProfileViewStateCopyWith<$Res> {
__$EditChildProfileViewStateCopyWithImpl(this._self, this._then);
final _EditChildProfileViewState _self;
final $Res Function(_EditChildProfileViewState) _then;
/// Create a copy of EditChildProfileViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isSaving = null,Object? isSigning = null,Object? showPin = null,Object? pin = null,Object? firstName = null,Object? lastName = null,Object? address = null,Object? childProfileId = null,Object? treezorUserId = null,Object? errorMessage = null,Object? saveSuccess = null,}) {
return _then(_EditChildProfileViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
as bool,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,showPin: null == showPin ? _self.showPin : showPin // ignore: cast_nullable_to_non_nullable
as bool,pin: null == pin ? _self.pin : pin // 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,address: null == address ? _self.address : address // ignore: cast_nullable_to_non_nullable
as String,childProfileId: null == childProfileId ? _self.childProfileId : childProfileId // ignore: cast_nullable_to_non_nullable
as String,treezorUserId: null == treezorUserId ? _self.treezorUserId : treezorUserId // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_shared/sf_shared.dart';
import '../../presentation/state/home_view_model.dart';
import '../child_wallet/child_data_provider.dart';
import 'extract_view_state.dart';
@@ -92,8 +91,7 @@ class ExtractViewModel extends Notifier<ExtractViewState> {
);
if (!ref.mounted) return;
ref.read(childDataProvider(childId).notifier).load();
ref.read(homeViewModelProvider.notifier).refreshChildWallet(childId);
ref.read(walletRefreshProvider.notifier).refresh();
ref.read(parentWalletBalanceProvider.notifier).applyOptimisticPayin(amount);
state = state.copyWith(isSubmitting: false, success: true);
} catch (e) {

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:navigation/navigation.dart';
import 'renew_card_screen.dart';
class RenewCardBuilder {
const RenewCardBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final childWalletId = state.pathParameters['childWalletId'] ?? '';
final navigationContract = GetIt.I<NavigationContract>();
return MaterialPage(
key: state.pageKey,
child: RenewCardScreen(
childId: childWalletId,
navigation: navigationContract,
),
);
}
}

View File

@@ -0,0 +1,162 @@
import 'package:auth/auth.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'renew_card_view_model.dart';
import 'renew_card_view_state.dart';
class RenewCardScreen extends ConsumerWidget {
final String childId;
final NavigationContract navigation;
const RenewCardScreen({
super.key,
required this.childId,
required this.navigation,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewState = ref.watch(renewCardViewModelProvider(childId));
final viewModel = ref.read(renewCardViewModelProvider(childId).notifier);
ref.listen(renewCardViewModelProvider(childId), (prev, next) {
if (next.success && !(prev?.success ?? false)) {
showTopSnackbar(
context,
message: context.translate(I18n.renewCardSuccess),
type: MessageType.success,
);
navigation.goBack();
return;
}
if (next.errorMessage.isNotEmpty &&
next.errorMessage != (prev?.errorMessage ?? '')) {
showTopSnackbar(
context,
message: context.translate(I18n.renewCardError),
type: MessageType.error,
);
}
});
if (viewState.isLoading) {
return const Scaffold(body: Center(child: AppLoadingIndicator()));
}
if (viewState.errorMessage.isNotEmpty && viewState.childProfile == null) {
return Scaffold(
body: Center(child: Text('Error: ${viewState.errorMessage}')),
);
}
if (viewState.step == RenewCardStep.scaPin) {
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
appBar: AppBar(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: theme.getColorFor(ThemeCode.textPrimary),
),
onPressed: viewModel.goBackToToken,
),
),
body: SafeArea(
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: ScaPinView(
title: context.translate(I18n.renewCardPinTitle),
pin: viewState.pin,
isProcessing:
viewState.isSigning || viewState.isSubmitting,
processingText: context.translate(I18n.scaSigning),
canSubmit: viewModel.canSubmitPin,
submitText: context.translate(I18n.scaConnect),
clearPinText: context.translate(I18n.scaClearPin),
onDigitPressed: viewModel.onDigitPressed,
onBackspacePressed: viewModel.onBackspacePressed,
onClearPin: viewModel.onClearPin,
onSubmit: () => viewModel.onPinSubmit(),
),
),
),
TextButton(
onPressed: () => navigation.goBack(),
child: Text(context.translate(I18n.cancel)),
),
],
),
),
);
}
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
appBar: AppBar(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: theme.getColorFor(ThemeCode.textPrimary),
),
onPressed: () => navigation.goBack(),
),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Text(
context.translate(I18n.renewCardTitle),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text(
context.translate(I18n.renewCardTokenHint),
style: const TextStyle(fontSize: 16),
),
const SizedBox(height: 20),
CustomTextField(
controller: viewModel.cardTokenController,
hint: 'XXXXXXXXXX',
),
const Spacer(),
SizedBox(
width: double.infinity,
child: PrimaryButton(
onPressed: viewState.cardToken.isEmpty
? null
: viewModel.onCardTokenSubmit,
text: context.translate(I18n.cardPinNext),
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
),
const SizedBox(height: 8),
Center(
child: TextButton(
onPressed: () => navigation.goBack(),
child: Text(context.translate(I18n.cancel)),
),
),
const SizedBox(height: 16),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,141 @@
import 'dart:convert';
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 '../child_wallet/child_data_provider.dart';
import 'renew_card_view_state.dart';
final renewCardViewModelProvider = NotifierProvider.autoDispose
.family<RenewCardViewModel, RenewCardViewState, String>(
RenewCardViewModel.new,
);
class RenewCardViewModel extends Notifier<RenewCardViewState> {
final String childId;
RenewCardViewModel(this.childId);
late final TextEditingController cardTokenController;
late final TreezorWalletConnectionService _connectionService;
late final TreezorWalletSignatureService _signatureService;
@override
RenewCardViewState build() {
_connectionService = GetIt.I<TreezorWalletConnectionService>();
_signatureService = GetIt.I<TreezorWalletSignatureService>();
cardTokenController = TextEditingController();
cardTokenController.addListener(_onTokenChanged);
ref.onDispose(_disposeControllers);
ref.listen(childDataProvider(childId), (prev, next) {
state = state.copyWith(
isLoading: next.isLoading,
childProfile: next.childProfile,
childWallet: next.childWallet,
device: next.device,
errorMessage: next.errorMessage,
);
});
final data = ref.read(childDataProvider(childId));
return RenewCardViewState(
isLoading: data.isLoading,
childProfile: data.childProfile,
childWallet: data.childWallet,
device: data.device,
errorMessage: data.errorMessage,
);
}
void _disposeControllers() {
cardTokenController.removeListener(_onTokenChanged);
cardTokenController.dispose();
}
void _onTokenChanged() {
state = state.copyWith(cardToken: cardTokenController.text.trim());
}
void onCardTokenSubmit() {
if (state.cardToken.isEmpty) return;
state = state.copyWith(step: RenewCardStep.scaPin, pin: '', errorMessage: '');
}
void goBackToToken() {
state = state.copyWith(step: RenewCardStep.cardToken, pin: '');
}
void onDigitPressed(String digit) {
if (state.pin.length >= 6) return;
state = state.copyWith(pin: state.pin + digit, errorMessage: '');
}
void onBackspacePressed() {
if (state.pin.isEmpty) return;
state = state.copyWith(pin: state.pin.substring(0, state.pin.length - 1));
}
void onClearPin() {
state = state.copyWith(pin: '');
}
bool get canSubmitPin => state.pin.length == 6;
Future<void> onPinSubmit() async {
final childProfile = state.childProfile;
final cardToken = state.cardToken;
if (childProfile == null || cardToken.isEmpty) return;
state = state.copyWith(isSigning: true, errorMessage: '');
try {
debugPrint('[RenewCard] connecting with SCA PIN...');
await _connectionService.connectWithPin(loginPin: state.pin);
if (!ref.mounted) return;
debugPrint('[RenewCard] SCA connection successful');
final url =
'https://savefamily.sandbox.treezor.co/v1/cards/$cardToken/public-token-activation';
final scaInput = jsonEncode({
'url': url,
'body': <String, dynamic>{},
});
debugPrint('[RenewCard] scaInput: $scaInput');
final scaProof = await _signatureService.generateJwsWithPin(
message: '',
input: scaInput,
pin: state.pin,
);
debugPrint('[RenewCard] scaProof: $scaProof');
if (!ref.mounted) return;
state = state.copyWith(isSigning: false, isSubmitting: true, pin: '');
debugPrint('[RenewCard] calling backend renewCard childProfileId=${childProfile.id} publicToken=$cardToken');
await ref.read(treezorRepositoryProvider).renewCard(
childProfileId: childProfile.id,
publicToken: cardToken,
scaProof: scaProof,
);
debugPrint('[RenewCard] renewCard SUCCESS');
if (!ref.mounted) return;
ref.read(walletRefreshProvider.notifier).refresh();
state = state.copyWith(isSubmitting: false, success: true);
} catch (e) {
debugPrint('[RenewCard] ERROR: $e');
if (!ref.mounted) return;
state = state.copyWith(
isSigning: false,
isSubmitting: false,
pin: '',
errorMessage: e.toString(),
);
}
}
}

View File

@@ -0,0 +1,23 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:sf_shared/sf_shared.dart';
part 'renew_card_view_state.freezed.dart';
enum RenewCardStep { cardToken, scaPin }
@freezed
abstract class RenewCardViewState with _$RenewCardViewState {
const factory RenewCardViewState({
@Default(true) bool isLoading,
ChildProfileEntity? childProfile,
ChildWalletEntity? childWallet,
DeviceEntity? device,
@Default('') String cardToken,
@Default('') String pin,
@Default(RenewCardStep.cardToken) RenewCardStep step,
@Default(false) bool isSigning,
@Default(false) bool isSubmitting,
@Default(false) bool success,
@Default('') String errorMessage,
}) = _RenewCardViewState;
}

View File

@@ -0,0 +1,373 @@
// 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 'renew_card_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$RenewCardViewState {
bool get isLoading; ChildProfileEntity? get childProfile; ChildWalletEntity? get childWallet; DeviceEntity? get device; String get cardToken; String get pin; RenewCardStep get step; bool get isSigning; bool get isSubmitting; bool get success; String get errorMessage;
/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$RenewCardViewStateCopyWith<RenewCardViewState> get copyWith => _$RenewCardViewStateCopyWithImpl<RenewCardViewState>(this as RenewCardViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RenewCardViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardToken, cardToken) || other.cardToken == cardToken)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.step, step) || other.step == step)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.success, success) || other.success == success)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,childProfile,childWallet,device,cardToken,pin,step,isSigning,isSubmitting,success,errorMessage);
@override
String toString() {
return 'RenewCardViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardToken: $cardToken, pin: $pin, step: $step, isSigning: $isSigning, isSubmitting: $isSubmitting, success: $success, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class $RenewCardViewStateCopyWith<$Res> {
factory $RenewCardViewStateCopyWith(RenewCardViewState value, $Res Function(RenewCardViewState) _then) = _$RenewCardViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardToken, String pin, RenewCardStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage
});
$ChildProfileEntityCopyWith<$Res>? get childProfile;$ChildWalletEntityCopyWith<$Res>? get childWallet;$DeviceEntityCopyWith<$Res>? get device;
}
/// @nodoc
class _$RenewCardViewStateCopyWithImpl<$Res>
implements $RenewCardViewStateCopyWith<$Res> {
_$RenewCardViewStateCopyWithImpl(this._self, this._then);
final RenewCardViewState _self;
final $Res Function(RenewCardViewState) _then;
/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardToken = null,Object? pin = null,Object? step = null,Object? isSigning = null,Object? isSubmitting = null,Object? success = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,childProfile: freezed == childProfile ? _self.childProfile : childProfile // ignore: cast_nullable_to_non_nullable
as ChildProfileEntity?,childWallet: freezed == childWallet ? _self.childWallet : childWallet // ignore: cast_nullable_to_non_nullable
as ChildWalletEntity?,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable
as DeviceEntity?,cardToken: null == cardToken ? _self.cardToken : cardToken // ignore: cast_nullable_to_non_nullable
as String,pin: null == pin ? _self.pin : pin // ignore: cast_nullable_to_non_nullable
as String,step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable
as RenewCardStep,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileEntityCopyWith<$Res>? get childProfile {
if (_self.childProfile == null) {
return null;
}
return $ChildProfileEntityCopyWith<$Res>(_self.childProfile!, (value) {
return _then(_self.copyWith(childProfile: value));
});
}/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildWalletEntityCopyWith<$Res>? get childWallet {
if (_self.childWallet == null) {
return null;
}
return $ChildWalletEntityCopyWith<$Res>(_self.childWallet!, (value) {
return _then(_self.copyWith(childWallet: value));
});
}/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DeviceEntityCopyWith<$Res>? get device {
if (_self.device == null) {
return null;
}
return $DeviceEntityCopyWith<$Res>(_self.device!, (value) {
return _then(_self.copyWith(device: value));
});
}
}
/// Adds pattern-matching-related methods to [RenewCardViewState].
extension RenewCardViewStatePatterns on RenewCardViewState {
/// 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( _RenewCardViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _RenewCardViewState() 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( _RenewCardViewState value) $default,){
final _that = this;
switch (_that) {
case _RenewCardViewState():
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( _RenewCardViewState value)? $default,){
final _that = this;
switch (_that) {
case _RenewCardViewState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardToken, String pin, RenewCardStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _RenewCardViewState() when $default != null:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardToken,_that.pin,_that.step,_that.isSigning,_that.isSubmitting,_that.success,_that.errorMessage);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardToken, String pin, RenewCardStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _RenewCardViewState():
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardToken,_that.pin,_that.step,_that.isSigning,_that.isSubmitting,_that.success,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardToken, String pin, RenewCardStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _RenewCardViewState() when $default != null:
return $default(_that.isLoading,_that.childProfile,_that.childWallet,_that.device,_that.cardToken,_that.pin,_that.step,_that.isSigning,_that.isSubmitting,_that.success,_that.errorMessage);case _:
return null;
}
}
}
/// @nodoc
class _RenewCardViewState implements RenewCardViewState {
const _RenewCardViewState({this.isLoading = true, this.childProfile, this.childWallet, this.device, this.cardToken = '', this.pin = '', this.step = RenewCardStep.cardToken, this.isSigning = false, this.isSubmitting = false, this.success = false, this.errorMessage = ''});
@override@JsonKey() final bool isLoading;
@override final ChildProfileEntity? childProfile;
@override final ChildWalletEntity? childWallet;
@override final DeviceEntity? device;
@override@JsonKey() final String cardToken;
@override@JsonKey() final String pin;
@override@JsonKey() final RenewCardStep step;
@override@JsonKey() final bool isSigning;
@override@JsonKey() final bool isSubmitting;
@override@JsonKey() final bool success;
@override@JsonKey() final String errorMessage;
/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$RenewCardViewStateCopyWith<_RenewCardViewState> get copyWith => __$RenewCardViewStateCopyWithImpl<_RenewCardViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RenewCardViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.childProfile, childProfile) || other.childProfile == childProfile)&&(identical(other.childWallet, childWallet) || other.childWallet == childWallet)&&(identical(other.device, device) || other.device == device)&&(identical(other.cardToken, cardToken) || other.cardToken == cardToken)&&(identical(other.pin, pin) || other.pin == pin)&&(identical(other.step, step) || other.step == step)&&(identical(other.isSigning, isSigning) || other.isSigning == isSigning)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.success, success) || other.success == success)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,childProfile,childWallet,device,cardToken,pin,step,isSigning,isSubmitting,success,errorMessage);
@override
String toString() {
return 'RenewCardViewState(isLoading: $isLoading, childProfile: $childProfile, childWallet: $childWallet, device: $device, cardToken: $cardToken, pin: $pin, step: $step, isSigning: $isSigning, isSubmitting: $isSubmitting, success: $success, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class _$RenewCardViewStateCopyWith<$Res> implements $RenewCardViewStateCopyWith<$Res> {
factory _$RenewCardViewStateCopyWith(_RenewCardViewState value, $Res Function(_RenewCardViewState) _then) = __$RenewCardViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, ChildProfileEntity? childProfile, ChildWalletEntity? childWallet, DeviceEntity? device, String cardToken, String pin, RenewCardStep step, bool isSigning, bool isSubmitting, bool success, String errorMessage
});
@override $ChildProfileEntityCopyWith<$Res>? get childProfile;@override $ChildWalletEntityCopyWith<$Res>? get childWallet;@override $DeviceEntityCopyWith<$Res>? get device;
}
/// @nodoc
class __$RenewCardViewStateCopyWithImpl<$Res>
implements _$RenewCardViewStateCopyWith<$Res> {
__$RenewCardViewStateCopyWithImpl(this._self, this._then);
final _RenewCardViewState _self;
final $Res Function(_RenewCardViewState) _then;
/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? childProfile = freezed,Object? childWallet = freezed,Object? device = freezed,Object? cardToken = null,Object? pin = null,Object? step = null,Object? isSigning = null,Object? isSubmitting = null,Object? success = null,Object? errorMessage = null,}) {
return _then(_RenewCardViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,childProfile: freezed == childProfile ? _self.childProfile : childProfile // ignore: cast_nullable_to_non_nullable
as ChildProfileEntity?,childWallet: freezed == childWallet ? _self.childWallet : childWallet // ignore: cast_nullable_to_non_nullable
as ChildWalletEntity?,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable
as DeviceEntity?,cardToken: null == cardToken ? _self.cardToken : cardToken // ignore: cast_nullable_to_non_nullable
as String,pin: null == pin ? _self.pin : pin // ignore: cast_nullable_to_non_nullable
as String,step: null == step ? _self.step : step // ignore: cast_nullable_to_non_nullable
as RenewCardStep,isSigning: null == isSigning ? _self.isSigning : isSigning // ignore: cast_nullable_to_non_nullable
as bool,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,success: null == success ? _self.success : success // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildProfileEntityCopyWith<$Res>? get childProfile {
if (_self.childProfile == null) {
return null;
}
return $ChildProfileEntityCopyWith<$Res>(_self.childProfile!, (value) {
return _then(_self.copyWith(childProfile: value));
});
}/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ChildWalletEntityCopyWith<$Res>? get childWallet {
if (_self.childWallet == null) {
return null;
}
return $ChildWalletEntityCopyWith<$Res>(_self.childWallet!, (value) {
return _then(_self.copyWith(childWallet: value));
});
}/// Create a copy of RenewCardViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DeviceEntityCopyWith<$Res>? get device {
if (_self.device == null) {
return null;
}
return $DeviceEntityCopyWith<$Res>(_self.device!, (value) {
return _then(_self.copyWith(device: value));
});
}
}
// dart format on

View File

@@ -37,32 +37,20 @@ class HomeScreen extends ConsumerWidget {
margin: EdgeInsets.all(30),
child: Column(
children: [
Row(
children: [
Expanded(
child: Text.rich(
Align(
alignment: Alignment.centerLeft,
child: Text.rich(
TextSpan(
text: context.translate(I18n.homeGreeting),
style: TextStyle(fontSize: 25),
children: <TextSpan>[
TextSpan(
text: context.translate(I18n.homeGreeting),
style: TextStyle(fontSize: 25),
children: <TextSpan>[
TextSpan(
text: viewState.userName,
style: TextStyle(fontWeight: FontWeight.w500),
),
],
text: viewState.userName,
style: TextStyle(fontWeight: FontWeight.w500),
),
),
],
),
IconButton(
onPressed: () =>
navigationContract.pushTo(AppRoutes.deviceSetup),
icon: Icon(
Icons.person_add_outlined,
color: theme.getColorFor(ThemeCode.textPrimary),
),
tooltip: context.translate(I18n.homeAddAnotherKid),
),
],
),
),
const ChildWalletsSlider(),
Align(

View File

@@ -1,11 +1,9 @@
# 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,payments,activity
# melos_managed_dependency_overrides: activity,auth,design_system,flutter_treezor_entrust_sdk_bridge,fonts,navigation,notifications,payments,sca_treezor,sf_infrastructure,sf_localizations,sf_shared,utils
dependency_overrides:
activity:
path: ../activity
auth:
path: ../auth
dashboard_shell:
path: ../dashboard_shell
design_system:
path: ../../packages/design_system
flutter_treezor_entrust_sdk_bridge:
@@ -18,8 +16,6 @@ dependency_overrides:
path: ../notifications
payments:
path: ../../packages/payments
profile:
path: ../profile
sca_treezor:
path: ../../packages/sca_treezor
sf_infrastructure:

48
modules/legacy/.gitignore vendored Normal file
View File

@@ -0,0 +1,48 @@
.DS_Store
.dart_tool/
.pub/
.idea/
.vagrant/
.sconsign.dblite
.svn/
migrate_working_dir/
*.swp
profile
DerivedData/
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
build/
.android/
.ios/
.flutter-plugins-dependencies
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json

10
modules/legacy/.metadata Normal file
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: "9f455d2486bcb28cad87b062475f42edc959f636"
channel: "stable"
project_type: module

11
modules/legacy/README.md Normal file
View File

@@ -0,0 +1,11 @@
# legacy
A new Flutter project.
## Getting Started
For help getting started with Flutter development, view the online
[documentation](https://flutter.dev/).
For instructions integrating Flutter modules to your existing applications,
see the [add-to-app documentation](https://flutter.dev/to/add-to-app).

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

14
modules/legacy/legacy.iml Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/android/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/android/.idea" />
<excludeFolder url="file://$MODULE_DIR$/ios/Flutter" />
<excludeFolder url="file://$MODULE_DIR$/ios/Pods" />
<excludeFolder url="file://$MODULE_DIR$/ios/.symlinks" />
<excludeFolder url="file://$MODULE_DIR$/macos/Flutter" />
<excludeFolder url="file://$MODULE_DIR$/macos/Pods" />
<excludeFolder url="file://$MODULE_DIR$/macos/.symlinks" />
<excludeFolder url="file://$MODULE_DIR$/modules/customer_service/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/customer_service/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/customer_service/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/account/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/account/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/account/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/functions/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/functions/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/functions/build" />
<excludeFolder url="file://$MODULE_DIR$/packages/legacy_shared/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/packages/legacy_shared/.pub" />
<excludeFolder url="file://$MODULE_DIR$/packages/legacy_shared/build" />
<excludeFolder url="file://$MODULE_DIR$/packages/legacy_design_system/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/packages/legacy_design_system/.pub" />
<excludeFolder url="file://$MODULE_DIR$/packages/legacy_design_system/build" />
<excludeFolder url="file://$MODULE_DIR$/modules/settings/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/modules/settings/.pub" />
<excludeFolder url="file://$MODULE_DIR$/modules/settings/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@@ -0,0 +1,8 @@
library account;
export 'src/features/account_settings/account_settings_builder.dart';
export 'src/features/personal_data/personal_data_builder.dart';
export 'src/features/change_password/change_password_builder.dart';
export 'src/features/linked_devices/linked_devices_builder.dart';
export 'src/features/app_users/app_users_builder.dart';
export 'src/features/delete_account/delete_account_builder.dart';

View File

@@ -0,0 +1,5 @@
import 'package:account/src/features/change_password/domain/models/entities/change_password_request_entity.dart';
abstract class ChangePasswordRemoteDatasource {
Future<void> changePassword({required String userId, required ChangePasswordRequestEntity request});
}

View File

@@ -0,0 +1,27 @@
import 'package:account/src/core/data/models/change_password_request_model.dart';
import 'package:dio/dio.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import '../../../features/change_password/domain/models/entities/change_password_request_entity.dart';
import 'change_password_remote_datasource.dart';
class ChangePasswordRemoteDatasourceImpl implements ChangePasswordRemoteDatasource {
ChangePasswordRemoteDatasourceImpl(this._repository);
final QuestiaRepository _repository;
@override
Future<void> changePassword(
{required String userId, required ChangePasswordRequestEntity request}) async {
try {
final body = request.toModel().toJson();
await _repository.put<void>(
'/auth/change-password',
body: body,
);
} on DioException catch (error) {
throw mapDioError(error, defaultMessage: 'Error to change password');
}
}
}

View File

@@ -0,0 +1,7 @@
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
abstract class DevicesRemoteDatasource {
Future<void> deleteDevice({required String deviceId});
Future<void> updateDevice({required UpdateDeviceRequestEntity request});
}

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