Compare commits

...

12 Commits

Author SHA1 Message Date
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
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
48d2430c9c remote call command 2026-03-18 17:09:03 +01:00
133 changed files with 5982 additions and 1292 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>

View File

@@ -39,7 +39,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,16 @@ 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,
),
],
),
],

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

@@ -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:
@@ -1117,6 +1125,22 @@ packages:
relative: true
source: path
version: "0.0.1"
share_plus:
dependency: transitive
description:
name: share_plus
sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da
url: "https://pub.dev"
source: hosted
version: "10.1.4"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b
url: "https://pub.dev"
source: hosted
version: "5.0.2"
shared_preferences:
dependency: transitive
description:
@@ -1560,6 +1584,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

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

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

@@ -27,21 +27,23 @@ class LinkedDevicesScreen extends ConsumerWidget {
title: context.translate(I18n.linkedDevices),
showEdit: true,
onEditChange: vm.toggleIsEditing,
body: 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]),
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
),
),
separatorBuilder: (BuildContext context, int index)=>SizedBox(
height: SizeUtils.getByScreen(small: 18, big: 17)
),
itemCount: state.linkedDevices.length
),
),
);
}
}

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

@@ -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();

View File

@@ -59,6 +59,7 @@ 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,
@@ -88,7 +89,7 @@ class _DeviceMapState extends ConsumerState<DeviceMap> {
),
width: 100,
height: 100,
child: const PulsingLocationMarker(),
child: PulsingLocationMarker(color: primaryColor),
rotate: true,
),
],

View File

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

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',
);
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,50 @@
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,
required String deviceIdentificator,
String? imgType,
String? timestamp,
required String fileId,
String? fileName,
String? contentType,
required int createdAt,
}) = _GetPicturesItemResponseModel;
factory GetPicturesItemResponseModel.fromJson(Map<String, dynamic> json) =>
_$GetPicturesItemResponseModelFromJson(json);
}
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,
))
.toList();
}
}

View File

@@ -0,0 +1,567 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of '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;
/// 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));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
@override
String toString() {
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
}
}
/// @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
});
}
/// @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,}) {
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,
));
}
}
/// 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)? $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);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) $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);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)? $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);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _GetPicturesItemResponseModel implements GetPicturesItemResponseModel {
const _GetPicturesItemResponseModel({required this.id, required this.deviceIdentificator, this.imgType, this.timestamp, required this.fileId, this.fileName, this.contentType, required this.createdAt});
factory _GetPicturesItemResponseModel.fromJson(Map<String, dynamic> json) => _$GetPicturesItemResponseModelFromJson(json);
@override final String id;
@override final String deviceIdentificator;
@override final String? imgType;
@override final String? timestamp;
@override final String fileId;
@override final String? fileName;
@override final String? contentType;
@override final int createdAt;
/// 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));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
@override
String toString() {
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
}
}
/// @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
});
}
/// @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,}) {
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,
));
}
}
// dart format on

View File

@@ -0,0 +1,47 @@
// 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(),
);
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,
};

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);
}
}

View File

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

View File

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

View File

@@ -1,9 +0,0 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:device_management/src/core/data/datasources/functions_remote_datasource.dart';
import 'package:device_management/src/core/data/datasources/functions_remote_datasource_impl.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
final functionsRemoteDatasourceProvider = Provider<FunctionsRemoteDatasource>((ref) {
final questiaRepository = getIt<QuestiaRepository>();
return FunctionsRemoteDatasourceImpl(questiaRepository);
});

View File

@@ -1,9 +0,0 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:device_management/src/core/providers/functions_remote_datasource_provider.dart';
import 'package:device_management/src/core/data/repositories/functions_repository_impl.dart';
import 'package:device_management/src/core/domain/repositories/functions_repository.dart';
final functionsRepositoryProvider = Provider<FunctionsRepository>((ref) {
final remote = ref.read(functionsRemoteDatasourceProvider);
return FunctionsRepositoryImpl(remote);
});

View File

@@ -0,0 +1,10 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import '../data/datasources/pictures_remote_datasource.dart';
import '../data/datasources/pictures_remote_datasource_impl.dart';
final picturesRemoteDatasourceProvider = Provider<PicturesRemoteDatasource>((ref) {
final questiaRepository = getIt<QuestiaRepository>();
return PicturesRemoteDatasourceImpl(questiaRepository);
});

View File

@@ -0,0 +1,10 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../data/repositories/pictures_repository_impl.dart';
import '../domain/repositories/pictures_repository.dart';
import 'pictures_remote_datasource_provider.dart';
final picturesRepositoryProvider = Provider<PicturesRepository>((ref) {
final remote = ref.read(picturesRemoteDatasourceProvider);
return PicturesRepositoryImpl(remote);
});

View File

@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'presentation/call_history_screen.dart';
class CallHistoryBuilder {
const CallHistoryBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
return MaterialPage<void>(
key: state.pageKey,
child: const CallHistoryScreen(),
);
}
}

View File

@@ -0,0 +1,42 @@
import 'package:dio/dio.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'call_history_entity.dart';
import 'call_history_response_model.dart';
class CallHistoryDatasource {
CallHistoryDatasource(this._repository);
final QuestiaRepository _repository;
Future<List<CallHistoryEntity>> getCallHistory({
required String deviceIdentificator,
}) async {
try {
final response = await _repository.get<Map<String, dynamic>>(
'/devices/identificator/$deviceIdentificator/call-histories',
);
final data = response.data;
if (data == null || data.isEmpty) return [];
final model = CallHistoryResponseModel.fromJson(data);
return model.items
.map((item) => CallHistoryEntity(
deviceIdentificator: item.deviceIdentificator,
phone: item.phone,
name: item.name,
isIncoming: item.isIncoming,
isAccepted: item.isAccepted,
duration: item.duration,
occurredAt: item.occurredAt,
createdAt: item.createdAt,
))
.toList();
} on DioException catch (error) {
if (error.response?.statusCode == 404) return [];
throw mapDioError(error, defaultMessage: 'Error getting call history');
}
}
}

View File

@@ -0,0 +1,9 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'call_history_datasource.dart';
final callHistoryDatasourceProvider = Provider<CallHistoryDatasource>((ref) {
return CallHistoryDatasource(GetIt.I<QuestiaRepository>());
});

View File

