Compare commits

...

55 Commits

Author SHA1 Message Date
6a29c75630 Merge remote-tracking branch 'origin/fusion-app' into feature/contacts
# Conflicts:
#	packages/sf_localizations/assets/l10n/en.json
#	packages/sf_localizations/assets/l10n/es.json
#	packages/sf_localizations/lib/src/generated/i18n.dart
2026-03-26 19:11:03 +01:00
1810dc6e2a fix 2026-03-25 18:53:21 +01:00
4d2d25f47b fix 2026-03-25 16:39:47 +01:00
c79cbeffcc bump build to 6 2026-03-25 14:43:27 +01:00
b7614a39f1 feat: sync device settings after updates and improve remote connection features
- Add DeviceSettingsSync extension on Ref to centralize device provider
    updates after settings changes (sound, volume, language, timezone,
    battery, disable functions, alerts, pedometer, heart rate freq,
    location freq, background image)
  - Add photo capture countdown with Lottie animation in remote camera
  - Replace Image.network with Image.memory for photo display
  - Fix commands datasource to handle text/html responses (post<dynamic>)
  - Add typed error/success events to RemoteConnectionViewModel
  - Add background image active indicator and backgroundImageId to device settings
  - Change photos endpoint from /devices/identificator/:id/photos/files to /photos/files
  - Remove dead deviceId param from getBackgroundImage chain
  - Relax PictureEntity required fields to @Default for API compatibility
  - Fix LocationViewModel rebuild crash (ref.watch → ref.read)
  - Use .select() in RemoteCameraScreen for optimized rebuilds
  - Increase health measure countdown to 60s
2026-03-25 13:51:48 +01:00
429b67a536 Merge branch 'refs/heads/fusion-app' into feature/contacts
# Conflicts:
#	modules/legacy/packages/legacy_shared/lib/legacy_shared.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-25 09:22:11 +01:00
a05c167f30 fix 2026-03-25 06:11:48 +01:00
c1c903ac93 feat: add country code picker to call watch dialog 2026-03-25 06:06:33 +01:00
a0a782c91b clean 2026-03-25 05:46:49 +01:00
c140daa7ae refactor: extract timezone data, remove duplicate i18n keys 2026-03-25 05:45:05 +01:00
6d30a59651 feat: implement alerts, disable functions, and battery night mode settings
- Rename sms_alert feature to alerts with toggle list from device capabilities
  - Implement disable functions (keyboard, GPS) with device settings update
  - Implement battery night mode with dedicated view model
  - Add keyboard, gps, nightMode fields to DeviceSettingsEntity/Model
  - Fix photos endpoint to use /photos/files for file content
2026-03-25 05:03:40 +01:00
8d453dc980 feature/background-image into fusion-app with fixes
- Fix upload flow: capture photo ID from POST /photos response
  - Fix endpoint: use /devices/identificator/{id}/photos/files for listing
  - Fix setBackgroundImage: use device.id (UUID) instead of identificator
  - Redesign screen as photo gallery with grid view
  - Add image compression on pick (maxWidth: 800, quality: 80%)
  - Fix multipart upload: remove content-type header for FormData auto-detection
  - Replace hardcoded Spanish text with i18n in 6 languages
  - Add typed error/success enums (BackgroundImageErrorEvent, BackgroundImageSuccessEvent)
  - Revert initialLocation to splash
  - Add missing translations for contacts and background-image features
2026-03-25 03:52:24 +01:00
0a50de3d70 Merge remote-tracking branch 'origin/feature/contacts' into fusion-app 2026-03-25 02:30:09 +01:00
02053182db Merge remote-tracking branch 'origin/feature/linked-devices' into fusion-app 2026-03-25 02:22:48 +01:00
33f3dfa252 feat: type device settings and capabilities, centralize device updates, and improve error handling
- Type device.settings and device.capabilities from untyped maps to Freezed models
  - Centralize all device settings updates through shared DeviceSettingsUpdateDatasource
  - Add frequency selectors for location and heart rate, pedometer toggle, and health measurement countdown with Lottie animation
  - Replace raw backend error messages with typed i18n error events across location, health, and activity meter
  - Fix silent error swallowing in commands datasource and stuck dialog in locate device
2026-03-25 02:15:25 +01:00
cb70973d3b feat: change background image 2026-03-24 17:31:49 +01:00
75df4736e2 change block phones behaviour 2026-03-24 13:43:26 +01:00
1ffeea8b77 background image screen and state 2026-03-24 11:25:45 +01:00
5f484036f8 feat: contacts upper and lower limits 2026-03-23 15:38:47 +01:00
73927557ca fix: show activation code dialog with qr scan 2026-03-23 13:58:59 +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
4eb4ac81ce Merge origin/feature/remote-management
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 12:46:58 +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
394 changed files with 39154 additions and 4294 deletions

View File

@@ -3,349 +3,349 @@
"packages": [
{
"name": "ansi_styles",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/ansi_styles-0.3.2+1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/ansi_styles-0.3.2+1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "args",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/args-2.7.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/args-2.7.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "async",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/async-2.13.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/async-2.13.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "characters",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/characters-1.4.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/characters-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "charcode",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/charcode-1.4.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/charcode-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "checked_yaml",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/checked_yaml-2.0.4",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/checked_yaml-2.0.4",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "cli_launcher",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/cli_launcher-0.3.2+1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/cli_launcher-0.3.2+1",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "cli_util",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/cli_util-0.4.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/cli_util-0.4.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "collection",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/collection-1.19.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/collection-1.19.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "conventional_commit",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/conventional_commit-0.6.1+1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/conventional_commit-0.6.1+1",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "ffi",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/ffi-2.1.4",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/ffi-2.1.4",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "file",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/file-7.0.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/file-7.0.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "flutter",
"rootUri": "file:///C:/Program%20Files/Flutter/packages/flutter",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/packages/flutter",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "flutter_secure_storage",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage-9.2.4",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_linux",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_macos",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_platform_interface",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_platform_interface-1.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_platform_interface-1.1.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_web",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_windows",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_web_plugins",
"rootUri": "file:///C:/Program%20Files/Flutter/packages/flutter_web_plugins",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/packages/flutter_web_plugins",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "glob",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/glob-2.1.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/glob-2.1.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "graphs",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/graphs-2.3.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/graphs-2.3.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "http",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/http-1.5.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/http-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "http_parser",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/http_parser-4.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/http_parser-4.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "io",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/io-1.0.5",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/io-1.0.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "js",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/js-0.6.7",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/js-0.6.7",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "json_annotation",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/json_annotation-4.9.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/json_annotation-4.9.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "material_color_utilities",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/material_color_utilities-0.11.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/material_color_utilities-0.11.1",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "melos",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/melos-6.3.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/melos-6.3.3",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "meta",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.16.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/meta-1.16.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "mustache_template",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/mustache_template-2.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/mustache_template-2.0.2",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "path",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path-1.9.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path-1.9.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_provider",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider-2.1.5",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider-2.1.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_provider_android",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_android-2.2.20",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "path_provider_foundation",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_foundation-2.4.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "path_provider_linux",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_linux-2.2.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "path_provider_platform_interface",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_platform_interface-2.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_platform_interface-2.1.2",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "path_provider_windows",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_windows-2.3.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "platform",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/platform-3.1.6",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/platform-3.1.6",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "plugin_platform_interface",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "pool",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/pool-1.5.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pool-1.5.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "process",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/process-5.0.5",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/process-5.0.5",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "prompts",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/prompts-2.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/prompts-2.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "pub_semver",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/pub_semver-2.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pub_semver-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "pub_updater",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/pub_updater-0.5.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pub_updater-0.5.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "pubspec_parse",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/pubspec_parse-1.5.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "sky_engine",
"rootUri": "file:///C:/Program%20Files/Flutter/bin/cache/pkg/sky_engine",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/bin/cache/pkg/sky_engine",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "source_span",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/source_span-1.10.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/source_span-1.10.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "stack_trace",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/stack_trace-1.12.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/stack_trace-1.12.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "string_scanner",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/string_scanner-1.4.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/string_scanner-1.4.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "term_glyph",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/term_glyph-1.2.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/term_glyph-1.2.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "typed_data",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/typed_data-1.4.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/typed_data-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "vector_math",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/vector_math-2.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/vector_math-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "web",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/web-1.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/web-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "win32",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/win32-5.15.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/win32-5.15.0",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "xdg_directories",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/xdg_directories-1.1.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "yaml",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/yaml-3.1.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/yaml-3.1.3",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "yaml_edit",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/yaml_edit-2.2.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/yaml_edit-2.2.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
@@ -358,7 +358,7 @@
],
"generator": "pub",
"generatorVersion": "3.9.2",
"flutterRoot": "file:///C:/Program%20Files/Flutter",
"flutterVersion": "3.35.6",
"pubCache": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache"
"flutterRoot": "file:///Users/juliandalcalaf/Development/flutter",
"flutterVersion": "3.35.7",
"pubCache": "file:///Users/juliandalcalaf/.pub-cache"
}

1
.idea/modules.xml generated
View File

@@ -17,7 +17,6 @@
<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_design_system/melos_legacy_design_system.iml" filepath="$PROJECT_DIR$/modules/legacy/packages/legacy_design_system/melos_legacy_design_system.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" />

View File

@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Flutter Run -&gt; 'flutter_treezor_entrust_sdk_bridge_example'" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/packages//flutter_treezor_entrust_sdk_bridge//example/lib/main.dart" />
<option name="filePath" value="$PROJECT_DIR$/packages/flutter_treezor_entrust_sdk_bridge/example/lib/main.dart" />
<method v="2" />
</configuration>
</component>

View File

@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Flutter Run -&gt; 'sf_app_platform'" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/apps//mobile_app/lib/main.dart" />
<option name="filePath" value="$PROJECT_DIR$/apps/mobile_app/lib/main.dart" />
<method v="2" />
</configuration>
</component>

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
{
"env": "development",
"apiBaseUrl": "https://api-neki-b2b.neki.es/gateway/api/",
"apiOrigin": "bde6ea73-d09c-475f-aabf-1d11137e4d0d"
}
"apiOrigin": "https://neki-b2b.neki.es"
}

View File