@@ -0,0 +1,17 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'call_history_entity.freezed.dart';
@freezed
abstract class CallHistoryEntity with _$CallHistoryEntity {
const factory CallHistoryEntity({
required String deviceIdentificator,
required String phone,
String? name,
required bool isIncoming,
required bool isAccepted,
required int duration,
required int occurredAt,
required int createdAt,
}) = _CallHistoryEntity;
}

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 'call_history_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$CallHistoryEntity {
String get deviceIdentificator; String get phone; String? get name; bool get isIncoming; bool get isAccepted; int get duration; int get occurredAt; int get createdAt;
/// Create a copy of CallHistoryEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CallHistoryEntityCopyWith<CallHistoryEntity> get copyWith => _$CallHistoryEntityCopyWithImpl<CallHistoryEntity>(this as CallHistoryEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallHistoryEntity&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.name, name) || other.name == name)&&(identical(other.isIncoming, isIncoming) || other.isIncoming == isIncoming)&&(identical(other.isAccepted, isAccepted) || other.isAccepted == isAccepted)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.occurredAt, occurredAt) || other.occurredAt == occurredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@override
int get hashCode => Object.hash(runtimeType,deviceIdentificator,phone,name,isIncoming,isAccepted,duration,occurredAt,createdAt);
@override
String toString() {
return 'CallHistoryEntity(deviceIdentificator: $deviceIdentificator, phone: $phone, name: $name, isIncoming: $isIncoming, isAccepted: $isAccepted, duration: $duration, occurredAt: $occurredAt, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class $CallHistoryEntityCopyWith<$Res> {
factory $CallHistoryEntityCopyWith(CallHistoryEntity value, $Res Function(CallHistoryEntity) _then) = _$CallHistoryEntityCopyWithImpl;
@useResult
$Res call({
String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt
});
}
/// @nodoc
class _$CallHistoryEntityCopyWithImpl<$Res>
implements $CallHistoryEntityCopyWith<$Res> {
_$CallHistoryEntityCopyWithImpl(this._self, this._then);
final CallHistoryEntity _self;
final $Res Function(CallHistoryEntity) _then;
/// Create a copy of CallHistoryEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? deviceIdentificator = null,Object? phone = null,Object? name = freezed,Object? isIncoming = null,Object? isAccepted = null,Object? duration = null,Object? occurredAt = null,Object? createdAt = null,}) {
return _then(_self.copyWith(
deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,isIncoming: null == isIncoming ? _self.isIncoming : isIncoming // ignore: cast_nullable_to_non_nullable
as bool,isAccepted: null == isAccepted ? _self.isAccepted : isAccepted // ignore: cast_nullable_to_non_nullable
as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable
as int,occurredAt: null == occurredAt ? _self.occurredAt : occurredAt // ignore: cast_nullable_to_non_nullable
as int,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [CallHistoryEntity].
extension CallHistoryEntityPatterns on CallHistoryEntity {
/// 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( _CallHistoryEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CallHistoryEntity() 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( _CallHistoryEntity value) $default,){
final _that = this;
switch (_that) {
case _CallHistoryEntity():
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( _CallHistoryEntity value)? $default,){
final _that = this;
switch (_that) {
case _CallHistoryEntity() 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 deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CallHistoryEntity() when $default != null:
return $default(_that.deviceIdentificator,_that.phone,_that.name,_that.isIncoming,_that.isAccepted,_that.duration,_that.occurredAt,_that.createdAt);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt) $default,) {final _that = this;
switch (_that) {
case _CallHistoryEntity():
return $default(_that.deviceIdentificator,_that.phone,_that.name,_that.isIncoming,_that.isAccepted,_that.duration,_that.occurredAt,_that.createdAt);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt)? $default,) {final _that = this;
switch (_that) {
case _CallHistoryEntity() when $default != null:
return $default(_that.deviceIdentificator,_that.phone,_that.name,_that.isIncoming,_that.isAccepted,_that.duration,_that.occurredAt,_that.createdAt);case _:
return null;
}
}
}
/// @nodoc
class _CallHistoryEntity implements CallHistoryEntity {
const _CallHistoryEntity({required this.deviceIdentificator, required this.phone, this.name, required this.isIncoming, required this.isAccepted, required this.duration, required this.occurredAt, required this.createdAt});
@override final String deviceIdentificator;
@override final String phone;
@override final String? name;
@override final bool isIncoming;
@override final bool isAccepted;
@override final int duration;
@override final int occurredAt;
@override final int createdAt;
/// Create a copy of CallHistoryEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CallHistoryEntityCopyWith<_CallHistoryEntity> get copyWith => __$CallHistoryEntityCopyWithImpl<_CallHistoryEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallHistoryEntity&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.name, name) || other.name == name)&&(identical(other.isIncoming, isIncoming) || other.isIncoming == isIncoming)&&(identical(other.isAccepted, isAccepted) || other.isAccepted == isAccepted)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.occurredAt, occurredAt) || other.occurredAt == occurredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@override
int get hashCode => Object.hash(runtimeType,deviceIdentificator,phone,name,isIncoming,isAccepted,duration,occurredAt,createdAt);
@override
String toString() {
return 'CallHistoryEntity(deviceIdentificator: $deviceIdentificator, phone: $phone, name: $name, isIncoming: $isIncoming, isAccepted: $isAccepted, duration: $duration, occurredAt: $occurredAt, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class _$CallHistoryEntityCopyWith<$Res> implements $CallHistoryEntityCopyWith<$Res> {
factory _$CallHistoryEntityCopyWith(_CallHistoryEntity value, $Res Function(_CallHistoryEntity) _then) = __$CallHistoryEntityCopyWithImpl;
@override @useResult
$Res call({
String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt
});
}
/// @nodoc
class __$CallHistoryEntityCopyWithImpl<$Res>
implements _$CallHistoryEntityCopyWith<$Res> {
__$CallHistoryEntityCopyWithImpl(this._self, this._then);
final _CallHistoryEntity _self;
final $Res Function(_CallHistoryEntity) _then;
/// Create a copy of CallHistoryEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? deviceIdentificator = null,Object? phone = null,Object? name = freezed,Object? isIncoming = null,Object? isAccepted = null,Object? duration = null,Object? occurredAt = null,Object? createdAt = null,}) {
return _then(_CallHistoryEntity(
deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,isIncoming: null == isIncoming ? _self.isIncoming : isIncoming // ignore: cast_nullable_to_non_nullable
as bool,isAccepted: null == isAccepted ? _self.isAccepted : isAccepted // ignore: cast_nullable_to_non_nullable
as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable
as int,occurredAt: null == occurredAt ? _self.occurredAt : occurredAt // ignore: cast_nullable_to_non_nullable
as int,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -0,0 +1,32 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'call_history_response_model.freezed.dart';
part 'call_history_response_model.g.dart';
@freezed
abstract class CallHistoryResponseModel with _$CallHistoryResponseModel {
const factory CallHistoryResponseModel({
required int total,
required List<CallHistoryItemModel> items,
}) = _CallHistoryResponseModel;
factory CallHistoryResponseModel.fromJson(Map<String, dynamic> json) =>
_$CallHistoryResponseModelFromJson(json);
}
@freezed
abstract class CallHistoryItemModel with _$CallHistoryItemModel {
const factory CallHistoryItemModel({
required String deviceIdentificator,
required String phone,
String? name,
required bool isIncoming,
required bool isAccepted,
required int duration,
required int occurredAt,
required int createdAt,
}) = _CallHistoryItemModel;
factory CallHistoryItemModel.fromJson(Map<String, dynamic> json) =>
_$CallHistoryItemModelFromJson(json);
}

View File

@@ -0,0 +1,570 @@
// 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 'call_history_response_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$CallHistoryResponseModel {
int get total; List<CallHistoryItemModel> get items;
/// Create a copy of CallHistoryResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CallHistoryResponseModelCopyWith<CallHistoryResponseModel> get copyWith => _$CallHistoryResponseModelCopyWithImpl<CallHistoryResponseModel>(this as CallHistoryResponseModel, _$identity);
/// Serializes this CallHistoryResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallHistoryResponseModel&&(identical(other.total, total) || other.total == total)&&const DeepCollectionEquality().equals(other.items, items));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,total,const DeepCollectionEquality().hash(items));
@override
String toString() {
return 'CallHistoryResponseModel(total: $total, items: $items)';
}
}
/// @nodoc
abstract mixin class $CallHistoryResponseModelCopyWith<$Res> {
factory $CallHistoryResponseModelCopyWith(CallHistoryResponseModel value, $Res Function(CallHistoryResponseModel) _then) = _$CallHistoryResponseModelCopyWithImpl;
@useResult
$Res call({
int total, List<CallHistoryItemModel> items
});
}
/// @nodoc
class _$CallHistoryResponseModelCopyWithImpl<$Res>
implements $CallHistoryResponseModelCopyWith<$Res> {
_$CallHistoryResponseModelCopyWithImpl(this._self, this._then);
final CallHistoryResponseModel _self;
final $Res Function(CallHistoryResponseModel) _then;
/// Create a copy of CallHistoryResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? total = null,Object? items = null,}) {
return _then(_self.copyWith(
total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
as int,items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
as List<CallHistoryItemModel>,
));
}
}
/// Adds pattern-matching-related methods to [CallHistoryResponseModel].
extension CallHistoryResponseModelPatterns on CallHistoryResponseModel {
/// 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( _CallHistoryResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CallHistoryResponseModel() 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( _CallHistoryResponseModel value) $default,){
final _that = this;
switch (_that) {
case _CallHistoryResponseModel():
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( _CallHistoryResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _CallHistoryResponseModel() 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( int total, List<CallHistoryItemModel> items)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CallHistoryResponseModel() when $default != null:
return $default(_that.total,_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( int total, List<CallHistoryItemModel> items) $default,) {final _that = this;
switch (_that) {
case _CallHistoryResponseModel():
return $default(_that.total,_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( int total, List<CallHistoryItemModel> items)? $default,) {final _that = this;
switch (_that) {
case _CallHistoryResponseModel() when $default != null:
return $default(_that.total,_that.items);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _CallHistoryResponseModel implements CallHistoryResponseModel {
const _CallHistoryResponseModel({required this.total, required final List<CallHistoryItemModel> items}): _items = items;
factory _CallHistoryResponseModel.fromJson(Map<String, dynamic> json) => _$CallHistoryResponseModelFromJson(json);
@override final int total;
final List<CallHistoryItemModel> _items;
@override List<CallHistoryItemModel> get items {
if (_items is EqualUnmodifiableListView) return _items;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_items);
}
/// Create a copy of CallHistoryResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CallHistoryResponseModelCopyWith<_CallHistoryResponseModel> get copyWith => __$CallHistoryResponseModelCopyWithImpl<_CallHistoryResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$CallHistoryResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallHistoryResponseModel&&(identical(other.total, total) || other.total == total)&&const DeepCollectionEquality().equals(other._items, _items));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,total,const DeepCollectionEquality().hash(_items));
@override
String toString() {
return 'CallHistoryResponseModel(total: $total, items: $items)';
}
}
/// @nodoc
abstract mixin class _$CallHistoryResponseModelCopyWith<$Res> implements $CallHistoryResponseModelCopyWith<$Res> {
factory _$CallHistoryResponseModelCopyWith(_CallHistoryResponseModel value, $Res Function(_CallHistoryResponseModel) _then) = __$CallHistoryResponseModelCopyWithImpl;
@override @useResult
$Res call({
int total, List<CallHistoryItemModel> items
});
}
/// @nodoc
class __$CallHistoryResponseModelCopyWithImpl<$Res>
implements _$CallHistoryResponseModelCopyWith<$Res> {
__$CallHistoryResponseModelCopyWithImpl(this._self, this._then);
final _CallHistoryResponseModel _self;
final $Res Function(_CallHistoryResponseModel) _then;
/// Create a copy of CallHistoryResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? total = null,Object? items = null,}) {
return _then(_CallHistoryResponseModel(
total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
as int,items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
as List<CallHistoryItemModel>,
));
}
}
/// @nodoc
mixin _$CallHistoryItemModel {
String get deviceIdentificator; String get phone; String? get name; bool get isIncoming; bool get isAccepted; int get duration; int get occurredAt; int get createdAt;
/// Create a copy of CallHistoryItemModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CallHistoryItemModelCopyWith<CallHistoryItemModel> get copyWith => _$CallHistoryItemModelCopyWithImpl<CallHistoryItemModel>(this as CallHistoryItemModel, _$identity);
/// Serializes this CallHistoryItemModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallHistoryItemModel&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.name, name) || other.name == name)&&(identical(other.isIncoming, isIncoming) || other.isIncoming == isIncoming)&&(identical(other.isAccepted, isAccepted) || other.isAccepted == isAccepted)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.occurredAt, occurredAt) || other.occurredAt == occurredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,deviceIdentificator,phone,name,isIncoming,isAccepted,duration,occurredAt,createdAt);
@override
String toString() {
return 'CallHistoryItemModel(deviceIdentificator: $deviceIdentificator, phone: $phone, name: $name, isIncoming: $isIncoming, isAccepted: $isAccepted, duration: $duration, occurredAt: $occurredAt, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class $CallHistoryItemModelCopyWith<$Res> {
factory $CallHistoryItemModelCopyWith(CallHistoryItemModel value, $Res Function(CallHistoryItemModel) _then) = _$CallHistoryItemModelCopyWithImpl;
@useResult
$Res call({
String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt
});
}
/// @nodoc
class _$CallHistoryItemModelCopyWithImpl<$Res>
implements $CallHistoryItemModelCopyWith<$Res> {
_$CallHistoryItemModelCopyWithImpl(this._self, this._then);
final CallHistoryItemModel _self;
final $Res Function(CallHistoryItemModel) _then;
/// Create a copy of CallHistoryItemModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? deviceIdentificator = null,Object? phone = null,Object? name = freezed,Object? isIncoming = null,Object? isAccepted = null,Object? duration = null,Object? occurredAt = null,Object? createdAt = null,}) {
return _then(_self.copyWith(
deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,isIncoming: null == isIncoming ? _self.isIncoming : isIncoming // ignore: cast_nullable_to_non_nullable
as bool,isAccepted: null == isAccepted ? _self.isAccepted : isAccepted // ignore: cast_nullable_to_non_nullable
as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable
as int,occurredAt: null == occurredAt ? _self.occurredAt : occurredAt // ignore: cast_nullable_to_non_nullable
as int,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [CallHistoryItemModel].
extension CallHistoryItemModelPatterns on CallHistoryItemModel {
/// 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( _CallHistoryItemModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CallHistoryItemModel() 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( _CallHistoryItemModel value) $default,){
final _that = this;
switch (_that) {
case _CallHistoryItemModel():
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( _CallHistoryItemModel value)? $default,){
final _that = this;
switch (_that) {
case _CallHistoryItemModel() 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 deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CallHistoryItemModel() when $default != null:
return $default(_that.deviceIdentificator,_that.phone,_that.name,_that.isIncoming,_that.isAccepted,_that.duration,_that.occurredAt,_that.createdAt);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt) $default,) {final _that = this;
switch (_that) {
case _CallHistoryItemModel():
return $default(_that.deviceIdentificator,_that.phone,_that.name,_that.isIncoming,_that.isAccepted,_that.duration,_that.occurredAt,_that.createdAt);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt)? $default,) {final _that = this;
switch (_that) {
case _CallHistoryItemModel() when $default != null:
return $default(_that.deviceIdentificator,_that.phone,_that.name,_that.isIncoming,_that.isAccepted,_that.duration,_that.occurredAt,_that.createdAt);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _CallHistoryItemModel implements CallHistoryItemModel {
const _CallHistoryItemModel({required this.deviceIdentificator, required this.phone, this.name, required this.isIncoming, required this.isAccepted, required this.duration, required this.occurredAt, required this.createdAt});
factory _CallHistoryItemModel.fromJson(Map<String, dynamic> json) => _$CallHistoryItemModelFromJson(json);
@override final String deviceIdentificator;
@override final String phone;
@override final String? name;
@override final bool isIncoming;
@override final bool isAccepted;
@override final int duration;
@override final int occurredAt;
@override final int createdAt;
/// Create a copy of CallHistoryItemModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CallHistoryItemModelCopyWith<_CallHistoryItemModel> get copyWith => __$CallHistoryItemModelCopyWithImpl<_CallHistoryItemModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$CallHistoryItemModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallHistoryItemModel&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.name, name) || other.name == name)&&(identical(other.isIncoming, isIncoming) || other.isIncoming == isIncoming)&&(identical(other.isAccepted, isAccepted) || other.isAccepted == isAccepted)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.occurredAt, occurredAt) || other.occurredAt == occurredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,deviceIdentificator,phone,name,isIncoming,isAccepted,duration,occurredAt,createdAt);
@override
String toString() {
return 'CallHistoryItemModel(deviceIdentificator: $deviceIdentificator, phone: $phone, name: $name, isIncoming: $isIncoming, isAccepted: $isAccepted, duration: $duration, occurredAt: $occurredAt, createdAt: $createdAt)';
}
}
/// @nodoc
abstract mixin class _$CallHistoryItemModelCopyWith<$Res> implements $CallHistoryItemModelCopyWith<$Res> {
factory _$CallHistoryItemModelCopyWith(_CallHistoryItemModel value, $Res Function(_CallHistoryItemModel) _then) = __$CallHistoryItemModelCopyWithImpl;
@override @useResult
$Res call({
String deviceIdentificator, String phone, String? name, bool isIncoming, bool isAccepted, int duration, int occurredAt, int createdAt
});
}
/// @nodoc
class __$CallHistoryItemModelCopyWithImpl<$Res>
implements _$CallHistoryItemModelCopyWith<$Res> {
__$CallHistoryItemModelCopyWithImpl(this._self, this._then);
final _CallHistoryItemModel _self;
final $Res Function(_CallHistoryItemModel) _then;
/// Create a copy of CallHistoryItemModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? deviceIdentificator = null,Object? phone = null,Object? name = freezed,Object? isIncoming = null,Object? isAccepted = null,Object? duration = null,Object? occurredAt = null,Object? createdAt = null,}) {
return _then(_CallHistoryItemModel(
deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String?,isIncoming: null == isIncoming ? _self.isIncoming : isIncoming // ignore: cast_nullable_to_non_nullable
as bool,isAccepted: null == isAccepted ? _self.isAccepted : isAccepted // ignore: cast_nullable_to_non_nullable
as bool,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable
as int,occurredAt: null == occurredAt ? _self.occurredAt : occurredAt // ignore: cast_nullable_to_non_nullable
as int,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -0,0 +1,46 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'call_history_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_CallHistoryResponseModel _$CallHistoryResponseModelFromJson(
Map<String, dynamic> json,
) => _CallHistoryResponseModel(
total: (json['total'] as num).toInt(),
items: (json['items'] as List<dynamic>)
.map((e) => CallHistoryItemModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$CallHistoryResponseModelToJson(
_CallHistoryResponseModel instance,
) => <String, dynamic>{'total': instance.total, 'items': instance.items};
_CallHistoryItemModel _$CallHistoryItemModelFromJson(
Map<String, dynamic> json,
) => _CallHistoryItemModel(
deviceIdentificator: json['deviceIdentificator'] as String,
phone: json['phone'] as String,
name: json['name'] as String?,
isIncoming: json['isIncoming'] as bool,
isAccepted: json['isAccepted'] as bool,
duration: (json['duration'] as num).toInt(),
occurredAt: (json['occurredAt'] as num).toInt(),
createdAt: (json['createdAt'] as num).toInt(),
);
Map<String, dynamic> _$CallHistoryItemModelToJson(
_CallHistoryItemModel instance,
) => <String, dynamic>{
'deviceIdentificator': instance.deviceIdentificator,
'phone': instance.phone,
'name': instance.name,
'isIncoming': instance.isIncoming,
'isAccepted': instance.isAccepted,
'duration': instance.duration,
'occurredAt': instance.occurredAt,
'createdAt': instance.createdAt,
};

View File

@@ -0,0 +1,321 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
import '../data/call_history_entity.dart';
import 'state/call_history_view_model.dart';
import 'state/call_history_view_state.dart';
class CallHistoryScreen extends ConsumerWidget {
const CallHistoryScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final state = ref.watch(callHistoryViewModelProvider);
final vm = ref.read(callHistoryViewModelProvider.notifier);
final filtered = state.filteredCalls;
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.callHistory),
body: state.isLoading
? const Center(child: CircularProgressIndicator())
: state.errorMessage.isNotEmpty
? Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline,
size: 64, color: Colors.grey.shade300),
const SizedBox(height: 12),
Text(
state.errorMessage,
style: TextStyle(
color: Colors.grey.shade500, fontSize: 14),
textAlign: TextAlign.center,
),
],
),
)
: Column(
children: [
_FilterBar(
selected: state.filter,
onChanged: vm.setFilter,
),
Expanded(
child: filtered.isEmpty
? Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.phone_missed_outlined,
size: 64,
color: Colors.grey.shade300),
const SizedBox(height: 12),
Text(
context
.translate(I18n.callHistoryEmpty),
style: TextStyle(
color: Colors.grey.shade500,
fontSize: 14),
),
],
),
)
: ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 4),
itemCount: filtered.length,
itemBuilder: (context, index) {
final call = filtered[index];
final showDateHeader = index == 0 ||
!_isSameDay(
filtered[index - 1].occurredAt,
call.occurredAt,
);
return Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
if (showDateHeader)
_DateHeader(
timestamp: call.occurredAt),
_CallTile(
call: call,
primaryColor: theme.getColorFor(
ThemeCode.legacyPrimary),
),
],
);
},
),
),
],
),
);
}
static bool _isSameDay(int ts1, int ts2) {
final d1 = DateTime.fromMillisecondsSinceEpoch(ts1);
final d2 = DateTime.fromMillisecondsSinceEpoch(ts2);
return d1.year == d2.year && d1.month == d2.month && d1.day == d2.day;
}
}
class _FilterBar extends StatelessWidget {
final CallFilter selected;
final ValueChanged<CallFilter> onChanged;
const _FilterBar({required this.selected, required this.onChanged});
@override
Widget build(BuildContext context) {
final filters = CallFilter.values;
final selectedIndex = filters.indexOf(selected);
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(4),
height: 40,
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(10),
),
child: LayoutBuilder(
builder: (context, constraints) {
final tabWidth = constraints.maxWidth / filters.length;
return Stack(
children: [
AnimatedPositioned(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
left: selectedIndex * tabWidth,
top: 0,
bottom: 0,
width: tabWidth,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 4,
offset: const Offset(0, 1),
),
],
),
),
),
Row(
children: filters.map((filter) {
final isSelected = filter == selected;
final label = switch (filter) {
CallFilter.all =>
context.translate(I18n.locationListAll),
CallFilter.incoming =>
context.translate(I18n.callIncoming),
CallFilter.outgoing =>
context.translate(I18n.callOutgoing),
CallFilter.missed =>
context.translate(I18n.callMissed),
};
return Expanded(
child: GestureDetector(
onTap: () => onChanged(filter),
behavior: HitTestBehavior.opaque,
child: Center(
child: Text(
label,
style: TextStyle(
fontSize: 13,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.w500,
color: isSelected
? Colors.black87
: Colors.grey.shade600,
),
),
),
),
);
}).toList(),
),
],
);
},
),
);
}
}
class _DateHeader extends StatelessWidget {
final int timestamp;
const _DateHeader({required this.timestamp});
@override
Widget build(BuildContext context) {
final date = DateTime.fromMillisecondsSinceEpoch(timestamp);
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final dateDay = DateTime(date.year, date.month, date.day);
final diff = today.difference(dateDay).inDays;
String label;
if (diff == 0) {
label = context.translate(I18n.today);
} else if (diff == 1) {
label = context.translate(I18n.yesterday);
} else {
label =
'${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}';
}
return Padding(
padding: const EdgeInsets.only(top: 12, bottom: 6, left: 4),
child: Text(
label,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: Colors.grey.shade500,
),
),
);
}
}
class _CallTile extends StatelessWidget {
final CallHistoryEntity call;
final Color primaryColor;
const _CallTile({required this.call, required this.primaryColor});
@override
Widget build(BuildContext context) {
final date = DateTime.fromMillisecondsSinceEpoch(call.occurredAt);
final timeStr =
'${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
final displayName = call.name ?? call.phone;
final subtitle = call.name != null ? call.phone : null;
final durationMin = call.duration ~/ 60;
final durationSec = call.duration % 60;
final durationStr = durationMin > 0
? '${durationMin}m ${durationSec}s'
: '${durationSec}s';
final isAccepted = call.isAccepted;
final isIncoming = call.isIncoming;
final IconData icon;
final Color iconColor;
if (!isAccepted) {
icon = isIncoming ? Icons.phone_missed : Icons.phone_disabled;
iconColor = Colors.red;
} else {
icon = isIncoming ? Icons.call_received : Icons.call_made;
iconColor = primaryColor;
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 4),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: iconColor.withValues(alpha: 0.08),
shape: BoxShape.circle,
),
child: Icon(icon, color: iconColor, size: 22),
),
title: Text(
displayName,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: isAccepted ? Colors.black87 : Colors.red.shade700,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
subtitle: Row(
children: [
Icon(
isIncoming ? Icons.south_west : Icons.north_east,
size: 12,
color: iconColor.withValues(alpha: 0.7),
),
const SizedBox(width: 4),
if (subtitle != null) ...[
Text(subtitle,
style:
TextStyle(fontSize: 12, color: Colors.grey.shade600)),
const SizedBox(width: 8),
],
if (isAccepted)
Text(durationStr,
style:
TextStyle(fontSize: 12, color: Colors.grey.shade500)),
],
),
trailing: Text(
timeStr,
style: TextStyle(fontSize: 13, color: Colors.grey.shade500),
),
),
);
}
}

View File

@@ -0,0 +1,71 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import '../../data/call_history_datasource.dart';
import '../../data/call_history_datasource_provider.dart';
import '../../data/call_history_entity.dart';
import 'call_history_view_state.dart';
final callHistoryViewModelProvider =
NotifierProvider.autoDispose<CallHistoryViewModel, CallHistoryViewState>(
CallHistoryViewModel.new,
);
class CallHistoryViewModel extends Notifier<CallHistoryViewState> {
late final CallHistoryDatasource _datasource;
@override
CallHistoryViewState build() {
_datasource = ref.read(callHistoryDatasourceProvider);
Future.microtask(() => _load());
return const CallHistoryViewState();
}
Future<void> _load() async {
final device = ref.read(selectedDeviceProvider);
if (device == null) {
state = state.copyWith(isLoading: false);
return;
}
try {
final calls = await _datasource.getCallHistory(
deviceIdentificator: device.identificator,
);
if (!ref.mounted) return;
state = state.copyWith(
calls: calls,
filteredCalls: calls,
isLoading: false,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: e.toString(),
);
}
}
void setFilter(CallFilter filter) {
state = state.copyWith(
filter: filter,
filteredCalls: _applyFilter(state.calls, filter),
);
}
List<CallHistoryEntity> _applyFilter(
List<CallHistoryEntity> calls, CallFilter filter) {
switch (filter) {
case CallFilter.all:
return calls;
case CallFilter.incoming:
return calls.where((c) => c.isIncoming).toList();
case CallFilter.outgoing:
return calls.where((c) => !c.isIncoming).toList();
case CallFilter.missed:
return calls.where((c) => !c.isAccepted).toList();
}
}
}

View File

@@ -0,0 +1,18 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../data/call_history_entity.dart';
part 'call_history_view_state.freezed.dart';
enum CallFilter { all, incoming, outgoing, missed }
@freezed
abstract class CallHistoryViewState with _$CallHistoryViewState {
const factory CallHistoryViewState({
@Default(true) bool isLoading,
@Default([]) List<CallHistoryEntity> calls,
@Default([]) List<CallHistoryEntity> filteredCalls,
@Default(CallFilter.all) CallFilter filter,
@Default('') String errorMessage,
}) = _CallHistoryViewState;
}

View File

@@ -0,0 +1,295 @@
// 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 'call_history_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$CallHistoryViewState {
bool get isLoading; List<CallHistoryEntity> get calls; List<CallHistoryEntity> get filteredCalls; CallFilter get filter; String get errorMessage;
/// Create a copy of CallHistoryViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CallHistoryViewStateCopyWith<CallHistoryViewState> get copyWith => _$CallHistoryViewStateCopyWithImpl<CallHistoryViewState>(this as CallHistoryViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallHistoryViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other.calls, calls)&&const DeepCollectionEquality().equals(other.filteredCalls, filteredCalls)&&(identical(other.filter, filter) || other.filter == filter)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,const DeepCollectionEquality().hash(calls),const DeepCollectionEquality().hash(filteredCalls),filter,errorMessage);
@override
String toString() {
return 'CallHistoryViewState(isLoading: $isLoading, calls: $calls, filteredCalls: $filteredCalls, filter: $filter, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class $CallHistoryViewStateCopyWith<$Res> {
factory $CallHistoryViewStateCopyWith(CallHistoryViewState value, $Res Function(CallHistoryViewState) _then) = _$CallHistoryViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, List<CallHistoryEntity> calls, List<CallHistoryEntity> filteredCalls, CallFilter filter, String errorMessage
});
}
/// @nodoc
class _$CallHistoryViewStateCopyWithImpl<$Res>
implements $CallHistoryViewStateCopyWith<$Res> {
_$CallHistoryViewStateCopyWithImpl(this._self, this._then);
final CallHistoryViewState _self;
final $Res Function(CallHistoryViewState) _then;
/// Create a copy of CallHistoryViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? calls = null,Object? filteredCalls = null,Object? filter = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,calls: null == calls ? _self.calls : calls // ignore: cast_nullable_to_non_nullable
as List<CallHistoryEntity>,filteredCalls: null == filteredCalls ? _self.filteredCalls : filteredCalls // ignore: cast_nullable_to_non_nullable
as List<CallHistoryEntity>,filter: null == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable
as CallFilter,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [CallHistoryViewState].
extension CallHistoryViewStatePatterns on CallHistoryViewState {
/// 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( _CallHistoryViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CallHistoryViewState() 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( _CallHistoryViewState value) $default,){
final _that = this;
switch (_that) {
case _CallHistoryViewState():
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( _CallHistoryViewState value)? $default,){
final _that = this;
switch (_that) {
case _CallHistoryViewState() 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, List<CallHistoryEntity> calls, List<CallHistoryEntity> filteredCalls, CallFilter filter, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CallHistoryViewState() when $default != null:
return $default(_that.isLoading,_that.calls,_that.filteredCalls,_that.filter,_that.errorMessage);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, List<CallHistoryEntity> calls, List<CallHistoryEntity> filteredCalls, CallFilter filter, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _CallHistoryViewState():
return $default(_that.isLoading,_that.calls,_that.filteredCalls,_that.filter,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, List<CallHistoryEntity> calls, List<CallHistoryEntity> filteredCalls, CallFilter filter, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _CallHistoryViewState() when $default != null:
return $default(_that.isLoading,_that.calls,_that.filteredCalls,_that.filter,_that.errorMessage);case _:
return null;
}
}
}
/// @nodoc
class _CallHistoryViewState implements CallHistoryViewState {
const _CallHistoryViewState({this.isLoading = true, final List<CallHistoryEntity> calls = const [], final List<CallHistoryEntity> filteredCalls = const [], this.filter = CallFilter.all, this.errorMessage = ''}): _calls = calls,_filteredCalls = filteredCalls;
@override@JsonKey() final bool isLoading;
final List<CallHistoryEntity> _calls;
@override@JsonKey() List<CallHistoryEntity> get calls {
if (_calls is EqualUnmodifiableListView) return _calls;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_calls);
}
final List<CallHistoryEntity> _filteredCalls;
@override@JsonKey() List<CallHistoryEntity> get filteredCalls {
if (_filteredCalls is EqualUnmodifiableListView) return _filteredCalls;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_filteredCalls);
}
@override@JsonKey() final CallFilter filter;
@override@JsonKey() final String errorMessage;
/// Create a copy of CallHistoryViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CallHistoryViewStateCopyWith<_CallHistoryViewState> get copyWith => __$CallHistoryViewStateCopyWithImpl<_CallHistoryViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallHistoryViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other._calls, _calls)&&const DeepCollectionEquality().equals(other._filteredCalls, _filteredCalls)&&(identical(other.filter, filter) || other.filter == filter)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,const DeepCollectionEquality().hash(_calls),const DeepCollectionEquality().hash(_filteredCalls),filter,errorMessage);
@override
String toString() {
return 'CallHistoryViewState(isLoading: $isLoading, calls: $calls, filteredCalls: $filteredCalls, filter: $filter, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class _$CallHistoryViewStateCopyWith<$Res> implements $CallHistoryViewStateCopyWith<$Res> {
factory _$CallHistoryViewStateCopyWith(_CallHistoryViewState value, $Res Function(_CallHistoryViewState) _then) = __$CallHistoryViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, List<CallHistoryEntity> calls, List<CallHistoryEntity> filteredCalls, CallFilter filter, String errorMessage
});
}
/// @nodoc
class __$CallHistoryViewStateCopyWithImpl<$Res>
implements _$CallHistoryViewStateCopyWith<$Res> {
__$CallHistoryViewStateCopyWithImpl(this._self, this._then);
final _CallHistoryViewState _self;
final $Res Function(_CallHistoryViewState) _then;
/// Create a copy of CallHistoryViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? calls = null,Object? filteredCalls = null,Object? filter = null,Object? errorMessage = null,}) {
return _then(_CallHistoryViewState(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,calls: null == calls ? _self._calls : calls // ignore: cast_nullable_to_non_nullable
as List<CallHistoryEntity>,filteredCalls: null == filteredCalls ? _self._filteredCalls : filteredCalls // ignore: cast_nullable_to_non_nullable
as List<CallHistoryEntity>,filter: null == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable
as CallFilter,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -27,14 +27,14 @@ class DeviceManagementScreen extends ConsumerWidget {
),
child: Column(
children: [
// AppMenuButton(
// color: theme.getColorFor(ThemeCode.legacyPrimary),
// onPressed: () =>
// navigationContract.pushTo(AppRoutes.remoteConnection),
// icon: SFIcons.connection,
// text: context.translate(I18n.remoteConnection),
// ),
// SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: () =>
navigationContract.pushTo(AppRoutes.remoteConnection),
icon: SFIcons.connection,
text: context.translate(I18n.remoteConnection),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: () =>
@@ -68,6 +68,15 @@ class DeviceManagementScreen extends ConsumerWidget {
// text: context.translate(I18n.videoCall),
// ),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: () =>
navigationContract.pushTo(AppRoutes.volumeControl),
icon: Icons.volume_up_outlined,
iconSize: SizeUtils.getByScreen(small: 42, big: 40),
text: context.translate(I18n.volumeControl),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: () => navigationContract.pushTo(AppRoutes.health),
@@ -104,6 +113,15 @@ class DeviceManagementScreen extends ConsumerWidget {
text: context.translate(I18n.callWatch),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: () =>
navigationContract.pushTo(AppRoutes.callHistory),
icon: Icons.history_outlined,
iconSize: SizeUtils.getByScreen(small: 42, big: 40),
text: context.translate(I18n.callHistory),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: () => navigationContract.pushTo(AppRoutes.appsUse),

View File

@@ -56,14 +56,14 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
final state = ref.watch(healthViewModelProvider);
final vm = ref.read(healthViewModelProvider.notifier);
ref.listen(
healthViewModelProvider.select((s) => s.errorMessage),
(previous, next) {
if (next.isNotEmpty) {
showTopSnackbar(context, message: next, type: MessageType.error);
}
},
);
ref.listen(healthViewModelProvider.select((s) => s.errorMessage), (
previous,
next,
) {
if (next.isNotEmpty) {
showTopSnackbar(context, message: next, type: MessageType.error);
}
});
return LegacyPageLayout(
theme: theme,
@@ -130,13 +130,12 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
),
],
),
footer: _SaveSection()
footer: _SaveSection(),
);
}
}
class _SaveSection extends ConsumerWidget{
class _SaveSection extends ConsumerWidget {
const _SaveSection();
@override
@@ -146,15 +145,14 @@ class _SaveSection extends ConsumerWidget{
final vm = ref.read(healthViewModelProvider.notifier);
return Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child:
PrimaryButton(
onPressed: () async {
await vm.measure();
},
text: context.translate(I18n.measure),
color: theme.getColorFor(ThemeCode.legacyPrimary)
)
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: PrimaryButton(
onPressed: () async {
await vm.measure();
},
text: context.translate(I18n.measure),
color: theme.getColorFor(ThemeCode.legacyPrimary),
),
);
}
}
}

View File

@@ -151,6 +151,8 @@ class HealthViewModel extends Notifier<HealthViewState> {
identificator: identificator,
queryParameters: HealthQueryBuilder.build(
orderDirection: OrderDirection.asc,
page: 1,
pageSize: 1000,
filters: filters,
),
),
@@ -158,6 +160,8 @@ class HealthViewModel extends Notifier<HealthViewState> {
identificator: identificator,
queryParameters: HealthQueryBuilder.build(
orderDirection: OrderDirection.asc,
page: 1,
pageSize: 1000,
filters: filters,
),
),
@@ -247,14 +251,11 @@ class HealthViewModel extends Notifier<HealthViewState> {
}
Future<void> measure() async {
final device = ref.read(selectedDeviceProvider);
if (device == null) return;
try {
state = state.copyWith(
isLoading: true,
);
final device = ref.read(selectedDeviceProvider);
if (device == null) return;
state = state.copyWith(isLoading: true);
final request = SendCommandRequestModel(
device: device.identificator,
@@ -262,14 +263,14 @@ class HealthViewModel extends Notifier<HealthViewState> {
);
await _commandsRepository.send(request: request);
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
);
state = state.copyWith(isLoading: false);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: e.toString(),
errorMessage: _formatError(e),
);
}
}

View File

@@ -6,9 +6,12 @@ part 'picture_entity.freezed.dart';
abstract class PictureEntity with _$PictureEntity {
const factory PictureEntity({
required String id,
required String? deviceId,
required DateTime createdAt,
required DateTime takenAt,
required String asset,
required String deviceIdentificator,
String? imgType,
String? timestamp,
required String fileId,
String? fileName,
String? contentType,
required int createdAt,
}) = _PictureEntity;
}

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$PictureEntity {
String get id; String? get deviceId; DateTime get createdAt; DateTime get takenAt; String get asset;
String get id; String get deviceIdentificator; String? get imgType; String? get timestamp; String get fileId; String? get fileName; String? get contentType; int get createdAt;
/// Create a copy of PictureEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $PictureEntityCopyWith<PictureEntity> get copyWith => _$PictureEntityCopyWithImp
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PictureEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.takenAt, takenAt) || other.takenAt == takenAt)&&(identical(other.asset, asset) || other.asset == asset));
return identical(this, other) || (other.runtimeType == runtimeType&&other is PictureEntity&&(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));
}
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,createdAt,takenAt,asset);
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
@override
String toString() {
return 'PictureEntity(id: $id, deviceId: $deviceId, createdAt: $createdAt, takenAt: $takenAt, asset: $asset)';
return 'PictureEntity(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
}
@@ -45,7 +45,7 @@ abstract mixin class $PictureEntityCopyWith<$Res> {
factory $PictureEntityCopyWith(PictureEntity value, $Res Function(PictureEntity) _then) = _$PictureEntityCopyWithImpl;
@useResult
$Res call({
String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt
});
@@ -62,14 +62,17 @@ class _$PictureEntityCopyWithImpl<$Res>
/// Create a copy of PictureEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceId = freezed,Object? createdAt = null,Object? takenAt = null,Object? asset = null,}) {
@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,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceId: freezed == deviceId ? _self.deviceId : deviceId // 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 DateTime,takenAt: null == takenAt ? _self.takenAt : takenAt // ignore: cast_nullable_to_non_nullable
as DateTime,asset: null == asset ? _self.asset : asset // ignore: cast_nullable_to_non_nullable
as String,
as int,
));
}
@@ -154,10 +157,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PictureEntity() when $default != null:
return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asset);case _:
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
return orElse();
}
@@ -175,10 +178,10 @@ return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asse
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt) $default,) {final _that = this;
switch (_that) {
case _PictureEntity():
return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asset);case _:
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
throw StateError('Unexpected subclass');
}
@@ -195,10 +198,10 @@ return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asse
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt)? $default,) {final _that = this;
switch (_that) {
case _PictureEntity() when $default != null:
return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asset);case _:
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
return null;
}
@@ -210,14 +213,17 @@ return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asse
class _PictureEntity implements PictureEntity {
const _PictureEntity({required this.id, required this.deviceId, required this.createdAt, required this.takenAt, required this.asset});
const _PictureEntity({required this.id, required this.deviceIdentificator, this.imgType, this.timestamp, required this.fileId, this.fileName, this.contentType, required this.createdAt});
@override final String id;
@override final String? deviceId;
@override final DateTime createdAt;
@override final DateTime takenAt;
@override final String asset;
@override final String deviceIdentificator;
@override final String? imgType;
@override final String? timestamp;
@override final String fileId;
@override final String? fileName;
@override final String? contentType;
@override final int createdAt;
/// Create a copy of PictureEntity
/// with the given fields replaced by the non-null parameter values.
@@ -229,16 +235,16 @@ _$PictureEntityCopyWith<_PictureEntity> get copyWith => __$PictureEntityCopyWith
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PictureEntity&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.takenAt, takenAt) || other.takenAt == takenAt)&&(identical(other.asset, asset) || other.asset == asset));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PictureEntity&&(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));
}
@override
int get hashCode => Object.hash(runtimeType,id,deviceId,createdAt,takenAt,asset);
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
@override
String toString() {
return 'PictureEntity(id: $id, deviceId: $deviceId, createdAt: $createdAt, takenAt: $takenAt, asset: $asset)';
return 'PictureEntity(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
}
@@ -249,7 +255,7 @@ abstract mixin class _$PictureEntityCopyWith<$Res> implements $PictureEntityCopy
factory _$PictureEntityCopyWith(_PictureEntity value, $Res Function(_PictureEntity) _then) = __$PictureEntityCopyWithImpl;
@override @useResult
$Res call({
String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt
});
@@ -266,14 +272,17 @@ class __$PictureEntityCopyWithImpl<$Res>
/// Create a copy of PictureEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceId = freezed,Object? createdAt = null,Object? takenAt = null,Object? asset = null,}) {
@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,}) {
return _then(_PictureEntity(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,deviceId: freezed == deviceId ? _self.deviceId : deviceId // 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 DateTime,takenAt: null == takenAt ? _self.takenAt : takenAt // ignore: cast_nullable_to_non_nullable
as DateTime,asset: null == asset ? _self.asset : asset // ignore: cast_nullable_to_non_nullable
as String,
as int,
));
}

View File

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

View File

@@ -1,44 +0,0 @@
import 'package:device_management/src/core/domain/repositories/functions_repository.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:device_management/src/features/remote_connection/domain/get_pictures_use_case.dart';
class GetPicturesUseCaseImpl implements GetPicturesUseCase {
GetPicturesUseCaseImpl(this._repository);
final FunctionsRepository _repository;
@override
Future<List<PictureEntity>> getPictures({required String userId}) async {
// return _repository.getPictures(userId: userId);
return [
PictureEntity(
id: '1',
deviceId: '1111',
createdAt: DateTime.now(),
asset: 'assets/shared/images/iso_sf.png',
takenAt: DateTime.now(),
),
PictureEntity(
id: '2',
deviceId: '1111',
createdAt: DateTime.now(),
asset: 'assets/shared/images/iso_sf.png',
takenAt: DateTime.now(),
),
PictureEntity(
id: '3',
deviceId: '1111',
createdAt: DateTime.now(),
asset: 'assets/shared/images/iso_sf.png',
takenAt: DateTime.now(),
),
PictureEntity(
id: '4',
deviceId: '1111',
createdAt: DateTime.now(),
asset: 'assets/shared/images/iso_sf.png',
takenAt: DateTime.now(),
),
];
}
}

View File

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

View File

@@ -1,14 +0,0 @@
import 'package:device_management/src/core/domain/repositories/functions_repository.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:device_management/src/features/remote_connection/domain/take_picture_use_case.dart';
class TakePictureUseCaseImpl implements TakePictureUseCase {
TakePictureUseCaseImpl(this._repository);
final FunctionsRepository _repository;
@override
Future<PictureEntity> takePicture({required String userId}) {
return _repository.takePicture(userId: userId);
}
}

View File

@@ -1,9 +0,0 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:device_management/src/core/providers/functions_repository_provider.dart';
import 'package:device_management/src/features/remote_connection/domain/get_pictures_use_case.dart';
import 'package:device_management/src/features/remote_connection/domain/get_pictures_use_case_impl.dart';
final getPicturesUseCaseProvider = Provider.autoDispose<GetPicturesUseCase>((ref) {
final functionsRepository = ref.read(functionsRepositoryProvider);
return GetPicturesUseCaseImpl(functionsRepository);
});

View File

@@ -1,9 +0,0 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:device_management/src/core/providers/functions_repository_provider.dart';
import 'package:device_management/src/features/remote_connection/domain/take_picture_use_case.dart';
import 'package:device_management/src/features/remote_connection/domain/take_picture_use_case_impl.dart';
final takePictureUseCaseProvider = Provider.autoDispose<TakePictureUseCase>((ref) {
final functionsRepository = ref.read(functionsRepositoryProvider);
return TakePictureUseCaseImpl(functionsRepository);
});

View File

@@ -15,19 +15,32 @@ class RemoteCameraScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(
remoteConnectionViewModelProvider.select((s) => s.successMessage),
(_, successMessage) {
if (successMessage.isNotEmpty) {
showTopSnackbar(context, message: context.translate(successMessage), type: MessageType.success);
ref.read(remoteConnectionViewModelProvider.notifier).clearSuccess();
}
},
);
final theme = ref.watch(themePortProvider);
final isLoadingPictures = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.isLoadingPictures)
remoteConnectionViewModelProvider.select((s)=>s.isLoadingPictures)
);
final isTakingPicture = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.isTakingPicture)
);
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.remoteCamera),
body: Expanded(child: isLoadingPictures
body: isLoadingPictures || isTakingPicture
? const Center(child: CircularProgressIndicator())
: const _GallerySection()
),
: const _GallerySection(),
footer: _TakePictureSection(),
);
}
@@ -74,10 +87,11 @@ class _GallerySection extends ConsumerWidget {
color: theme.getColorFor(ThemeCode.textTertiary)
))
),
child: Column(
children: [
Image.asset(pictures[index].asset),
],
child: Image.network(
pictures[index].fileId,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) =>
const Icon(Icons.broken_image, color: Colors.grey),
)
)
)
@@ -103,24 +117,7 @@ class _TakePictureSection extends ConsumerWidget {
big: EdgeInsets.symmetric(vertical: 10, horizontal: 25)
),
child: PrimaryButton(
onPressed: () async {
showDialog(context: context, builder: (context)=>Dialog(
child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
),
width: SizeUtils.getByScreen(small: 360, big: 350),
height: SizeUtils.getByScreen(small: 195, big: 185),
child: Center(child: Text(context.translate(I18n.loadingPhoto),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 26, big: 25)),
)),
),
));
await vm.takePicture();
Navigator.pop(context);
},
onPressed: vm.takePicture,
text: context.translate(I18n.takePicture),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 36, big: 35),

View File