@@ -1,9 +1,13 @@
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):
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- mobile_scanner (7.0.0):
- Flutter
- FlutterMacOS
@@ -12,6 +16,8 @@ PODS:
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
@@ -23,11 +29,14 @@ 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`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
@@ -35,16 +44,22 @@ 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:
:path: ".symlinks/plugins/flutter_treezor_entrust_sdk_bridge/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
mobile_scanner:
:path: ".symlinks/plugins/mobile_scanner/darwin"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
url_launcher_ios:
@@ -54,11 +69,14 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
flutter_treezor_entrust_sdk_bridge: 4c2c94fb74ab57576e8d49f5f2a4b214e41141fe
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4

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

@@ -30,7 +30,9 @@ Future<void> initApp(EnvironmentEnum env) async {
await configureDependencies(
QuestiaEnvConfig(),
log: env.isDevelopment || kDebugMode,
onTokenExpired: () => appRouter.go(AppRoutes.scaTreezor),
onTokenExpired: () => appRouter.go(
AppRoutes.legacyLogin,
), //change to payments app to AppRoutes.scaTreezor
onUnauthorized: () async {
final currentLocation =
appRouter.routerDelegate.currentConfiguration.uri.path;
@@ -39,7 +41,7 @@ Future<void> initApp(EnvironmentEnum env) async {
await GetIt.I<TreezorWalletConnectionService>().logout();
} catch (_) {}
await clearSessionData();
appRouter.go(AppRoutes.login);
appRouter.go(AppRoutes.legacyLogin);
},
);

View File

@@ -141,6 +141,21 @@ void configureAppRouter() {
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,
),
GoRoute(
path: 'background_image',
name: 'background_image',
pageBuilder: const BackgroundImageBuilder().buildPage,
),
],
),
],
@@ -237,9 +252,9 @@ void configureAppRouter() {
pageBuilder: RemoteOnOffBuilder().buildPage,
),
GoRoute(
path: 'sms_alert',
name: 'sms_alert',
pageBuilder: SmsAlertBuilder().buildPage,
path: 'alerts',
name: 'alerts',
pageBuilder: AlertsBuilder().buildPage,
),
GoRoute(
path: 'timezone',
@@ -386,6 +401,12 @@ void configureAppRouter() {
name: 'home_extract',
pageBuilder: const ExtractBuilder().buildPage,
),
GoRoute(
path: 'edit',
name: 'home_edit_child_profile',
pageBuilder:
const EditChildProfileBuilder().buildPage,
),
],
),
],
@@ -427,6 +448,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

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
@@ -35,8 +36,10 @@ class LegacyHeartbeatService {
debugPrint('[LegacyHeartbeat] /auth/me => OK');
} catch (e) {
debugPrint('[LegacyHeartbeat] error: $e');
stop();
_onUnauthorized();
if (e is DioException && e.response?.statusCode == 401) {
stop();
_onUnauthorized();
}
}
}
}

View File

@@ -34,7 +34,9 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
walletHeartbeat = WalletHeartbeatService(
repository: ref.read(treezorRepositoryProvider),
sessionLocal: SessionLocalDatasourceImpl(),
onError: () => appRouter.go(AppRoutes.scaTreezor),
onError: () => appRouter.go(
AppRoutes.legacyLogin,
), //change to payments app to AppRoutes.scaTreezor
);
legacyHeartbeat = LegacyHeartbeatService(
repository: GetIt.I<QuestiaRepository>(),
@@ -47,12 +49,21 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
walletHeartbeat.stop();
legacyHeartbeat.stop();
};
// walletHeartbeat.start();
legacyHeartbeat.start();
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);
@@ -65,7 +76,7 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
ref.read(appLifecycleStateProvider.notifier).setState(state);
if (state == AppLifecycleState.resumed) {
// walletHeartbeat.start();
legacyHeartbeat.start();
_onRouteChanged();
ref.read(permissionsProvider.notifier).checkPermissions();
} else if (state == AppLifecycleState.paused) {
// walletHeartbeat.stop();

View File

@@ -0,0 +1 @@
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/file_selector_linux-0.9.4/

View File

@@ -0,0 +1 @@
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/image_picker_linux-0.2.2/

View File

@@ -1 +1 @@
C:/Users/Aitor Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_linux-2.2.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

@@ -1 +1 @@
C:/Users/Aitor Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_linux-2.4.1/
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/

View File

@@ -1 +1 @@
C:/Users/Aitor Arana/AppData/Local/Pub/Cache/hosted/pub.dev/url_launcher_linux-3.2.2/
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2/

View File

@@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
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

@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
url_launcher_linux
)

View File

@@ -245,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:
@@ -385,6 +393,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
file_selector_linux:
dependency: transitive
description:
name: file_selector_linux
sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0"
url: "https://pub.dev"
source: hosted
version: "0.9.4"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a"
url: "https://pub.dev"
source: hosted
version: "0.9.5"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85"
url: "https://pub.dev"
source: hosted
version: "2.7.0"
file_selector_windows:
dependency: transitive
description:
name: file_selector_windows
sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd"
url: "https://pub.dev"
source: hosted
version: "0.9.3+5"
fixnum:
dependency: transitive
description:
@@ -451,6 +491,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.4.7"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1
url: "https://pub.dev"
source: hosted
version: "2.0.33"
flutter_riverpod:
dependency: "direct main"
description:
@@ -602,6 +650,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.8.0"
image_picker:
dependency: transitive
description:
name: image_picker
sha256: "784210112be18ea55f69d7076e2c656a4e24949fa9e76429fe53af0c0f4fa320"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
sha256: eda9b91b7e266d9041084a42d605a74937d996b87083395c5e47835916a86156
url: "https://pub.dev"
source: hosted
version: "0.8.13+14"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
sha256: "956c16a42c0c708f914021666ffcd8265dde36e673c9fa68c81f7d085d9774ad"
url: "https://pub.dev"
source: hosted
version: "0.8.13+3"
image_picker_linux:
dependency: transitive
description:
name: image_picker_linux
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
image_picker_macos:
dependency: transitive
description:
name: image_picker_macos
sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91"
url: "https://pub.dev"
source: hosted
version: "0.2.2+1"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"
url: "https://pub.dev"
source: hosted
version: "2.11.1"
image_picker_windows:
dependency: transitive
description:
name: image_picker_windows
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
url: "https://pub.dev"
source: hosted
version: "0.2.2"
intl:
dependency: transitive
description:
@@ -1117,6 +1229,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:
@@ -1560,6 +1688,14 @@ packages:
url: "https://pub.dev"
source: hosted
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:

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+6
environment:
sdk: ^3.9.2
@@ -123,6 +123,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/shared/images/
- assets/shared/animations/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images

File diff suppressed because one or more lines are too long

View File

@@ -3,163 +3,163 @@
"packages": [
{
"name": "_fe_analyzer_shared",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/_fe_analyzer_shared-85.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-85.0.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "analyzer",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/analyzer-7.7.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/analyzer-7.7.1",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "archive",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/archive-4.0.9",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/archive-4.0.9",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "args",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/args-2.7.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/args-2.7.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "async",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/async-2.13.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/async-2.13.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "boolean_selector",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/boolean_selector-2.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/boolean_selector-2.1.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "build",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/build-3.1.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/build-3.1.0",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "build_config",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/build_config-1.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/build_config-1.2.0",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "build_daemon",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/build_daemon-4.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/build_daemon-4.1.1",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "build_resolvers",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/build_resolvers-3.0.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/build_resolvers-3.0.3",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "build_runner",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/build_runner-2.7.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/build_runner-2.7.1",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "build_runner_core",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/build_runner_core-9.3.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/build_runner_core-9.3.1",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "built_collection",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/built_collection-5.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/built_collection-5.1.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "built_value",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/built_value-8.12.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/built_value-8.12.3",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "characters",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/characters-1.4.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/characters-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "checked_yaml",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/checked_yaml-2.0.4",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/checked_yaml-2.0.4",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "cli_config",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/cli_config-0.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/cli_config-0.2.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "clock",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/clock-1.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/clock-1.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "code_builder",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/code_builder-4.11.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/code_builder-4.11.1",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "collection",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/collection-1.19.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/collection-1.19.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "confetti",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/confetti-0.7.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/confetti-0.7.0",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "convert",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/convert-3.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/convert-3.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "cookie_jar",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/cookie_jar-4.0.8",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/cookie_jar-4.0.8",
"packageUri": "lib/",
"languageVersion": "2.15"
},
{
"name": "country_code_picker",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/country_code_picker-3.4.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/country_code_picker-3.4.1",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "coverage",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/coverage-1.15.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/coverage-1.15.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "crypto",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/crypto-3.0.7",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/crypto-3.0.7",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "dart_style",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/dart_style-3.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/dart_style-3.1.1",
"packageUri": "lib/",
"languageVersion": "3.7"
},
@@ -171,91 +171,91 @@
},
{
"name": "diacritic",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/diacritic-0.1.6",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/diacritic-0.1.6",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "dio",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/dio-5.9.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/dio-5.9.1",
"packageUri": "lib/",
"languageVersion": "2.18"
},
{
"name": "dio_cookie_manager",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/dio_cookie_manager-3.3.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/dio_cookie_manager-3.3.0",
"packageUri": "lib/",
"languageVersion": "2.18"
},
{
"name": "dio_web_adapter",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/dio_web_adapter-2.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/dio_web_adapter-2.1.1",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "equatable",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/equatable-2.0.8",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/equatable-2.0.8",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "fake_async",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/fake_async-1.3.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/fake_async-1.3.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "ffi",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/ffi-2.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/ffi-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "file",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/file-7.0.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/file-7.0.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "fixnum",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/fixnum-1.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/fixnum-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "fl_chart",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/fl_chart-1.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/fl_chart-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "flutter",
"rootUri": "file:///C:/Program%20Files/Flutter/packages/flutter",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/packages/flutter",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "flutter_lints",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_lints-5.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_lints-5.0.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "flutter_riverpod",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_riverpod-3.2.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_riverpod-3.2.1",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "flutter_svg",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/flutter_svg-2.2.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_svg-2.2.3",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "flutter_test",
"rootUri": "file:///C:/Program%20Files/Flutter/packages/flutter_test",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/packages/flutter_test",
"packageUri": "lib/",
"languageVersion": "3.8"
},
@@ -267,7 +267,7 @@
},
{
"name": "flutter_web_plugins",
"rootUri": "file:///C:/Program%20Files/Flutter/packages/flutter_web_plugins",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/packages/flutter_web_plugins",
"packageUri": "lib/",
"languageVersion": "3.8"
},
@@ -279,259 +279,259 @@
},
{
"name": "freezed",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/freezed-3.2.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/freezed-3.2.3",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "freezed_annotation",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/freezed_annotation-3.1.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/freezed_annotation-3.1.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "frontend_server_client",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/frontend_server_client-4.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/frontend_server_client-4.0.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "get_it",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/get_it-9.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/get_it-9.2.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "glob",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/glob-2.1.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/glob-2.1.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "go_router",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/go_router-17.1.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/go_router-17.1.0",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "graphs",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/graphs-2.3.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/graphs-2.3.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "http",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/http-1.6.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/http-1.6.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "http_multi_server",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/http_multi_server-3.2.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/http_multi_server-3.2.2",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "http_parser",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/http_parser-4.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/http_parser-4.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "intl",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/intl-0.20.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/intl-0.20.2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "io",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/io-1.0.5",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/io-1.0.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "js",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/js-0.7.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/js-0.7.2",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "json_annotation",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/json_annotation-4.9.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/json_annotation-4.9.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "json_serializable",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/json_serializable-6.11.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/json_serializable-6.11.2",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "leak_tracker",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker-11.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/leak_tracker-11.0.2",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "leak_tracker_flutter_testing",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.10",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.10",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "leak_tracker_testing",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/leak_tracker_testing-3.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/leak_tracker_testing-3.0.2",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "lints",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/lints-5.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/lints-5.1.1",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "logging",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/logging-1.3.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/logging-1.3.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "lottie",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/lottie-3.3.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/lottie-3.3.2",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "matcher",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/matcher-0.12.17",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/matcher-0.12.17",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "material_color_utilities",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/material_color_utilities-0.11.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/material_color_utilities-0.11.1",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "meta",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/meta-1.16.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/meta-1.16.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "mime",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/mime-2.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/mime-2.0.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "node_preamble",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/node_preamble-2.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/node_preamble-2.0.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "package_config",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/package_config-2.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/package_config-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path-1.9.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path-1.9.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_parsing",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_parsing-1.1.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_parsing-1.1.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "path_provider",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider-2.1.5",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider-2.1.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_provider_android",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_android-2.2.22",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.22",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "path_provider_foundation",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_foundation-2.5.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.5.1",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "path_provider_linux",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_linux-2.2.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "path_provider_platform_interface",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_platform_interface-2.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_platform_interface-2.1.2",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "path_provider_windows",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_windows-2.3.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "petitparser",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/petitparser-7.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/petitparser-7.0.2",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "platform",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/platform-3.1.6",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/platform-3.1.6",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "plugin_platform_interface",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "pool",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/pool-1.5.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pool-1.5.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "posix",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/posix-6.5.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/posix-6.5.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "pub_semver",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/pub_semver-2.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pub_semver-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "pubspec_parse",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/pubspec_parse-1.5.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "riverpod",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/riverpod-3.2.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/riverpod-3.2.1",
"packageUri": "lib/",
"languageVersion": "3.7"
},
@@ -561,181 +561,181 @@
},
{
"name": "shared_preferences",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences-2.5.4",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences-2.5.4",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "shared_preferences_android",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_android-2.4.20",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.20",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "shared_preferences_foundation",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_foundation-2.5.6",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.6",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "shared_preferences_linux",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_linux-2.4.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "shared_preferences_platform_interface",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_platform_interface-2.4.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_platform_interface-2.4.1",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "shared_preferences_web",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_web-2.4.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "shared_preferences_windows",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_windows-2.4.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "shelf",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shelf-1.4.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shelf-1.4.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "shelf_packages_handler",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_packages_handler-3.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "shelf_static",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_static-1.1.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shelf_static-1.1.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "shelf_web_socket",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shelf_web_socket-3.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shelf_web_socket-3.0.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "sky_engine",
"rootUri": "file:///C:/Program%20Files/Flutter/bin/cache/pkg/sky_engine",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/bin/cache/pkg/sky_engine",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "source_gen",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/source_gen-4.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/source_gen-4.0.0",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "source_helper",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/source_helper-1.3.8",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/source_helper-1.3.8",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "source_map_stack_trace",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/source_map_stack_trace-2.1.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.2",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "source_maps",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/source_maps-0.10.13",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/source_maps-0.10.13",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "source_span",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/source_span-1.10.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/source_span-1.10.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "stack_trace",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/stack_trace-1.12.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/stack_trace-1.12.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "state_notifier",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/state_notifier-1.0.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/state_notifier-1.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "stream_channel",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/stream_channel-2.1.4",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/stream_channel-2.1.4",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "stream_transform",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/stream_transform-2.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/stream_transform-2.1.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "string_scanner",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/string_scanner-1.4.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/string_scanner-1.4.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "term_glyph",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/term_glyph-1.2.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/term_glyph-1.2.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "test",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/test-1.26.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/test-1.26.2",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "test_api",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/test_api-0.7.6",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/test_api-0.7.6",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "test_core",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/test_core-0.6.11",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/test_core-0.6.11",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "timing",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/timing-1.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/timing-1.0.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "top_snackbar_flutter",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/top_snackbar_flutter-3.3.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/top_snackbar_flutter-3.3.0",
"packageUri": "lib/",
"languageVersion": "2.15"
},
{
"name": "typed_data",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/typed_data-1.4.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/typed_data-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "universal_io",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/universal_io-2.3.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/universal_io-2.3.1",
"packageUri": "lib/",
"languageVersion": "3.6"
},
@@ -747,79 +747,79 @@
},
{
"name": "vector_graphics",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics-1.1.19",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/vector_graphics-1.1.19",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "vector_graphics_codec",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics_codec-1.1.13",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/vector_graphics_codec-1.1.13",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "vector_graphics_compiler",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/vector_graphics_compiler-1.1.20",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/vector_graphics_compiler-1.1.20",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "vector_math",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/vector_math-2.2.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/vector_math-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "vm_service",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/vm_service-15.0.2",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/vm_service-15.0.2",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "watcher",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/watcher-1.2.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/watcher-1.2.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "web",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/web-1.1.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/web-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "web_socket",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/web_socket-1.0.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/web_socket-1.0.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "web_socket_channel",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/web_socket_channel-3.0.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/web_socket_channel-3.0.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "webkit_inspection_protocol",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "xdg_directories",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/xdg_directories-1.1.0",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "xml",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/xml-6.6.1",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/xml-6.6.1",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "yaml",
"rootUri": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/yaml-3.1.3",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/yaml-3.1.3",
"packageUri": "lib/",
"languageVersion": "3.4"
},
@@ -832,7 +832,7 @@
],
"generator": "pub",
"generatorVersion": "3.9.2",
"flutterRoot": "file:///C:/Program%20Files/Flutter",
"flutterVersion": "3.35.6",
"pubCache": "file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache"
"flutterRoot": "file:///Users/juliandalcalaf/Development/flutter",
"flutterVersion": "3.35.7",
"pubCache": "file:///Users/juliandalcalaf/.pub-cache"
}

View File

@@ -1 +1 @@
3.35.6
3.35.7

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":"C:\\\\dev\\\\sf-app-platform\\\\packages\\\\flutter_treezor_entrust_sdk_bridge\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\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":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\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":"C:\\\\dev\\\\sf-app-platform\\\\packages\\\\flutter_treezor_entrust_sdk_bridge\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.22\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.20\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\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":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\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":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\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":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\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":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\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-16 09:33:25.098373","version":"3.35.6","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-25 02:38:09.414683","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}

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

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

View File

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

@@ -7,3 +7,4 @@ 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';

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,6 +5,7 @@ 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';
@@ -162,14 +163,60 @@ class _ChildWalletScreenState extends ConsumerState<ChildWalletScreen> {
),
),
_buildGenderAvatar(device?.carrierGenre, 50),
Text(
childName,
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
fontWeight: FontWeight.bold,
fontSize: 20,
Expanded(
child: Text(
childName,
style: TextStyle(
color: theme.getColorFor(
ThemeCode.backgroundPrimary,
),
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
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();
}
},
itemBuilder: (_) => [
PopupMenuItem(
value: 'edit',
child: Row(
spacing: 8,
children: [
Icon(Icons.edit_outlined),
Text(
context.translate(I18n.editChildProfile),
),
],
),
),
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(
@@ -196,76 +243,37 @@ class _ChildWalletScreenState extends ConsumerState<ChildWalletScreen> {
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)),
),
onPressed: viewState.isUpdatingCard
? null
: () => _showDeleteConfirmation(context, ref),
child: Row(
spacing: 10,
children: [
Icon(
Icons.delete_outline,
size: 24,
color: theme.getColorFor(
ThemeCode.textSecondary,
),
),
Text(
context.translate(I18n.deleteDevice),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: theme.getColorFor(
ThemeCode.textSecondary,
),
),
),
],
),
)
else
TextButton(
style: ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.all(0)),
),
onPressed: () =>
_showCardStatusSheet(context, ref, theme),
child: Row(
spacing: 10,
children: [
Icon(
Icons.lock_outline,
size: 24,
color: theme.getColorFor(
ThemeCode.textSecondary,
),
),
Text(
locked
? context.translate(
I18n.childWalletUnlockCard,
)
: context.translate(
I18n.childWalletLockCard,
),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: theme.getColorFor(
ThemeCode.textSecondary,
),
),
),
],
),
TextButton(
style: ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.all(0)),
),
onPressed: () =>
_showCardStatusSheet(context, ref, theme),
child: Row(
spacing: 10,
children: [
Icon(
Icons.lock_outline,
size: 24,
color: theme.getColorFor(ThemeCode.textSecondary),
),
Text(
locked
? context.translate(
I18n.childWalletUnlockCard,
)
: context.translate(I18n.childWalletLockCard),
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16,
color: theme.getColorFor(
ThemeCode.textSecondary,
),
),
),
],
),
),
],
),
Column(
@@ -302,7 +310,7 @@ class _ChildWalletScreenState extends ConsumerState<ChildWalletScreen> {
child: AppLoadingIndicator(size: 48),
),
)
else if (viewState.transactions.isEmpty)
else if (viewState.transactionPages.isEmpty)
Padding(
padding: const EdgeInsets.all(24),
child: Center(
@@ -319,13 +327,34 @@ class _ChildWalletScreenState extends ConsumerState<ChildWalletScreen> {
),
),
)
else
...viewState.transactions.map(
else ...[
...viewState.transactionPages[viewState.currentPage].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(),
),
],
],
),
),
@@ -342,17 +371,13 @@ class _ChildWalletScreenState extends ConsumerState<ChildWalletScreen> {
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,
@@ -376,47 +401,108 @@ class _ChildWalletScreenState extends ConsumerState<ChildWalletScreen> {
);
}
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)),
title: Text(context.translate(I18n.deleteDeviceConfirmTitle)),
content: Text(context.translate(I18n.deleteDeviceConfirmMessage)),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(context.translate(I18n.cancel)),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
final viewModel = ref.read(
childWalletViewModelProvider(widget.childId).notifier,
);
final success = await viewModel.deleteDevice();
if (success && context.mounted) {
ref
.read(homeViewModelProvider.notifier)
.removeChild(widget.childId);
showTopSnackbar(
context,
message: context.translate(I18n.deleteDeviceSuccess),
type: MessageType.success,
);
widget.navigation.goBack();
}
},
child: Text(
context.translate(I18n.deleteDevice),
style: TextStyle(color: Colors.red),
),
),
],
backgroundColor: bgColor,
shape: shape,
content: Row(
spacing: 16,
children: [const AppLoadingIndicator(size: 24), Text(checkingText)],
),
),
);
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);
}
}
}
@@ -463,13 +549,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
@@ -181,6 +216,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 +236,10 @@ 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;
}
}

View File

@@ -22,6 +22,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; 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.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,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, 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, 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? 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
@@ -80,8 +80,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 +205,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, 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.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 +226,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, 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.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 +246,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, 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.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 +261,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.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;
@@ -277,13 +280,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 +301,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.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,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, 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 +321,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, 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 +338,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? 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
@@ -350,8 +356,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

@@ -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

@@ -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

@@ -32,6 +32,9 @@
<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" />

View File

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

View File

@@ -12,7 +12,7 @@ class DevicesRemoteDatasourceImpl implements DevicesRemoteDatasource {
final QuestiaRepository _repository;
@override
Future<void> deleteDevice({required String userId, required String deviceId}) async {
Future<void> deleteDevice({required String deviceId}) async {
try {
await _repository.delete<void>(
'/devices/$deviceId',
@@ -23,7 +23,7 @@ class DevicesRemoteDatasourceImpl implements DevicesRemoteDatasource {
}
@override
Future<void> updateDevice({required String userId, required UpdateDeviceRequestEntity request}) async {
Future<void> updateDevice({required UpdateDeviceRequestEntity request}) async {
try {
final body = request.toModel().toJson();
await _repository.put<void>(

View File

@@ -9,12 +9,12 @@ class DevicesRepositoryImpl implements DevicesRepository {
final DevicesRemoteDatasource _remote;
@override
Future<void> deleteDevice({required String userId, required String deviceId}) {
return _remote.deleteDevice(userId: userId, deviceId: deviceId);
Future<void> deleteDevice({required String deviceId}) {
return _remote.deleteDevice(deviceId: deviceId);
}
@override
Future<void> updateDevice({required String userId, required UpdateDeviceRequestEntity request}) {
return _remote.updateDevice(userId: userId, request: request);
Future<void> updateDevice({required UpdateDeviceRequestEntity request}) {
return _remote.updateDevice(request: request);
}
}

View File

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

View File

@@ -16,6 +16,9 @@ class ChangePasswordScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final password = ref.watch(
changePasswordViewModelProvider.select((s)=>s.newPassword)
);
return LegacyPageLayout(
theme: theme,
@@ -28,11 +31,11 @@ class ChangePasswordScreen extends ConsumerWidget {
child: SingleChildScrollView(child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const _PasswordSection(),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
const _NewPasswordSection(),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
const _RepeatPasswordSection(),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
_PasswordCriteriaList(password: password),
const _ErrorMessageSection()
],
))
@@ -42,29 +45,6 @@ class ChangePasswordScreen extends ConsumerWidget {
}
}
class _PasswordSection extends ConsumerWidget {
const _PasswordSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(changePasswordViewModelProvider.notifier);
final showPassword = ref.watch(
changePasswordViewModelProvider.select((s)=>s.showCurrentPassword)
);
return CustomTextField(
controller: vm.currentPasswordController,
hint: '********',
label: context.translate(I18n.password),
showPassword: showPassword,
onVisibilityChanged: vm.toggleCurrentPasswordVisibility,
);
}
}
class _NewPasswordSection extends ConsumerWidget {
const _NewPasswordSection();
@@ -111,6 +91,89 @@ class _RepeatPasswordSection extends ConsumerWidget {
}
class _PasswordCriteriaList extends StatelessWidget {
final String password;
const _PasswordCriteriaList({required this.password});
static final _upperRegex = RegExp(r'[A-Z]');
static final _digitRegex = RegExp(r'[0-9]');
static final _specialRegex =
RegExp(r'[!@#$%^&*(),.?":{}|<>\-_+=\[\]\\\/~`]');
@override
Widget build(BuildContext context) {
final hasInput = password.isNotEmpty;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 6,
children: [
_CriteriaRow(
label: context.translate(I18n.passwordLength),
met: password.length >= 8,
hasInput: hasInput,
),
_CriteriaRow(
label: context.translate(I18n.passwordCapital),
met: _upperRegex.hasMatch(password),
hasInput: hasInput,
),
_CriteriaRow(
label: context.translate(I18n.passwordNumber),
met: _digitRegex.hasMatch(password),
hasInput: hasInput,
),
_CriteriaRow(
label: context.translate(I18n.passwordSpecial),
met: _specialRegex.hasMatch(password),
hasInput: hasInput,
),
],
);
}
}
class _CriteriaRow extends StatelessWidget {
final String label;
final bool met;
final bool hasInput;
const _CriteriaRow({
required this.label,
required this.met,
required this.hasInput,
});
@override
Widget build(BuildContext context) {
final Color color;
final IconData icon;
if (!hasInput) {
color = Colors.grey;
icon = Icons.circle_outlined;
} else if (met) {
color = Colors.green;
icon = Icons.check_circle;
} else {
color = Colors.red.shade400;
icon = Icons.cancel;
}
return Row(
spacing: 8,
children: [
Icon(icon, size: 16, color: color),
Text(
label,
style: TextStyle(fontSize: 13, color: color),
),
],
);
}
}
class _ErrorMessageSection extends ConsumerWidget {
const _ErrorMessageSection();
@@ -136,7 +199,9 @@ class _ErrorMessageSection extends ConsumerWidget {
),
],
);
} else return SizedBox.shrink();
} else {
return SizedBox.shrink();
}
}
}