@@ -1,13 +1,12 @@
import 'package:device_management/src/core/providers/pictures_repository_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:device_management/src/features/remote_connection/domain/get_pictures_use_case.dart';
import 'package:device_management/src/features/remote_connection/domain/take_picture_use_case.dart';
import 'package:device_management/src/features/remote_connection/presentation/providers/get_pictures_use_case_provider.dart';
import 'package:device_management/src/features/remote_connection/presentation/providers/take_picture_use_case_provider.dart';
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
import '../../../../core/domain/repositories/pictures_repository.dart';
final remoteConnectionViewModelProvider =
NotifierProvider.autoDispose<RemoteConnectionViewModel, RemoteConnectionViewState>(
@@ -15,17 +14,17 @@ NotifierProvider.autoDispose<RemoteConnectionViewModel, RemoteConnectionViewStat
);
class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
late final GetPicturesUseCase _getPicturesUseCase;
late final TakePictureUseCase _takePictureUseCase;
late final TextEditingController phoneController;
late final CommandsRepository _commandsRepository;
late final PicturesRepository _picturesRepository;
static final RegExp _phoneRegex = RegExp(r'^\+?\d{6,15}$');
@override
RemoteConnectionViewState build() {
_getPicturesUseCase = ref.read(getPicturesUseCaseProvider);
_takePictureUseCase = ref.read(takePictureUseCaseProvider);
_commandsRepository = ref.read(commandsRepositoryProvider);
_picturesRepository = ref.read(picturesRepositoryProvider);
phoneController = TextEditingController();
phoneController.addListener(_onPhoneChanged);
@@ -38,17 +37,23 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
}
Future<void> load() async {
final user = await ref.read(userInfoProvider.future);
final device = ref.read(selectedDeviceProvider);
if (device == null) return;
final pictures = await _getPicturesUseCase.getPictures(userId: user.id);
setImages(pictures);
}
state = state.copyWith(deviceId: device.identificator);
void setImages(List<PictureEntity> pictures) {
state = state.copyWith(
pictures: pictures,
isLoadingPictures: false,
);
try {
final pictures = await _picturesRepository.getPictures(deviceId: device.identificator);
if (!ref.mounted) return;
state = state.copyWith(
pictures: pictures,
isLoadingPictures: false,
);
} catch (_) {
if (!ref.mounted) return;
state = state.copyWith(isLoadingPictures: false);
}
}
void _onPhoneChanged() {
@@ -58,6 +63,11 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
state = state.copyWith(phone: text, errorMessage: '');
}
void updateDialCode(String value) {
if (value == state.dialCode) return;
state = state.copyWith(dialCode: value, errorMessage: '');
}
void prevPicture() {
int pictureIndex = state.pictureIndex - 1;
@@ -84,19 +94,39 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
);
}
void clearSuccess() {
state = state.copyWith(
successMessage: '',
);
}
Future<void> takePicture() async {
try {
state = state.copyWith(isTakingPicture: true);
state = state.copyWith(
isTakingPicture: true,
successMessage: '',
);
final request = SendCommandRequestModel(
device: state.deviceId,
command: DeviceCommand.requestPhoto,
);
await _takePictureUseCase.takePicture(userId: '')
.then((picture) {
List<PictureEntity> pictures = state.pictures;
pictures.add(picture);
state = state.copyWith(
isTakingPicture: true,
);
});
} catch (e){
await _commandsRepository.send(request: request);
if (!ref.mounted) return;
final pictures = await _picturesRepository.getPictures(
deviceId: state.deviceId,
);
if (!ref.mounted) return;
state = state.copyWith(
isTakingPicture: false,
pictures: pictures,
successMessage: I18n.photoTaken,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isTakingPicture: false,
errorMessage: e.toString(),
@@ -106,14 +136,36 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
Future<void> call() async {
final phone = phoneController.text;
final dialCode = state.dialCode;
if (phone.isEmpty){
state = state.copyWith(errorMessage: 'errorMessagePhoneIsEmpty');
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
return;
}
if (!_phoneRegex.hasMatch(phone)) {
state = state.copyWith(errorMessage: 'errorMessagePhoneIsInvalid');
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
return;
}
try {
state = state.copyWith(isCalling: true);
final fullPhone = dialCode + phone;
final request = SendCommandRequestModel(
device: state.deviceId,
command: DeviceCommand.callCenter,
data: {'phone_number': fullPhone},
);
await _commandsRepository.send(request: request);
if (!ref.mounted) return;
state = state.copyWith(isCalling: false);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isCalling: false,
errorMessage: e.toString(),
);
}
}
void disposeControllers() {

View File

@@ -6,12 +6,15 @@ part 'remote_connection_view_state.freezed.dart';
@freezed
abstract class RemoteConnectionViewState with _$RemoteConnectionViewState {
const factory RemoteConnectionViewState({
@Default('') String deviceId,
@Default('+34') String dialCode,
@Default('') String phone,
@Default([]) List<PictureEntity> pictures,
@Default(0) int pictureIndex,
@Default(true) bool isLoadingPictures,
@Default(false) bool isTakingPicture,
@Default(false) bool isCalling,
@Default('') String errorMessage
@Default('') String errorMessage,
@Default('') String successMessage
}) = _RemoteConnectionViewState;
}

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$RemoteConnectionViewState {
String get phone; List<PictureEntity> get pictures; int get pictureIndex; bool get isLoadingPictures; bool get isTakingPicture; bool get isCalling; String get errorMessage;
String get deviceId; String get dialCode; String get phone; List<PictureEntity> get pictures; int get pictureIndex; bool get isLoadingPictures; bool get isTakingPicture; bool get isCalling; String get errorMessage; String get successMessage;
/// Create a copy of RemoteConnectionViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $RemoteConnectionViewStateCopyWith<RemoteConnectionViewState> get copyWith => _$
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteConnectionViewState&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other.pictures, pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteConnectionViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other.pictures, pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage));
}
@override
int get hashCode => Object.hash(runtimeType,phone,const DeepCollectionEquality().hash(pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage);
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage,successMessage);
@override
String toString() {
return 'RemoteConnectionViewState(phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage)';
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage, successMessage: $successMessage)';
}
@@ -45,7 +45,7 @@ abstract mixin class $RemoteConnectionViewStateCopyWith<$Res> {
factory $RemoteConnectionViewStateCopyWith(RemoteConnectionViewState value, $Res Function(RemoteConnectionViewState) _then) = _$RemoteConnectionViewStateCopyWithImpl;
@useResult
$Res call({
String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage
});
@@ -62,15 +62,18 @@ class _$RemoteConnectionViewStateCopyWithImpl<$Res>
/// Create a copy of RemoteConnectionViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? dialCode = null,Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,Object? successMessage = null,}) {
return _then(_self.copyWith(
phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,pictures: null == pictures ? _self.pictures : pictures // ignore: cast_nullable_to_non_nullable
as List<PictureEntity>,pictureIndex: null == pictureIndex ? _self.pictureIndex : pictureIndex // ignore: cast_nullable_to_non_nullable
as int,isLoadingPictures: null == isLoadingPictures ? _self.isLoadingPictures : isLoadingPictures // ignore: cast_nullable_to_non_nullable
as bool,isTakingPicture: null == isTakingPicture ? _self.isTakingPicture : isTakingPicture // ignore: cast_nullable_to_non_nullable
as bool,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
@@ -156,10 +159,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _RemoteConnectionViewState() when $default != null:
return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
return orElse();
}
@@ -177,10 +180,10 @@ return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPic
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage) $default,) {final _that = this;
switch (_that) {
case _RemoteConnectionViewState():
return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
throw StateError('Unexpected subclass');
}
@@ -197,10 +200,10 @@ return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPic
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage)? $default,) {final _that = this;
switch (_that) {
case _RemoteConnectionViewState() when $default != null:
return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
return null;
}
@@ -212,9 +215,11 @@ return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPic
class _RemoteConnectionViewState implements RemoteConnectionViewState {
const _RemoteConnectionViewState({this.phone = '', final List<PictureEntity> pictures = const [], this.pictureIndex = 0, this.isLoadingPictures = true, this.isTakingPicture = false, this.isCalling = false, this.errorMessage = ''}): _pictures = pictures;
const _RemoteConnectionViewState({this.deviceId = '', this.dialCode = '+34', this.phone = '', final List<PictureEntity> pictures = const [], this.pictureIndex = 0, this.isLoadingPictures = true, this.isTakingPicture = false, this.isCalling = false, this.errorMessage = '', this.successMessage = ''}): _pictures = pictures;
@override@JsonKey() final String deviceId;
@override@JsonKey() final String dialCode;
@override@JsonKey() final String phone;
final List<PictureEntity> _pictures;
@override@JsonKey() List<PictureEntity> get pictures {
@@ -228,6 +233,7 @@ class _RemoteConnectionViewState implements RemoteConnectionViewState {
@override@JsonKey() final bool isTakingPicture;
@override@JsonKey() final bool isCalling;
@override@JsonKey() final String errorMessage;
@override@JsonKey() final String successMessage;
/// Create a copy of RemoteConnectionViewState
/// with the given fields replaced by the non-null parameter values.
@@ -239,16 +245,16 @@ _$RemoteConnectionViewStateCopyWith<_RemoteConnectionViewState> get copyWith =>
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteConnectionViewState&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other._pictures, _pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteConnectionViewState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other._pictures, _pictures)&&(identical(other.pictureIndex, pictureIndex) || other.pictureIndex == pictureIndex)&&(identical(other.isLoadingPictures, isLoadingPictures) || other.isLoadingPictures == isLoadingPictures)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage));
}
@override
int get hashCode => Object.hash(runtimeType,phone,const DeepCollectionEquality().hash(_pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage);
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(_pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage,successMessage);
@override
String toString() {
return 'RemoteConnectionViewState(phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage)';
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage, successMessage: $successMessage)';
}
@@ -259,7 +265,7 @@ abstract mixin class _$RemoteConnectionViewStateCopyWith<$Res> implements $Remot
factory _$RemoteConnectionViewStateCopyWith(_RemoteConnectionViewState value, $Res Function(_RemoteConnectionViewState) _then) = __$RemoteConnectionViewStateCopyWithImpl;
@override @useResult
$Res call({
String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage
});
@@ -276,15 +282,18 @@ class __$RemoteConnectionViewStateCopyWithImpl<$Res>
/// Create a copy of RemoteConnectionViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? dialCode = null,Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,Object? successMessage = null,}) {
return _then(_RemoteConnectionViewState(
phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,pictures: null == pictures ? _self._pictures : pictures // ignore: cast_nullable_to_non_nullable
as List<PictureEntity>,pictureIndex: null == pictureIndex ? _self.pictureIndex : pictureIndex // ignore: cast_nullable_to_non_nullable
as int,isLoadingPictures: null == isLoadingPictures ? _self.isLoadingPictures : isLoadingPictures // ignore: cast_nullable_to_non_nullable
as bool,isTakingPicture: null == isTakingPicture ? _self.isTakingPicture : isTakingPicture // ignore: cast_nullable_to_non_nullable
as bool,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}

View File

@@ -1,38 +1,53 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
import 'package:utils/utils.dart';
class ShowPictureDialog extends ConsumerWidget {
const ShowPictureDialog();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final viewModel = ref.read(remoteConnectionViewModelProvider.notifier);
final pictures = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.pictures)
remoteConnectionViewModelProvider.select((s) => s.pictures),
);
final pictureIndex = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.pictureIndex)
remoteConnectionViewModelProvider.select((s) => s.pictureIndex),
);
if (pictures.isEmpty) {
return Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
height: SizeUtils.getByScreen(small: 200, big: 190),
child: Center(
child: Text(context.translate(I18n.noPhotosAvailable),
style: const TextStyle(color: Colors.grey)),
),
);
}
final picture = pictures[pictureIndex];
return Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(8))
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
height: SizeUtils.getByScreen(small: 350, big: 340),
child: Column(
children: [
_PictureSection(asset: pictures[pictureIndex].asset),
_MetadataSection(picture: pictures[pictureIndex]),
Expanded(
child: _PictureSection(fileId: picture.fileId),
),
_MetadataSection(picture: picture),
_ControlsSection(
prev: viewModel.prevPicture,
next: viewModel.nextPicture,
@@ -43,63 +58,64 @@ class ShowPictureDialog extends ConsumerWidget {
}
}
class _PictureSection extends ConsumerWidget {
class _PictureSection extends StatelessWidget {
final String fileId;
final String asset;
const _PictureSection({
required this.asset,
});
const _PictureSection({required this.fileId});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Expanded(
child: Center(
child: Image.asset(asset),
)
Widget build(BuildContext context) {
return Center(
child: Image.network(
fileId,
fit: BoxFit.contain,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.broken_image, size: 64, color: Colors.grey);
},
),
);
}
}
class _MetadataSection extends ConsumerWidget {
class _MetadataSection extends StatelessWidget {
final PictureEntity picture;
const _MetadataSection({
required this.picture,
});
const _MetadataSection({required this.picture});
@override
Widget build(BuildContext context, WidgetRef ref) {
Widget build(BuildContext context) {
final date = DateTime.fromMillisecondsSinceEpoch(picture.createdAt);
final dateStr =
'${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year} '
'${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
return Column(
children: [
Text(picture.createdAt.toString())
],
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(dateStr, style: TextStyle(fontSize: 13, color: Colors.grey.shade600)),
);
}
}
class _ControlsSection extends ConsumerWidget {
class _ControlsSection extends StatelessWidget {
final VoidCallback prev;
final VoidCallback next;
const _ControlsSection({
required this.prev,
required this.next
});
const _ControlsSection({required this.prev, required this.next});
@override
Widget build(BuildContext context, WidgetRef ref) {
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(onPressed: prev, icon: Icon(Icons.arrow_back_ios_new_rounded)),
IconButton(onPressed: next, icon: Icon(Icons.arrow_forward_ios_rounded)),
IconButton(
onPressed: prev, icon: const Icon(Icons.arrow_back_ios_new_rounded)),
IconButton(
onPressed: next, icon: const Icon(Icons.arrow_forward_ios_rounded)),
],
);
}
}
}

View File

@@ -6,47 +6,50 @@ import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class SpyCallDialog extends ConsumerWidget {
Future<void> _onCall(BuildContext context, WidgetRef ref) async {
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
await vm.call();
if (!context.mounted) return;
final errorMessage = ref.read(
remoteConnectionViewModelProvider.select((s)=>s.errorMessage)
);
if (errorMessage.isNotEmpty) return;
Navigator.pop(context);
}
const SpyCallDialog();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
ref.listen(remoteConnectionViewModelProvider.select((s) => s.errorMessage),
(_, msg) {
if (msg.isNotEmpty) {
showTopSnackbar(context,
message: context.translate(msg), type: MessageType.error);
}
});
ref.listen(remoteConnectionViewModelProvider.select((s) => s.isCalling),
(prev, isCalling) {
if (prev == true && !isCalling) {
final error = ref.read(remoteConnectionViewModelProvider).errorMessage;
if (error.isEmpty && context.mounted) {
Navigator.pop(context);
showTopSnackbar(context,
message: context.translate(I18n.remoteListening),
type: MessageType.success);
}
}
});
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 26, vertical: 20),
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18)
),
small: const EdgeInsets.symmetric(horizontal: 26, vertical: 20),
big: const EdgeInsets.symmetric(horizontal: 24, vertical: 18)),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
color: theme.getColorFor(ThemeCode.backgroundSecondary)
),
borderRadius: const BorderRadius.all(Radius.circular(8)),
color: theme.getColorFor(ThemeCode.backgroundSecondary)),
width: SizeUtils.getByScreen(small: 390, big: 380),
height: SizeUtils.getByScreen(small: 250, big: 243),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_Header(theme: theme),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_PhoneSection(onSubmit: () {_onCall(context, ref);}),
const _ErrorMessageSection(),
_PhoneSection(onSubmit: vm.call),
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
_CallSection(onPressed: () {_onCall(context, ref);}),
_CallSection(onPressed: vm.call),
],
),
);
@@ -54,103 +57,90 @@ class SpyCallDialog extends ConsumerWidget {
}
class _Header extends StatelessWidget {
final ThemePort theme;
const _Header({
required this.theme,
});
const _Header({required this.theme});
@override
Widget build(BuildContext context) {
return Stack(
children: [
Center(child: Text(context.translate(I18n.remoteListening),
Center(
child: Text(
context.translate(I18n.remoteListening),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
style:
TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
)),
Align(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: (){Navigator.pop(context);},
icon: Icon(Icons.close, color: theme.getColorFor(ThemeCode.legacyPrimary)),
)
)
alignment: Alignment.centerRight,
child: IconButton(
onPressed: () => Navigator.pop(context),
icon: Icon(Icons.close,
color: theme.getColorFor(ThemeCode.legacyPrimary)),
))
],
);
}
}
class _PhoneSection extends ConsumerWidget {
final VoidCallback onSubmit;
const _PhoneSection({
required this.onSubmit,
});
const _PhoneSection({required this.onSubmit});
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
return CustomTextField(
controller: vm.phoneController,
hint: context.translate(I18n.insertPhone),
keyboardType: TextInputType.number,
onSubmitted: (_) => onSubmit(),
final dialCode = ref.watch(
remoteConnectionViewModelProvider.select((s) => s.dialCode),
);
}
}
class _ErrorMessageSection extends ConsumerWidget {
const _ErrorMessageSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewState = ref.watch(remoteConnectionViewModelProvider);
if (viewState.errorMessage.isNotEmpty) {
return Column(
children: [
const SizedBox(height: 4),
Text(
viewState.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CountryPrefixPicker(
headerText: context.translate(I18n.selectYourCountry),
initialSelection: dialCode,
onChanged: (country) {
vm.updateDialCode(country.dialCode ?? dialCode);
},
width: 80,
),
SizedBox(width: SizeUtils.getByScreen(small: 8, big: 7)),
Expanded(
child: CustomTextField(
controller: vm.phoneController,
hint: context.translate(I18n.insertPhone),
keyboardType: TextInputType.phone,
onSubmitted: (_) => onSubmit(),
),
],
);
} else return SizedBox.shrink();
),
],
);
}
}
class _CallSection extends ConsumerWidget {
final VoidCallback onPressed;
const _CallSection({
required this.onPressed,
});
const _CallSection({required this.onPressed});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return PrimaryButton(
onPressed: onPressed,
text: context.translate(I18n.call),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
final isCalling = ref.watch(
remoteConnectionViewModelProvider.select((s) => s.isCalling),
);
}
}
return isCalling
? const Center(child: CircularProgressIndicator())
: PrimaryButton(
onPressed: onPressed,
text: context.translate(I18n.call),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_shared/sf_shared.dart';
class DeviceUpdateDatasource {
DeviceUpdateDatasource(this._repository);
final QuestiaRepository _repository;
Future<void> updateDeviceSettings({
required DeviceEntity device,
required Map<String, dynamic> updatedSettings,
}) async {
final csvBase64 = DeviceCsvBuilder.buildBase64Csv(
device: device,
settings: updatedSettings,
);
await safeCall(
() => _repository.put<dynamic>(
'/devices',
body: {'csv': csvBase64},
),
'Error updating device settings',
);
}
}

View File

@@ -0,0 +1,9 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'device_update_datasource.dart';
final deviceUpdateDatasourceProvider = Provider<DeviceUpdateDatasource>((ref) {
return DeviceUpdateDatasource(GetIt.I<QuestiaRepository>());
});

View File

@@ -0,0 +1,73 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import '../../data/device_update_datasource.dart';
import '../../data/device_update_datasource_provider.dart';
import 'volume_control_view_state.dart';
final volumeControlViewModelProvider =
NotifierProvider.autoDispose<VolumeControlViewModel, VolumeControlViewState>(
VolumeControlViewModel.new,
);
class VolumeControlViewModel extends Notifier<VolumeControlViewState> {
late final DeviceUpdateDatasource _datasource;
@override
VolumeControlViewState build() {
_datasource = ref.read(deviceUpdateDatasourceProvider);
Future.microtask(() => _load());
return const VolumeControlViewState();
}
Future<void> _load() async {
try {
final device = ref.read(selectedDeviceProvider);
if (device == null) return;
final volume = device.settings['volume'] as Map<String, dynamic>? ?? {};
state = state.copyWith(
isLoading: false,
device: device,
media: (volume['media'] as num?)?.toInt() ?? 50,
ringtone: (volume['ringtone'] as num?)?.toInt() ?? 50,
alarm: (volume['alarm'] as num?)?.toInt() ?? 50,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
}
}
void setMedia(int value) => state = state.copyWith(media: value);
void setRingtone(int value) => state = state.copyWith(ringtone: value);
void setAlarm(int value) => state = state.copyWith(alarm: value);
Future<void> submit() async {
final device = state.device;
if (device == null) return;
try {
state = state.copyWith(isLoading: true, isComplete: false, errorMessage: '');
final settings = Map<String, dynamic>.from(device.settings);
settings['volume'] = {
'media': state.media,
'ringtone': state.ringtone,
'alarm': state.alarm,
};
await _datasource.updateDeviceSettings(
device: device,
updatedSettings: settings,
);
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, isComplete: true);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
}
}
}

View File

@@ -0,0 +1,17 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:sf_shared/sf_shared.dart';
part 'volume_control_view_state.freezed.dart';
@freezed
abstract class VolumeControlViewState with _$VolumeControlViewState {
const factory VolumeControlViewState({
@Default(true) bool isLoading,
@Default(false) bool isComplete,
DeviceEntity? device,
@Default(50) int media,
@Default(50) int ringtone,
@Default(50) int alarm,
@Default('') String errorMessage,
}) = _VolumeControlViewState;
}

View File

@@ -0,0 +1,313 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'volume_control_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$VolumeControlViewState {
bool get isLoading; bool get isComplete; DeviceEntity? get device; int get media; int get ringtone; int get alarm; String get errorMessage;
/// Create a copy of VolumeControlViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$VolumeControlViewStateCopyWith<VolumeControlViewState> get copyWith => _$VolumeControlViewStateCopyWithImpl<VolumeControlViewState>(this as VolumeControlViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is VolumeControlViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.media, media) || other.media == media)&&(identical(other.ringtone, ringtone) || other.ringtone == ringtone)&&(identical(other.alarm, alarm) || other.alarm == alarm)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,media,ringtone,alarm,errorMessage);
@override
String toString() {
return 'VolumeControlViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, media: $media, ringtone: $ringtone, alarm: $alarm, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class $VolumeControlViewStateCopyWith<$Res> {
factory $VolumeControlViewStateCopyWith(VolumeControlViewState value, $Res Function(VolumeControlViewState) _then) = _$VolumeControlViewStateCopyWithImpl;
@useResult
$Res call({
bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage
});
$DeviceEntityCopyWith<$Res>? get device;
}
/// @nodoc
class _$VolumeControlViewStateCopyWithImpl<$Res>
implements $VolumeControlViewStateCopyWith<$Res> {
_$VolumeControlViewStateCopyWithImpl(this._self, this._then);
final VolumeControlViewState _self;
final $Res Function(VolumeControlViewState) _then;
/// Create a copy of VolumeControlViewState
/// 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? device = freezed,Object? media = null,Object? ringtone = null,Object? alarm = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isComplete: null == isComplete ? _self.isComplete : isComplete // ignore: cast_nullable_to_non_nullable
as bool,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable
as DeviceEntity?,media: null == media ? _self.media : media // ignore: cast_nullable_to_non_nullable
as int,ringtone: null == ringtone ? _self.ringtone : ringtone // ignore: cast_nullable_to_non_nullable
as int,alarm: null == alarm ? _self.alarm : alarm // ignore: cast_nullable_to_non_nullable
as int,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of VolumeControlViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DeviceEntityCopyWith<$Res>? get device {
if (_self.device == null) {
return null;
}
return $DeviceEntityCopyWith<$Res>(_self.device!, (value) {
return _then(_self.copyWith(device: value));
});
}
}
/// Adds pattern-matching-related methods to [VolumeControlViewState].
extension VolumeControlViewStatePatterns on VolumeControlViewState {
/// 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( _VolumeControlViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _VolumeControlViewState() 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( _VolumeControlViewState value) $default,){
final _that = this;
switch (_that) {
case _VolumeControlViewState():
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( _VolumeControlViewState value)? $default,){
final _that = this;
switch (_that) {
case _VolumeControlViewState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _VolumeControlViewState() when $default != null:
return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.errorMessage);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _VolumeControlViewState():
return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _VolumeControlViewState() when $default != null:
return $default(_that.isLoading,_that.isComplete,_that.device,_that.media,_that.ringtone,_that.alarm,_that.errorMessage);case _:
return null;
}
}
}
/// @nodoc
class _VolumeControlViewState implements VolumeControlViewState {
const _VolumeControlViewState({this.isLoading = true, this.isComplete = false, this.device, this.media = 50, this.ringtone = 50, this.alarm = 50, this.errorMessage = ''});
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isComplete;
@override final DeviceEntity? device;
@override@JsonKey() final int media;
@override@JsonKey() final int ringtone;
@override@JsonKey() final int alarm;
@override@JsonKey() final String errorMessage;
/// Create a copy of VolumeControlViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$VolumeControlViewStateCopyWith<_VolumeControlViewState> get copyWith => __$VolumeControlViewStateCopyWithImpl<_VolumeControlViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _VolumeControlViewState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isComplete, isComplete) || other.isComplete == isComplete)&&(identical(other.device, device) || other.device == device)&&(identical(other.media, media) || other.media == media)&&(identical(other.ringtone, ringtone) || other.ringtone == ringtone)&&(identical(other.alarm, alarm) || other.alarm == alarm)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,isLoading,isComplete,device,media,ringtone,alarm,errorMessage);
@override
String toString() {
return 'VolumeControlViewState(isLoading: $isLoading, isComplete: $isComplete, device: $device, media: $media, ringtone: $ringtone, alarm: $alarm, errorMessage: $errorMessage)';
}
}
/// @nodoc
abstract mixin class _$VolumeControlViewStateCopyWith<$Res> implements $VolumeControlViewStateCopyWith<$Res> {
factory _$VolumeControlViewStateCopyWith(_VolumeControlViewState value, $Res Function(_VolumeControlViewState) _then) = __$VolumeControlViewStateCopyWithImpl;
@override @useResult
$Res call({
bool isLoading, bool isComplete, DeviceEntity? device, int media, int ringtone, int alarm, String errorMessage
});
@override $DeviceEntityCopyWith<$Res>? get device;
}
/// @nodoc
class __$VolumeControlViewStateCopyWithImpl<$Res>
implements _$VolumeControlViewStateCopyWith<$Res> {
__$VolumeControlViewStateCopyWithImpl(this._self, this._then);
final _VolumeControlViewState _self;
final $Res Function(_VolumeControlViewState) _then;
/// Create a copy of VolumeControlViewState
/// 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? device = freezed,Object? media = null,Object? ringtone = null,Object? alarm = null,Object? errorMessage = null,}) {
return _then(_VolumeControlViewState(
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,device: freezed == device ? _self.device : device // ignore: cast_nullable_to_non_nullable
as DeviceEntity?,media: null == media ? _self.media : media // ignore: cast_nullable_to_non_nullable
as int,ringtone: null == ringtone ? _self.ringtone : ringtone // ignore: cast_nullable_to_non_nullable
as int,alarm: null == alarm ? _self.alarm : alarm // ignore: cast_nullable_to_non_nullable
as int,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
/// Create a copy of VolumeControlViewState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DeviceEntityCopyWith<$Res>? get device {
if (_self.device == null) {
return null;
}
return $DeviceEntityCopyWith<$Res>(_self.device!, (value) {
return _then(_self.copyWith(device: value));
});
}
}
// dart format on

View File

@@ -0,0 +1,167 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'state/volume_control_view_model.dart';
import 'widgets/volume_thumb_shape.dart';
class VolumeControlScreen extends ConsumerWidget {
const VolumeControlScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final vm = ref.read(volumeControlViewModelProvider.notifier);
final state = ref.watch(volumeControlViewModelProvider);
ref.listen(volumeControlViewModelProvider.select((s) => s.errorMessage),
(_, msg) {
if (msg.isNotEmpty) {
showTopSnackbar(context, message: msg, type: MessageType.error);
}
});
ref.listen(volumeControlViewModelProvider.select((s) => s.isComplete),
(_, done) {
if (done) Navigator.pop(context);
});
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.volumeControl),
body: state.isLoading
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_VolumeCard(
label: context.translate(I18n.volumeMedia),
value: state.media,
color: primaryColor,
onChanged: vm.setMedia,
),
const SizedBox(height: 12),
_VolumeCard(
label: context.translate(I18n.volumeRingtone),
value: state.ringtone,
color: primaryColor,
onChanged: vm.setRingtone,
),
const SizedBox(height: 12),
_VolumeCard(
label: context.translate(I18n.volumeAlarm),
value: state.alarm,
color: primaryColor,
onChanged: vm.setAlarm,
),
const SizedBox(height: 20),
Text(
context.translate(I18n.volumeHint),
style: TextStyle(
fontSize: 13,
color: Colors.grey.shade500,
height: 1.4,
),
),
],
),
),
footer: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: state.isLoading
? const Center(child: CircularProgressIndicator())
: PrimaryButton(
onPressed: vm.submit,
text: context.translate(I18n.volumeSend),
color: primaryColor,
),
),
);
}
}
class _VolumeCard extends StatelessWidget {
final String label;
final int value;
final Color color;
final ValueChanged<int> onChanged;
const _VolumeCard({
required this.label,
required this.value,
required this.color,
required this.onChanged,
});
int get _displayValue => (value / 10).round();
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(16, 14, 16, 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
),
const SizedBox(height: 4),
Row(
children: [
Icon(Icons.volume_up, color: Colors.grey.shade400, size: 22),
Expanded(
child: SliderTheme(
data: SliderThemeData(
activeTrackColor: color,
thumbColor: Colors.white,
thumbShape: VolumeThumbShape(color: color),
inactiveTrackColor: Colors.grey.shade200,
overlayColor: color.withValues(alpha: 0.1),
trackHeight: 6,
),
child: Slider(
value: value.toDouble(),
min: 0,
max: 100,
divisions: 10,
onChanged: (v) => onChanged(v.round()),
),
),
),
SizedBox(
width: 24,
child: Text(
'$_displayValue',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: Colors.grey.shade600,
),
),
),
],
),
],
),
);
}
}