View File

@@ -14,7 +14,6 @@ NotifierProvider.autoDispose<ChangePasswordViewModel, ChangePasswordViewState>(
class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
late final ChangePasswordUseCase _changePasswordUseCase;
late final TextEditingController currentPasswordController;
late final TextEditingController newPasswordController;
late final TextEditingController repeatPasswordController;
late final TextEditingController passwordController;
@@ -29,10 +28,6 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
}
void _initControllers() {
currentPasswordController = TextEditingController();
currentPasswordController.addListener(_onCurrentPasswordChanged);
newPasswordController = TextEditingController();
newPasswordController.addListener(_onNewPasswordChanged);
@@ -60,17 +55,6 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
);
}
void _onCurrentPasswordChanged() {
final value = currentPasswordController.text;
if (value == state.currentPassword) return;
state = state.copyWith(
currentPassword: value,
errorMessage: ''
);
}
void _onNewPasswordChanged() {
final value = newPasswordController.text;
@@ -94,11 +78,14 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
}
bool _validateForm() {
if (state.currentPassword.trim().isEmpty){
state = state.copyWith(errorMessage: 'errorMessageCurrentPasswordIsEmpty');
return false;
}
if (state.newPassword.trim().isEmpty){
final _upperRegex = RegExp(r'[A-Z]');
final _digitRegex = RegExp(r'[0-9]');
final _specialRegex =
RegExp(r'[!@#$%^&*(),.?":{}|<>\-_+=\[\]\\\/~`]');
final password = state.newPassword.trim();
if (password.isEmpty){
state = state.copyWith(errorMessage: 'errorMessageNewPasswordIsEmpty');
return false;
}
@@ -106,10 +93,26 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
state = state.copyWith(errorMessage: 'errorMessageRepeatPasswordIsEmpty');
return false;
}
if (state.newPassword.trim() != state.repeatPassword.trim()){
if (password != state.repeatPassword.trim()){
state = state.copyWith(errorMessage: 'errorMessagePasswordsDontMatch');
return false;
}
if (password.length < 8){
state = state.copyWith(errorMessage: 'errorPasswordMinLength');
return false;
}
if (!_upperRegex.hasMatch(password)) {
state = state.copyWith(errorMessage: 'errorPasswordUppercase');
return false;
}
if (!_digitRegex.hasMatch(password)) {
state = state.copyWith(errorMessage: 'errorPasswordDigits');
return false;
}
if (!_specialRegex.hasMatch(password)) {
state = state.copyWith(errorMessage: 'errorPasswordSpecial');
return false;
}
return true;
}
@@ -155,9 +158,6 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
}
void disposeControllers() {
currentPasswordController.removeListener(_onCurrentPasswordChanged);
currentPasswordController.dispose();
newPasswordController.removeListener(_onNewPasswordChanged);
newPasswordController.dispose();

View File

@@ -7,10 +7,8 @@ abstract class ChangePasswordViewState with _$ChangePasswordViewState {
const factory ChangePasswordViewState({
@Default(false) bool isLoading,
@Default(false) bool isComplete,
@Default(false) bool showCurrentPassword,
@Default(false) bool showNewPassword,
@Default(false) bool showRepeatedPassword,
@Default('') String currentPassword,
@Default('') String newPassword,
@Default('') String repeatPassword,
@Default('') String errorMessage

View File

@@ -27,28 +27,36 @@ class LinkedDevicesScreen extends ConsumerWidget {
title: context.translate(I18n.linkedDevices),
showEdit: true,
onEditChange: vm.toggleIsEditing,
body: ListView.separated(
itemBuilder: (BuildContext context, int index)=>_LinkedDeviceCard(
device: state.linkedDevices[index],
isEditing: state.isEditing,
onDelete: ()=>vm.deleteDevice(state.linkedDevices[index]),
),
separatorBuilder: (BuildContext context, int index)=>SizedBox(
height: SizeUtils.getByScreen(small: 18, big: 17)
),
itemCount: state.linkedDevices.length
),
body: state.isLoading
? const Center(child: CircularProgressIndicator())
: Padding(
padding: EdgeInsets.symmetric(horizontal: SizeUtils.getByScreen(small: 10, big: 12)),
child: ListView.separated(
itemBuilder: (BuildContext context, int index)=>_LinkedDeviceCard(
navigationContract: navigationContract,
device: state.linkedDevices[index],
isEditing: state.isEditing,
onDelete: ()=>vm.deleteDevice(state.linkedDevices[index]),
),
separatorBuilder: (BuildContext context, int index)=>SizedBox(
height: SizeUtils.getByScreen(small: 18, big: 17)
),
itemCount: state.linkedDevices.length
),
),
);
}
}
class _LinkedDeviceCard extends ConsumerWidget {
final NavigationContract navigationContract;
final DeviceEntity device;
final bool isEditing;
final Function onDelete;
const _LinkedDeviceCard({
required this.navigationContract,
required this.device,
required this.isEditing,
required this.onDelete,
@@ -76,7 +84,7 @@ class _LinkedDeviceCard extends ConsumerWidget {
shape: BoxShape.circle,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 8, big: 12)),
child: Icon(SFIcons.watch,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: theme.getColorFor(ThemeCode.legacyPrimary),
@@ -110,7 +118,10 @@ class _LinkedDeviceCard extends ConsumerWidget {
),
child: IconButton(
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
child: DeleteDeviceDialog(device: device),
child: DeleteDeviceDialog(
navigationContract: navigationContract,
device: device,
),
));},
icon: Icon(
Icons.close,

View File

@@ -70,19 +70,32 @@ class LinkedDevicesViewModel extends Notifier<LinkedDevicesViewState> {
);
}
Future<bool> deleteDevice(DeviceEntity device) async {
Future<void> deleteDevice(DeviceEntity device) async {
try {
await _devicesRepository.deleteDevice(userId: state.loggedUser!.id, deviceId: device.identificator);
List<DeviceEntity> newList = state.linkedDevices;
newList.remove(device);
state = state.copyWith(
linkedDevices: newList
isLoading: true,
isComplete: false,
);
return true;
await _devicesRepository.deleteDevice(deviceId: device.id);
List<DeviceEntity> newList = state.linkedDevices.toList();
newList.remove(device);
if (device == state.selectedDevice) {
ref.invalidate(selectedDeviceProvider);
}
state = state.copyWith(
linkedDevices: newList,
isLoading: false,
isComplete: true,
);
} catch (e) {
return false;
state = state.copyWith(
isLoading: false,
errorMessage: e.toString(),
);
return;
}
}
@@ -102,9 +115,7 @@ class LinkedDevicesViewModel extends Notifier<LinkedDevicesViewState> {
if (deviceName.isEmpty) return;
try {
final userId = state.loggedUser!.id;
_devicesRepository.updateDevice(
userId: userId,
request: _toRequest(device));
} catch(e) {
@@ -114,8 +125,6 @@ class LinkedDevicesViewModel extends Notifier<LinkedDevicesViewState> {
errorMessage: e.toString()
);
}
}
void disposeControllers() {

View File

@@ -2,15 +2,20 @@ import 'package:account/src/features/linked_devices/presentation/state/linked_de
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 'package:sf_shared/sf_shared.dart';
import 'package:utils/utils.dart';
class DeleteDeviceDialog extends ConsumerWidget {
final NavigationContract navigationContract;
final DeviceEntity device;
const DeleteDeviceDialog({required this.device});
const DeleteDeviceDialog({
required this.navigationContract,
required this.device
});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -21,15 +26,15 @@ class DeleteDeviceDialog extends ConsumerWidget {
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18)
),
width: SizeUtils.getByScreen(small: 360, big: 350),
height: SizeUtils.getByScreen(small: 195, big: 185),
height: SizeUtils.getByScreen(small: 184, big: 160),
child: Column(
children: [
Text(context.translate(I18n.deleteDeviceDialog),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 17, big: 16)),
),
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
Row(
@@ -46,7 +51,20 @@ class DeleteDeviceDialog extends ConsumerWidget {
Expanded(child: PrimaryButton(
onPressed: () async {
await vm.deleteDevice(device);
Navigator.pop(context);
final isComplete = ref.read(
linkedDevicesViewModelProvider.select((s)=>s.isComplete)
);
if (isComplete) {
Navigator.pop(context);
}
final noMoreDevices = ref.read(
linkedDevicesViewModelProvider.select((s)=>s.linkedDevices)
).isEmpty;
if (noMoreDevices) {
navigationContract.goTo(AppRoutes.legacyDeviceSetup);
}
},
text: context.translate(I18n.delete),
color: theme.getColorFor(ThemeCode.legacyPrimary),

View File

@@ -77,7 +77,7 @@ class PersonalDataScreen extends ConsumerWidget {
child: Column(
children: [
Text(context.translate(I18n.personalDataMessage)),
const SizedBox(height: 14),
const SizedBox(height: 4),
const _SaveButton(),
],
),
@@ -169,12 +169,33 @@ class _PhoneField extends ConsumerWidget {
final hint = ref.watch(
personalDataViewModelProvider.select((s) => s.user?.phone ?? ''),
);
final dialCode = ref.read(
personalDataViewModelProvider.select((s)=>s.dialCode)
);
return CustomTextField(
controller: vm.phoneController,
hint: hint,
label: context.translate(I18n.phoneLabel),
keyboardType: TextInputType.phone,
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
CountryPrefixPicker(
headerText: context.translate(I18n.selectYourCountry),
initialSelection: dialCode,
onChanged: (country) {
vm.updateDialCode(
country.dialCode ?? dialCode,
);
},
width: 80,
),
SizedBox(width: 8),
Expanded(
child: CustomTextField(
controller: vm.phoneController,
hint: hint,
label: context.translate(I18n.phoneLabel),
keyboardType: TextInputType.phone,
)
),
],
);
}
}

View File

@@ -39,12 +39,26 @@ class PersonalDataViewModel extends Notifier<PersonalDataViewState> {
state = state.copyWith(user: user, isLoading: false);
}
void updateDialCode(String value) {
if (value == state.dialCode) return;
state = state.copyWith(
dialCode: value,
errorMessage: '',
);
}
bool get _hasChanges =>
firstNameController.text.trim().isNotEmpty ||
lastNameController.text.trim().isNotEmpty ||
phoneController.text.trim().isNotEmpty;
UpdateUserRequestEntity _toRequest() {
final dialCode = state.dialCode;
final fullPhone = phoneController.text.trim().isNotEmpty
? dialCode+phoneController.text.trim()
: null;
return UpdateUserRequestEntity(
firstName: firstNameController.text.trim().isNotEmpty
? firstNameController.text.trim()
@@ -52,9 +66,7 @@ class PersonalDataViewModel extends Notifier<PersonalDataViewState> {
lastName: lastNameController.text.trim().isNotEmpty
? lastNameController.text.trim()
: null,
phone: phoneController.text.trim().isNotEmpty
? phoneController.text.trim()
: null,
phone: fullPhone,
);
}

View File

@@ -8,6 +8,7 @@ abstract class PersonalDataViewState with _$PersonalDataViewState {
const factory PersonalDataViewState({
@Default(true) bool isLoading,
@Default(false) bool isComplete,
@Default('+34') String dialCode,
UserEntity? user,
@Default('') String errorMessage,
}) = _PersonalDataViewState;

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$PersonalDataViewState {
bool get isLoading; bool get isComplete; UserEntity? get user; String get errorMessage;
bool get isLoading; bool get isComplete; String get dialCode; UserEntity? get user; String get errorMessage;
/// Create a copy of PersonalDataViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $PersonalDataViewStateCopyWith<PersonalDataViewState> get copyWith => _$Personal
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PersonalDataViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.user, user) || other.user == user)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is PersonalDataViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.user, user) || other.user == user)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,user,errorMessage);
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,dialCode,user,errorMessage);
@override
String toString() {
return 'PersonalDataViewState(isLoading: $isLoading, isComplete: $isComplete, user: $user, errorMessage: $errorMessage)';
return 'PersonalDataViewState(isLoading: $isLoading, isComplete: $isComplete, dialCode: $dialCode, user: $user, errorMessage: $errorMessage)';
}
@@ -45,7 +45,7 @@ abstract mixin class $PersonalDataViewStateCopyWith<$Res> {
factory $PersonalDataViewStateCopyWith(PersonalDataViewState value, $Res Function(PersonalDataViewState) _then) = _$PersonalDataViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, bool isComplete, UserEntity? user, String errorMessage
bool isLoading, bool isComplete, String dialCode, UserEntity? user, String errorMessage
});
@@ -62,11 +62,12 @@ class _$PersonalDataViewStateCopyWithImpl<$Res>
/// Create a copy of PersonalDataViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isComplete = null,Object? user = freezed,Object? errorMessage = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? isComplete = null,Object? dialCode = null,Object? user = freezed,Object? errorMessage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable
as bool,user: freezed == user ? _self.user : user // ignore: cast_nullable_to_non_nullable
as bool,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,user: freezed == user ? _self.user : user // ignore: cast_nullable_to_non_nullable
as UserEntity?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
@@ -165,10 +166,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, bool isComplete, UserEntity? user, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, bool isComplete, String dialCode, UserEntity? user, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PersonalDataViewState() when $default != null:
return $default(_that.isLoading,_that.isComplete,_that.user,_that.errorMessage);case _:
return $default(_that.isLoading,_that.isComplete,_that.dialCode,_that.user,_that.errorMessage);case _:
return orElse();
}
@@ -186,10 +187,10 @@ return $default(_that.isLoading,_that.isComplete,_that.user,_that.errorMessage);
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, bool isComplete, UserEntity? user, String errorMessage) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, bool isComplete, String dialCode, UserEntity? user, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _PersonalDataViewState():
return $default(_that.isLoading,_that.isComplete,_that.user,_that.errorMessage);case _:
return $default(_that.isLoading,_that.isComplete,_that.dialCode,_that.user,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
@@ -206,10 +207,10 @@ return $default(_that.isLoading,_that.isComplete,_that.user,_that.errorMessage);
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, bool isComplete, UserEntity? user, String errorMessage)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, bool isComplete, String dialCode, UserEntity? user, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _PersonalDataViewState() when $default != null:
return $default(_that.isLoading,_that.isComplete,_that.user,_that.errorMessage);case _:
return $default(_that.isLoading,_that.isComplete,_that.dialCode,_that.user,_that.errorMessage);case _:
return null;
}
@@ -221,11 +222,12 @@ return $default(_that.isLoading,_that.isComplete,_that.user,_that.errorMessage);
class _PersonalDataViewState implements PersonalDataViewState {
const _PersonalDataViewState({this.isLoading = true, this.isComplete = false, this.user, this.errorMessage = ''});
const _PersonalDataViewState({this.isLoading = true, this.isComplete = false, this.dialCode = '+34', this.user, this.errorMessage = ''});
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isComplete;
@override@JsonKey() final String dialCode;
@override final UserEntity? user;
@override@JsonKey() final String errorMessage;
@@ -239,16 +241,16 @@ _$PersonalDataViewStateCopyWith<_PersonalDataViewState> get copyWith => __$Perso
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PersonalDataViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.user, user) || other.user == user)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PersonalDataViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.user, user) || other.user == user)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,user,errorMessage);
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,dialCode,user,errorMessage);
@override
String toString() {
return 'PersonalDataViewState(isLoading: $isLoading, isComplete: $isComplete, user: $user, errorMessage: $errorMessage)';
return 'PersonalDataViewState(isLoading: $isLoading, isComplete: $isComplete, dialCode: $dialCode, user: $user, errorMessage: $errorMessage)';
}
@@ -259,7 +261,7 @@ abstract mixin class _$PersonalDataViewStateCopyWith<$Res> implements $PersonalD
factory _$PersonalDataViewStateCopyWith(_PersonalDataViewState value, $Res Function(_PersonalDataViewState) _then) = __$PersonalDataViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, bool isComplete, UserEntity? user, String errorMessage
bool isLoading, bool isComplete, String dialCode, UserEntity? user, String errorMessage
});
@@ -276,11 +278,12 @@ class __$PersonalDataViewStateCopyWithImpl<$Res>
/// Create a copy of PersonalDataViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isComplete = null,Object? user = freezed,Object? errorMessage = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? isComplete = null,Object? dialCode = null,Object? user = freezed,Object? errorMessage = null,}) {
return _then(_PersonalDataViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable
as bool,user: freezed == user ? _self.user : user // ignore: cast_nullable_to_non_nullable
as bool,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,user: freezed == user ? _self.user : user // ignore: cast_nullable_to_non_nullable
as UserEntity?,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));