View File

@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
class VolumeThumbShape extends SliderComponentShape {
final Color color;
static const _radius = 11.0;
static const _strokeWidth = 2.5;
static const _lineSpacing = 2.5;
static const _lineLength = 4.0;
const VolumeThumbShape({required this.color});
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) =>
const Size(_radius * 2, _radius * 2);
@override
void paint(
PaintingContext context,
Offset center, {
required Animation<double> activationAnimation,
required Animation<double> enableAnimation,
required bool isDiscrete,
required TextPainter labelPainter,
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required TextDirection textDirection,
required double value,
required double textScaleFactor,
required Size sizeWithOverflow,
}) {
final canvas = context.canvas;
canvas.drawCircle(
center,
_radius,
Paint()
..color = Colors.white
..style = PaintingStyle.fill,
);
canvas.drawCircle(
center,
_radius,
Paint()
..color = color
..style = PaintingStyle.stroke
..strokeWidth = _strokeWidth,
);
final linePaint = Paint()
..color = color
..strokeWidth = 2
..strokeCap = StrokeCap.round;
canvas.drawLine(
Offset(center.dx - _lineSpacing, center.dy - _lineLength),
Offset(center.dx - _lineSpacing, center.dy + _lineLength),
linePaint,
);
canvas.drawLine(
Offset(center.dx + _lineSpacing, center.dy - _lineLength),
Offset(center.dx + _lineSpacing, center.dy + _lineLength),
linePaint,
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'presentation/volume_control_screen.dart';
class VolumeControlBuilder {
const VolumeControlBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
return MaterialPage<void>(
key: state.pageKey,
child: const VolumeControlScreen(),
);
}
}

View File

@@ -6,6 +6,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:sf_localizations/sf_localizations.dart';
import '../widgets/activation_code_dialog.dart';
class LegacyScanWatchStepScreen extends ConsumerWidget {
const LegacyScanWatchStepScreen({super.key});
@@ -43,6 +45,7 @@ class LegacyScanWatchStepScreen extends ConsumerWidget {
);
if (result == null || result.isEmpty) return;
vm.onWatchQrScanned(result);
showActivationCodeDialog(context);
},
child: Container(
width: 170,

View File

@@ -106,4 +106,20 @@ class LocationMapViewModel extends Notifier<LocationMapViewState> {
void clearSelectedHistoryPosition() {
state = state.copyWith(selectedHistoryPosition: null);
}
void toggleFollowing() {
state = state.copyWith(isFollowing: !state.isFollowing);
}
void stopFollowing() {
state = state.copyWith(isFollowing: false);
}
void toggleActionsExpanded() {
state = state.copyWith(actionsExpanded: !state.actionsExpanded);
}
void updateMapZoom(double zoom) {
state = state.copyWith(mapZoom: zoom);
}
}

View File

@@ -6,6 +6,8 @@ import 'package:location/src/core/domain/entities/frequent_place_entity.dart';
part 'location_map_view_state.freezed.dart';
const _defaultZoom = 17.0;
enum PlacingMode { none, geofence, frequentPlace }
@freezed
@@ -21,5 +23,8 @@ abstract class LocationMapViewState with _$LocationMapViewState {
GeofenceEntity? editingGeofence,
FrequentPlaceEntity? selectedFrequentPlace,
PositionEntity? selectedHistoryPosition,
@Default(false) bool isFollowing,
@Default(false) bool actionsExpanded,
@Default(_defaultZoom) double mapZoom,
}) = _LocationMapViewState;
}

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$LocationMapViewState {
bool get showGeofences; bool get showFrequentPlaces; PlacingMode get placingMode; bool get adjustingRadius; double get previewRadius; LatLng? get previewPoint; GeofenceEntity? get selectedGeofence; GeofenceEntity? get editingGeofence; FrequentPlaceEntity? get selectedFrequentPlace; PositionEntity? get selectedHistoryPosition;
bool get showGeofences; bool get showFrequentPlaces; PlacingMode get placingMode; bool get adjustingRadius; double get previewRadius; LatLng? get previewPoint; GeofenceEntity? get selectedGeofence; GeofenceEntity? get editingGeofence; FrequentPlaceEntity? get selectedFrequentPlace; PositionEntity? get selectedHistoryPosition; bool get isFollowing; bool get actionsExpanded; double get mapZoom;
/// Create a copy of LocationMapViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $LocationMapViewStateCopyWith<LocationMapViewState> get copyWith => _$LocationMa
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocationMapViewState&&(identical(other.showGeofences, showGeofences) || other.showGeofences == showGeofences)&&(identical(other.showFrequentPlaces, showFrequentPlaces) || other.showFrequentPlaces == showFrequentPlaces)&&(identical(other.placingMode, placingMode) || other.placingMode == placingMode)&&(identical(other.adjustingRadius, adjustingRadius) || other.adjustingRadius == adjustingRadius)&&(identical(other.previewRadius, previewRadius) || other.previewRadius == previewRadius)&&(identical(other.previewPoint, previewPoint) || other.previewPoint == previewPoint)&&(identical(other.selectedGeofence, selectedGeofence) || other.selectedGeofence == selectedGeofence)&&(identical(other.editingGeofence, editingGeofence) || other.editingGeofence == editingGeofence)&&(identical(other.selectedFrequentPlace, selectedFrequentPlace) || other.selectedFrequentPlace == selectedFrequentPlace)&&(identical(other.selectedHistoryPosition, selectedHistoryPosition) || other.selectedHistoryPosition == selectedHistoryPosition));
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocationMapViewState&&(identical(other.showGeofences, showGeofences) || other.showGeofences == showGeofences)&&(identical(other.showFrequentPlaces, showFrequentPlaces) || other.showFrequentPlaces == showFrequentPlaces)&&(identical(other.placingMode, placingMode) || other.placingMode == placingMode)&&(identical(other.adjustingRadius, adjustingRadius) || other.adjustingRadius == adjustingRadius)&&(identical(other.previewRadius, previewRadius) || other.previewRadius == previewRadius)&&(identical(other.previewPoint, previewPoint) || other.previewPoint == previewPoint)&&(identical(other.selectedGeofence, selectedGeofence) || other.selectedGeofence == selectedGeofence)&&(identical(other.editingGeofence, editingGeofence) || other.editingGeofence == editingGeofence)&&(identical(other.selectedFrequentPlace, selectedFrequentPlace) || other.selectedFrequentPlace == selectedFrequentPlace)&&(identical(other.selectedHistoryPosition, selectedHistoryPosition) || other.selectedHistoryPosition == selectedHistoryPosition)&&(identical(other.isFollowing, isFollowing) || other.isFollowing == isFollowing)&&(identical(other.actionsExpanded, actionsExpanded) || other.actionsExpanded == actionsExpanded)&&(identical(other.mapZoom, mapZoom) || other.mapZoom == mapZoom));
}
@override
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition);
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition,isFollowing,actionsExpanded,mapZoom);
@override
String toString() {
return 'LocationMapViewState(showGeofences: $showGeofences, showFrequentPlaces: $showFrequentPlaces, placingMode: $placingMode, adjustingRadius: $adjustingRadius, previewRadius: $previewRadius, previewPoint: $previewPoint, selectedGeofence: $selectedGeofence, editingGeofence: $editingGeofence, selectedFrequentPlace: $selectedFrequentPlace, selectedHistoryPosition: $selectedHistoryPosition)';
return 'LocationMapViewState(showGeofences: $showGeofences, showFrequentPlaces: $showFrequentPlaces, placingMode: $placingMode, adjustingRadius: $adjustingRadius, previewRadius: $previewRadius, previewPoint: $previewPoint, selectedGeofence: $selectedGeofence, editingGeofence: $editingGeofence, selectedFrequentPlace: $selectedFrequentPlace, selectedHistoryPosition: $selectedHistoryPosition, isFollowing: $isFollowing, actionsExpanded: $actionsExpanded, mapZoom: $mapZoom)';
}
@@ -45,7 +45,7 @@ abstract mixin class $LocationMapViewStateCopyWith<$Res> {
factory $LocationMapViewStateCopyWith(LocationMapViewState value, $Res Function(LocationMapViewState) _then) = _$LocationMapViewStateCopyWithImpl;
@useResult
$Res call({
bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition
bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition, bool isFollowing, bool actionsExpanded, double mapZoom
});
@@ -62,7 +62,7 @@ class _$LocationMapViewStateCopyWithImpl<$Res>
/// Create a copy of LocationMapViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? showGeofences = null,Object? showFrequentPlaces = null,Object? placingMode = null,Object? adjustingRadius = null,Object? previewRadius = null,Object? previewPoint = freezed,Object? selectedGeofence = freezed,Object? editingGeofence = freezed,Object? selectedFrequentPlace = freezed,Object? selectedHistoryPosition = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? showGeofences = null,Object? showFrequentPlaces = null,Object? placingMode = null,Object? adjustingRadius = null,Object? previewRadius = null,Object? previewPoint = freezed,Object? selectedGeofence = freezed,Object? editingGeofence = freezed,Object? selectedFrequentPlace = freezed,Object? selectedHistoryPosition = freezed,Object? isFollowing = null,Object? actionsExpanded = null,Object? mapZoom = null,}) {
return _then(_self.copyWith(
showGeofences: null == showGeofences ? _self.showGeofences : showGeofences // ignore: cast_nullable_to_non_nullable
as bool,showFrequentPlaces: null == showFrequentPlaces ? _self.showFrequentPlaces : showFrequentPlaces // ignore: cast_nullable_to_non_nullable
@@ -74,7 +74,10 @@ as LatLng?,selectedGeofence: freezed == selectedGeofence ? _self.selectedGeofenc
as GeofenceEntity?,editingGeofence: freezed == editingGeofence ? _self.editingGeofence : editingGeofence // ignore: cast_nullable_to_non_nullable
as GeofenceEntity?,selectedFrequentPlace: freezed == selectedFrequentPlace ? _self.selectedFrequentPlace : selectedFrequentPlace // ignore: cast_nullable_to_non_nullable
as FrequentPlaceEntity?,selectedHistoryPosition: freezed == selectedHistoryPosition ? _self.selectedHistoryPosition : selectedHistoryPosition // ignore: cast_nullable_to_non_nullable
as PositionEntity?,
as PositionEntity?,isFollowing: null == isFollowing ? _self.isFollowing : isFollowing // ignore: cast_nullable_to_non_nullable
as bool,actionsExpanded: null == actionsExpanded ? _self.actionsExpanded : actionsExpanded // ignore: cast_nullable_to_non_nullable
as bool,mapZoom: null == mapZoom ? _self.mapZoom : mapZoom // ignore: cast_nullable_to_non_nullable
as double,
));
}
/// Create a copy of LocationMapViewState
@@ -207,10 +210,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition, bool isFollowing, bool actionsExpanded, double mapZoom)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _LocationMapViewState() when $default != null:
return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_that.adjustingRadius,_that.previewRadius,_that.previewPoint,_that.selectedGeofence,_that.editingGeofence,_that.selectedFrequentPlace,_that.selectedHistoryPosition);case _:
return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_that.adjustingRadius,_that.previewRadius,_that.previewPoint,_that.selectedGeofence,_that.editingGeofence,_that.selectedFrequentPlace,_that.selectedHistoryPosition,_that.isFollowing,_that.actionsExpanded,_that.mapZoom);case _:
return orElse();
}
@@ -228,10 +231,10 @@ return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition, bool isFollowing, bool actionsExpanded, double mapZoom) $default,) {final _that = this;
switch (_that) {
case _LocationMapViewState():
return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_that.adjustingRadius,_that.previewRadius,_that.previewPoint,_that.selectedGeofence,_that.editingGeofence,_that.selectedFrequentPlace,_that.selectedHistoryPosition);case _:
return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_that.adjustingRadius,_that.previewRadius,_that.previewPoint,_that.selectedGeofence,_that.editingGeofence,_that.selectedFrequentPlace,_that.selectedHistoryPosition,_that.isFollowing,_that.actionsExpanded,_that.mapZoom);case _:
throw StateError('Unexpected subclass');
}
@@ -248,10 +251,10 @@ return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition, bool isFollowing, bool actionsExpanded, double mapZoom)? $default,) {final _that = this;
switch (_that) {
case _LocationMapViewState() when $default != null:
return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_that.adjustingRadius,_that.previewRadius,_that.previewPoint,_that.selectedGeofence,_that.editingGeofence,_that.selectedFrequentPlace,_that.selectedHistoryPosition);case _:
return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_that.adjustingRadius,_that.previewRadius,_that.previewPoint,_that.selectedGeofence,_that.editingGeofence,_that.selectedFrequentPlace,_that.selectedHistoryPosition,_that.isFollowing,_that.actionsExpanded,_that.mapZoom);case _:
return null;
}
@@ -263,7 +266,7 @@ return $default(_that.showGeofences,_that.showFrequentPlaces,_that.placingMode,_
class _LocationMapViewState implements LocationMapViewState {
const _LocationMapViewState({this.showGeofences = true, this.showFrequentPlaces = true, this.placingMode = PlacingMode.none, this.adjustingRadius = false, this.previewRadius = 200.0, this.previewPoint, this.selectedGeofence, this.editingGeofence, this.selectedFrequentPlace, this.selectedHistoryPosition});
const _LocationMapViewState({this.showGeofences = true, this.showFrequentPlaces = true, this.placingMode = PlacingMode.none, this.adjustingRadius = false, this.previewRadius = 200.0, this.previewPoint, this.selectedGeofence, this.editingGeofence, this.selectedFrequentPlace, this.selectedHistoryPosition, this.isFollowing = false, this.actionsExpanded = false, this.mapZoom = _defaultZoom});
@override@JsonKey() final bool showGeofences;
@@ -276,6 +279,9 @@ class _LocationMapViewState implements LocationMapViewState {
@override final GeofenceEntity? editingGeofence;
@override final FrequentPlaceEntity? selectedFrequentPlace;
@override final PositionEntity? selectedHistoryPosition;
@override@JsonKey() final bool isFollowing;
@override@JsonKey() final bool actionsExpanded;
@override@JsonKey() final double mapZoom;
/// Create a copy of LocationMapViewState
/// with the given fields replaced by the non-null parameter values.
@@ -287,16 +293,16 @@ _$LocationMapViewStateCopyWith<_LocationMapViewState> get copyWith => __$Locatio
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocationMapViewState&&(identical(other.showGeofences, showGeofences) || other.showGeofences == showGeofences)&&(identical(other.showFrequentPlaces, showFrequentPlaces) || other.showFrequentPlaces == showFrequentPlaces)&&(identical(other.placingMode, placingMode) || other.placingMode == placingMode)&&(identical(other.adjustingRadius, adjustingRadius) || other.adjustingRadius == adjustingRadius)&&(identical(other.previewRadius, previewRadius) || other.previewRadius == previewRadius)&&(identical(other.previewPoint, previewPoint) || other.previewPoint == previewPoint)&&(identical(other.selectedGeofence, selectedGeofence) || other.selectedGeofence == selectedGeofence)&&(identical(other.editingGeofence, editingGeofence) || other.editingGeofence == editingGeofence)&&(identical(other.selectedFrequentPlace, selectedFrequentPlace) || other.selectedFrequentPlace == selectedFrequentPlace)&&(identical(other.selectedHistoryPosition, selectedHistoryPosition) || other.selectedHistoryPosition == selectedHistoryPosition));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocationMapViewState&&(identical(other.showGeofences, showGeofences) || other.showGeofences == showGeofences)&&(identical(other.showFrequentPlaces, showFrequentPlaces) || other.showFrequentPlaces == showFrequentPlaces)&&(identical(other.placingMode, placingMode) || other.placingMode == placingMode)&&(identical(other.adjustingRadius, adjustingRadius) || other.adjustingRadius == adjustingRadius)&&(identical(other.previewRadius, previewRadius) || other.previewRadius == previewRadius)&&(identical(other.previewPoint, previewPoint) || other.previewPoint == previewPoint)&&(identical(other.selectedGeofence, selectedGeofence) || other.selectedGeofence == selectedGeofence)&&(identical(other.editingGeofence, editingGeofence) || other.editingGeofence == editingGeofence)&&(identical(other.selectedFrequentPlace, selectedFrequentPlace) || other.selectedFrequentPlace == selectedFrequentPlace)&&(identical(other.selectedHistoryPosition, selectedHistoryPosition) || other.selectedHistoryPosition == selectedHistoryPosition)&&(identical(other.isFollowing, isFollowing) || other.isFollowing == isFollowing)&&(identical(other.actionsExpanded, actionsExpanded) || other.actionsExpanded == actionsExpanded)&&(identical(other.mapZoom, mapZoom) || other.mapZoom == mapZoom));
}
@override
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition);
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition,isFollowing,actionsExpanded,mapZoom);
@override
String toString() {
return 'LocationMapViewState(showGeofences: $showGeofences, showFrequentPlaces: $showFrequentPlaces, placingMode: $placingMode, adjustingRadius: $adjustingRadius, previewRadius: $previewRadius, previewPoint: $previewPoint, selectedGeofence: $selectedGeofence, editingGeofence: $editingGeofence, selectedFrequentPlace: $selectedFrequentPlace, selectedHistoryPosition: $selectedHistoryPosition)';
return 'LocationMapViewState(showGeofences: $showGeofences, showFrequentPlaces: $showFrequentPlaces, placingMode: $placingMode, adjustingRadius: $adjustingRadius, previewRadius: $previewRadius, previewPoint: $previewPoint, selectedGeofence: $selectedGeofence, editingGeofence: $editingGeofence, selectedFrequentPlace: $selectedFrequentPlace, selectedHistoryPosition: $selectedHistoryPosition, isFollowing: $isFollowing, actionsExpanded: $actionsExpanded, mapZoom: $mapZoom)';
}
@@ -307,7 +313,7 @@ abstract mixin class _$LocationMapViewStateCopyWith<$Res> implements $LocationMa
factory _$LocationMapViewStateCopyWith(_LocationMapViewState value, $Res Function(_LocationMapViewState) _then) = __$LocationMapViewStateCopyWithImpl;
@override @useResult
$Res call({
bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition
bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition, bool isFollowing, bool actionsExpanded, double mapZoom
});
@@ -324,7 +330,7 @@ class __$LocationMapViewStateCopyWithImpl<$Res>
/// Create a copy of LocationMapViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? showGeofences = null,Object? showFrequentPlaces = null,Object? placingMode = null,Object? adjustingRadius = null,Object? previewRadius = null,Object? previewPoint = freezed,Object? selectedGeofence = freezed,Object? editingGeofence = freezed,Object? selectedFrequentPlace = freezed,Object? selectedHistoryPosition = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? showGeofences = null,Object? showFrequentPlaces = null,Object? placingMode = null,Object? adjustingRadius = null,Object? previewRadius = null,Object? previewPoint = freezed,Object? selectedGeofence = freezed,Object? editingGeofence = freezed,Object? selectedFrequentPlace = freezed,Object? selectedHistoryPosition = freezed,Object? isFollowing = null,Object? actionsExpanded = null,Object? mapZoom = null,}) {
return _then(_LocationMapViewState(
showGeofences: null == showGeofences ? _self.showGeofences : showGeofences // ignore: cast_nullable_to_non_nullable
as bool,showFrequentPlaces: null == showFrequentPlaces ? _self.showFrequentPlaces : showFrequentPlaces // ignore: cast_nullable_to_non_nullable
@@ -336,7 +342,10 @@ as LatLng?,selectedGeofence: freezed == selectedGeofence ? _self.selectedGeofenc
as GeofenceEntity?,editingGeofence: freezed == editingGeofence ? _self.editingGeofence : editingGeofence // ignore: cast_nullable_to_non_nullable
as GeofenceEntity?,selectedFrequentPlace: freezed == selectedFrequentPlace ? _self.selectedFrequentPlace : selectedFrequentPlace // ignore: cast_nullable_to_non_nullable
as FrequentPlaceEntity?,selectedHistoryPosition: freezed == selectedHistoryPosition ? _self.selectedHistoryPosition : selectedHistoryPosition // ignore: cast_nullable_to_non_nullable
as PositionEntity?,
as PositionEntity?,isFollowing: null == isFollowing ? _self.isFollowing : isFollowing // ignore: cast_nullable_to_non_nullable
as bool,actionsExpanded: null == actionsExpanded ? _self.actionsExpanded : actionsExpanded // ignore: cast_nullable_to_non_nullable
as bool,mapZoom: null == mapZoom ? _self.mapZoom : mapZoom // ignore: cast_nullable_to_non_nullable
as double,
));
}

View File

@@ -0,0 +1,288 @@
import 'package:control_panel/control_panel.dart';
import 'package:flutter/material.dart';
import 'package:location/src/core/domain/entities/geofence_entity.dart';
import 'package:location/src/core/domain/entities/frequent_place_entity.dart';
import 'package:sf_localizations/sf_localizations.dart';
class LocationListSheet extends StatefulWidget {
final List<GeofenceEntity> geofences;
final List<FrequentPlaceEntity> frequentPlaces;
final List<PositionEntity> positionHistory;
final Color primaryColor;
final ValueChanged<GeofenceEntity> onGeofenceTap;
final ValueChanged<FrequentPlaceEntity> onFrequentPlaceTap;
final ValueChanged<PositionEntity> onHistoryTap;
const LocationListSheet({
super.key,
required this.geofences,
required this.frequentPlaces,
required this.positionHistory,
required this.primaryColor,
required this.onGeofenceTap,
required this.onFrequentPlaceTap,
required this.onHistoryTap,
});
@override
State<LocationListSheet> createState() => _LocationListSheetState();
}
class _LocationListSheetState extends State<LocationListSheet> {
String? _selectedType;
List<PositionEntity> get _filteredHistory {
if (_selectedType == null) return widget.positionHistory;
return widget.positionHistory
.where((p) => p.type == _selectedType)
.toList();
}
Set<String> get _availableTypes {
return widget.positionHistory.map((p) => p.type).toSet();
}
@override
Widget build(BuildContext context) {
final filtered = _filteredHistory;
return DraggableScrollableSheet(
initialChildSize: 0.45,
minChildSize: 0.3,
maxChildSize: 0.85,
builder: (context, scrollController) {
return Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, -2),
),
],
),
child: Column(
children: [
_buildHandle(),
Expanded(
child: ListView(
controller: scrollController,
padding: const EdgeInsets.symmetric(horizontal: 16),
children: [
if (widget.geofences.isNotEmpty) ...[
_buildSectionHeader(
icon: Icons.shield,
color: Colors.blue,
title: context.translate(I18n.locationListGeofences),
count: widget.geofences.length,
),
...widget.geofences.map((g) => _buildListTile(
title: g.name,
subtitle:
'${g.radius.round()}m · ${g.isActive ? context.translate(I18n.locationStatusActive) : context.translate(I18n.locationStatusInactive)}',
color: Colors.blue,
icon: Icons.shield,
onTap: () => widget.onGeofenceTap(g),
)),
const SizedBox(height: 16),
],
if (widget.frequentPlaces.isNotEmpty) ...[
_buildSectionHeader(
icon: Icons.home_rounded,
color: Colors.orange,
title: context.translate(I18n.locationListFrequentPlaces),
count: widget.frequentPlaces.length,
),
...widget.frequentPlaces.map((fp) => _buildListTile(
title: fp.name,
subtitle:
'${fp.lat.toStringAsFixed(5)}, ${fp.lng.toStringAsFixed(5)}',
color: Colors.orange,
icon: Icons.home_rounded,
onTap: () => widget.onFrequentPlaceTap(fp),
)),
const SizedBox(height: 16),
],
if (widget.positionHistory.isNotEmpty) ...[
_buildSectionHeader(
icon: Icons.route,
color: Colors.purple,
title: context.translate(I18n.locationListPositionHistory),
count: filtered.length,
),
if (_availableTypes.length > 1) _buildTypeFilters(context),
...filtered.map(_buildHistoryTile),
const SizedBox(height: 16),
],
if (widget.geofences.isEmpty &&
widget.frequentPlaces.isEmpty &&
widget.positionHistory.isEmpty)
Padding(
padding: const EdgeInsets.only(top: 40),
child: Center(
child: Text(
context.translate(I18n.locationListNoItems),
style: const TextStyle(
color: Colors.grey, fontSize: 14),
),
),
),
],
),
),
],
),
);
},
);
}
Widget _buildTypeFilters(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Wrap(
spacing: 6,
children: [
_buildFilterChip(
label: context.translate(I18n.locationListAll), value: null),
for (final type in _availableTypes.toList()..sort())
_buildFilterChip(label: type, value: type),
],
),
);
}
Widget _buildFilterChip({required String label, required String? value}) {
final isSelected = _selectedType == value;
return GestureDetector(
onTap: () => setState(() => _selectedType = value),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: isSelected
? Colors.purple.withValues(alpha: 0.15)
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isSelected ? Colors.purple : Colors.grey.shade300,
width: 1.5,
),
),
child: Text(
label,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: isSelected ? Colors.purple : Colors.grey.shade600,
),
),
),
);
}
Widget _buildHandle() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey.shade300,
borderRadius: BorderRadius.circular(2),
),
),
);
}
Widget _buildSectionHeader({
required IconData icon,
required Color color,
required String title,
required int count,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 8, top: 4),
child: Row(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(width: 8),
Text(title,
style:
const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)),
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(10),
),
child: Text('$count',
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.w600, color: color)),
),
],
),
);
}
Widget _buildListTile({
required String title,
required String subtitle,
required Color color,
required IconData icon,
required VoidCallback onTap,
}) {
return Card(
margin: const EdgeInsets.only(bottom: 6),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Colors.grey.shade200),
),
child: ListTile(
dense: true,
leading: Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: Icon(icon, color: color, size: 18),
),
title: Text(title,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis),
subtitle: Text(subtitle,
style: TextStyle(fontSize: 11, color: Colors.grey.shade600)),
trailing:
Icon(Icons.chevron_right, color: Colors.grey.shade400, size: 20),
onTap: onTap,
),
);
}
Widget _buildHistoryTile(PositionEntity position) {
final date = DateTime.fromMillisecondsSinceEpoch(position.positionDate);
final dateStr =
'${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year} '
'${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
final hasCoords = position.latitude != 0 || position.longitude != 0;
final addressParts = [
position.address?.street,
position.address?.city,
].where((s) => s != null && s.isNotEmpty).join(', ');
return _buildListTile(
title: addressParts.isNotEmpty ? addressParts : position.type,
subtitle: '$dateStr · ${position.type}',
color: hasCoords ? Colors.purple : Colors.grey,
icon: hasCoords ? Icons.location_on : Icons.location_off,
onTap: () => widget.onHistoryTap(position),
);
}
}