View File

@@ -96,7 +96,7 @@ packages:
source: hosted
version: "3.0.3"
build_runner:
dependency: transitive
dependency: "direct main"
description:
name: build_runner
sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30
@@ -338,10 +338,10 @@ packages:
dependency: transitive
description:
name: fl_chart
sha256: "7ca9a40f4eb85949190e54087be8b4d6ac09dc4c54238d782a34cf1f7c011de9"
sha256: b938f77d042cbcd822936a7a359a7235bad8bd72070de1f827efc2cc297ac888
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
@@ -367,18 +367,18 @@ packages:
dependency: "direct main"
description:
name: flutter_riverpod
sha256: e2026c72738a925a60db30258ff1f29974e40716749f3c9850aabf34ffc1a14c
sha256: "4e166be88e1dbbaa34a280bdb744aeae73b7ef25fdf8db7a3bb776760a3648e2"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
version: "3.3.1"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
sha256: "1ded017b39c8e15c8948ea855070a5ff8ff8b3d5e83f3446e02d6bb12add7ad9"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
version: "2.2.4"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -620,10 +620,10 @@ packages:
dependency: transitive
description:
name: logger
sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3
sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c"
url: "https://pub.dev"
source: hosted
version: "2.6.2"
version: "2.7.0"
logging:
dependency: transitive
description:
@@ -1257,10 +1257,10 @@ packages:
dependency: transitive
description:
name: vector_graphics
sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
sha256: "7076216a10d5c390315fbe536a30f1254c341e7543e6c4c8a815e591307772b1"
url: "https://pub.dev"
source: hosted
version: "1.1.19"
version: "1.1.20"
vector_graphics_codec:
dependency: transitive
description:
@@ -1361,10 +1361,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: fc0af89d403e1c053f03d023d97550412fa79f35332e2939514c82e6fe633198
sha256: "2df8fd9ada04d699b9db8e79aa783a16e5d89b69e5b74009b87e16b59912cf98"
url: "https://pub.dev"
source: hosted
version: "3.23.8"
version: "3.24.0"
wkt_parser:
dependency: transitive
description:

View File

@@ -56,6 +56,7 @@ dependencies:
uuid: ^4.5.2
qr_flutter: ^4.1.0
url_launcher: ^6.3.2
build_runner: ^2.7.1
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.

View File

@@ -1,3 +1,9 @@
export 'src/core/data/models/latest_positions_response_model.dart';
export 'src/core/domain/entities/address_entity.dart';
export 'src/core/domain/entities/network_entity.dart';
export 'src/core/domain/entities/position_entity.dart';
export 'src/core/utils/battery_utils.dart';
export 'src/core/utils/date_format_utils.dart';
export 'src/features/control_panel/control_panel_builder.dart';
export 'src/features/control_panel/presentation/state/control_panel_view_model.dart';
export 'src/shared/widgets/device_map.dart';

View File

@@ -72,11 +72,11 @@ extension LatestPositionsResponseModelMapper on LatestPositionsResponseModel {
@freezed
abstract class LatestPositionsAddressResponseModel with _$LatestPositionsAddressResponseModel {
const factory LatestPositionsAddressResponseModel({
required String street,
required String city,
required String province,
required String state,
required String country,
String? street,
String? city,
String? province,
String? state,
String? country,
}) = _LatestPositionsAddressResponseModel;
factory LatestPositionsAddressResponseModel.fromJson(Map<String, dynamic> json) =>

View File

@@ -628,7 +628,7 @@ $LatestPositionsAddressResponseModelCopyWith<$Res>? get address {
/// @nodoc
mixin _$LatestPositionsAddressResponseModel {
String get street; String get city; String get province; String get state; String get country;
String? get street; String? get city; String? get province; String? get state; String? get country;
/// Create a copy of LatestPositionsAddressResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -661,7 +661,7 @@ abstract mixin class $LatestPositionsAddressResponseModelCopyWith<$Res> {
factory $LatestPositionsAddressResponseModelCopyWith(LatestPositionsAddressResponseModel value, $Res Function(LatestPositionsAddressResponseModel) _then) = _$LatestPositionsAddressResponseModelCopyWithImpl;
@useResult
$Res call({
String street, String city, String province, String state, String country
String? street, String? city, String? province, String? state, String? country
});
@@ -678,14 +678,14 @@ class _$LatestPositionsAddressResponseModelCopyWithImpl<$Res>
/// Create a copy of LatestPositionsAddressResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? street = freezed,Object? city = freezed,Object? province = freezed,Object? state = freezed,Object? country = freezed,}) {
return _then(_self.copyWith(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,
street: freezed == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String?,city: freezed == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String?,province: freezed == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String?,state: freezed == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String?,country: freezed == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String?,
));
}
@@ -770,7 +770,7 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? street, String? city, String? province, String? state, String? country)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _LatestPositionsAddressResponseModel() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country);case _:
@@ -791,7 +791,7 @@ return $default(_that.street,_that.city,_that.province,_that.state,_that.country
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? street, String? city, String? province, String? state, String? country) $default,) {final _that = this;
switch (_that) {
case _LatestPositionsAddressResponseModel():
return $default(_that.street,_that.city,_that.province,_that.state,_that.country);case _:
@@ -811,7 +811,7 @@ return $default(_that.street,_that.city,_that.province,_that.state,_that.country
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String street, String city, String province, String state, String country)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? street, String? city, String? province, String? state, String? country)? $default,) {final _that = this;
switch (_that) {
case _LatestPositionsAddressResponseModel() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country);case _:
@@ -826,14 +826,14 @@ return $default(_that.street,_that.city,_that.province,_that.state,_that.country
@JsonSerializable()
class _LatestPositionsAddressResponseModel implements LatestPositionsAddressResponseModel {
const _LatestPositionsAddressResponseModel({required this.street, required this.city, required this.province, required this.state, required this.country});
const _LatestPositionsAddressResponseModel({this.street, this.city, this.province, this.state, this.country});
factory _LatestPositionsAddressResponseModel.fromJson(Map<String, dynamic> json) => _$LatestPositionsAddressResponseModelFromJson(json);
@override final String street;
@override final String city;
@override final String province;
@override final String state;
@override final String country;
@override final String? street;
@override final String? city;
@override final String? province;
@override final String? state;
@override final String? country;
/// Create a copy of LatestPositionsAddressResponseModel
/// with the given fields replaced by the non-null parameter values.
@@ -868,7 +868,7 @@ abstract mixin class _$LatestPositionsAddressResponseModelCopyWith<$Res> impleme
factory _$LatestPositionsAddressResponseModelCopyWith(_LatestPositionsAddressResponseModel value, $Res Function(_LatestPositionsAddressResponseModel) _then) = __$LatestPositionsAddressResponseModelCopyWithImpl;
@override @useResult
$Res call({
String street, String city, String province, String state, String country
String? street, String? city, String? province, String? state, String? country
});
@@ -885,14 +885,14 @@ class __$LatestPositionsAddressResponseModelCopyWithImpl<$Res>
/// Create a copy of LatestPositionsAddressResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? street = freezed,Object? city = freezed,Object? province = freezed,Object? state = freezed,Object? country = freezed,}) {
return _then(_LatestPositionsAddressResponseModel(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,
street: freezed == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String?,city: freezed == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String?,province: freezed == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String?,state: freezed == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String?,country: freezed == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String?,
));
}

View File

@@ -81,11 +81,11 @@ Map<String, dynamic> _$LatestPositionsItemResponseModelToJson(
_LatestPositionsAddressResponseModel
_$LatestPositionsAddressResponseModelFromJson(Map<String, dynamic> json) =>
_LatestPositionsAddressResponseModel(
street: json['street'] as String,
city: json['city'] as String,
province: json['province'] as String,
state: json['state'] as String,
country: json['country'] as String,
street: json['street'] as String?,
city: json['city'] as String?,
province: json['province'] as String?,
state: json['state'] as String?,
country: json['country'] as String?,
);
Map<String, dynamic> _$LatestPositionsAddressResponseModelToJson(

View File

@@ -13,28 +13,21 @@ import 'package:utils/utils.dart';
class ControlPanelScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const ControlPanelScreen({
super.key,
required this.navigationContract,
});
const ControlPanelScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final state = ref.watch(controlPanelViewModelProvider);
ref.listen(
controlPanelViewModelProvider.select((s) => s.errorMessage),
(previous, next) {
if (next.isNotEmpty) {
showTopSnackbar(
context,
message: next,
type: MessageType.error,
);
}
},
);
ref.listen(controlPanelViewModelProvider.select((s) => s.errorMessage), (
previous,
next,
) {
if (next.isNotEmpty) {
showTopSnackbar(context, message: next, type: MessageType.error);
}
});
if (state.isLoading) {
return Scaffold(
@@ -90,7 +83,9 @@ class _Header extends ConsumerWidget {
alignment: Alignment.center,
children: [
Padding(
padding: EdgeInsets.only(top: SizeUtils.getByScreen(small: 14, big: 14)),
padding: EdgeInsets.only(
top: SizeUtils.getByScreen(small: 14, big: 14),
),
child: Row(
children: [
Image.asset(
@@ -102,17 +97,13 @@ class _Header extends ConsumerWidget {
width: SizeUtils.getByScreen(small: 130, big: 140),
height: 32,
child: CustomDropdown(
items: state.devices
.map(
(DeviceEntity device) {
final name = device.carrierName ?? '';
return Text(
name.length > 10 ? '${name.substring(0, 10)}...' : name,
overflow: TextOverflow.ellipsis,
);
},
)
.toList(),
items: state.devices.map((DeviceEntity device) {
final name = device.carrierName ?? '';
return Text(
name.length > 10 ? '${name.substring(0, 10)}...' : name,
overflow: TextOverflow.ellipsis,
);
}).toList(),
values: state.devices,
value: state.selectedDevice,
onChanged: (device) {
@@ -153,13 +144,13 @@ class _MenuSection extends ConsumerWidget {
text: I18n.customerService,
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_SectionButton(
onPressed: () {
navigationContract.pushTo(AppRoutes.dashboardHome);
},
icon: SFIcons.payments,
text: I18n.sfPay,
),
// _SectionButton(
// onPressed: () {
// navigationContract.pushTo(AppRoutes.dashboardHome);
// },
// icon: SFIcons.payments,
// text: I18n.sfPay,
// ),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_SectionButton(
onPressed: () {
@@ -267,7 +258,6 @@ class _MapSection extends ConsumerWidget {
child: DeviceMap(
selectedPosition: state.selectedPosition,
selectedDevice: state.selectedDevice,
markerSize: 40,
),
),
),

View File

@@ -69,8 +69,9 @@ class ControlPanelViewModel extends Notifier<ControlPanelViewState> {
final latestPositions = positionLists
.where((list) => list.isNotEmpty)
.map((list) {
final valid = list.where((p) => p.latitude != 0 || p.longitude != 0);
return valid.isNotEmpty ? valid.last : list.last;
final valid = list.where((p) => p.latitude != 0 || p.longitude != 0).toList()
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
return valid.isNotEmpty ? valid.first : list.last;
})
.toList();
@@ -92,6 +93,7 @@ class ControlPanelViewModel extends Notifier<ControlPanelViewState> {
Future<void> refreshPositions() async {
if (state.devices.isEmpty) return;
state = state.copyWith(errorMessage: '');
try {
final positionLists = await Future.wait(
state.devices.map(

View File

@@ -5,25 +5,21 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:latlong2/latlong.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:utils/utils.dart';
const _defaultCenter = LatLng(40.4168, -3.7038);
const _defaultZoom = 15.0;
const _tileServerUrl = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
class DeviceMap extends ConsumerStatefulWidget {
final PositionEntity? selectedPosition;
final DeviceEntity? selectedDevice;
final double markerSize;
const DeviceMap({
super.key,
required this.selectedPosition,
required this.selectedDevice,
this.markerSize = 80,
});
@override
@@ -62,6 +58,8 @@ class _DeviceMapState extends ConsumerState<DeviceMap> {
@override
Widget build(BuildContext context) {
final mapStyle = ref.watch(mapStyleProvider);
final primaryColor = ref.read(themePortProvider).getColorFor(ThemeCode.legacyPrimary);
final initialCenter = widget.selectedPosition != null
? LatLng(
widget.selectedPosition!.latitude,
@@ -78,7 +76,7 @@ class _DeviceMapState extends ConsumerState<DeviceMap> {
),
children: [
TileLayer(
urlTemplate: _tileServerUrl,
urlTemplate: mapStyle.urlTemplate,
userAgentPackageName: 'com.savefamily.sf_platform',
),
MarkerLayer(
@@ -89,15 +87,9 @@ class _DeviceMapState extends ConsumerState<DeviceMap> {
widget.selectedPosition!.latitude,
widget.selectedPosition!.longitude,
),
width: widget.markerSize * 2.5,
height: widget.markerSize * 1.8,
child: Align(
alignment: Alignment.topCenter,
child: SvgPicture.asset(
'assets/shared/images/location.svg',
height: widget.markerSize,
),
),
width: 100,
height: 100,
child: PulsingLocationMarker(color: primaryColor),
rotate: true,
),
],
@@ -107,7 +99,7 @@ class _DeviceMapState extends ConsumerState<DeviceMap> {
alignment: Alignment.bottomCenter,
child: LocationBanner(
position: widget.selectedPosition!,
battery: widget.selectedDevice?.battery ?? 0,
device: widget.selectedDevice,
),
),
],
@@ -117,86 +109,156 @@ class _DeviceMapState extends ConsumerState<DeviceMap> {
class LocationBanner extends ConsumerWidget {
final PositionEntity position;
final int battery;
final DeviceEntity? device;
const LocationBanner({
super.key,
required this.position,
required this.battery,
this.device,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final batteryIcon = toBatteryIcon(battery);
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
final batteryValue = device?.battery ?? 0;
final batteryIcon = toBatteryIcon(batteryValue);
final dateText = formatPositionDate(position.positionDate);
final localDevice = device;
final name = localDevice?.carrierName;
final deviceName = localDevice != null
? (name != null && name.isNotEmpty ? name : localDevice.identificator)
: null;
final initial = deviceName != null && deviceName.isNotEmpty
? deviceName[0].toUpperCase()
: null;
final addressText = [
position.address?.street,
position.address?.province,
position.address?.country,
].whereType<String>().where((s) => s.isNotEmpty).join(', ');
return Container(
height: SizeUtils.getByScreen(small: 60, big: 58),
width: SizeUtils.getByScreen(small: 300, big: 298),
margin: EdgeInsets.only(
bottom: SizeUtils.getByScreen(small: 20, big: 16),
),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(
Radius.circular(SizeUtils.getByScreen(small: 9, big: 8)),
),
),
child: Row(
children: [
Icon(
SFIcons.location,
size: SizeUtils.getByScreen(small: 40, big: 38),
color: theme.getColorFor(ThemeCode.legacyPrimary),
return LayoutBuilder(
builder: (context, constraints) {
final isCompact = constraints.maxWidth < 320;
return Container(
width: isCompact ? constraints.maxWidth * 0.85 : 340,
margin: EdgeInsets.only(bottom: isCompact ? 8 : 16),
padding: EdgeInsets.symmetric(
horizontal: isCompact ? 8 : 12,
vertical: isCompact ? 6 : 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
addressText,
overflow: TextOverflow.ellipsis,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(isCompact ? 10 : 16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: isCompact ? 6 : 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
if (initial != null)
Container(
width: isCompact ? 30 : 42,
height: isCompact ? 30 : 42,
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.12),
shape: BoxShape.circle,
),
child: Center(
child: Text(
initial,
style: TextStyle(
fontSize: isCompact ? 13 : 18,
fontWeight: FontWeight.w700,
color: primaryColor,
),
),
),
),
Row(
SizedBox(width: isCompact ? 6 : 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Text(
dateText,
if (deviceName != null)
Text(
deviceName,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11),
fontSize: isCompact ? 12 : 14,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
overflow: TextOverflow.ellipsis,
),
),
if (position.networks.isNotEmpty)
Text(
' | ${position.networks.first.signal}',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11),
if (addressText.isNotEmpty)
Padding(
padding: EdgeInsets.only(
top: deviceName != null ? 1 : 0),
child: Text(
addressText,
style: TextStyle(
fontSize: isCompact ? 9 : 11,
color: Colors.grey.shade500,
),
overflow: TextOverflow.ellipsis,
),
),
Icon(batteryIcon),
Text(
'$battery%',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11),
),
),
],
),
],
),
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
batteryIcon,
size: isCompact ? 13 : 16,
color: batteryValue > 20
? primaryColor
: Colors.orange,
),
const SizedBox(width: 2),
Text(
'$batteryValue%',
style: TextStyle(
fontSize: isCompact ? 10 : 12,
fontWeight: FontWeight.w600,
color: batteryValue > 20
? primaryColor
: Colors.orange,
),
),
],
),
if (!isCompact)
Padding(
padding: const EdgeInsets.only(top: 3),
child: Text(
dateText,
style: TextStyle(
fontSize: 10,
color: Colors.grey.shade400,
),
),
),
],
),
],
),
],
),
);
},
);
}
}

View File

@@ -9,4 +9,7 @@ export 'src/features/locate_device/locate_device_builder.dart';
export 'src/features/health/health_builder.dart';
export 'src/features/rewards/rewards_builder.dart';
export 'src/features/activity_meter/activity_meter_builder.dart';
export 'src/features/apps_use/apps_use_builder.dart';
export 'src/features/apps_use/apps_use_builder.dart';
export 'src/features/volume_control/volume_control_builder.dart';
export 'src/features/call_history/call_history_builder.dart';
export 'src/features/background_image/background_image_builder.dart';

View File

@@ -0,0 +1,14 @@
import '../models/get_background_image_response_model.dart';
abstract class BackgroundImageRemoteDatasource {
Future<GetBackgroundImageResponseModel> getBackgroundImage();
Future<String> uploadImage({
required String path,
});
Future<void> setBackgroundImage({
required String deviceId,
required String photoId,
});
}

View File

@@ -0,0 +1,73 @@
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/configure_dependencies.dart';
import '../models/get_background_image_response_model.dart';
import 'background_image_remote_datasource.dart';
class BackgroundImageRemoteDatasourceImpl
implements BackgroundImageRemoteDatasource {
BackgroundImageRemoteDatasourceImpl(this._repository);
final QuestiaRepository _repository;
@override
Future<GetBackgroundImageResponseModel> getBackgroundImage() async {
final response = await safeCall(
() => _repository.get<dynamic>('/photos/files'),
'Error getting background image',
);
final data = response.data;
if (data == null || (data is Map && data.isEmpty)) {
return const GetBackgroundImageResponseModel(items: []);
}
return GetBackgroundImageResponseModel.fromJson(
data is Map<String, dynamic> ? data : data as Map<String, dynamic>,
);
}
@override
Future<String> uploadImage({required String path}) async {
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(path, filename: 'photo.jpg'),
});
final dio = GetIt.I<Dio>();
dio.options.headers.remove('content-type');
try {
final response = await dio.post<dynamic>('/photos', data: formData);
final data = response.data;
if (data == null) {
throw Exception('Empty response from upload');
}
final map = data is Map<String, dynamic> ? data : <String, dynamic>{};
final id = map['id'] as String?;
if (id == null) {
throw Exception('No photo ID in upload response');
}
return id;
} on DioException catch (e) {
throw mapDioError(e, defaultMessage: 'Error uploading image');
} finally {
dio.options.headers['content-type'] = 'application/json';
}
}
@override
Future<void> setBackgroundImage({
required String deviceId,
required String photoId,
}) async {
await safeCall(
() => _repository.put<dynamic>(
'/devices/$deviceId/background-image',
body: {'photoId': photoId},
),
'Error setting background image',
);
}
}

View File

@@ -68,17 +68,31 @@ class ContactsRemoteDatasourceImpl implements ContactsRemoteDatasource {
required String deviceId,
required List<Map<String, String>> contacts,
}) async {
await safeCall(
() => _repository.post<dynamic>(
'/contact-lists',
body: {
'userId': userId,
'deviceId': deviceId,
'type': 'secondary',
'contacts': contacts,
},
await Future.wait([
safeCall(
() => _repository.post<dynamic>(
'/contact-lists',
body: {
'userId': userId,
'deviceId': deviceId,
'type': 'secondary',
'contacts': contacts,
},
),
'Error syncing contacts to device',
),
'Error syncing contacts to device',
);
safeCall(
() => _repository.post<dynamic>(
'/contact-lists',
body: {
'userId': userId,
'deviceId': deviceId,
'type': 'white',
'contacts': contacts,
},
),
'Error syncing whitelist to device',
),
]);
}
}

View File

@@ -1,7 +0,0 @@
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
abstract class FunctionsRemoteDatasource {
Future<List<PictureEntity>> getPictures({required String userId});
Future<PictureEntity> takePicture({required String userId});
}

View File

@@ -1,55 +0,0 @@
import 'package:device_management/src/core/data/datasources/functions_remote_datasource.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
class FunctionsRemoteDatasourceImpl implements FunctionsRemoteDatasource {
FunctionsRemoteDatasourceImpl(this._repository);
final QuestiaRepository _repository;
@override
Future<List<PictureEntity>> getPictures({required String userId}) async {
/*try {
final response = await _repository.get<Map<String, dynamic>>(
'',
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /users/:userId/contacts');
}
final model = GetPicturesResponseModel.fromJson(data);
return model.toEntity();
} on DioException catch (error) {
throw mapDioError(error, defaultMessage: 'Error to get pictures');
}*/
return [];
}
@override
Future<PictureEntity> takePicture({required String userId}) async {
/*try {
final response = await _repository.get<Map<String, dynamic>>(
'',
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /users/:userId/contacts');
}
final model = GetContactsResponseModel.fromJson(data);
return model.toEntity();
} on DioException catch (error) {
throw mapDioError(error, defaultMessage: 'Error to get contacts');
}*/
return PictureEntity(
id: '1',
deviceId: '1111',
createdAt: DateTime.now(),
takenAt: DateTime.now(),
asset: 'assets/shared/images/iso_sf.png',
);
}
}

View File

@@ -19,7 +19,7 @@ class HealthQueryBuilder {
String orderField = 'occurredAt',
}) {
final orderBy = base64Encode(
utf8.encode('[{"field":"$orderField","order":"${orderDirection.value}"}]'),
utf8.encode('[{"field":"$orderField","sortDirection":"${orderDirection.value}"}]'),
);
final params = <String, dynamic>{'orderBy': orderBy};

View File

@@ -0,0 +1,7 @@
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
abstract class PicturesRemoteDatasource {
Future<List<PictureEntity>> getPictures({required String deviceId});
Future<PictureEntity> takePicture({required String deviceId});
}

View File

@@ -0,0 +1,37 @@
import 'package:device_management/src/core/data/datasources/pictures_remote_datasource.dart';
import 'package:device_management/src/core/data/models/get_pictures_response_model.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:dio/dio.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
class PicturesRemoteDatasourceImpl implements PicturesRemoteDatasource {
PicturesRemoteDatasourceImpl(this._repository);
final QuestiaRepository _repository;
@override
Future<List<PictureEntity>> getPictures({required String deviceId}) async {
try {
final response = await _repository.get<Map<String, dynamic>>(
'/devices/identificator/$deviceId/photos/files',
);
final data = response.data;
if (data == null || data.isEmpty) {
return [];
}
final model = GetPicturesResponseModel.fromJson(data);
return model.toEntity();
} on DioException catch (error) {
if (error.response?.statusCode == 404) return [];
throw mapDioError(error, defaultMessage: 'Error getting pictures');
}
}
@override
Future<PictureEntity> takePicture({required String deviceId}) async {
throw UnimplementedError('takePicture is handled via commands');
}
}

View File

@@ -0,0 +1,38 @@
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'get_pictures_response_model.dart';
part 'get_background_image_response_model.freezed.dart';
part 'get_background_image_response_model.g.dart';
@freezed
abstract class GetBackgroundImageResponseModel with _$GetBackgroundImageResponseModel {
const factory GetBackgroundImageResponseModel({
@Default([]) List<GetPicturesItemResponseModel> items,
@Default(0) int total,
@Default(1) int page,
@Default(1) int pages,
}) = _GetBackgroundImageResponseModel;
factory GetBackgroundImageResponseModel.fromJson(Map<String, dynamic> json) =>
_$GetBackgroundImageResponseModelFromJson(json);
}
extension GetBackgroundImageResponseModelMapper on GetBackgroundImageResponseModel {
List<PictureEntity> toEntities() {
return items
.map((item) => PictureEntity(
id: item.id,
deviceIdentificator: item.deviceIdentificator,
imgType: item.imgType,
timestamp: item.timestamp,
fileId: item.fileId,
fileName: item.fileName,
contentType: item.contentType,
createdAt: item.createdAt,
fileBytes: extractFileBytes(item.file),
))
.toList();
}
}

View File

@@ -0,0 +1,292 @@
// 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 'get_background_image_response_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$GetBackgroundImageResponseModel {
List<GetPicturesItemResponseModel> get items; int get total; int get page; int get pages;
/// Create a copy of GetBackgroundImageResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$GetBackgroundImageResponseModelCopyWith<GetBackgroundImageResponseModel> get copyWith => _$GetBackgroundImageResponseModelCopyWithImpl<GetBackgroundImageResponseModel>(this as GetBackgroundImageResponseModel, _$identity);
/// Serializes this GetBackgroundImageResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is GetBackgroundImageResponseModel&&const DeepCollectionEquality().equals(other.items, items)&&(identical(other.total, total) || other.total == total)&&(identical(other.page, page) || other.page == page)&&(identical(other.pages, pages) || other.pages == pages));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(items),total,page,pages);
@override
String toString() {
return 'GetBackgroundImageResponseModel(items: $items, total: $total, page: $page, pages: $pages)';
}
}
/// @nodoc
abstract mixin class $GetBackgroundImageResponseModelCopyWith<$Res> {
factory $GetBackgroundImageResponseModelCopyWith(GetBackgroundImageResponseModel value, $Res Function(GetBackgroundImageResponseModel) _then) = _$GetBackgroundImageResponseModelCopyWithImpl;
@useResult
$Res call({
List<GetPicturesItemResponseModel> items, int total, int page, int pages
});
}
/// @nodoc
class _$GetBackgroundImageResponseModelCopyWithImpl<$Res>
implements $GetBackgroundImageResponseModelCopyWith<$Res> {
_$GetBackgroundImageResponseModelCopyWithImpl(this._self, this._then);
final GetBackgroundImageResponseModel _self;
final $Res Function(GetBackgroundImageResponseModel) _then;
/// Create a copy of GetBackgroundImageResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? items = null,Object? total = null,Object? page = null,Object? pages = null,}) {
return _then(_self.copyWith(
items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
as List<GetPicturesItemResponseModel>,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
as int,page: null == page ? _self.page : page // ignore: cast_nullable_to_non_nullable
as int,pages: null == pages ? _self.pages : pages // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [GetBackgroundImageResponseModel].
extension GetBackgroundImageResponseModelPatterns on GetBackgroundImageResponseModel {
/// 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( _GetBackgroundImageResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _GetBackgroundImageResponseModel() 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( _GetBackgroundImageResponseModel value) $default,){
final _that = this;
switch (_that) {
case _GetBackgroundImageResponseModel():
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( _GetBackgroundImageResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _GetBackgroundImageResponseModel() 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( List<GetPicturesItemResponseModel> items, int total, int page, int pages)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _GetBackgroundImageResponseModel() when $default != null:
return $default(_that.items,_that.total,_that.page,_that.pages);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( List<GetPicturesItemResponseModel> items, int total, int page, int pages) $default,) {final _that = this;
switch (_that) {
case _GetBackgroundImageResponseModel():
return $default(_that.items,_that.total,_that.page,_that.pages);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( List<GetPicturesItemResponseModel> items, int total, int page, int pages)? $default,) {final _that = this;
switch (_that) {
case _GetBackgroundImageResponseModel() when $default != null:
return $default(_that.items,_that.total,_that.page,_that.pages);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _GetBackgroundImageResponseModel implements GetBackgroundImageResponseModel {
const _GetBackgroundImageResponseModel({final List<GetPicturesItemResponseModel> items = const [], this.total = 0, this.page = 1, this.pages = 1}): _items = items;
factory _GetBackgroundImageResponseModel.fromJson(Map<String, dynamic> json) => _$GetBackgroundImageResponseModelFromJson(json);
final List<GetPicturesItemResponseModel> _items;
@override@JsonKey() List<GetPicturesItemResponseModel> get items {
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_items);
}
@override@JsonKey() final int total;
@override@JsonKey() final int page;
@override@JsonKey() final int pages;
/// Create a copy of GetBackgroundImageResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$GetBackgroundImageResponseModelCopyWith<_GetBackgroundImageResponseModel> get copyWith => __$GetBackgroundImageResponseModelCopyWithImpl<_GetBackgroundImageResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$GetBackgroundImageResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _GetBackgroundImageResponseModel&&const DeepCollectionEquality().equals(other._items, _items)&&(identical(other.total, total) || other.total == total)&&(identical(other.page, page) || other.page == page)&&(identical(other.pages, pages) || other.pages == pages));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_items),total,page,pages);
@override
String toString() {
return 'GetBackgroundImageResponseModel(items: $items, total: $total, page: $page, pages: $pages)';
}
}
/// @nodoc
abstract mixin class _$GetBackgroundImageResponseModelCopyWith<$Res> implements $GetBackgroundImageResponseModelCopyWith<$Res> {
factory _$GetBackgroundImageResponseModelCopyWith(_GetBackgroundImageResponseModel value, $Res Function(_GetBackgroundImageResponseModel) _then) = __$GetBackgroundImageResponseModelCopyWithImpl;
@override @useResult
$Res call({
List<GetPicturesItemResponseModel> items, int total, int page, int pages
});
}
/// @nodoc
class __$GetBackgroundImageResponseModelCopyWithImpl<$Res>
implements _$GetBackgroundImageResponseModelCopyWith<$Res> {
__$GetBackgroundImageResponseModelCopyWithImpl(this._self, this._then);
final _GetBackgroundImageResponseModel _self;
final $Res Function(_GetBackgroundImageResponseModel) _then;
/// Create a copy of GetBackgroundImageResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? items = null,Object? total = null,Object? page = null,Object? pages = null,}) {
return _then(_GetBackgroundImageResponseModel(
items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
as List<GetPicturesItemResponseModel>,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
as int,page: null == page ? _self.page : page // ignore: cast_nullable_to_non_nullable
as int,pages: null == pages ? _self.pages : pages // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -0,0 +1,33 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'get_background_image_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_GetBackgroundImageResponseModel _$GetBackgroundImageResponseModelFromJson(
Map<String, dynamic> json,
) => _GetBackgroundImageResponseModel(
items:
(json['items'] as List<dynamic>?)
?.map(
(e) => GetPicturesItemResponseModel.fromJson(
e as Map<String, dynamic>,
),
)
.toList() ??
const [],
total: (json['total'] as num?)?.toInt() ?? 0,
page: (json['page'] as num?)?.toInt() ?? 1,
pages: (json['pages'] as num?)?.toInt() ?? 1,
);
Map<String, dynamic> _$GetBackgroundImageResponseModelToJson(
_GetBackgroundImageResponseModel instance,
) => <String, dynamic>{
'items': instance.items,
'total': instance.total,
'page': instance.page,
'pages': instance.pages,
};

View File

@@ -0,0 +1,74 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'get_pictures_response_model.freezed.dart';
part 'get_pictures_response_model.g.dart';
@freezed
abstract class GetPicturesResponseModel with _$GetPicturesResponseModel {
const factory GetPicturesResponseModel({
required List<GetPicturesItemResponseModel> items,
}) = _GetPicturesResponseModel;
factory GetPicturesResponseModel.fromJson(Map<String, dynamic> json) =>
_$GetPicturesResponseModelFromJson(json);
}
@freezed
abstract class GetPicturesItemResponseModel
with _$GetPicturesItemResponseModel {
const factory GetPicturesItemResponseModel({
required String id,
@Default('') String deviceIdentificator,
String? imgType,
String? timestamp,
@Default('') String fileId,
String? fileName,
String? contentType,
@Default(0) int createdAt,
Map<String, dynamic>? file,
}) = _GetPicturesItemResponseModel;
factory GetPicturesItemResponseModel.fromJson(Map<String, dynamic> json) =>
_$GetPicturesItemResponseModelFromJson(json);
}
Uint8List? extractFileBytes(Map<String, dynamic>? file) {
if (file == null) return null;
final fileData = file['file'];
if (fileData is String) {
try {
return base64Decode(fileData);
} catch (_) {
return null;
}
}
if (fileData is Map) {
final data = fileData['data'];
if (data is List) {
return Uint8List.fromList(data.cast<int>());
}
}
return null;
}
extension GetPicturesResponseModelMapper on GetPicturesResponseModel {
List<PictureEntity> toEntity() {
return items
.map((item) => PictureEntity(
id: item.id,
deviceIdentificator: item.deviceIdentificator,
imgType: item.imgType,
timestamp: item.timestamp,
fileId: item.fileId,
fileName: item.fileName,
contentType: item.contentType,
createdAt: item.createdAt,
fileBytes: extractFileBytes(item.file),
))
.toList();
}
}

View File

@@ -0,0 +1,578 @@
// 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 'get_pictures_response_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$GetPicturesResponseModel {
List<GetPicturesItemResponseModel> get items;
/// Create a copy of GetPicturesResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$GetPicturesResponseModelCopyWith<GetPicturesResponseModel> get copyWith => _$GetPicturesResponseModelCopyWithImpl<GetPicturesResponseModel>(this as GetPicturesResponseModel, _$identity);
/// Serializes this GetPicturesResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is GetPicturesResponseModel&&const DeepCollectionEquality().equals(other.items, items));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(items));
@override
String toString() {
return 'GetPicturesResponseModel(items: $items)';
}
}
/// @nodoc
abstract mixin class $GetPicturesResponseModelCopyWith<$Res> {
factory $GetPicturesResponseModelCopyWith(GetPicturesResponseModel value, $Res Function(GetPicturesResponseModel) _then) = _$GetPicturesResponseModelCopyWithImpl;
@useResult
$Res call({
List<GetPicturesItemResponseModel> items
});
}
/// @nodoc
class _$GetPicturesResponseModelCopyWithImpl<$Res>
implements $GetPicturesResponseModelCopyWith<$Res> {
_$GetPicturesResponseModelCopyWithImpl(this._self, this._then);
final GetPicturesResponseModel _self;
final $Res Function(GetPicturesResponseModel) _then;
/// Create a copy of GetPicturesResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? items = null,}) {
return _then(_self.copyWith(
items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
as List<GetPicturesItemResponseModel>,
));
}
}
/// Adds pattern-matching-related methods to [GetPicturesResponseModel].
extension GetPicturesResponseModelPatterns on GetPicturesResponseModel {
/// 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( _GetPicturesResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _GetPicturesResponseModel() 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( _GetPicturesResponseModel value) $default,){
final _that = this;
switch (_that) {
case _GetPicturesResponseModel():
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( _GetPicturesResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _GetPicturesResponseModel() 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( List<GetPicturesItemResponseModel> items)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _GetPicturesResponseModel() when $default != null:
return $default(_that.items);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( List<GetPicturesItemResponseModel> items) $default,) {final _that = this;
switch (_that) {
case _GetPicturesResponseModel():
return $default(_that.items);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( List<GetPicturesItemResponseModel> items)? $default,) {final _that = this;
switch (_that) {
case _GetPicturesResponseModel() when $default != null:
return $default(_that.items);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _GetPicturesResponseModel implements GetPicturesResponseModel {
const _GetPicturesResponseModel({required final List<GetPicturesItemResponseModel> items}): _items = items;
factory _GetPicturesResponseModel.fromJson(Map<String, dynamic> json) => _$GetPicturesResponseModelFromJson(json);
final List<GetPicturesItemResponseModel> _items;
@override List<GetPicturesItemResponseModel> get items {
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_items);
}
/// Create a copy of GetPicturesResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$GetPicturesResponseModelCopyWith<_GetPicturesResponseModel> get copyWith => __$GetPicturesResponseModelCopyWithImpl<_GetPicturesResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$GetPicturesResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _GetPicturesResponseModel&&const DeepCollectionEquality().equals(other._items, _items));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_items));
@override
String toString() {
return 'GetPicturesResponseModel(items: $items)';
}
}
/// @nodoc
abstract mixin class _$GetPicturesResponseModelCopyWith<$Res> implements $GetPicturesResponseModelCopyWith<$Res> {
factory _$GetPicturesResponseModelCopyWith(_GetPicturesResponseModel value, $Res Function(_GetPicturesResponseModel) _then) = __$GetPicturesResponseModelCopyWithImpl;
@override @useResult
$Res call({
List<GetPicturesItemResponseModel> items
});
}
/// @nodoc
class __$GetPicturesResponseModelCopyWithImpl<$Res>
implements _$GetPicturesResponseModelCopyWith<$Res> {
__$GetPicturesResponseModelCopyWithImpl(this._self, this._then);
final _GetPicturesResponseModel _self;
final $Res Function(_GetPicturesResponseModel) _then;
/// Create a copy of GetPicturesResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? items = null,}) {
return _then(_GetPicturesResponseModel(
items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
as List<GetPicturesItemResponseModel>,
));
}
}
/// @nodoc
mixin _$GetPicturesItemResponseModel {
String get id; String get deviceIdentificator; String? get imgType; String? get timestamp; String get fileId; String? get fileName; String? get contentType; int get createdAt; Map<String, dynamic>? get file;
/// Create a copy of GetPicturesItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$GetPicturesItemResponseModelCopyWith<GetPicturesItemResponseModel> get copyWith => _$GetPicturesItemResponseModelCopyWithImpl<GetPicturesItemResponseModel>(this as GetPicturesItemResponseModel, _$identity);
/// Serializes this GetPicturesItemResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is GetPicturesItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.imgType, imgType) || other.imgType == imgType)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.fileId, fileId) || other.fileId == fileId)&&(identical(other.fileName, fileName) || other.fileName == fileName)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&const DeepCollectionEquality().equals(other.file, file));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt,const DeepCollectionEquality().hash(file));
@override
String toString() {
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt, file: $file)';
}
}
/// @nodoc
abstract mixin class $GetPicturesItemResponseModelCopyWith<$Res> {
factory $GetPicturesItemResponseModelCopyWith(GetPicturesItemResponseModel value, $Res Function(GetPicturesItemResponseModel) _then) = _$GetPicturesItemResponseModelCopyWithImpl;
@useResult
$Res call({
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file
});
}
/// @nodoc
class _$GetPicturesItemResponseModelCopyWithImpl<$Res>
implements $GetPicturesItemResponseModelCopyWith<$Res> {
_$GetPicturesItemResponseModelCopyWithImpl(this._self, this._then);
final GetPicturesItemResponseModel _self;
final $Res Function(GetPicturesItemResponseModel) _then;
/// Create a copy of GetPicturesItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceIdentificator = null,Object? imgType = freezed,Object? timestamp = freezed,Object? fileId = null,Object? fileName = freezed,Object? contentType = freezed,Object? createdAt = null,Object? file = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,imgType: freezed == imgType ? _self.imgType : imgType // ignore: cast_nullable_to_non_nullable
as String?,timestamp: freezed == timestamp ? _self.timestamp : timestamp // ignore: cast_nullable_to_non_nullable
as String?,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullable_to_non_nullable
as String,fileName: freezed == fileName ? _self.fileName : fileName // ignore: cast_nullable_to_non_nullable
as String?,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,file: freezed == file ? _self.file : file // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
));
}
}
/// Adds pattern-matching-related methods to [GetPicturesItemResponseModel].
extension GetPicturesItemResponseModelPatterns on GetPicturesItemResponseModel {
/// 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( _GetPicturesItemResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _GetPicturesItemResponseModel() 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( _GetPicturesItemResponseModel value) $default,){
final _that = this;
switch (_that) {
case _GetPicturesItemResponseModel():
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( _GetPicturesItemResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _GetPicturesItemResponseModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _GetPicturesItemResponseModel() when $default != null:
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.file);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file) $default,) {final _that = this;
switch (_that) {
case _GetPicturesItemResponseModel():
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.file);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file)? $default,) {final _that = this;
switch (_that) {
case _GetPicturesItemResponseModel() when $default != null:
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.file);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _GetPicturesItemResponseModel implements GetPicturesItemResponseModel {
const _GetPicturesItemResponseModel({required this.id, this.deviceIdentificator = '', this.imgType, this.timestamp, this.fileId = '', this.fileName, this.contentType, this.createdAt = 0, final Map<String, dynamic>? file}): _file = file;
factory _GetPicturesItemResponseModel.fromJson(Map<String, dynamic> json) => _$GetPicturesItemResponseModelFromJson(json);
@override final String id;
@override@JsonKey() final String deviceIdentificator;
@override final String? imgType;
@override final String? timestamp;
@override@JsonKey() final String fileId;
@override final String? fileName;
@override final String? contentType;
@override@JsonKey() final int createdAt;
final Map<String, dynamic>? _file;
@override Map<String, dynamic>? get file {
final value = _file;
if (value == null) return null;
if (_file is EqualUnmodifiableMapView) return _file;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(value);
}
/// Create a copy of GetPicturesItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$GetPicturesItemResponseModelCopyWith<_GetPicturesItemResponseModel> get copyWith => __$GetPicturesItemResponseModelCopyWithImpl<_GetPicturesItemResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$GetPicturesItemResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _GetPicturesItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.imgType, imgType) || other.imgType == imgType)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.fileId, fileId) || other.fileId == fileId)&&(identical(other.fileName, fileName) || other.fileName == fileName)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&const DeepCollectionEquality().equals(other._file, _file));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt,const DeepCollectionEquality().hash(_file));
@override
String toString() {
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt, file: $file)';
}
}
/// @nodoc
abstract mixin class _$GetPicturesItemResponseModelCopyWith<$Res> implements $GetPicturesItemResponseModelCopyWith<$Res> {
factory _$GetPicturesItemResponseModelCopyWith(_GetPicturesItemResponseModel value, $Res Function(_GetPicturesItemResponseModel) _then) = __$GetPicturesItemResponseModelCopyWithImpl;
@override @useResult
$Res call({
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file
});
}
/// @nodoc
class __$GetPicturesItemResponseModelCopyWithImpl<$Res>
implements _$GetPicturesItemResponseModelCopyWith<$Res> {
__$GetPicturesItemResponseModelCopyWithImpl(this._self, this._then);
final _GetPicturesItemResponseModel _self;
final $Res Function(_GetPicturesItemResponseModel) _then;
/// Create a copy of GetPicturesItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceIdentificator = null,Object? imgType = freezed,Object? timestamp = freezed,Object? fileId = null,Object? fileName = freezed,Object? contentType = freezed,Object? createdAt = null,Object? file = freezed,}) {
return _then(_GetPicturesItemResponseModel(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,imgType: freezed == imgType ? _self.imgType : imgType // ignore: cast_nullable_to_non_nullable
as String?,timestamp: freezed == timestamp ? _self.timestamp : timestamp // ignore: cast_nullable_to_non_nullable
as String?,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullable_to_non_nullable
as String,fileName: freezed == fileName ? _self.fileName : fileName // ignore: cast_nullable_to_non_nullable
as String?,contentType: freezed == contentType ? _self.contentType : contentType // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,file: freezed == file ? _self._file : file // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
));
}
}
// dart format on

View File

@@ -0,0 +1,49 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'get_pictures_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_GetPicturesResponseModel _$GetPicturesResponseModelFromJson(
Map<String, dynamic> json,
) => _GetPicturesResponseModel(
items: (json['items'] as List<dynamic>)
.map(
(e) => GetPicturesItemResponseModel.fromJson(e as Map<String, dynamic>),
)
.toList(),
);
Map<String, dynamic> _$GetPicturesResponseModelToJson(
_GetPicturesResponseModel instance,
) => <String, dynamic>{'items': instance.items};
_GetPicturesItemResponseModel _$GetPicturesItemResponseModelFromJson(
Map<String, dynamic> json,
) => _GetPicturesItemResponseModel(
id: json['id'] as String,
deviceIdentificator: json['deviceIdentificator'] as String? ?? '',
imgType: json['imgType'] as String?,
timestamp: json['timestamp'] as String?,
fileId: json['fileId'] as String? ?? '',
fileName: json['fileName'] as String?,
contentType: json['contentType'] as String?,
createdAt: (json['createdAt'] as num?)?.toInt() ?? 0,
file: json['file'] as Map<String, dynamic>?,
);
Map<String, dynamic> _$GetPicturesItemResponseModelToJson(
_GetPicturesItemResponseModel instance,
) => <String, dynamic>{
'id': instance.id,
'deviceIdentificator': instance.deviceIdentificator,
'imgType': instance.imgType,
'timestamp': instance.timestamp,
'fileId': instance.fileId,
'fileName': instance.fileName,
'contentType': instance.contentType,
'createdAt': instance.createdAt,
'file': instance.file,
};

View File

@@ -0,0 +1,28 @@
import 'package:device_management/src/core/data/models/get_background_image_response_model.dart';
import '../../../features/remote_connection/domain/entities/picture_entity.dart';
import '../../domain/repositories/background_image_repository.dart';
import '../datasources/background_image_remote_datasource.dart';
class BackgroundImageRepositoryImpl implements BackgroundImageRepository {
const BackgroundImageRepositoryImpl(this._remote);
final BackgroundImageRemoteDatasource _remote;
@override
Future<List<PictureEntity>> getPhotos() async {
final model = await _remote.getBackgroundImage();
return model.toEntities();
}
@override
Future<String> uploadImage({required String path}) {
return _remote.uploadImage(path: path);
}
@override
Future<void> setBackgroundImage({required String deviceId, required String photoId}) {
return _remote.setBackgroundImage(deviceId: deviceId, photoId: photoId);
}
}

View File

@@ -1,21 +0,0 @@
import 'package:device_management/src/core/data/datasources/functions_remote_datasource.dart';
import 'package:device_management/src/core/domain/repositories/functions_repository.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
class FunctionsRepositoryImpl implements FunctionsRepository {
const FunctionsRepositoryImpl(this._remote);
final FunctionsRemoteDatasource _remote;
@override
Future<List<PictureEntity>> getPictures({required String userId}) async {
await Future<void>.delayed(const Duration(milliseconds: 2000));
return _remote.getPictures(userId: userId);
}
@override
Future<PictureEntity> takePicture({required String userId}) async {
await Future<void>.delayed(const Duration(milliseconds: 2000));
return _remote.takePicture(userId: userId);
}
}

View File

@@ -0,0 +1,20 @@
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import '../../domain/repositories/pictures_repository.dart';
import '../datasources/pictures_remote_datasource.dart';
class PicturesRepositoryImpl implements PicturesRepository {
const PicturesRepositoryImpl(this._remote);
final PicturesRemoteDatasource _remote;
@override
Future<List<PictureEntity>> getPictures({required String deviceId}) {
return _remote.getPictures(deviceId: deviceId);
}
@override
Future<PictureEntity> takePicture({required String deviceId}) {
return _remote.takePicture(deviceId: deviceId);
}
}

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