View File

@@ -1,4 +1,7 @@
import 'dart:async';
import 'package:control_panel/control_panel.dart';
import 'package:share_plus/share_plus.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
@@ -16,8 +19,9 @@ import 'package:utils/utils.dart';
import 'device_banner.dart';
import 'name_input_sheet.dart';
import 'location_list_sheet.dart';
import 'map_controls/layer_toggles.dart';
import 'map_controls/map_action_button.dart';
import 'map_controls/map_actions_panel.dart';
import 'map_controls/map_style_selector.dart';
import 'map_controls/placement_banner.dart';
import 'map_controls/radius_slider_bar.dart';
@@ -25,9 +29,10 @@ import 'map_info_cards/frequent_place_info_card.dart';
import 'map_info_cards/geofence_info_card.dart';
import 'map_info_cards/history_position_info_card.dart';
import 'modal_overlay.dart';
import 'route_history_layer.dart';
const _defaultCenter = LatLng(40.4168, -3.7038);
const _defaultZoom = 15.0;
const _defaultZoom = 17.0;
class LocationMap extends ConsumerStatefulWidget {
final PositionEntity? selectedPosition;
@@ -59,12 +64,18 @@ class LocationMap extends ConsumerStatefulWidget {
ConsumerState<LocationMap> createState() => _LocationMapState();
}
class _LocationMapState extends ConsumerState<LocationMap> {
class _LocationMapState extends ConsumerState<LocationMap>
with TickerProviderStateMixin {
late final MapController _mapController;
AnimationController? _moveAnimation;
Timer? _followTimer;
LocationMapViewModel get _vm =>
ref.read(locationMapViewModelProvider.notifier);
Color get _primaryColor =>
ref.read(themePortProvider).getColorFor(ThemeCode.legacyPrimary);
@override
void initState() {
super.initState();
@@ -73,6 +84,8 @@ class _LocationMapState extends ConsumerState<LocationMap> {
@override
void dispose() {
_followTimer?.cancel();
_moveAnimation?.dispose();
_mapController.dispose();
super.dispose();
}
@@ -80,15 +93,62 @@ class _LocationMapState extends ConsumerState<LocationMap> {
@override
void didUpdateWidget(LocationMap oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.selectedDevice?.id != oldWidget.selectedDevice?.id) {
_stopFollowing();
}
if (widget.selectedPosition != null &&
widget.selectedPosition != oldWidget.selectedPosition) {
_centerOnDevice();
final mapState = ref.read(locationMapViewModelProvider);
if (mapState.isFollowing) _centerOnDevice();
}
if (widget.positionHistory.length > 1 &&
widget.showRouteTrail &&
(oldWidget.positionHistory.length != widget.positionHistory.length ||
!oldWidget.showRouteTrail)) {
_fitHistoryBounds();
}
}
void _animatedMove(LatLng dest, double zoom) {
_moveAnimation?.dispose();
final camera = _mapController.camera;
final latTween =
Tween<double>(begin: camera.center.latitude, end: dest.latitude);
final lngTween =
Tween<double>(begin: camera.center.longitude, end: dest.longitude);
final zoomTween = Tween<double>(begin: camera.zoom, end: zoom);
final controller = AnimationController(
duration: const Duration(milliseconds: 400),
vsync: this,
);
_moveAnimation = controller;
final animation =
CurvedAnimation(parent: controller, curve: Curves.easeInOut);
controller.addListener(() {
_mapController.move(
LatLng(latTween.evaluate(animation), lngTween.evaluate(animation)),
zoomTween.evaluate(animation),
);
});
controller.addStatusListener((status) {
if (status == AnimationStatus.completed ||
status == AnimationStatus.dismissed) {
controller.dispose();
if (_moveAnimation == controller) _moveAnimation = null;
}
});
controller.forward();
}
void _centerOnDevice() {
if (widget.selectedPosition == null) return;
_mapController.move(
_animatedMove(
LatLng(
widget.selectedPosition!.latitude,
widget.selectedPosition!.longitude,
@@ -97,6 +157,69 @@ class _LocationMapState extends ConsumerState<LocationMap> {
);
}
void _fitHistoryBounds() {
final valid = widget.positionHistory
.where((p) => p.latitude != 0 || p.longitude != 0)
.toList();
if (valid.length < 2) return;
final lats = valid.map((p) => p.latitude);
final lngs = valid.map((p) => p.longitude);
final bounds = LatLngBounds(
LatLng(lats.reduce((a, b) => a < b ? a : b),
lngs.reduce((a, b) => a < b ? a : b)),
LatLng(lats.reduce((a, b) => a > b ? a : b),
lngs.reduce((a, b) => a > b ? a : b)),
);
final fit = CameraFit.bounds(bounds: bounds, padding: const EdgeInsets.all(60));
final target = fit.fit(_mapController.camera);
_animatedMove(target.center, target.zoom);
}
void _toggleFollowing() {
final wasFollowing = ref.read(locationMapViewModelProvider).isFollowing;
_vm.toggleFollowing();
if (!wasFollowing) {
_centerOnDevice();
widget.onRefreshPosition();
_followTimer = Timer.periodic(const Duration(seconds: 30), (_) {
widget.onRefreshPosition();
});
} else {
_stopFollowing();
}
}
void _stopFollowing() {
_followTimer?.cancel();
_followTimer = null;
_vm.stopFollowing();
}
void _shareLocation() {
final position = widget.selectedPosition;
if (position == null) return;
final lat = position.latitude;
final lng = position.longitude;
if (lat == 0 && lng == 0) return;
final deviceName = widget.selectedDevice?.carrierName ?? '';
final address = position.address;
final addressStr = [address?.street, address?.city]
.where((s) => s != null && s.isNotEmpty)
.join(', ');
final mapsUrl = 'https://www.google.com/maps?q=$lat,$lng';
final text = StringBuffer();
if (deviceName.isNotEmpty) text.writeln(deviceName);
if (addressStr.isNotEmpty) text.writeln(addressStr);
text.writeln(mapsUrl);
Share.share(text.toString().trim());
}
void _confirmPlacement() {
final center = _mapController.camera.center;
final mapState = ref.read(locationMapViewModelProvider);
@@ -160,6 +283,40 @@ class _LocationMapState extends ConsumerState<LocationMap> {
).then((_) => _vm.clearPreviewPoint());
}
void _showListSheet() {
final locationState = ref.read(locationViewModelProvider);
final mapState = ref.read(locationMapViewModelProvider);
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => LocationListSheet(
geofences: mapState.showGeofences ? locationState.geofences : [],
frequentPlaces: mapState.showFrequentPlaces
? locationState.frequentPlaces
: [],
positionHistory: locationState.showRouteTrail
? locationState.positionHistory
: [],
primaryColor: _primaryColor,
onGeofenceTap: (g) {
Navigator.pop(context);
_animatedMove(LatLng(g.latitude, g.longitude), _defaultZoom);
},
onFrequentPlaceTap: (fp) {
Navigator.pop(context);
_animatedMove(LatLng(fp.lat, fp.lng), _defaultZoom);
},
onHistoryTap: (p) {
Navigator.pop(context);
if (p.latitude != 0 || p.longitude != 0) {
_animatedMove(LatLng(p.latitude, p.longitude), _defaultZoom);
}
},
),
);
}
void _handleHistoryTap() {
if (widget.positionHistory.isEmpty) {
_openDateRangePicker();
@@ -169,8 +326,6 @@ class _LocationMapState extends ConsumerState<LocationMap> {
}
Future<void> _openDateRangePicker() async {
final primaryColor =
ref.read(themePortProvider).getColorFor(ThemeCode.legacyPrimary);
final now = DateTime.now();
final picked = await showDateRangePicker(
@@ -183,7 +338,7 @@ class _LocationMapState extends ConsumerState<LocationMap> {
),
builder: (context, child) => Theme(
data: Theme.of(context).copyWith(
colorScheme: ColorScheme.light(primary: primaryColor),
colorScheme: ColorScheme.light(primary: _primaryColor),
),
child: child!,
),
@@ -214,31 +369,17 @@ class _LocationMapState extends ConsumerState<LocationMap> {
);
}
Marker _buildHistoryMarker({
required PositionEntity position,
required IconData icon,
required Color color,
}) {
return Marker(
point: LatLng(position.latitude, position.longitude),
width: 24,
height: 24,
child: GestureDetector(
onTap: () => _vm.selectHistoryPosition(position),
child: Container(
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
),
child: Icon(icon, color: Colors.white, size: 14),
),
),
rotate: true,
);
}
List<Widget> _buildMapLayers(LocationMapViewState mapState, Color primaryColor) {
List<Widget> _buildMapLayers(LocationMapViewState mapState) {
final historyLayer = widget.showRouteTrail && widget.positionHistory.isNotEmpty
? RouteHistoryLayer(
positionHistory: widget.positionHistory,
zoom: mapState.mapZoom,
onPositionTap: _vm.selectHistoryPosition,
onClusterTap: _animatedMove,
)
: null;
return [
TileLayer(
urlTemplate: ref.watch(mapStyleProvider).urlTemplate,
@@ -258,18 +399,8 @@ class _LocationMapState extends ConsumerState<LocationMap> {
))
.toList(),
),
if (widget.showRouteTrail && widget.positionHistory.isNotEmpty)
PolylineLayer(
polylines: [
Polyline(
points: widget.positionHistory
.map((p) => LatLng(p.latitude, p.longitude))
.toList(),
color: Colors.purple,
strokeWidth: 3.0,
),
],
),
if (historyLayer != null)
PolylineLayer(polylines: historyLayer.buildRouteSegments()),
if (mapState.previewPoint != null)
CircleLayer(
circles: [
@@ -277,13 +408,13 @@ class _LocationMapState extends ConsumerState<LocationMap> {
point: mapState.previewPoint!,
radius: mapState.previewRadius,
useRadiusInMeter: true,
color: primaryColor.withValues(alpha: 0.2),
borderColor: primaryColor.withValues(alpha: 0.8),
color: _primaryColor.withValues(alpha: 0.2),
borderColor: _primaryColor.withValues(alpha: 0.8),
borderStrokeWidth: 2,
),
],
),
MarkerLayer(markers: _buildMarkers(mapState, primaryColor)),
MarkerLayer(markers: _buildMarkers(mapState, historyLayer)),
if (widget.selectedDevice != null)
Align(
alignment: Alignment.bottomCenter,
@@ -297,33 +428,23 @@ class _LocationMapState extends ConsumerState<LocationMap> {
];
}
List<Marker> _buildMarkers(LocationMapViewState mapState, Color primaryColor) {
List<Marker> _buildMarkers(
LocationMapViewState mapState,
RouteHistoryLayer? historyLayer,
) {
return [
if (mapState.previewPoint != null)
Marker(
point: mapState.previewPoint!,
width: 40,
height: 40,
child: Icon(Icons.add_location_alt, color: primaryColor, size: 36),
child: Icon(Icons.add_location_alt, color: _primaryColor, size: 36),
rotate: true,
),
if (widget.showRouteTrail && widget.positionHistory.length == 1)
_buildHistoryMarker(
position: widget.positionHistory.first,
icon: Icons.location_on,
color: Colors.purple,
),
if (widget.showRouteTrail && widget.positionHistory.length >= 2) ...[
_buildHistoryMarker(
position: widget.positionHistory.first,
icon: Icons.play_arrow,
color: Colors.purple,
),
_buildHistoryMarker(
position: widget.positionHistory.last,
icon: Icons.stop,
color: Colors.purple.shade800,
),
if (historyLayer != null) ...[
...historyLayer.buildRouteSegments().isEmpty
? <Marker>[]
: (historyLayer.build(context) as MarkerLayer).markers,
],
if (widget.selectedPosition != null)
Marker(
@@ -333,7 +454,7 @@ class _LocationMapState extends ConsumerState<LocationMap> {
),
width: 100,
height: 100,
child: PulsingLocationMarker(color: primaryColor),
child: PulsingLocationMarker(color: _primaryColor),
rotate: true,
),
if (mapState.showFrequentPlaces)
@@ -360,34 +481,34 @@ class _LocationMapState extends ConsumerState<LocationMap> {
),
if (mapState.showGeofences)
...widget.geofences.where((g) => g.isActive).map(
(g) => Marker(
point: LatLng(g.latitude, g.longitude),
width: 36,
height: 36,
child: GestureDetector(
onLongPress: () => _vm.selectGeofence(g),
child: Container(
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.3),
shape: BoxShape.circle,
border: Border.all(
color: Colors.blue.withValues(alpha: 0.7), width: 2),
(g) => Marker(
point: LatLng(g.latitude, g.longitude),
width: 36,
height: 36,
child: GestureDetector(
onLongPress: () => _vm.selectGeofence(g),
child: Container(
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.3),
shape: BoxShape.circle,
border: Border.all(
color: Colors.blue.withValues(alpha: 0.7), width: 2),
),
child:
const Icon(Icons.shield, color: Colors.blue, size: 18),
),
),
child: const Icon(Icons.shield, color: Colors.blue, size: 18),
rotate: true,
),
),
rotate: true,
),
),
];
}
List<Widget> _buildControls(
LocationMapViewState mapState, Color primaryColor) {
List<Widget> _buildControls(LocationMapViewState mapState) {
if (mapState.placingMode != PlacingMode.none) {
return [
Center(
child: Icon(Icons.add_circle_outline, size: 48, color: primaryColor),
child: Icon(Icons.add_circle_outline, size: 48, color: _primaryColor),
),
Positioned(
top: 12,
@@ -409,7 +530,7 @@ class _LocationMapState extends ConsumerState<LocationMap> {
right: 12,
child: RadiusSliderBar(
radius: mapState.previewRadius,
primaryColor: primaryColor,
primaryColor: _primaryColor,
onChanged: _vm.updatePreviewRadius,
onCancel: _vm.cancelPlacing,
onConfirm: _confirmRadius,
@@ -419,11 +540,7 @@ class _LocationMapState extends ConsumerState<LocationMap> {
}
return [
Positioned(
top: 12,
left: 12,
child: const MapStyleSelector(),
),
Positioned(top: 12, left: 12, child: const MapStyleSelector()),
Positioned(
top: 12,
right: 12,
@@ -442,31 +559,18 @@ class _LocationMapState extends ConsumerState<LocationMap> {
Positioned(
bottom: SizeUtils.getByScreen(small: 120, big: 110),
right: 12,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MapActionButton(
icon: Icons.add_location_alt,
onTap: () => _vm.startPlacing(PlacingMode.geofence),
),
const SizedBox(height: 8),
MapActionButton(
icon: Icons.add_home,
onTap: () => _vm.startPlacing(PlacingMode.frequentPlace),
),
const SizedBox(height: 8),
MapActionButton(
icon: Icons.refresh,
onTap: widget.onRefreshPosition,
),
if (widget.selectedPosition != null) ...[
const SizedBox(height: 8),
MapActionButton(
icon: Icons.my_location,
onTap: _centerOnDevice,
),
],
],
child: MapActionsPanel(
actionsExpanded: mapState.actionsExpanded,
isFollowing: mapState.isFollowing,
hasPosition: widget.selectedPosition != null,
onToggleExpanded: _vm.toggleActionsExpanded,
onListTap: _showListSheet,
onAddGeofence: () => _vm.startPlacing(PlacingMode.geofence),
onAddFrequentPlace: () => _vm.startPlacing(PlacingMode.frequentPlace),
onShareTap: _shareLocation,
onFollowTap: _toggleFollowing,
onRefreshTap: widget.onRefreshPosition,
onCenterTap: _centerOnDevice,
),
),
];
@@ -517,8 +621,6 @@ class _LocationMapState extends ConsumerState<LocationMap> {
@override
Widget build(BuildContext context) {
final mapState = ref.watch(locationMapViewModelProvider);
final primaryColor =
ref.read(themePortProvider).getColorFor(ThemeCode.legacyPrimary);
final initialCenter = widget.selectedPosition != null
? LatLng(
widget.selectedPosition!.latitude,
@@ -533,12 +635,17 @@ class _LocationMapState extends ConsumerState<LocationMap> {
options: MapOptions(
initialCenter: initialCenter,
initialZoom: _defaultZoom,
minZoom: 11,
minZoom: 5,
keepAlive: true,
onPositionChanged: (camera, _) {
if (widget.showRouteTrail && widget.positionHistory.length > 2) {
_vm.updateMapZoom(camera.zoom);
}
},
),
children: _buildMapLayers(mapState, primaryColor),
children: _buildMapLayers(mapState),
),
..._buildControls(mapState, primaryColor),
..._buildControls(mapState),
..._buildInfoCards(mapState),
],
);

View File

@@ -5,19 +5,22 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
class MapActionButton extends ConsumerWidget {
final IconData icon;
final VoidCallback onTap;
final bool isActive;
const MapActionButton({
super.key,
required this.icon,
required this.onTap,
this.isActive = false,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
return Material(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
color: isActive ? primaryColor : theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.circular(8),
elevation: 2,
child: InkWell(
@@ -29,7 +32,7 @@ class MapActionButton extends ConsumerWidget {
child: Icon(
icon,
size: 24,
color: theme.getColorFor(ThemeCode.legacyPrimary),
color: isActive ? Colors.white : primaryColor,
),
),
),

View File

@@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:location/src/features/location/presentation/state/location_map_view_state.dart';
import 'map_action_button.dart';
class MapActionsPanel extends StatelessWidget {
final bool actionsExpanded;
final bool isFollowing;
final bool hasPosition;
final VoidCallback onToggleExpanded;
final VoidCallback onListTap;
final VoidCallback onAddGeofence;
final VoidCallback onAddFrequentPlace;
final VoidCallback onShareTap;
final VoidCallback onFollowTap;
final VoidCallback onRefreshTap;
final VoidCallback onCenterTap;
const MapActionsPanel({
super.key,
required this.actionsExpanded,
required this.isFollowing,
required this.hasPosition,
required this.onToggleExpanded,
required this.onListTap,
required this.onAddGeofence,
required this.onAddFrequentPlace,
required this.onShareTap,
required this.onFollowTap,
required this.onRefreshTap,
required this.onCenterTap,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedSize(
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
alignment: Alignment.bottomCenter,
child: actionsExpanded
? Column(
mainAxisSize: MainAxisSize.min,
children: [
MapActionButton(icon: Icons.list_alt, onTap: onListTap),
const SizedBox(height: 8),
MapActionButton(
icon: Icons.add_location_alt, onTap: onAddGeofence),
const SizedBox(height: 8),
MapActionButton(
icon: Icons.add_home, onTap: onAddFrequentPlace),
const SizedBox(height: 8),
MapActionButton(icon: Icons.share, onTap: onShareTap),
const SizedBox(height: 8),
MapActionButton(
icon: isFollowing ? Icons.gps_fixed : Icons.gps_not_fixed,
onTap: onFollowTap,
isActive: isFollowing,
),
const SizedBox(height: 8),
],
)
: const SizedBox.shrink(),
),
MapActionButton(
icon: actionsExpanded ? Icons.close : Icons.more_vert,
onTap: onToggleExpanded,
),
const SizedBox(height: 8),
MapActionButton(icon: Icons.refresh, onTap: onRefreshTap),
if (hasPosition) ...[
const SizedBox(height: 8),
MapActionButton(icon: Icons.my_location, onTap: onCenterTap),
],
],
);
}
}

View File

@@ -0,0 +1,255 @@
import 'dart:math' as math;
import 'package:control_panel/control_panel.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
const _clusterBaseDegrees = 0.5;
const _clusterBaseZoom = 8;
const routeStartColor = Color(0xFF4CAF50);
bool _hasValidCoords(PositionEntity p) =>
p.latitude != 0 || p.longitude != 0;
const routeEndColor = Color(0xFFF44336);
Color routeGradient(double t) =>
Color.lerp(routeStartColor, routeEndColor, t)!;
class RouteHistoryLayer extends StatelessWidget {
final List<PositionEntity> positionHistory;
final double zoom;
final ValueChanged<PositionEntity> onPositionTap;
final void Function(LatLng point, double zoom) onClusterTap;
const RouteHistoryLayer({
super.key,
required this.positionHistory,
required this.zoom,
required this.onPositionTap,
required this.onClusterTap,
});
@override
Widget build(BuildContext context) {
return MarkerLayer(markers: [
..._buildDirectionArrows(),
..._buildHistoryMarkers(),
]);
}
List<Polyline> buildRouteSegments() {
if (positionHistory.length < 2) return [];
final segments = <Polyline>[];
for (int i = 0; i < positionHistory.length - 1; i++) {
final t = i / (positionHistory.length - 1);
segments.add(Polyline(
points: [
LatLng(positionHistory[i].latitude, positionHistory[i].longitude),
LatLng(
positionHistory[i + 1].latitude, positionHistory[i + 1].longitude),
],
color: routeGradient(t),
strokeWidth: 4.0,
));
}
return segments;
}
List<Marker> _buildDirectionArrows() {
if (positionHistory.length < 2) return [];
final arrows = <Marker>[];
for (int i = 0; i < positionHistory.length - 1; i++) {
final from = positionHistory[i];
final to = positionHistory[i + 1];
if (!_hasValidCoords(from)) continue;
if (!_hasValidCoords(to)) continue;
final midLat = (from.latitude + to.latitude) / 2;
final midLng = (from.longitude + to.longitude) / 2;
final angle = math.atan2(
to.longitude - from.longitude,
to.latitude - from.latitude,
);
final t = i / (positionHistory.length - 1);
arrows.add(Marker(
point: LatLng(midLat, midLng),
width: 16,
height: 16,
child: Transform.rotate(
angle: -angle,
child: Icon(Icons.navigation, color: routeGradient(t), size: 16),
),
rotate: false,
));
}
return arrows;
}
List<Marker> _buildHistoryMarkers() {
if (positionHistory.isEmpty) return [];
final markers = <Marker>[];
final threshold = _clusterBaseDegrees / math.pow(2, zoom - _clusterBaseZoom);
final first = positionHistory.first;
final last = positionHistory.last;
markers.add(_buildEndpointMarker(
position: first,
color: routeStartColor,
icon: Icons.play_arrow,
));
if (positionHistory.length > 1) {
markers.add(_buildEndpointMarker(
position: last,
color: routeEndColor,
icon: Icons.stop,
));
}
if (positionHistory.length <= 2) return markers;
final intermediates = positionHistory.sublist(1, positionHistory.length - 1);
final clustered = <List<int>>[];
final visited = List.filled(intermediates.length, false);
for (int i = 0; i < intermediates.length; i++) {
if (visited[i]) continue;
final cluster = [i];
visited[i] = true;
for (int j = i + 1; j < intermediates.length; j++) {
if (visited[j]) continue;
final dLat = intermediates[i].latitude - intermediates[j].latitude;
final dLng = intermediates[i].longitude - intermediates[j].longitude;
if (dLat * dLat + dLng * dLng < threshold * threshold) {
cluster.add(j);
visited[j] = true;
}
}
clustered.add(cluster);
}
for (final cluster in clustered) {
final firstIdx = cluster.first;
final position = intermediates[firstIdx];
final originalIdx = firstIdx + 1;
final t = originalIdx / (positionHistory.length - 1);
final color = routeGradient(t);
if (cluster.length == 1) {
markers.add(_buildIntermediateMarker(position: position, color: color));
} else {
markers.add(_buildClusterMarker(
position: position,
count: cluster.length,
color: color,
));
}
}
return markers;
}
Marker _buildEndpointMarker({
required PositionEntity position,
required Color color,
required IconData icon,
}) {
return Marker(
point: LatLng(position.latitude, position.longitude),
width: 32,
height: 32,
child: GestureDetector(
onTap: () => onPositionTap(position),
child: _CircleMarkerIcon(color: color, size: 32, child: Icon(icon, color: Colors.white, size: 18)),
),
rotate: true,
);
}
Marker _buildIntermediateMarker({
required PositionEntity position,
required Color color,
}) {
return Marker(
point: LatLng(position.latitude, position.longitude),
width: 18,
height: 18,
child: GestureDetector(
onTap: () => onPositionTap(position),
child: _CircleMarkerIcon(color: color, size: 18),
),
rotate: true,
);
}
Marker _buildClusterMarker({
required PositionEntity position,
required int count,
required Color color,
}) {
return Marker(
point: LatLng(position.latitude, position.longitude),
width: 32,
height: 32,
child: GestureDetector(
onTap: () => onClusterTap(
LatLng(position.latitude, position.longitude),
zoom + 2,
),
child: _CircleMarkerIcon(
color: color,
size: 32,
child: Text(
'$count',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
),
rotate: true,
);
}
}
class _CircleMarkerIcon extends StatelessWidget {
final Color color;
final double size;
final Widget? child;
const _CircleMarkerIcon({
required this.color,
required this.size,
this.child,
});
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: size > 20 ? 2.5 : 2),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: size > 20 ? 0.3 : 0.2),
blurRadius: size > 20 ? 4 : 2,
offset: Offset(0, size > 20 ? 2 : 1),
),
],
),
child: child != null ? Center(child: child) : null,
);
}
}

View File

@@ -222,6 +222,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:
@@ -934,6 +942,22 @@ packages:
relative: true
source: path
version: "0.0.1"
share_plus:
dependency: "direct main"
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:
@@ -1370,6 +1394,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

@@ -33,6 +33,7 @@ dependencies:
latlong2: ^0.9.1
flutter_svg: ^2.2.1
uuid: ^4.5.1
share_plus: ^10.1.4
dev_dependencies:
flutter_test:

View File

@@ -1,6 +1,8 @@
import 'package:sf_shared/sf_shared.dart';
abstract class LanguageRemoteDatasource {
Future<void> updateDeviceLanguage({
required Map<String, dynamic> deviceData,
required DeviceEntity device,
required String newLanguage,
});
}

View File

@@ -1,7 +1,6 @@
import 'dart:convert';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_shared/sf_shared.dart';
import 'language_remote_datasource.dart';
@@ -12,50 +11,16 @@ class LanguageRemoteDatasourceImpl implements LanguageRemoteDatasource {
@override
Future<void> updateDeviceLanguage({
required Map<String, dynamic> deviceData,
required DeviceEntity device,
required String newLanguage,
}) async {
final settings = Map<String, dynamic>.from(
deviceData['settings'] as Map? ?? {},
);
final settings = Map<String, dynamic>.from(device.settings);
settings['language'] = newLanguage;
final flags = Map<String, dynamic>.from(
deviceData['flags'] as Map? ?? {},
final csvBase64 = DeviceCsvBuilder.buildBase64Csv(
device: device,
settings: settings,
);
final tags = deviceData['tags'] as List? ?? [];
final id = deviceData['id'] ?? '';
final carrierName = deviceData['carrierName'] ?? '';
final battery = deviceData['battery'] ?? '';
final carrierBirthday = deviceData['carrierBirthday'] ?? '';
final carrierWeight = deviceData['carrierWeight'] ?? '';
final carrierStepLength = deviceData['carrierStepLength'] ?? '';
final carrierGenre = deviceData['carrierGenre'] ?? '';
final comment = deviceData['comment'] ?? '';
final groupId = deviceData['groupId'] ?? '';
final lastConnection = deviceData['lastConnection'] ?? '';
final phone = deviceData['phone'] ?? '';
final simId = deviceData['simId'] ?? '';
final paymentOptions = deviceData['paymentOptions'];
final paymentOptionsStr = paymentOptions != null
? jsonEncode(paymentOptions)
: '';
final csvHeader =
'id,carrierName,flags,settings,battery,carrierBirthday,'
'carrierWeight,carrierStepLength,carrierGenre,comment,'
'groupId,lastConnection,paymentOptions,phone,simId,tags';
final csvRow =
'$id,$carrierName,${jsonEncode(flags)},${jsonEncode(settings)},'
'$battery,$carrierBirthday,$carrierWeight,$carrierStepLength,'
'$carrierGenre,$comment,$groupId,$lastConnection,'
'$paymentOptionsStr,$phone,$simId,"${jsonEncode(tags)}"';
final csv = '$csvHeader\n$csvRow';
final csvBase64 = base64Encode(utf8.encode(csv));
await safeCall(
() => _repository.put<dynamic>(

View File

@@ -1,3 +1,4 @@
import 'package:sf_shared/sf_shared.dart';
import 'package:settings/src/core/data/datasources/language_remote_datasource.dart';
import 'package:settings/src/core/domain/repositories/language_repository.dart';
@@ -8,11 +9,11 @@ class LanguageRepositoryImpl implements LanguageRepository {
@override
Future<void> updateDeviceLanguage({
required Map<String, dynamic> deviceData,
required DeviceEntity device,
required String newLanguage,
}) async {
await _datasource.updateDeviceLanguage(
deviceData: deviceData,
}) {
return _datasource.updateDeviceLanguage(
device: device,
newLanguage: newLanguage,
);
}

View File

@@ -1,6 +1,8 @@
import 'package:sf_shared/sf_shared.dart';
abstract class LanguageRepository {
Future<void> updateDeviceLanguage({
required Map<String, dynamic> deviceData,
required DeviceEntity device,
required String newLanguage,
});
}

View File

@@ -1,5 +1,7 @@
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/language_screen.dart';
@@ -7,9 +9,11 @@ class LanguageBuilder {
const LanguageBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: const LanguageScreen(),
child: LanguageScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -2,21 +2,123 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:navigation/navigation.dart';
import 'package:settings/src/features/language/presentation/state/language_view_model.dart';
import 'package:sf_localizations/sf_localizations.dart';
class LanguageScreen extends ConsumerWidget {
const LanguageScreen({super.key});
final NavigationContract navigationContract;
const LanguageScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final vm = ref.read(languageViewModelProvider.notifier);
final language = ref.watch(
languageViewModelProvider.select((s)=>s.language)
);
const Map<String, String> languageOptions = <String, String>{
'es': 'español',
'en': 'English',
'pt': 'português',
'it': 'italiano',
'fr': 'français',
'de': 'Deutsch',
};
final languageCodes = languageOptions.keys.toList(growable: false);
final languageLabels = languageOptions.values.toList(growable: false);
ref.listen(languageViewModelProvider.select((s) => s.errorMessage), (
_,
errorMessage,
) {
if (errorMessage.isNotEmpty) {
showTopSnackbar(
context,
message: errorMessage,
type: MessageType.error,
);
}
});
ref.listen(languageViewModelProvider.select((s) => s.isComplete), (
_,
isComplete,
) {
if (isComplete) navigationContract.goBack();
});
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.language),
body: const Center(
child: Text('Coming soon'),
body: SingleChildScrollView(
child: RadioGroup(
groupValue: language,
onChanged: (value) {vm.selectLanguage(value.toString());},
child: Column(
children: List.generate(languageOptions.length, (int i) {
return _Option(
value: languageCodes.elementAt(i),
label: languageLabels.elementAt(i),
);
}),
)
),
),
footer: const _SaveSection(),
);
}
}
class _Option extends ConsumerWidget {
final String value;
final String label;
const _Option({
required this.value,
required this.label,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return RadioListTile<String>(
title: Text(label),
value: value,
activeColor: theme.getColorFor(ThemeCode.legacyPrimary),
controlAffinity: ListTileControlAffinity.trailing,
);
}
}
class _SaveSection extends ConsumerWidget {
const _SaveSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final vm = ref.read(languageViewModelProvider.notifier);
final isLoading = ref.watch(
languageViewModelProvider.select((s) => s.isLoading),
);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: isLoading
? const Center(child: CircularProgressIndicator())
: PrimaryButton(
onPressed: vm.submit,
text: context.translate(I18n.save),
color: theme.getColorFor(ThemeCode.legacyPrimary),
),
);
}
}

View File

@@ -0,0 +1,66 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:settings/src/core/domain/repositories/language_repository.dart';
import 'package:settings/src/core/providers/language_repository_provider.dart';
import 'language_view_state.dart';
final languageViewModelProvider =
NotifierProvider.autoDispose<LanguageViewModel, LanguageViewState>(
LanguageViewModel.new,
);
class LanguageViewModel extends Notifier<LanguageViewState> {
late final LanguageRepository _languageRepository;
@override
LanguageViewState build() {
_languageRepository = ref.read(languageRepositoryProvider);
Future.microtask(() => load());
return const LanguageViewState(isLoading: true);
}
Future<void> load() async {
state = state.copyWith(isLoading: true, errorMessage: '');
try {
final device = ref.read(selectedDeviceProvider);
state = state.copyWith(
isLoading: false,
device: device,
language: device?.settings['language'] ?? 'es',
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
}
}
void selectLanguage(String value) {
if (value == state.language) return;
state = state.copyWith(language: value);
}
Future<void> submit() async {
final device = state.device;
if (device == null) return;
if (state.language == device.settings['language']) {
state = state.copyWith(isComplete: true);
return;
}
try {
state = state.copyWith(isLoading: true, isComplete: false);
await _languageRepository.updateDeviceLanguage(
device: device,
newLanguage: state.language,
);
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, isComplete: true);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
}
}
}

View File

@@ -0,0 +1,17 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:sf_shared/sf_shared.dart';
part 'language_view_state.freezed.dart';
@freezed
abstract class LanguageViewState with _$LanguageViewState {
const LanguageViewState._();
const factory LanguageViewState({
@Default(false) bool isLoading,
@Default(false) bool isComplete,
DeviceEntity? device,
@Default('es') String language,
@Default('') String errorMessage,
}) = _LanguageViewState;
}

View File

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

View File

@@ -94,13 +94,13 @@ class SettingsScreen extends ConsumerWidget {
text: I18n.sosContacts,
color: color,
),
// _item(
// context,
// onPressed: () => navigationContract.pushTo(AppRoutes.sound),
// icon: Icons.volume_up_outlined,
// text: I18n.sound,
// color: color,
// ),
_item(
context,
onPressed: () => navigationContract.pushTo(AppRoutes.sound),
icon: Icons.volume_up_outlined,
text: I18n.sound,
color: color,
),
// _item(
// context,
// onPressed: () =>

View File

@@ -1,3 +0,0 @@
abstract class SetSoundUseCase {
Future<void> setSound({required String deviceId});
}

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