Compare commits
16 Commits
feature/ba
...
sizing
| Author | SHA1 | Date | |
|---|---|---|---|
| ecb9d7316e | |||
| 1810dc6e2a | |||
| 4d2d25f47b | |||
| c79cbeffcc | |||
| b7614a39f1 | |||
| a05c167f30 | |||
| c1c903ac93 | |||
| a0a782c91b | |||
| c140daa7ae | |||
| 6d30a59651 | |||
| 8d453dc980 | |||
| 0a50de3d70 | |||
| 02053182db | |||
| 33f3dfa252 | |||
| 5f484036f8 | |||
| 73927557ca |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,6 +6,8 @@ PODS:
|
||||
- Flutter
|
||||
- flutter_treezor_entrust_sdk_bridge (0.0.1):
|
||||
- Flutter
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- mobile_scanner (7.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -14,6 +16,8 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -28,9 +32,11 @@ DEPENDENCIES:
|
||||
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_treezor_entrust_sdk_bridge (from `.symlinks/plugins/flutter_treezor_entrust_sdk_bridge/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
@@ -44,12 +50,16 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||
flutter_treezor_entrust_sdk_bridge:
|
||||
:path: ".symlinks/plugins/flutter_treezor_entrust_sdk_bridge/ios"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
mobile_scanner:
|
||||
:path: ".symlinks/plugins/mobile_scanner/darwin"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
url_launcher_ios:
|
||||
@@ -62,9 +72,11 @@ SPEC CHECKSUMS:
|
||||
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
|
||||
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
|
||||
flutter_treezor_entrust_sdk_bridge: 4c2c94fb74ab57576e8d49f5f2a4b214e41141fe
|
||||
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b
|
||||
mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e
|
||||
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
|
||||
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa
|
||||
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4
|
||||
|
||||
@@ -30,7 +30,9 @@ Future<void> initApp(EnvironmentEnum env) async {
|
||||
await configureDependencies(
|
||||
QuestiaEnvConfig(),
|
||||
log: env.isDevelopment || kDebugMode,
|
||||
onTokenExpired: () => appRouter.go(AppRoutes.scaTreezor),
|
||||
onTokenExpired: () => appRouter.go(
|
||||
AppRoutes.legacyLogin,
|
||||
), //change to payments app to AppRoutes.scaTreezor
|
||||
onUnauthorized: () async {
|
||||
final currentLocation =
|
||||
appRouter.routerDelegate.currentConfiguration.uri.path;
|
||||
|
||||
@@ -26,7 +26,7 @@ late final GoRouter appRouter;
|
||||
void configureAppRouter() {
|
||||
appRouter = GoRouter(
|
||||
navigatorKey: rootNavigatorKey,
|
||||
initialLocation: AppRoutes.controlPanel,
|
||||
initialLocation: AppRoutes.splash,
|
||||
debugLogDiagnostics: true,
|
||||
routes: [
|
||||
GoRoute(
|
||||
@@ -252,9 +252,9 @@ void configureAppRouter() {
|
||||
pageBuilder: RemoteOnOffBuilder().buildPage,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'sms_alert',
|
||||
name: 'sms_alert',
|
||||
pageBuilder: SmsAlertBuilder().buildPage,
|
||||
path: 'alerts',
|
||||
name: 'alerts',
|
||||
pageBuilder: AlertsBuilder().buildPage,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'timezone',
|
||||
|
||||
@@ -34,7 +34,9 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
|
||||
walletHeartbeat = WalletHeartbeatService(
|
||||
repository: ref.read(treezorRepositoryProvider),
|
||||
sessionLocal: SessionLocalDatasourceImpl(),
|
||||
onError: () => appRouter.go(AppRoutes.scaTreezor),
|
||||
onError: () => appRouter.go(
|
||||
AppRoutes.legacyLogin,
|
||||
), //change to payments app to AppRoutes.scaTreezor
|
||||
);
|
||||
legacyHeartbeat = LegacyHeartbeatService(
|
||||
repository: GetIt.I<QuestiaRepository>(),
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/file_selector_linux-0.9.4/
|
||||
@@ -0,0 +1 @@
|
||||
/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/image_picker_linux-0.2.2/
|
||||
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0+5
|
||||
version: 1.0.0+6
|
||||
|
||||
environment:
|
||||
sdk: ^3.9.2
|
||||
@@ -123,6 +123,7 @@ flutter:
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
assets:
|
||||
- assets/shared/images/
|
||||
- assets/shared/animations/
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":2,"entries":[{"package":"design_system","rootUri":"../../../packages/design_system/","packageUri":"lib/"},{"package":"flutter_treezor_entrust_sdk_bridge","rootUri":"../../../packages/flutter_treezor_entrust_sdk_bridge/","packageUri":"lib/"},{"package":"fonts","rootUri":"../../../packages/fonts/","packageUri":"lib/"},{"package":"get_it","rootUri":"file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/get_it-9.2.0/","packageUri":"lib/","config":{"name":"get_it","issueTracker":"https://github.com/fluttercommunity/get_it/issues","version":"0.0.1","materialIconCodePoint":"0xe189"}},{"package":"sca_treezor","rootUri":"../../../packages/sca_treezor/","packageUri":"lib/"},{"package":"sf_infrastructure","rootUri":"../../../packages/sf_infrastructure/","packageUri":"lib/"},{"package":"sf_localizations","rootUri":"../../../packages/sf_localizations/","packageUri":"lib/"},{"package":"sf_shared","rootUri":"../../../packages/sf_shared/","packageUri":"lib/"},{"package":"shared_preferences","rootUri":"file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/shared_preferences-2.5.4/","packageUri":"lib/","config":{"name":"shared_preferences","issueTracker":"https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22","version":"1.0.0","materialIconCodePoint":"0xe683"}},{"package":"utils","rootUri":"../../../packages/utils/","packageUri":"lib/"},{"package":"activity","rootUri":"../","packageUri":"lib/"}]}
|
||||
{"version":2,"entries":[{"package":"design_system","rootUri":"../../../packages/design_system/","packageUri":"lib/"},{"package":"flutter_treezor_entrust_sdk_bridge","rootUri":"../../../packages/flutter_treezor_entrust_sdk_bridge/","packageUri":"lib/"},{"package":"fonts","rootUri":"../../../packages/fonts/","packageUri":"lib/"},{"package":"get_it","rootUri":"file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/get_it-9.2.0/","packageUri":"lib/","config":{"name":"get_it","issueTracker":"https://github.com/fluttercommunity/get_it/issues","version":"0.0.1","materialIconCodePoint":"0xe189"}},{"package":"sca_treezor","rootUri":"../../../packages/sca_treezor/","packageUri":"lib/"},{"package":"sf_infrastructure","rootUri":"../../../packages/sf_infrastructure/","packageUri":"lib/"},{"package":"sf_localizations","rootUri":"../../../packages/sf_localizations/","packageUri":"lib/"},{"package":"sf_shared","rootUri":"../../../packages/sf_shared/","packageUri":"lib/"},{"package":"shared_preferences","rootUri":"file:///C:/Users/Aitor%20Arana/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences-2.5.4/","packageUri":"lib/","config":{"name":"shared_preferences","issueTracker":"https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22","version":"1.0.0","materialIconCodePoint":"0xe683"}},{"package":"utils","rootUri":"../../../packages/utils/","packageUri":"lib/"},{"package":"activity","rootUri":"../","packageUri":"lib/"}]}
|
||||
@@ -1 +1 @@
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"C:\\\\dev\\\\sf-app-platform\\\\packages\\\\flutter_treezor_entrust_sdk_bridge\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.5.1\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"C:\\\\dev\\\\sf-app-platform\\\\packages\\\\flutter_treezor_entrust_sdk_bridge\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.22\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.20\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.5.1\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"path_provider_linux","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false}],"windows":[{"name":"path_provider_windows","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false}],"web":[{"name":"shared_preferences_web","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.3\\\\","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_treezor_entrust_sdk_bridge","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2026-03-23 15:48:17.871943","version":"3.35.6","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"C:\\\\dev\\\\sf-app-platform\\\\packages\\\\flutter_treezor_entrust_sdk_bridge\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.5.1\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_treezor_entrust_sdk_bridge","path":"C:\\\\dev\\\\sf-app-platform\\\\packages\\\\flutter_treezor_entrust_sdk_bridge\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.22\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.20\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.5.1\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.6\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"path_provider_linux","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false}],"windows":[{"name":"path_provider_windows","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false}],"web":[{"name":"shared_preferences_web","path":"C:\\\\Users\\\\Aitor Arana\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.3\\\\","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_treezor_entrust_sdk_bridge","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2026-03-26 13:15:48.703271","version":"3.35.6","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
||||
3
modules/auth/devtools_options.yaml
Normal file
3
modules/auth/devtools_options.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
3
modules/home/devtools_options.yaml
Normal file
3
modules/home/devtools_options.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
@@ -163,9 +163,10 @@ class AccountSettingsScreen extends ConsumerWidget {
|
||||
onPressed: onPressed,
|
||||
icon: Icon(
|
||||
icon,
|
||||
size: SizeUtils.getByScreen(small: 28, big: 26),
|
||||
size: SizeUtils.getByScreen(small: 36, big: 34),
|
||||
color: color,
|
||||
),
|
||||
iconPadding: SizeUtils.getByScreen(small: 12, big: 14),
|
||||
body: Text(
|
||||
context.translate(text),
|
||||
style: TextStyle(
|
||||
|
||||
@@ -28,6 +28,10 @@ class DeleteDeviceDialog extends ConsumerWidget {
|
||||
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
|
||||
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18)
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(14))
|
||||
),
|
||||
width: SizeUtils.getByScreen(small: 360, big: 350),
|
||||
height: SizeUtils.getByScreen(small: 184, big: 160),
|
||||
child: Column(
|
||||
|
||||
@@ -59,11 +59,11 @@ class PersonalDataScreen extends ConsumerWidget {
|
||||
const _ProfilePicture(),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 16)),
|
||||
const _EmailLabel(),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 22, big: 20)),
|
||||
const _FirstNameField(),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 22, big: 20)),
|
||||
const _LastNameField(),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22)),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 22, big: 20)),
|
||||
const _PhoneField(),
|
||||
],
|
||||
),
|
||||
@@ -117,7 +117,9 @@ class _EmailLabel extends ConsumerWidget {
|
||||
),
|
||||
Text(
|
||||
email,
|
||||
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 18, big: 17)),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 18, big: 17, xl: 16)
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -254,7 +254,7 @@ class _MapSection extends ConsumerWidget {
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 4, big: 8)),
|
||||
AbsorbPointer(
|
||||
child: SizedBox(
|
||||
height: SizeUtils.getByScreen(small: 200, big: 300),
|
||||
height: SizeUtils.getByScreen(small: 260, big: 300),
|
||||
child: DeviceMap(
|
||||
selectedPosition: state.selectedPosition,
|
||||
selectedDevice: state.selectedDevice,
|
||||
|
||||
@@ -93,6 +93,7 @@ class ControlPanelViewModel extends Notifier<ControlPanelViewState> {
|
||||
|
||||
Future<void> refreshPositions() async {
|
||||
if (state.devices.isEmpty) return;
|
||||
state = state.copyWith(errorMessage: '');
|
||||
try {
|
||||
final positionLists = await Future.wait(
|
||||
state.devices.map(
|
||||
|
||||
@@ -163,7 +163,7 @@ class _MessageSection extends ConsumerWidget {
|
||||
controller: vm.bodyController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
hint: context.translate(I18n.enterMessage),
|
||||
lines: 8,
|
||||
lines: 7,
|
||||
onSubmitted: (_) => onSubmit(),
|
||||
);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ class _SendSection extends ConsumerWidget {
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 38, vertical: 14),
|
||||
big: EdgeInsets.symmetric(horizontal: 36, vertical: 12)
|
||||
big: EdgeInsets.symmetric(horizontal: 36, vertical: 10)
|
||||
),
|
||||
child: PrimaryButton(
|
||||
onPressed: onSend,
|
||||
|
||||
@@ -86,7 +86,7 @@ class CustomerServiceScreen extends ConsumerWidget {
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.email_outlined,
|
||||
size: SizeUtils.getByScreen(small: 44, big: 48),
|
||||
size: SizeUtils.getByScreen(small: 46, big: 48),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
weight: 30,
|
||||
),
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import '../models/get_background_image_response_model.dart';
|
||||
|
||||
abstract class BackgroundImageRemoteDatasource {
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage({
|
||||
required String deviceId,
|
||||
});
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage();
|
||||
|
||||
Future<void> uploadImage({
|
||||
required String deviceId,
|
||||
required String path
|
||||
Future<String> uploadImage({
|
||||
required String path,
|
||||
});
|
||||
|
||||
Future<void> setBackgroundImage({
|
||||
required String deviceId,
|
||||
required String fileId
|
||||
required String photoId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,61 +1,73 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_infrastructure/configure_dependencies.dart';
|
||||
|
||||
import '../models/get_background_image_response_model.dart';
|
||||
import 'background_image_remote_datasource.dart';
|
||||
|
||||
class BackgroundImageRemoteDatasourceImpl implements BackgroundImageRemoteDatasource {
|
||||
class BackgroundImageRemoteDatasourceImpl
|
||||
implements BackgroundImageRemoteDatasource {
|
||||
BackgroundImageRemoteDatasourceImpl(this._repository);
|
||||
|
||||
final QuestiaRepository _repository;
|
||||
|
||||
@override
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage({required String deviceId}) async {
|
||||
|
||||
Future<GetBackgroundImageResponseModel> getBackgroundImage() async {
|
||||
final response = await safeCall(
|
||||
() => _repository.get<dynamic>(
|
||||
'/devices/$deviceId/background-image',
|
||||
),
|
||||
() => _repository.get<dynamic>('/photos/files'),
|
||||
'Error getting background image',
|
||||
);
|
||||
|
||||
final data = response.data;
|
||||
if (data == null || data.isEmpty) {
|
||||
throw Exception('Empty response from /auth/totp/secret');
|
||||
if (data == null || (data is Map && data.isEmpty)) {
|
||||
return const GetBackgroundImageResponseModel(items: []);
|
||||
}
|
||||
|
||||
final model = GetBackgroundImageResponseModel.fromJson(data);
|
||||
return model;
|
||||
return GetBackgroundImageResponseModel.fromJson(
|
||||
data is Map<String, dynamic> ? data : data as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> uploadImage({required String deviceId, required String path}) async {
|
||||
|
||||
Future<String> uploadImage({required String path}) async {
|
||||
final formData = FormData.fromMap({
|
||||
'file': await MultipartFile.fromFile(path),
|
||||
'file': await MultipartFile.fromFile(path, filename: 'photo.jpg'),
|
||||
});
|
||||
|
||||
await safeCall(
|
||||
() => _repository.post<dynamic>(
|
||||
'/photos',
|
||||
body: formData
|
||||
),
|
||||
'Error creating image',
|
||||
);
|
||||
final dio = GetIt.I<Dio>();
|
||||
dio.options.headers.remove('content-type');
|
||||
try {
|
||||
final response = await dio.post<dynamic>('/photos', data: formData);
|
||||
|
||||
final data = response.data;
|
||||
if (data == null) {
|
||||
throw Exception('Empty response from upload');
|
||||
}
|
||||
final map = data is Map<String, dynamic> ? data : <String, dynamic>{};
|
||||
final id = map['id'] as String?;
|
||||
if (id == null) {
|
||||
throw Exception('No photo ID in upload response');
|
||||
}
|
||||
return id;
|
||||
} on DioException catch (e) {
|
||||
throw mapDioError(e, defaultMessage: 'Error uploading image');
|
||||
} finally {
|
||||
dio.options.headers['content-type'] = 'application/json';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setBackgroundImage({required String deviceId, required String fileId}) async {
|
||||
|
||||
Future<void> setBackgroundImage({
|
||||
required String deviceId,
|
||||
required String photoId,
|
||||
}) async {
|
||||
await safeCall(
|
||||
() => _repository.put<dynamic>(
|
||||
() => _repository.put<dynamic>(
|
||||
'/devices/$deviceId/background-image',
|
||||
body: {
|
||||
'photoId': fileId
|
||||
}
|
||||
body: {'photoId': photoId},
|
||||
),
|
||||
'Error creating image',
|
||||
'Error setting background image',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class PicturesRemoteDatasourceImpl implements PicturesRemoteDatasource {
|
||||
Future<List<PictureEntity>> getPictures({required String deviceId}) async {
|
||||
try {
|
||||
final response = await _repository.get<Map<String, dynamic>>(
|
||||
'/devices/identificator/$deviceId/photos',
|
||||
'/devices/identificator/$deviceId/photos/files',
|
||||
);
|
||||
|
||||
final data = response.data;
|
||||
|
||||
@@ -9,7 +9,10 @@ part 'get_background_image_response_model.g.dart';
|
||||
@freezed
|
||||
abstract class GetBackgroundImageResponseModel with _$GetBackgroundImageResponseModel {
|
||||
const factory GetBackgroundImageResponseModel({
|
||||
required GetPicturesItemResponseModel item,
|
||||
@Default([]) List<GetPicturesItemResponseModel> items,
|
||||
@Default(0) int total,
|
||||
@Default(1) int page,
|
||||
@Default(1) int pages,
|
||||
}) = _GetBackgroundImageResponseModel;
|
||||
|
||||
factory GetBackgroundImageResponseModel.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -17,16 +20,19 @@ abstract class GetBackgroundImageResponseModel with _$GetBackgroundImageResponse
|
||||
}
|
||||
|
||||
extension GetBackgroundImageResponseModelMapper on GetBackgroundImageResponseModel {
|
||||
PictureEntity toEntity() {
|
||||
return 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,
|
||||
);
|
||||
List<PictureEntity> toEntities() {
|
||||
return items
|
||||
.map((item) => PictureEntity(
|
||||
id: item.id,
|
||||
deviceIdentificator: item.deviceIdentificator,
|
||||
imgType: item.imgType,
|
||||
timestamp: item.timestamp,
|
||||
fileId: item.fileId,
|
||||
fileName: item.fileName,
|
||||
contentType: item.contentType,
|
||||
createdAt: item.createdAt,
|
||||
fileBytes: extractFileBytes(item.file),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$GetBackgroundImageResponseModel {
|
||||
|
||||
GetPicturesItemResponseModel get item;
|
||||
List<GetPicturesItemResponseModel> get items; int get total; int get page; int get pages;
|
||||
/// Create a copy of GetBackgroundImageResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -28,16 +28,16 @@ $GetBackgroundImageResponseModelCopyWith<GetBackgroundImageResponseModel> get co
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is GetBackgroundImageResponseModel&&(identical(other.item, item) || other.item == item));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is GetBackgroundImageResponseModel&&const DeepCollectionEquality().equals(other.items, items)&&(identical(other.total, total) || other.total == total)&&(identical(other.page, page) || other.page == page)&&(identical(other.pages, pages) || other.pages == pages));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,item);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(items),total,page,pages);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetBackgroundImageResponseModel(item: $item)';
|
||||
return 'GetBackgroundImageResponseModel(items: $items, total: $total, page: $page, pages: $pages)';
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,11 @@ abstract mixin class $GetBackgroundImageResponseModelCopyWith<$Res> {
|
||||
factory $GetBackgroundImageResponseModelCopyWith(GetBackgroundImageResponseModel value, $Res Function(GetBackgroundImageResponseModel) _then) = _$GetBackgroundImageResponseModelCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
GetPicturesItemResponseModel item
|
||||
List<GetPicturesItemResponseModel> items, int total, int page, int pages
|
||||
});
|
||||
|
||||
|
||||
$GetPicturesItemResponseModelCopyWith<$Res> get item;
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -65,22 +65,16 @@ class _$GetBackgroundImageResponseModelCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of GetBackgroundImageResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? item = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? items = null,Object? total = null,Object? page = null,Object? pages = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
|
||||
as GetPicturesItemResponseModel,
|
||||
items: null == items ? _self.items : items // ignore: cast_nullable_to_non_nullable
|
||||
as List<GetPicturesItemResponseModel>,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
|
||||
as int,page: null == page ? _self.page : page // ignore: cast_nullable_to_non_nullable
|
||||
as int,pages: null == pages ? _self.pages : pages // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
/// Create a copy of GetBackgroundImageResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$GetPicturesItemResponseModelCopyWith<$Res> get item {
|
||||
|
||||
return $GetPicturesItemResponseModelCopyWith<$Res>(_self.item, (value) {
|
||||
return _then(_self.copyWith(item: value));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -162,10 +156,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( GetPicturesItemResponseModel item)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<GetPicturesItemResponseModel> items, int total, int page, int pages)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _GetBackgroundImageResponseModel() when $default != null:
|
||||
return $default(_that.item);case _:
|
||||
return $default(_that.items,_that.total,_that.page,_that.pages);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -183,10 +177,10 @@ return $default(_that.item);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( GetPicturesItemResponseModel item) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<GetPicturesItemResponseModel> items, int total, int page, int pages) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _GetBackgroundImageResponseModel():
|
||||
return $default(_that.item);case _:
|
||||
return $default(_that.items,_that.total,_that.page,_that.pages);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -203,10 +197,10 @@ return $default(_that.item);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( GetPicturesItemResponseModel item)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<GetPicturesItemResponseModel> items, int total, int page, int pages)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _GetBackgroundImageResponseModel() when $default != null:
|
||||
return $default(_that.item);case _:
|
||||
return $default(_that.items,_that.total,_that.page,_that.pages);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -218,10 +212,19 @@ return $default(_that.item);case _:
|
||||
@JsonSerializable()
|
||||
|
||||
class _GetBackgroundImageResponseModel implements GetBackgroundImageResponseModel {
|
||||
const _GetBackgroundImageResponseModel({required this.item});
|
||||
const _GetBackgroundImageResponseModel({final List<GetPicturesItemResponseModel> items = const [], this.total = 0, this.page = 1, this.pages = 1}): _items = items;
|
||||
factory _GetBackgroundImageResponseModel.fromJson(Map<String, dynamic> json) => _$GetBackgroundImageResponseModelFromJson(json);
|
||||
|
||||
@override final GetPicturesItemResponseModel item;
|
||||
final List<GetPicturesItemResponseModel> _items;
|
||||
@override@JsonKey() List<GetPicturesItemResponseModel> get items {
|
||||
if (_items is EqualUnmodifiableListView) return _items;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_items);
|
||||
}
|
||||
|
||||
@override@JsonKey() final int total;
|
||||
@override@JsonKey() final int page;
|
||||
@override@JsonKey() final int pages;
|
||||
|
||||
/// Create a copy of GetBackgroundImageResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -236,16 +239,16 @@ Map<String, dynamic> toJson() {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _GetBackgroundImageResponseModel&&(identical(other.item, item) || other.item == item));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _GetBackgroundImageResponseModel&&const DeepCollectionEquality().equals(other._items, _items)&&(identical(other.total, total) || other.total == total)&&(identical(other.page, page) || other.page == page)&&(identical(other.pages, pages) || other.pages == pages));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,item);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_items),total,page,pages);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetBackgroundImageResponseModel(item: $item)';
|
||||
return 'GetBackgroundImageResponseModel(items: $items, total: $total, page: $page, pages: $pages)';
|
||||
}
|
||||
|
||||
|
||||
@@ -256,11 +259,11 @@ abstract mixin class _$GetBackgroundImageResponseModelCopyWith<$Res> implements
|
||||
factory _$GetBackgroundImageResponseModelCopyWith(_GetBackgroundImageResponseModel value, $Res Function(_GetBackgroundImageResponseModel) _then) = __$GetBackgroundImageResponseModelCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
GetPicturesItemResponseModel item
|
||||
List<GetPicturesItemResponseModel> items, int total, int page, int pages
|
||||
});
|
||||
|
||||
|
||||
@override $GetPicturesItemResponseModelCopyWith<$Res> get item;
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -273,23 +276,17 @@ class __$GetBackgroundImageResponseModelCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of GetBackgroundImageResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? item = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? items = null,Object? total = null,Object? page = null,Object? pages = null,}) {
|
||||
return _then(_GetBackgroundImageResponseModel(
|
||||
item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
|
||||
as GetPicturesItemResponseModel,
|
||||
items: null == items ? _self._items : items // ignore: cast_nullable_to_non_nullable
|
||||
as List<GetPicturesItemResponseModel>,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable
|
||||
as int,page: null == page ? _self.page : page // ignore: cast_nullable_to_non_nullable
|
||||
as int,pages: null == pages ? _self.pages : pages // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of GetBackgroundImageResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$GetPicturesItemResponseModelCopyWith<$Res> get item {
|
||||
|
||||
return $GetPicturesItemResponseModelCopyWith<$Res>(_self.item, (value) {
|
||||
return _then(_self.copyWith(item: value));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
|
||||
@@ -9,11 +9,25 @@ part of 'get_background_image_response_model.dart';
|
||||
_GetBackgroundImageResponseModel _$GetBackgroundImageResponseModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _GetBackgroundImageResponseModel(
|
||||
item: GetPicturesItemResponseModel.fromJson(
|
||||
json['item'] as Map<String, dynamic>,
|
||||
),
|
||||
items:
|
||||
(json['items'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => GetPicturesItemResponseModel.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
const [],
|
||||
total: (json['total'] as num?)?.toInt() ?? 0,
|
||||
page: (json['page'] as num?)?.toInt() ?? 1,
|
||||
pages: (json['pages'] as num?)?.toInt() ?? 1,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetBackgroundImageResponseModelToJson(
|
||||
_GetBackgroundImageResponseModel instance,
|
||||
) => <String, dynamic>{'item': instance.item};
|
||||
) => <String, dynamic>{
|
||||
'items': instance.items,
|
||||
'total': instance.total,
|
||||
'page': instance.page,
|
||||
'pages': instance.pages,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -19,19 +22,39 @@ abstract class GetPicturesItemResponseModel
|
||||
with _$GetPicturesItemResponseModel {
|
||||
const factory GetPicturesItemResponseModel({
|
||||
required String id,
|
||||
required String deviceIdentificator,
|
||||
@Default('') String deviceIdentificator,
|
||||
String? imgType,
|
||||
String? timestamp,
|
||||
required String fileId,
|
||||
@Default('') String fileId,
|
||||
String? fileName,
|
||||
String? contentType,
|
||||
required int createdAt,
|
||||
@Default(0) int createdAt,
|
||||
Map<String, dynamic>? file,
|
||||
}) = _GetPicturesItemResponseModel;
|
||||
|
||||
factory GetPicturesItemResponseModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$GetPicturesItemResponseModelFromJson(json);
|
||||
}
|
||||
|
||||
Uint8List? extractFileBytes(Map<String, dynamic>? file) {
|
||||
if (file == null) return null;
|
||||
final fileData = file['file'];
|
||||
if (fileData is String) {
|
||||
try {
|
||||
return base64Decode(fileData);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (fileData is Map) {
|
||||
final data = fileData['data'];
|
||||
if (data is List) {
|
||||
return Uint8List.fromList(data.cast<int>());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
extension GetPicturesResponseModelMapper on GetPicturesResponseModel {
|
||||
List<PictureEntity> toEntity() {
|
||||
return items
|
||||
@@ -44,6 +67,7 @@ extension GetPicturesResponseModelMapper on GetPicturesResponseModel {
|
||||
fileName: item.fileName,
|
||||
contentType: item.contentType,
|
||||
createdAt: item.createdAt,
|
||||
fileBytes: extractFileBytes(item.file),
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ 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;
|
||||
String get id; String get deviceIdentificator; String? get imgType; String? get timestamp; String get fileId; String? get fileName; String? get contentType; int get createdAt; Map<String, dynamic>? get file;
|
||||
/// Create a copy of GetPicturesItemResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -297,16 +297,16 @@ $GetPicturesItemResponseModelCopyWith<GetPicturesItemResponseModel> get copyWith
|
||||
|
||||
@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));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is GetPicturesItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.imgType, imgType) || other.imgType == imgType)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.fileId, fileId) || other.fileId == fileId)&&(identical(other.fileName, fileName) || other.fileName == fileName)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&const DeepCollectionEquality().equals(other.file, file));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt,const DeepCollectionEquality().hash(file));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
|
||||
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt, file: $file)';
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ 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
|
||||
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file
|
||||
});
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ class _$GetPicturesItemResponseModelCopyWithImpl<$Res>
|
||||
|
||||
/// 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,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceIdentificator = null,Object? imgType = freezed,Object? timestamp = freezed,Object? fileId = null,Object? fileName = freezed,Object? contentType = freezed,Object? createdAt = null,Object? file = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
|
||||
@@ -344,7 +344,8 @@ as String?,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullab
|
||||
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,
|
||||
as int,file: freezed == file ? _self.file : file // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -429,10 +430,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _GetPicturesItemResponseModel() when $default != null:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.file);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -450,10 +451,10 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _GetPicturesItemResponseModel():
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.file);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -470,10 +471,10 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _GetPicturesItemResponseModel() when $default != null:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.file);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -485,17 +486,26 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
@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});
|
||||
const _GetPicturesItemResponseModel({required this.id, this.deviceIdentificator = '', this.imgType, this.timestamp, this.fileId = '', this.fileName, this.contentType, this.createdAt = 0, final Map<String, dynamic>? file}): _file = file;
|
||||
factory _GetPicturesItemResponseModel.fromJson(Map<String, dynamic> json) => _$GetPicturesItemResponseModelFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String deviceIdentificator;
|
||||
@override@JsonKey() final String deviceIdentificator;
|
||||
@override final String? imgType;
|
||||
@override final String? timestamp;
|
||||
@override final String fileId;
|
||||
@override@JsonKey() final String fileId;
|
||||
@override final String? fileName;
|
||||
@override final String? contentType;
|
||||
@override final int createdAt;
|
||||
@override@JsonKey() final int createdAt;
|
||||
final Map<String, dynamic>? _file;
|
||||
@override Map<String, dynamic>? get file {
|
||||
final value = _file;
|
||||
if (value == null) return null;
|
||||
if (_file is EqualUnmodifiableMapView) return _file;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
|
||||
/// Create a copy of GetPicturesItemResponseModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -510,16 +520,16 @@ 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));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _GetPicturesItemResponseModel&&(identical(other.id, id) || other.id == id)&&(identical(other.deviceIdentificator, deviceIdentificator) || other.deviceIdentificator == deviceIdentificator)&&(identical(other.imgType, imgType) || other.imgType == imgType)&&(identical(other.timestamp, timestamp) || other.timestamp == timestamp)&&(identical(other.fileId, fileId) || other.fileId == fileId)&&(identical(other.fileName, fileName) || other.fileName == fileName)&&(identical(other.contentType, contentType) || other.contentType == contentType)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&const DeepCollectionEquality().equals(other._file, _file));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt,const DeepCollectionEquality().hash(_file));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
|
||||
return 'GetPicturesItemResponseModel(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt, file: $file)';
|
||||
}
|
||||
|
||||
|
||||
@@ -530,7 +540,7 @@ abstract mixin class _$GetPicturesItemResponseModelCopyWith<$Res> implements $Ge
|
||||
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
|
||||
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Map<String, dynamic>? file
|
||||
});
|
||||
|
||||
|
||||
@@ -547,7 +557,7 @@ class __$GetPicturesItemResponseModelCopyWithImpl<$Res>
|
||||
|
||||
/// 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,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceIdentificator = null,Object? imgType = freezed,Object? timestamp = freezed,Object? fileId = null,Object? fileName = freezed,Object? contentType = freezed,Object? createdAt = null,Object? file = freezed,}) {
|
||||
return _then(_GetPicturesItemResponseModel(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
|
||||
@@ -557,7 +567,8 @@ as String?,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullab
|
||||
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,
|
||||
as int,file: freezed == file ? _self._file : file // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,14 @@ _GetPicturesItemResponseModel _$GetPicturesItemResponseModelFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _GetPicturesItemResponseModel(
|
||||
id: json['id'] as String,
|
||||
deviceIdentificator: json['deviceIdentificator'] as String,
|
||||
deviceIdentificator: json['deviceIdentificator'] as String? ?? '',
|
||||
imgType: json['imgType'] as String?,
|
||||
timestamp: json['timestamp'] as String?,
|
||||
fileId: json['fileId'] as String,
|
||||
fileId: json['fileId'] as String? ?? '',
|
||||
fileName: json['fileName'] as String?,
|
||||
contentType: json['contentType'] as String?,
|
||||
createdAt: (json['createdAt'] as num).toInt(),
|
||||
createdAt: (json['createdAt'] as num?)?.toInt() ?? 0,
|
||||
file: json['file'] as Map<String, dynamic>?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GetPicturesItemResponseModelToJson(
|
||||
@@ -44,4 +45,5 @@ Map<String, dynamic> _$GetPicturesItemResponseModelToJson(
|
||||
'fileName': instance.fileName,
|
||||
'contentType': instance.contentType,
|
||||
'createdAt': instance.createdAt,
|
||||
'file': instance.file,
|
||||
};
|
||||
|
||||
@@ -11,18 +11,18 @@ class BackgroundImageRepositoryImpl implements BackgroundImageRepository {
|
||||
final BackgroundImageRemoteDatasource _remote;
|
||||
|
||||
@override
|
||||
Future<PictureEntity> getBackgroundImage({required String deviceId}) async {
|
||||
final model = await _remote.getBackgroundImage(deviceId: deviceId);
|
||||
return model.toEntity();
|
||||
Future<List<PictureEntity>> getPhotos() async {
|
||||
final model = await _remote.getBackgroundImage();
|
||||
return model.toEntities();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> uploadImage({required String deviceId, required String path}) {
|
||||
return _remote.uploadImage(deviceId: deviceId, path: path);
|
||||
Future<String> uploadImage({required String path}) {
|
||||
return _remote.uploadImage(path: path);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setBackgroundImage({required String deviceId, required String fileId}) {
|
||||
return _remote.setBackgroundImage(deviceId: deviceId, fileId: fileId);
|
||||
Future<void> setBackgroundImage({required String deviceId, required String photoId}) {
|
||||
return _remote.setBackgroundImage(deviceId: deviceId, photoId: photoId);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
|
||||
abstract class BackgroundImageRepository {
|
||||
Future<PictureEntity> getBackgroundImage({required String deviceId});
|
||||
Future<void> uploadImage({required String deviceId, required String path});
|
||||
Future<void> setBackgroundImage({required String deviceId, required String fileId});
|
||||
Future<List<PictureEntity>> getPhotos();
|
||||
Future<String> uploadImage({required String path});
|
||||
Future<void> setBackgroundImage({required String deviceId, required String photoId});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:utils/utils.dart';
|
||||
|
||||
import '../../../core/presentation/widgets/time_range_selector.dart';
|
||||
import 'state/activity_meter_view_model.dart';
|
||||
import 'state/activity_meter_view_state.dart';
|
||||
import 'widgets/steps_bar_chart.dart';
|
||||
import 'widgets/steps_history_section.dart';
|
||||
import 'widgets/steps_progress_ring.dart';
|
||||
@@ -20,12 +21,18 @@ class ActivityMeterScreen extends ConsumerWidget {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final state = ref.watch(activityMeterViewModelProvider);
|
||||
final vm = ref.read(activityMeterViewModelProvider.notifier);
|
||||
final device = ref.watch(selectedDeviceProvider);
|
||||
|
||||
ref.listen(
|
||||
activityMeterViewModelProvider.select((s) => s.errorMessage),
|
||||
activityMeterViewModelProvider.select((s) => s.errorEvent),
|
||||
(previous, next) {
|
||||
if (next.isNotEmpty) {
|
||||
showTopSnackbar(context, message: next, type: MessageType.error);
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
ActivityMeterErrorEvent.loadData => context.translate(I18n.errorActivityData),
|
||||
ActivityMeterErrorEvent.loadMore => context.translate(I18n.errorActivityData),
|
||||
ActivityMeterErrorEvent.pedometer => context.translate(I18n.errorPedometer),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -69,6 +76,43 @@ class ActivityMeterScreen extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
vertical: SizeUtils.getByScreen(small: 4, big: 4),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.activityMeterPedometer),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Switch.adaptive(
|
||||
value: device?.settings.pedometer ?? false,
|
||||
activeTrackColor: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
onChanged: (value) async {
|
||||
final success = await vm.togglePedometer(enabled: value);
|
||||
if (!context.mounted) return;
|
||||
if (success) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(
|
||||
value
|
||||
? I18n.activityMeterPedometerEnabled
|
||||
: I18n.activityMeterPedometerDisabled,
|
||||
),
|
||||
type: MessageType.success,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
StepsProgressRing(
|
||||
steps: state.todayTotal,
|
||||
goal: state.dailyGoal,
|
||||
|
||||
@@ -79,7 +79,7 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoadingMore: false,
|
||||
errorMessage: _formatError(e),
|
||||
errorEvent: ActivityMeterErrorEvent.loadMore,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
|
||||
await _loadFilteredData();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(isLoading: false, errorMessage: _formatError(e));
|
||||
state = state.copyWith(isLoading: false, errorEvent: ActivityMeterErrorEvent.loadData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,11 +157,11 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
|
||||
hasMoreHistory: histSteps.length >= _historyPageSize,
|
||||
stats: _computeStats(chartDaily),
|
||||
isLoading: false,
|
||||
errorMessage: '',
|
||||
errorEvent: null,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(isLoading: false, errorMessage: _formatError(e));
|
||||
state = state.copyWith(isLoading: false, errorEvent: ActivityMeterErrorEvent.loadData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,8 +243,25 @@ class ActivityMeterViewModel extends Notifier<ActivityMeterViewState> {
|
||||
}
|
||||
}
|
||||
|
||||
String _formatError(Object e) {
|
||||
final msg = e.toString();
|
||||
return msg.startsWith('Exception: ') ? msg.substring(11) : msg;
|
||||
Future<bool> togglePedometer({required bool enabled}) async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return false;
|
||||
|
||||
state = state.copyWith(errorEvent: null);
|
||||
try {
|
||||
final updatedSettings = device.settings.copyWith(pedometer: enabled);
|
||||
await ref.read(deviceSettingsUpdateProvider).updateDeviceSettings(
|
||||
device: device,
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return false;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return false;
|
||||
state = state.copyWith(errorEvent: ActivityMeterErrorEvent.pedometer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,12 @@ import '../../../../core/presentation/time_range.dart';
|
||||
|
||||
part 'activity_meter_view_state.freezed.dart';
|
||||
|
||||
enum ActivityMeterErrorEvent {
|
||||
loadData,
|
||||
loadMore,
|
||||
pedometer,
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class DailySteps with _$DailySteps {
|
||||
const factory DailySteps({
|
||||
@@ -36,6 +42,6 @@ abstract class ActivityMeterViewState with _$ActivityMeterViewState {
|
||||
DateTime? customEnd,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isLoadingMore,
|
||||
@Default('') String errorMessage,
|
||||
ActivityMeterErrorEvent? errorEvent,
|
||||
}) = _ActivityMeterViewState;
|
||||
}
|
||||
|
||||
@@ -537,7 +537,7 @@ as int,
|
||||
/// @nodoc
|
||||
mixin _$ActivityMeterViewState {
|
||||
|
||||
int get todayTotal; int get dailyGoal; List<DailySteps> get chartData; List<DailySteps> get historyData; int get currentHistoryPage; bool get hasMoreHistory; StepsStats get stats; TimeRange get timeRange; DateTime? get customStart; DateTime? get customEnd; bool get isLoading; bool get isLoadingMore; String get errorMessage;
|
||||
int get todayTotal; int get dailyGoal; List<DailySteps> get chartData; List<DailySteps> get historyData; int get currentHistoryPage; bool get hasMoreHistory; StepsStats get stats; TimeRange get timeRange; DateTime? get customStart; DateTime? get customEnd; bool get isLoading; bool get isLoadingMore; ActivityMeterErrorEvent? get errorEvent;
|
||||
/// Create a copy of ActivityMeterViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -548,16 +548,16 @@ $ActivityMeterViewStateCopyWith<ActivityMeterViewState> get copyWith => _$Activi
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ActivityMeterViewState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other.chartData, chartData)&&const DeepCollectionEquality().equals(other.historyData, historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ActivityMeterViewState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other.chartData, chartData)&&const DeepCollectionEquality().equals(other.historyData, historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(chartData),const DeepCollectionEquality().hash(historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,isLoading,isLoadingMore,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(chartData),const DeepCollectionEquality().hash(historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,isLoading,isLoadingMore,errorEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ActivityMeterViewState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorMessage: $errorMessage)';
|
||||
return 'ActivityMeterViewState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -568,7 +568,7 @@ abstract mixin class $ActivityMeterViewStateCopyWith<$Res> {
|
||||
factory $ActivityMeterViewStateCopyWith(ActivityMeterViewState value, $Res Function(ActivityMeterViewState) _then) = _$ActivityMeterViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage
|
||||
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -585,7 +585,7 @@ class _$ActivityMeterViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ActivityMeterViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorEvent = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
todayTotal: null == todayTotal ? _self.todayTotal : todayTotal // ignore: cast_nullable_to_non_nullable
|
||||
as int,dailyGoal: null == dailyGoal ? _self.dailyGoal : dailyGoal // ignore: cast_nullable_to_non_nullable
|
||||
@@ -599,8 +599,8 @@ as TimeRange,customStart: freezed == customStart ? _self.customStart : customSta
|
||||
as DateTime?,customEnd: freezed == customEnd ? _self.customEnd : customEnd // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as ActivityMeterErrorEvent?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of ActivityMeterViewState
|
||||
@@ -694,10 +694,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ActivityMeterViewState() when $default != null:
|
||||
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorMessage);case _:
|
||||
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorEvent);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -715,10 +715,10 @@ return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyDa
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ActivityMeterViewState():
|
||||
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorMessage);case _:
|
||||
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorEvent);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -735,10 +735,10 @@ return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyDa
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ActivityMeterViewState() when $default != null:
|
||||
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorMessage);case _:
|
||||
return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyData,_that.currentHistoryPage,_that.hasMoreHistory,_that.stats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorEvent);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -750,7 +750,7 @@ return $default(_that.todayTotal,_that.dailyGoal,_that.chartData,_that.historyDa
|
||||
|
||||
|
||||
class _ActivityMeterViewState implements ActivityMeterViewState {
|
||||
const _ActivityMeterViewState({this.todayTotal = 0, this.dailyGoal = 8000, final List<DailySteps> chartData = const [], final List<DailySteps> historyData = const [], this.currentHistoryPage = 1, this.hasMoreHistory = false, this.stats = const StepsStats(), this.timeRange = TimeRange.today, this.customStart, this.customEnd, this.isLoading = true, this.isLoadingMore = false, this.errorMessage = ''}): _chartData = chartData,_historyData = historyData;
|
||||
const _ActivityMeterViewState({this.todayTotal = 0, this.dailyGoal = 8000, final List<DailySteps> chartData = const [], final List<DailySteps> historyData = const [], this.currentHistoryPage = 1, this.hasMoreHistory = false, this.stats = const StepsStats(), this.timeRange = TimeRange.today, this.customStart, this.customEnd, this.isLoading = true, this.isLoadingMore = false, this.errorEvent}): _chartData = chartData,_historyData = historyData;
|
||||
|
||||
|
||||
@override@JsonKey() final int todayTotal;
|
||||
@@ -777,7 +777,7 @@ class _ActivityMeterViewState implements ActivityMeterViewState {
|
||||
@override final DateTime? customEnd;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isLoadingMore;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override final ActivityMeterErrorEvent? errorEvent;
|
||||
|
||||
/// Create a copy of ActivityMeterViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -789,16 +789,16 @@ _$ActivityMeterViewStateCopyWith<_ActivityMeterViewState> get copyWith => __$Act
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActivityMeterViewState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other._chartData, _chartData)&&const DeepCollectionEquality().equals(other._historyData, _historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActivityMeterViewState&&(identical(other.todayTotal, todayTotal) || other.todayTotal == todayTotal)&&(identical(other.dailyGoal, dailyGoal) || other.dailyGoal == dailyGoal)&&const DeepCollectionEquality().equals(other._chartData, _chartData)&&const DeepCollectionEquality().equals(other._historyData, _historyData)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.stats, stats) || other.stats == stats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(_chartData),const DeepCollectionEquality().hash(_historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,isLoading,isLoadingMore,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,todayTotal,dailyGoal,const DeepCollectionEquality().hash(_chartData),const DeepCollectionEquality().hash(_historyData),currentHistoryPage,hasMoreHistory,stats,timeRange,customStart,customEnd,isLoading,isLoadingMore,errorEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ActivityMeterViewState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorMessage: $errorMessage)';
|
||||
return 'ActivityMeterViewState(todayTotal: $todayTotal, dailyGoal: $dailyGoal, chartData: $chartData, historyData: $historyData, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, stats: $stats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -809,7 +809,7 @@ abstract mixin class _$ActivityMeterViewStateCopyWith<$Res> implements $Activity
|
||||
factory _$ActivityMeterViewStateCopyWith(_ActivityMeterViewState value, $Res Function(_ActivityMeterViewState) _then) = __$ActivityMeterViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage
|
||||
int todayTotal, int dailyGoal, List<DailySteps> chartData, List<DailySteps> historyData, int currentHistoryPage, bool hasMoreHistory, StepsStats stats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, ActivityMeterErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -826,7 +826,7 @@ class __$ActivityMeterViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ActivityMeterViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? todayTotal = null,Object? dailyGoal = null,Object? chartData = null,Object? historyData = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? stats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorEvent = freezed,}) {
|
||||
return _then(_ActivityMeterViewState(
|
||||
todayTotal: null == todayTotal ? _self.todayTotal : todayTotal // ignore: cast_nullable_to_non_nullable
|
||||
as int,dailyGoal: null == dailyGoal ? _self.dailyGoal : dailyGoal // ignore: cast_nullable_to_non_nullable
|
||||
@@ -840,8 +840,8 @@ as TimeRange,customStart: freezed == customStart ? _self.customStart : customSta
|
||||
as DateTime?,customEnd: freezed == customEnd ? _self.customEnd : customEnd // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as ActivityMeterErrorEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
import 'state/background_image_view_model.dart';
|
||||
import 'state/background_image_view_state.dart';
|
||||
|
||||
class BackgroundImageScreen extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
const BackgroundImageScreen({super.key, required this.navigationContract});
|
||||
@@ -16,23 +16,42 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.buttonPrimary);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
final state = ref.watch(backgroundImageViewModelProvider);
|
||||
final vm = ref.read(backgroundImageViewModelProvider.notifier);
|
||||
|
||||
ref.listen(backgroundImageViewModelProvider.select((s) => s.errorMessage), (
|
||||
_,
|
||||
errorMessage,
|
||||
) {
|
||||
if (errorMessage.isNotEmpty) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: errorMessage,
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
});
|
||||
ref.listen(
|
||||
backgroundImageViewModelProvider.select((s) => s.errorEvent),
|
||||
(previous, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
BackgroundImageErrorEvent.load =>
|
||||
context.translate(I18n.errorBackgroundImageLoad),
|
||||
BackgroundImageErrorEvent.upload =>
|
||||
context.translate(I18n.errorBackgroundImageUpload),
|
||||
BackgroundImageErrorEvent.set =>
|
||||
context.translate(I18n.errorBackgroundImageSet),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
backgroundImageViewModelProvider.select((s) => s.successEvent),
|
||||
(previous, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
BackgroundImageSuccessEvent.uploaded =>
|
||||
context.translate(I18n.backgroundImageUploaded),
|
||||
BackgroundImageSuccessEvent.backgroundSet =>
|
||||
context.translate(I18n.backgroundImageSet),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.success);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
@@ -60,87 +79,172 @@ class BackgroundImageScreen extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
if (state.image.isNotEmpty)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: () {},
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
color: Colors.white,
|
||||
size: SizeUtils.getByScreen(small: 24, big: 22),
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: state.isSaving ? null : vm.uploadPhoto,
|
||||
icon: Icon(
|
||||
Icons.add_photo_alternate_outlined,
|
||||
color: Colors.white,
|
||||
size: SizeUtils.getByScreen(small: 24, big: 22),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: SizeUtils.getByScreen(small: 28, big: 27)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 28),
|
||||
Text('Configura la foto del teléfono como un protector de pantalla exclusivo para el dispositivo y personaliza la etiqueta',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 38),
|
||||
GestureDetector(
|
||||
onTap: vm.pickImage,
|
||||
child: Container(
|
||||
height: SizeUtils.getByScreen(small: 250, big: 240),
|
||||
width: SizeUtils.getByScreen(small: 250, big: 240),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.fromBorderSide(BorderSide(
|
||||
color: theme.getColorFor(ThemeCode.textTertiary)
|
||||
))
|
||||
),
|
||||
child: Image.network(
|
||||
'',
|
||||
fit: BoxFit.contain,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.add_photo_alternate_outlined, size: 140, color: Colors.grey),
|
||||
const SizedBox(height: 28),
|
||||
state.isSaving
|
||||
? const CircularProgressIndicator()
|
||||
: const Text('Pulsa para seleccionar una foto')
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (state.image.isNotEmpty) ...[
|
||||
SizedBox(height: 38),
|
||||
Text('Modifica el protector de pantalla pulsando en la imagen',
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: state.isSaving
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state.photos.isEmpty
|
||||
? _EmptyState(
|
||||
onUpload: vm.uploadPhoto,
|
||||
primaryColor: primaryColor,
|
||||
)
|
||||
: _PhotoGrid(
|
||||
state: state,
|
||||
onPhotoTap: vm.setAsBackground,
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
class _EmptyState extends StatelessWidget {
|
||||
final VoidCallback onUpload;
|
||||
final Color primaryColor;
|
||||
|
||||
const _EmptyState({required this.onUpload, required this.primaryColor});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_photo_alternate_outlined,
|
||||
size: 100,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
context.translate(I18n.backgroundImageDescription),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
PrimaryButton(
|
||||
onPressed: onUpload,
|
||||
text: context.translate(I18n.backgroundImageTapToSelect),
|
||||
color: primaryColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PhotoGrid extends StatelessWidget {
|
||||
final BackgroundImageViewState state;
|
||||
final ValueChanged<String> onPhotoTap;
|
||||
final Color primaryColor;
|
||||
|
||||
const _PhotoGrid({
|
||||
required this.state,
|
||||
required this.onPhotoTap,
|
||||
required this.primaryColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Text(
|
||||
context.translate(I18n.backgroundImageTapToChange),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: GridView.builder(
|
||||
padding: const EdgeInsets.all(12),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisSpacing: 10,
|
||||
),
|
||||
itemCount: state.photos.length,
|
||||
itemBuilder: (context, index) {
|
||||
final photo = state.photos[index];
|
||||
final isActive = state.currentBackgroundId == photo.id;
|
||||
return GestureDetector(
|
||||
onTap: () => onPhotoTap(photo.id),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: isActive ? primaryColor : Colors.grey.shade300,
|
||||
width: isActive ? 3 : 1,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: photo.fileBytes != null
|
||||
? Image.memory(
|
||||
photo.fileBytes!,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Center(
|
||||
child: Icon(
|
||||
Icons.image_outlined,
|
||||
size: 48,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isActive)
|
||||
Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.check,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
import '../../../../core/domain/repositories/background_image_repository.dart';
|
||||
import '../../../../core/providers/background_image_repository_provider.dart';
|
||||
import 'background_image_view_state.dart';
|
||||
|
||||
final backgroundImageViewModelProvider =
|
||||
NotifierProvider.autoDispose<BackgroundImageViewModel, BackgroundImageViewState>(
|
||||
BackgroundImageViewModel.new,
|
||||
);
|
||||
NotifierProvider.autoDispose<
|
||||
BackgroundImageViewModel,
|
||||
BackgroundImageViewState
|
||||
>(BackgroundImageViewModel.new);
|
||||
|
||||
class BackgroundImageViewModel extends Notifier<BackgroundImageViewState> {
|
||||
late final BackgroundImageRepository _repository;
|
||||
late final SharedDevicesRepository _devicesRepository;
|
||||
|
||||
@override
|
||||
BackgroundImageViewState build() {
|
||||
_repository = ref.read(backgroundImageRepositoryProvider);
|
||||
_devicesRepository = ref.read(sharedDevicesRepositoryProvider);
|
||||
Future.microtask(_load);
|
||||
return const BackgroundImageViewState();
|
||||
}
|
||||
@@ -27,77 +30,119 @@ class BackgroundImageViewModel extends Notifier<BackgroundImageViewState> {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
final image = await _repository.getBackgroundImage(deviceId: device.identificator);
|
||||
final photos = await _repository.getPhotos();
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(image: image.fileId, isLoading: false);
|
||||
state = state.copyWith(
|
||||
photos: photos,
|
||||
currentBackgroundId: device.backgroundImageId,
|
||||
isLoading: false,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: formatErrorMessage(e),
|
||||
errorEvent: BackgroundImageErrorEvent.load,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> pickImage() async {
|
||||
Future<void> reload() async {
|
||||
state = state.copyWith(isLoading: true, errorEvent: null);
|
||||
await _load();
|
||||
}
|
||||
|
||||
Future<void> uploadPhoto() async {
|
||||
if (state.isSaving) return;
|
||||
|
||||
final picker = ImagePicker();
|
||||
final image = await picker.pickImage(source: ImageSource.gallery);
|
||||
|
||||
final image = await picker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
maxWidth: 800,
|
||||
imageQuality: 80,
|
||||
);
|
||||
if (image == null) return;
|
||||
|
||||
updateImage(image);
|
||||
}
|
||||
|
||||
Future<void> updateImage(XFile image) async {
|
||||
state = state.copyWith(isSaving: true, errorMessage: '');
|
||||
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: true,
|
||||
errorEvent: null,
|
||||
successEvent: null,
|
||||
);
|
||||
|
||||
try {
|
||||
final photoId = await _repository.uploadImage(path: image.path);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _repository.uploadImage(deviceId: device.identificator, path: image.path);
|
||||
|
||||
final fileId = '';
|
||||
|
||||
await _repository.setBackgroundImage(deviceId: device.identificator, fileId: fileId);
|
||||
|
||||
state = state.copyWith(
|
||||
image: image.path,
|
||||
isSaving: false,
|
||||
successMessage: I18n.alarmUpdated,
|
||||
await _repository.setBackgroundImage(
|
||||
deviceId: device.id,
|
||||
photoId: photoId,
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _refreshDevice(device);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorMessage: formatErrorMessage(e),
|
||||
successEvent: BackgroundImageSuccessEvent.uploaded,
|
||||
);
|
||||
|
||||
await reload();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorEvent: BackgroundImageErrorEvent.upload,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteBackgroundImage() async {
|
||||
Future<void> setAsBackground(String photoId) async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isSaving: true,
|
||||
errorEvent: null,
|
||||
successEvent: null,
|
||||
);
|
||||
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isSaving: true,
|
||||
await _repository.setBackgroundImage(
|
||||
deviceId: device.id,
|
||||
photoId: photoId,
|
||||
);
|
||||
// await _repository.deleteBackgroundImage();
|
||||
if (!ref.mounted) return;
|
||||
|
||||
await _refreshDevice(device);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
image: '',
|
||||
isSaving: false,
|
||||
successMessage: I18n.alarmDeleted,
|
||||
successEvent: BackgroundImageSuccessEvent.backgroundSet,
|
||||
);
|
||||
|
||||
await reload();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorMessage: formatErrorMessage(e),
|
||||
errorEvent: BackgroundImageErrorEvent.set,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void clearSuccess() {
|
||||
state = state.copyWith(successMessage: '');
|
||||
Future<void> _refreshDevice(DeviceEntity currentDevice) async {
|
||||
final devices = await _devicesRepository.getDevices();
|
||||
if (!ref.mounted) return;
|
||||
|
||||
final updated = devices.firstWhere(
|
||||
(d) => d.identificator == currentDevice.identificator,
|
||||
orElse: () => currentDevice,
|
||||
);
|
||||
ref.read(selectedDeviceProvider.notifier).setSelectedDevice(updated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'background_image_view_state.freezed.dart';
|
||||
|
||||
enum BackgroundImageErrorEvent { load, upload, set }
|
||||
enum BackgroundImageSuccessEvent { uploaded, backgroundSet }
|
||||
|
||||
@freezed
|
||||
abstract class BackgroundImageViewState with _$BackgroundImageViewState {
|
||||
const factory BackgroundImageViewState({
|
||||
@Default('') String image,
|
||||
@Default([]) List<PictureEntity> photos,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isSaving,
|
||||
@Default('') String successMessage,
|
||||
@Default('') String errorMessage,
|
||||
String? currentBackgroundId,
|
||||
BackgroundImageSuccessEvent? successEvent,
|
||||
BackgroundImageErrorEvent? errorEvent,
|
||||
}) = _BackgroundImageViewState;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$BackgroundImageViewState {
|
||||
|
||||
String get image; bool get isLoading; bool get isSaving; String get successMessage; String get errorMessage;
|
||||
List<PictureEntity> get photos; bool get isLoading; bool get isSaving; String? get currentBackgroundId; BackgroundImageSuccessEvent? get successEvent; BackgroundImageErrorEvent? get errorEvent;
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $BackgroundImageViewStateCopyWith<BackgroundImageViewState> get copyWith => _$Ba
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is BackgroundImageViewState&&(identical(other.image, image) || other.image == image)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is BackgroundImageViewState&&const DeepCollectionEquality().equals(other.photos, photos)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.currentBackgroundId, currentBackgroundId) || other.currentBackgroundId == currentBackgroundId)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,image,isLoading,isSaving,successMessage,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(photos),isLoading,isSaving,currentBackgroundId,successEvent,errorEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BackgroundImageViewState(image: $image, isLoading: $isLoading, isSaving: $isSaving, successMessage: $successMessage, errorMessage: $errorMessage)';
|
||||
return 'BackgroundImageViewState(photos: $photos, isLoading: $isLoading, isSaving: $isSaving, currentBackgroundId: $currentBackgroundId, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $BackgroundImageViewStateCopyWith<$Res> {
|
||||
factory $BackgroundImageViewStateCopyWith(BackgroundImageViewState value, $Res Function(BackgroundImageViewState) _then) = _$BackgroundImageViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String image, bool isLoading, bool isSaving, String successMessage, String errorMessage
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -62,14 +62,15 @@ class _$BackgroundImageViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? image = null,Object? isLoading = null,Object? isSaving = null,Object? successMessage = null,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? photos = null,Object? isLoading = null,Object? isSaving = null,Object? currentBackgroundId = freezed,Object? successEvent = freezed,Object? errorEvent = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
image: null == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
photos: null == photos ? _self.photos : photos // ignore: cast_nullable_to_non_nullable
|
||||
as List<PictureEntity>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,currentBackgroundId: freezed == currentBackgroundId ? _self.currentBackgroundId : currentBackgroundId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageSuccessEvent?,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageErrorEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -154,10 +155,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String image, bool isLoading, bool isSaving, String successMessage, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState() when $default != null:
|
||||
return $default(_that.image,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -175,10 +176,10 @@ return $default(_that.image,_that.isLoading,_that.isSaving,_that.successMessage,
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String image, bool isLoading, bool isSaving, String successMessage, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState():
|
||||
return $default(_that.image,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -195,10 +196,10 @@ return $default(_that.image,_that.isLoading,_that.isSaving,_that.successMessage,
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String image, bool isLoading, bool isSaving, String successMessage, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BackgroundImageViewState() when $default != null:
|
||||
return $default(_that.image,_that.isLoading,_that.isSaving,_that.successMessage,_that.errorMessage);case _:
|
||||
return $default(_that.photos,_that.isLoading,_that.isSaving,_that.currentBackgroundId,_that.successEvent,_that.errorEvent);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -210,14 +211,21 @@ return $default(_that.image,_that.isLoading,_that.isSaving,_that.successMessage,
|
||||
|
||||
|
||||
class _BackgroundImageViewState implements BackgroundImageViewState {
|
||||
const _BackgroundImageViewState({this.image = '', this.isLoading = true, this.isSaving = false, this.successMessage = '', this.errorMessage = ''});
|
||||
const _BackgroundImageViewState({final List<PictureEntity> photos = const [], this.isLoading = true, this.isSaving = false, this.currentBackgroundId, this.successEvent, this.errorEvent}): _photos = photos;
|
||||
|
||||
|
||||
@override@JsonKey() final String image;
|
||||
final List<PictureEntity> _photos;
|
||||
@override@JsonKey() List<PictureEntity> get photos {
|
||||
if (_photos is EqualUnmodifiableListView) return _photos;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_photos);
|
||||
}
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isSaving;
|
||||
@override@JsonKey() final String successMessage;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override final String? currentBackgroundId;
|
||||
@override final BackgroundImageSuccessEvent? successEvent;
|
||||
@override final BackgroundImageErrorEvent? errorEvent;
|
||||
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -229,16 +237,16 @@ _$BackgroundImageViewStateCopyWith<_BackgroundImageViewState> get copyWith => __
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BackgroundImageViewState&&(identical(other.image, image) || other.image == image)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BackgroundImageViewState&&const DeepCollectionEquality().equals(other._photos, _photos)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.currentBackgroundId, currentBackgroundId) || other.currentBackgroundId == currentBackgroundId)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,image,isLoading,isSaving,successMessage,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_photos),isLoading,isSaving,currentBackgroundId,successEvent,errorEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BackgroundImageViewState(image: $image, isLoading: $isLoading, isSaving: $isSaving, successMessage: $successMessage, errorMessage: $errorMessage)';
|
||||
return 'BackgroundImageViewState(photos: $photos, isLoading: $isLoading, isSaving: $isSaving, currentBackgroundId: $currentBackgroundId, successEvent: $successEvent, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -249,7 +257,7 @@ abstract mixin class _$BackgroundImageViewStateCopyWith<$Res> implements $Backgr
|
||||
factory _$BackgroundImageViewStateCopyWith(_BackgroundImageViewState value, $Res Function(_BackgroundImageViewState) _then) = __$BackgroundImageViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String image, bool isLoading, bool isSaving, String successMessage, String errorMessage
|
||||
List<PictureEntity> photos, bool isLoading, bool isSaving, String? currentBackgroundId, BackgroundImageSuccessEvent? successEvent, BackgroundImageErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -266,14 +274,15 @@ class __$BackgroundImageViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of BackgroundImageViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? image = null,Object? isLoading = null,Object? isSaving = null,Object? successMessage = null,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? photos = null,Object? isLoading = null,Object? isSaving = null,Object? currentBackgroundId = freezed,Object? successEvent = freezed,Object? errorEvent = freezed,}) {
|
||||
return _then(_BackgroundImageViewState(
|
||||
image: null == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
photos: null == photos ? _self._photos : photos // ignore: cast_nullable_to_non_nullable
|
||||
as List<PictureEntity>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,successMessage: null == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,currentBackgroundId: freezed == currentBackgroundId ? _self.currentBackgroundId : currentBackgroundId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageSuccessEvent?,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BackgroundImageErrorEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class ContactsScreen extends ConsumerWidget {
|
||||
contactsViewModelProvider.select((s) => s.errorMessage),
|
||||
(previous, next) {
|
||||
if (next.isNotEmpty) {
|
||||
showTopSnackbar(context, message: next, type: MessageType.error);
|
||||
showTopSnackbar(context, message: context.translate(next), type: MessageType.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -58,14 +58,21 @@ class ContactsScreen extends ConsumerWidget {
|
||||
shape: const CircleBorder(),
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => const Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: NewContactDialog(),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
onTap: () {
|
||||
if (state.contacts.length == state.maxLimit) {
|
||||
showTopSnackbar(context, message: context.translate(I18n.errorContactsMax));
|
||||
return;
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => const Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: NewContactDialog(),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: SizedBox(
|
||||
width: SizeUtils.getByScreen(small: 48, big: 46),
|
||||
height: SizeUtils.getByScreen(small: 48, big: 46),
|
||||
child: Icon(
|
||||
|
||||
@@ -104,18 +104,22 @@ class _EditContactScreenState extends ConsumerState<EditContactScreen> {
|
||||
height: SizeUtils.getByScreen(small: 28, big: 26),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
CountryPrefixPicker(
|
||||
headerText: context.translate(I18n.selectYourCountry),
|
||||
initialSelection: state.dialCode,
|
||||
onChanged: (country) {
|
||||
final vm =
|
||||
ref.read(contactsViewModelProvider.notifier);
|
||||
vm.updateDialCode(
|
||||
country.dialCode ?? state.dialCode,
|
||||
);
|
||||
},
|
||||
width: 80,
|
||||
Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: CountryPrefixPicker(
|
||||
headerText: context.translate(I18n.selectYourCountry),
|
||||
initialSelection: state.dialCode,
|
||||
onChanged: (country) {
|
||||
final vm =
|
||||
ref.read(contactsViewModelProvider.notifier);
|
||||
vm.updateDialCode(
|
||||
country.dialCode ?? state.dialCode,
|
||||
);
|
||||
},
|
||||
width: 80,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: SizeUtils.getByScreen(small: 10, big: 10, xl: 6),
|
||||
|
||||
@@ -134,6 +134,11 @@ class ContactsViewModel extends Notifier<ContactsViewState> {
|
||||
Future<bool> deleteContact(ContactEntity contact) async {
|
||||
if (state.isLoading) return false;
|
||||
|
||||
if (state.contacts.length == 1){
|
||||
state = state.copyWith(errorMessage: I18n.errorContactsMin);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
state = state.copyWith(isLoading: true, errorMessage: '');
|
||||
|
||||
|
||||
@@ -8,9 +8,10 @@ part 'contacts_view_state.freezed.dart';
|
||||
abstract class ContactsViewState with _$ContactsViewState {
|
||||
const factory ContactsViewState({
|
||||
@Default([]) List<ContactEntity> contacts,
|
||||
@Default(10) int maxLimit,
|
||||
@Default('+34') String dialCode,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isEditing,
|
||||
@Default('') String errorMessage,
|
||||
@Default('') String errorMessage
|
||||
}) = _ContactsViewState;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$ContactsViewState {
|
||||
|
||||
List<ContactEntity> get contacts; String get dialCode; bool get isLoading; bool get isEditing; String get errorMessage;
|
||||
List<ContactEntity> get contacts; int get maxLimit; String get dialCode; bool get isLoading; bool get isEditing; String get errorMessage;
|
||||
/// Create a copy of ContactsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $ContactsViewStateCopyWith<ContactsViewState> get copyWith => _$ContactsViewStat
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ContactsViewState&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is ContactsViewState&&const DeepCollectionEquality().equals(other.contacts, contacts)&&(identical(other.maxLimit, maxLimit) || other.maxLimit == maxLimit)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(contacts),dialCode,isLoading,isEditing,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(contacts),maxLimit,dialCode,isLoading,isEditing,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ContactsViewState(contacts: $contacts, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)';
|
||||
return 'ContactsViewState(contacts: $contacts, maxLimit: $maxLimit, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $ContactsViewStateCopyWith<$Res> {
|
||||
factory $ContactsViewStateCopyWith(ContactsViewState value, $Res Function(ContactsViewState) _then) = _$ContactsViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage
|
||||
List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
@@ -62,10 +62,11 @@ class _$ContactsViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ContactsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? contacts = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? contacts = null,Object? maxLimit = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
contacts: null == contacts ? _self.contacts : contacts // ignore: cast_nullable_to_non_nullable
|
||||
as List<ContactEntity>,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
|
||||
as List<ContactEntity>,maxLimit: null == maxLimit ? _self.maxLimit : maxLimit // ignore: cast_nullable_to_non_nullable
|
||||
as int,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
@@ -154,10 +155,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ContactsViewState() when $default != null:
|
||||
return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
|
||||
return $default(_that.contacts,_that.maxLimit,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -175,10 +176,10 @@ return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_t
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ContactsViewState():
|
||||
return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
|
||||
return $default(_that.contacts,_that.maxLimit,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -195,10 +196,10 @@ return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_t
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _ContactsViewState() when $default != null:
|
||||
return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
|
||||
return $default(_that.contacts,_that.maxLimit,_that.dialCode,_that.isLoading,_that.isEditing,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -210,7 +211,7 @@ return $default(_that.contacts,_that.dialCode,_that.isLoading,_that.isEditing,_t
|
||||
|
||||
|
||||
class _ContactsViewState implements ContactsViewState {
|
||||
const _ContactsViewState({final List<ContactEntity> contacts = const [], this.dialCode = '+34', this.isLoading = true, this.isEditing = false, this.errorMessage = ''}): _contacts = contacts;
|
||||
const _ContactsViewState({final List<ContactEntity> contacts = const [], this.maxLimit = 10, this.dialCode = '+34', this.isLoading = true, this.isEditing = false, this.errorMessage = ''}): _contacts = contacts;
|
||||
|
||||
|
||||
final List<ContactEntity> _contacts;
|
||||
@@ -220,6 +221,7 @@ class _ContactsViewState implements ContactsViewState {
|
||||
return EqualUnmodifiableListView(_contacts);
|
||||
}
|
||||
|
||||
@override@JsonKey() final int maxLimit;
|
||||
@override@JsonKey() final String dialCode;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isEditing;
|
||||
@@ -235,16 +237,16 @@ _$ContactsViewStateCopyWith<_ContactsViewState> get copyWith => __$ContactsViewS
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ContactsViewState&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ContactsViewState&&const DeepCollectionEquality().equals(other._contacts, _contacts)&&(identical(other.maxLimit, maxLimit) || other.maxLimit == maxLimit)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isEditing, isEditing) || other.isEditing == isEditing)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_contacts),dialCode,isLoading,isEditing,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_contacts),maxLimit,dialCode,isLoading,isEditing,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ContactsViewState(contacts: $contacts, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)';
|
||||
return 'ContactsViewState(contacts: $contacts, maxLimit: $maxLimit, dialCode: $dialCode, isLoading: $isLoading, isEditing: $isEditing, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
@@ -255,7 +257,7 @@ abstract mixin class _$ContactsViewStateCopyWith<$Res> implements $ContactsViewS
|
||||
factory _$ContactsViewStateCopyWith(_ContactsViewState value, $Res Function(_ContactsViewState) _then) = __$ContactsViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<ContactEntity> contacts, String dialCode, bool isLoading, bool isEditing, String errorMessage
|
||||
List<ContactEntity> contacts, int maxLimit, String dialCode, bool isLoading, bool isEditing, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
@@ -272,10 +274,11 @@ class __$ContactsViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of ContactsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? contacts = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? contacts = null,Object? maxLimit = null,Object? dialCode = null,Object? isLoading = null,Object? isEditing = null,Object? errorMessage = null,}) {
|
||||
return _then(_ContactsViewState(
|
||||
contacts: null == contacts ? _self._contacts : contacts // ignore: cast_nullable_to_non_nullable
|
||||
as List<ContactEntity>,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
|
||||
as List<ContactEntity>,maxLimit: null == maxLimit ? _self.maxLimit : maxLimit // ignore: cast_nullable_to_non_nullable
|
||||
as int,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
|
||||
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isEditing: null == isEditing ? _self.isEditing : isEditing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
|
||||
@@ -24,7 +24,7 @@ class ContactCard extends ConsumerWidget {
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 22, big: 21),
|
||||
horizontal: SizeUtils.getByScreen(small: 22, big: 21, xl: 18),
|
||||
vertical: SizeUtils.getByScreen(small: 12, big: 8),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
@@ -71,11 +71,13 @@ class ContactCard extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
if (isEditing) ...[
|
||||
DecoratedBox(
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFF5D52),
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
height: SizeUtils.getByScreen(small: 48, big: 48, xl: 40),
|
||||
width: SizeUtils.getByScreen(small: 48, big: 48, xl: 40),
|
||||
child: IconButton(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
@@ -83,21 +85,30 @@ class ContactCard extends ConsumerWidget {
|
||||
child: ConfirmDeleteDialog(contact: contact),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.close, color: Colors.white),
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: Colors.white,
|
||||
size: SizeUtils.getByScreen(small: 28, big: 22),),
|
||||
),
|
||||
),
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 14)),
|
||||
DecoratedBox(
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 14, xl: 10)),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
height: SizeUtils.getByScreen(small: 48, big: 48, xl: 40),
|
||||
width: SizeUtils.getByScreen(small: 48, big: 48, xl: 40),
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
final nav = GetIt.I<NavigationContract>();
|
||||
nav.pushTo(AppRoutes.editContactPath(contact.id));
|
||||
},
|
||||
icon: const Icon(Icons.edit_outlined, color: Colors.white),
|
||||
icon: Icon(
|
||||
Icons.edit_outlined,
|
||||
color: Colors.white,
|
||||
size: SizeUtils.getByScreen(small: 28, big: 22),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -39,8 +39,8 @@ class _NewContactDialogState extends ConsumerState<NewContactDialog> {
|
||||
horizontal: SizeUtils.getByScreen(small: 10, big: 8),
|
||||
vertical: SizeUtils.getByScreen(small: 10, big: 8),
|
||||
),
|
||||
height: SizeUtils.getByScreen(small: 430, big: 410),
|
||||
width: SizeUtils.getByScreen(small: 400, big: 390),
|
||||
height: SizeUtils.getByScreen(small: 430, big: 410, xl: 400),
|
||||
width: SizeUtils.getByScreen(small: 400, big: 390, xl: 400),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
borderRadius: BorderRadius.all(
|
||||
@@ -49,6 +49,7 @@ class _NewContactDialogState extends ConsumerState<NewContactDialog> {
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
|
||||
@@ -51,14 +51,14 @@ class DeviceManagementScreen extends ConsumerWidget {
|
||||
negativeIcon: true,
|
||||
text: context.translate(I18n.contacts),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
AppMenuButton(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
onPressed: () {},
|
||||
icon: SFIcons.doNotDisturbCircle,
|
||||
negativeIcon: true,
|
||||
text: context.translate(I18n.doNotDisturb),
|
||||
),
|
||||
// SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
// AppMenuButton(
|
||||
// color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
// onPressed: () {},
|
||||
// icon: SFIcons.doNotDisturbCircle,
|
||||
// negativeIcon: true,
|
||||
// text: context.translate(I18n.doNotDisturb),
|
||||
// ),
|
||||
// SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
// AppMenuButton(
|
||||
// color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
@@ -128,21 +128,21 @@ class DeviceManagementScreen extends ConsumerWidget {
|
||||
icon: SFIcons.screenTime,
|
||||
text: context.translate(I18n.appsUse),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
AppMenuButton(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
onPressed: () {},
|
||||
icon: Icons.app_registration_sharp,
|
||||
text: context.translate(I18n.appsSurveillance),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
AppMenuButton(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
onPressed: () {},
|
||||
icon: SFIcons.friendsCircle,
|
||||
negativeIcon: true,
|
||||
text: context.translate(I18n.makeFriends),
|
||||
),
|
||||
// SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
// AppMenuButton(
|
||||
// color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
// onPressed: () {},
|
||||
// icon: Icons.app_registration_sharp,
|
||||
// text: context.translate(I18n.appsSurveillance),
|
||||
// ),
|
||||
// SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
// AppMenuButton(
|
||||
// color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
// onPressed: () {},
|
||||
// icon: SFIcons.friendsCircle,
|
||||
// negativeIcon: true,
|
||||
// text: context.translate(I18n.makeFriends),
|
||||
// ),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
AppMenuButton(
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
|
||||
@@ -32,10 +32,14 @@ class CallWatchViewModel extends Notifier<CallWatchViewState> {
|
||||
|
||||
state = state.copyWith(phone: text, errorMessage: '');
|
||||
}
|
||||
|
||||
|
||||
void updateDialCode(String code) {
|
||||
state = state.copyWith(dialCode: code, errorMessage: '');
|
||||
}
|
||||
|
||||
void call() async {
|
||||
final phone = state.phone;
|
||||
if (phone.isEmpty){
|
||||
if (phone.isEmpty) {
|
||||
state = state.copyWith(errorMessage: 'errorMessagePhoneIsEmpty');
|
||||
return;
|
||||
}
|
||||
@@ -44,7 +48,8 @@ class CallWatchViewModel extends Notifier<CallWatchViewState> {
|
||||
return;
|
||||
}
|
||||
|
||||
final url = Uri(scheme: 'tel', path: phone);
|
||||
final fullNumber = '${state.dialCode}$phone';
|
||||
final url = Uri(scheme: 'tel', path: fullNumber);
|
||||
|
||||
if (await canLaunchUrl(url)) {
|
||||
launchUrl(url);
|
||||
|
||||
@@ -5,6 +5,7 @@ part 'call_watch_view_state.freezed.dart';
|
||||
@freezed
|
||||
abstract class CallWatchViewState with _$CallWatchViewState {
|
||||
const factory CallWatchViewState({
|
||||
@Default('+34') String dialCode,
|
||||
@Default('') String phone,
|
||||
@Default('') String errorMessage,
|
||||
}) = _CallWatchViewState;
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$CallWatchViewState {
|
||||
|
||||
String get phone; String get errorMessage;
|
||||
String get dialCode; String get phone; String get errorMessage;
|
||||
/// Create a copy of CallWatchViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $CallWatchViewStateCopyWith<CallWatchViewState> get copyWith => _$CallWatchViewS
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallWatchViewState&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallWatchViewState&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,phone,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,dialCode,phone,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallWatchViewState(phone: $phone, errorMessage: $errorMessage)';
|
||||
return 'CallWatchViewState(dialCode: $dialCode, phone: $phone, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $CallWatchViewStateCopyWith<$Res> {
|
||||
factory $CallWatchViewStateCopyWith(CallWatchViewState value, $Res Function(CallWatchViewState) _then) = _$CallWatchViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String phone, String errorMessage
|
||||
String dialCode, String phone, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
@@ -62,9 +62,10 @@ class _$CallWatchViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of CallWatchViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? phone = null,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? dialCode = null,Object? phone = null,Object? errorMessage = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
|
||||
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,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
@@ -151,10 +152,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String phone, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String dialCode, String phone, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState() when $default != null:
|
||||
return $default(_that.phone,_that.errorMessage);case _:
|
||||
return $default(_that.dialCode,_that.phone,_that.errorMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -172,10 +173,10 @@ return $default(_that.phone,_that.errorMessage);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String phone, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String dialCode, String phone, String errorMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState():
|
||||
return $default(_that.phone,_that.errorMessage);case _:
|
||||
return $default(_that.dialCode,_that.phone,_that.errorMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -192,10 +193,10 @@ return $default(_that.phone,_that.errorMessage);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String phone, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String dialCode, String phone, String errorMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _CallWatchViewState() when $default != null:
|
||||
return $default(_that.phone,_that.errorMessage);case _:
|
||||
return $default(_that.dialCode,_that.phone,_that.errorMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -207,9 +208,10 @@ return $default(_that.phone,_that.errorMessage);case _:
|
||||
|
||||
|
||||
class _CallWatchViewState implements CallWatchViewState {
|
||||
const _CallWatchViewState({this.phone = '', this.errorMessage = ''});
|
||||
const _CallWatchViewState({this.dialCode = '+34', this.phone = '', this.errorMessage = ''});
|
||||
|
||||
|
||||
@override@JsonKey() final String dialCode;
|
||||
@override@JsonKey() final String phone;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
|
||||
@@ -223,16 +225,16 @@ _$CallWatchViewStateCopyWith<_CallWatchViewState> get copyWith => __$CallWatchVi
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallWatchViewState&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallWatchViewState&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,phone,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,dialCode,phone,errorMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallWatchViewState(phone: $phone, errorMessage: $errorMessage)';
|
||||
return 'CallWatchViewState(dialCode: $dialCode, phone: $phone, errorMessage: $errorMessage)';
|
||||
}
|
||||
|
||||
|
||||
@@ -243,7 +245,7 @@ abstract mixin class _$CallWatchViewStateCopyWith<$Res> implements $CallWatchVie
|
||||
factory _$CallWatchViewStateCopyWith(_CallWatchViewState value, $Res Function(_CallWatchViewState) _then) = __$CallWatchViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String phone, String errorMessage
|
||||
String dialCode, String phone, String errorMessage
|
||||
});
|
||||
|
||||
|
||||
@@ -260,9 +262,10 @@ class __$CallWatchViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of CallWatchViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? phone = null,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? dialCode = null,Object? phone = null,Object? errorMessage = null,}) {
|
||||
return _then(_CallWatchViewState(
|
||||
phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
|
||||
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,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
class CallWatchDialog extends ConsumerWidget {
|
||||
const CallWatchDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -16,33 +17,67 @@ class CallWatchDialog extends ConsumerWidget {
|
||||
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 26, vertical: 20),
|
||||
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18)
|
||||
small: EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
big: EdgeInsets.symmetric(horizontal: 14, vertical: 18),
|
||||
),
|
||||
width: SizeUtils.getByScreen(small: 390, big: 380),
|
||||
height: SizeUtils.getByScreen(small: 250, big: 243),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
),
|
||||
width: SizeUtils.getByScreen(small: 390, big: 380, xl: 390),
|
||||
height: SizeUtils.getByScreen(small: 250, big: 230),
|
||||
child: Column(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
Center(child: Text(context.translate(I18n.callWatch),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
|
||||
)),
|
||||
Center(
|
||||
child: Text(
|
||||
context.translate(I18n.callWatch),
|
||||
textAlign: TextAlign.center,
|
||||
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)),
|
||||
)
|
||||
)
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
|
||||
CustomTextField(
|
||||
controller: viewModel.phoneController,
|
||||
hint: context.translate(I18n.mainContactPhoneNumber),
|
||||
keyboardType: TextInputType.number,
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CountryPrefixPicker(
|
||||
headerText: context.translate(I18n.selectYourCountry),
|
||||
initialSelection: viewState.dialCode,
|
||||
onChanged: (country) {
|
||||
viewModel.updateDialCode(
|
||||
country.dialCode ?? viewState.dialCode,
|
||||
);
|
||||
},
|
||||
width: 80,
|
||||
backgroundColor: Colors.transparent,
|
||||
borderColor: Colors.black,
|
||||
),
|
||||
SizedBox(width: SizeUtils.getByScreen(small: 8, big: 7)),
|
||||
Expanded(
|
||||
child: CustomTextField(
|
||||
controller: viewModel.phoneController,
|
||||
hint: context.translate(I18n.mainContactPhoneNumber),
|
||||
keyboardType: TextInputType.phone,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (viewState.errorMessage.isNotEmpty)
|
||||
Padding(
|
||||
@@ -68,5 +103,4 @@ class CallWatchDialog extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@ 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:lottie/lottie.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
import 'state/health_view_model.dart';
|
||||
import 'state/health_view_state.dart';
|
||||
import 'widgets/blood_pressure_tab.dart';
|
||||
import 'widgets/health_summary_cards.dart';
|
||||
import 'widgets/heart_rate_tab.dart';
|
||||
@@ -55,13 +57,20 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final state = ref.watch(healthViewModelProvider);
|
||||
final vm = ref.read(healthViewModelProvider.notifier);
|
||||
final device = ref.watch(selectedDeviceProvider);
|
||||
|
||||
ref.listen(healthViewModelProvider.select((s) => s.errorMessage), (
|
||||
ref.listen(healthViewModelProvider.select((s) => s.errorEvent), (
|
||||
previous,
|
||||
next,
|
||||
) {
|
||||
if (next.isNotEmpty) {
|
||||
showTopSnackbar(context, message: next, type: MessageType.error);
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
HealthErrorEvent.loadData => context.translate(I18n.errorHealthData),
|
||||
HealthErrorEvent.loadMore => context.translate(I18n.errorHealthData),
|
||||
HealthErrorEvent.measure => context.translate(I18n.errorHealthMeasure),
|
||||
HealthErrorEvent.heartRateFrequency => context.translate(I18n.errorHeartRateFrequency),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -70,7 +79,12 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
|
||||
title: context.translate(I18n.health),
|
||||
body: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
: state.isMeasuringCountdown
|
||||
? _MeasuringOverlay(
|
||||
remainingSeconds: state.measureRemainingSeconds,
|
||||
theme: theme,
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
HealthSummaryCards(
|
||||
heartbeats: state.latestHeartbeats,
|
||||
@@ -84,6 +98,31 @@ class _HealthScreenState extends ConsumerState<HealthScreen>
|
||||
onCustomTap: () => _pickCustomRange(vm),
|
||||
theme: theme,
|
||||
),
|
||||
if (device?.capabilities?.heartbeats != null &&
|
||||
device!.capabilities!.heartbeats!.options.isNotEmpty)
|
||||
_HeartRateFrequencySelector(
|
||||
currentFrequency:
|
||||
device.settings.frequencyHeartRate,
|
||||
options:
|
||||
device.capabilities!.heartbeats!.options,
|
||||
theme: theme,
|
||||
onChanged: (frequency) async {
|
||||
final success =
|
||||
await vm.updateHeartRateFrequency(
|
||||
frequency: frequency);
|
||||
if (!context.mounted) return;
|
||||
if (success) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(
|
||||
I18n.locationFrequencyUpdated,
|
||||
args: {'minutes': '${frequency ~/ 60}'},
|
||||
),
|
||||
type: MessageType.success,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
TabBar(
|
||||
controller: _tabController,
|
||||
labelColor: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
@@ -141,18 +180,123 @@ class _SaveSection extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.read(themePortProvider);
|
||||
|
||||
final vm = ref.read(healthViewModelProvider.notifier);
|
||||
final isMeasuring = ref.watch(
|
||||
healthViewModelProvider.select((s) => s.isMeasuring),
|
||||
);
|
||||
final isCountdown = ref.watch(
|
||||
healthViewModelProvider.select((s) => s.isMeasuringCountdown),
|
||||
);
|
||||
|
||||
if (isCountdown) return const SizedBox.shrink();
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
await vm.measure();
|
||||
},
|
||||
text: context.translate(I18n.measure),
|
||||
onPressed: isMeasuring ? null : () => vm.measure(),
|
||||
text: isMeasuring
|
||||
? '...'
|
||||
: context.translate(I18n.measure),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeartRateFrequencySelector extends StatelessWidget {
|
||||
final int currentFrequency;
|
||||
final List<int> options;
|
||||
final ThemePort theme;
|
||||
final ValueChanged<int> onChanged;
|
||||
|
||||
const _HeartRateFrequencySelector({
|
||||
required this.currentFrequency,
|
||||
required this.options,
|
||||
required this.theme,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.timer_outlined, size: 18, color: primaryColor),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
context.translate(I18n.healthFrequency),
|
||||
style: TextStyle(fontSize: 13, color: primaryColor),
|
||||
),
|
||||
const Spacer(),
|
||||
...options.map(
|
||||
(opt) => Padding(
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
child: ChoiceChip(
|
||||
label: Text('${opt ~/ 60}m'),
|
||||
selected: opt == currentFrequency,
|
||||
selectedColor: primaryColor,
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 12,
|
||||
color: opt == currentFrequency ? Colors.white : primaryColor,
|
||||
),
|
||||
side: BorderSide(color: primaryColor),
|
||||
onSelected: (_) => onChanged(opt),
|
||||
visualDensity: VisualDensity.compact,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MeasuringOverlay extends StatelessWidget {
|
||||
final int remainingSeconds;
|
||||
final ThemePort theme;
|
||||
|
||||
const _MeasuringOverlay({
|
||||
required this.remainingSeconds,
|
||||
required this.theme,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Lottie.asset(
|
||||
'assets/shared/animations/fitness_tracker.json',
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
context.translate(I18n.healthMeasuring),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'${remainingSeconds}s',
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
|
||||
@@ -7,6 +9,18 @@ import '../../../../core/domain/repositories/health_repository.dart';
|
||||
import '../../../../core/providers/health_repository_provider.dart';
|
||||
import 'health_view_state.dart';
|
||||
|
||||
final _measureEndTimeProvider =
|
||||
NotifierProvider<_MeasureEndTimeNotifier, DateTime?>(
|
||||
_MeasureEndTimeNotifier.new,
|
||||
);
|
||||
|
||||
class _MeasureEndTimeNotifier extends Notifier<DateTime?> {
|
||||
@override
|
||||
DateTime? build() => null;
|
||||
|
||||
void set(DateTime? value) => state = value;
|
||||
}
|
||||
|
||||
final healthViewModelProvider =
|
||||
NotifierProvider.autoDispose<HealthViewModel, HealthViewState>(
|
||||
HealthViewModel.new,
|
||||
@@ -15,24 +29,46 @@ final healthViewModelProvider =
|
||||
class HealthViewModel extends Notifier<HealthViewState> {
|
||||
late final HealthRepository _repository;
|
||||
late final CommandsRepository _commandsRepository;
|
||||
Timer? _measureTimer;
|
||||
|
||||
static const int _historyPageSize = 20;
|
||||
static const int _measureDurationSeconds = 60;
|
||||
|
||||
@override
|
||||
HealthViewState build() {
|
||||
_repository = ref.read(healthRepositoryProvider);
|
||||
_commandsRepository = ref.read(commandsRepositoryProvider);
|
||||
_init();
|
||||
_resumeMeasureIfNeeded();
|
||||
return const HealthViewState();
|
||||
}
|
||||
|
||||
void _resumeMeasureIfNeeded() {
|
||||
final endTime = ref.read(_measureEndTimeProvider);
|
||||
if (endTime == null) return;
|
||||
|
||||
final remaining = endTime.difference(DateTime.now()).inSeconds;
|
||||
if (remaining <= 0) {
|
||||
ref.read(_measureEndTimeProvider.notifier).set(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Future.microtask(() {
|
||||
state = state.copyWith(
|
||||
isMeasuringCountdown: true,
|
||||
measureRemainingSeconds: remaining,
|
||||
);
|
||||
_startCountdownTimer();
|
||||
});
|
||||
}
|
||||
|
||||
String? get _identificator =>
|
||||
ref.read(selectedDeviceProvider)?.identificator;
|
||||
|
||||
|
||||
Future<void> selectTimeRange(TimeRange range) async {
|
||||
if (range == state.timeRange) return;
|
||||
state = state.copyWith(timeRange: range, isLoading: true);
|
||||
state = state.copyWith(timeRange: range, isLoading: true, errorEvent: null);
|
||||
await _loadFilteredData();
|
||||
}
|
||||
|
||||
@@ -42,6 +78,7 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
customStart: start,
|
||||
customEnd: end,
|
||||
isLoading: true,
|
||||
errorEvent: null,
|
||||
);
|
||||
await _loadFilteredData();
|
||||
}
|
||||
@@ -91,7 +128,7 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoadingMore: false,
|
||||
errorMessage: _formatError(e),
|
||||
errorEvent: HealthErrorEvent.loadMore,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -131,7 +168,7 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
await _loadFilteredData();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(isLoading: false, errorMessage: _formatError(e));
|
||||
state = state.copyWith(isLoading: false, errorEvent: HealthErrorEvent.loadData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,11 +238,11 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
chartOxygens.map((e) => e.oxygen).toList(),
|
||||
),
|
||||
isLoading: false,
|
||||
errorMessage: '',
|
||||
errorEvent: null,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(isLoading: false, errorMessage: _formatError(e));
|
||||
state = state.copyWith(isLoading: false, errorEvent: HealthErrorEvent.loadData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,9 +282,26 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
);
|
||||
}
|
||||
|
||||
String _formatError(Object e) {
|
||||
final msg = e.toString();
|
||||
return msg.startsWith('Exception: ') ? msg.substring(11) : msg;
|
||||
Future<bool> updateHeartRateFrequency({required int frequency}) async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return false;
|
||||
|
||||
try {
|
||||
final updatedSettings = device.settings.copyWith(
|
||||
frequencyHeartRate: frequency,
|
||||
);
|
||||
await ref.read(deviceSettingsUpdateProvider).updateDeviceSettings(
|
||||
device: device,
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return false;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return false;
|
||||
state = state.copyWith(errorEvent: HealthErrorEvent.heartRateFrequency);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> measure() async {
|
||||
@@ -255,23 +309,54 @@ class HealthViewModel extends Notifier<HealthViewState> {
|
||||
if (device == null) return;
|
||||
|
||||
try {
|
||||
state = state.copyWith(isLoading: true);
|
||||
state = state.copyWith(isMeasuring: true, errorEvent: null);
|
||||
|
||||
final request = SendCommandRequestModel(
|
||||
device: device.identificator,
|
||||
command: DeviceCommand.requestHeartRate,
|
||||
);
|
||||
|
||||
await _commandsRepository.send(request: request);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(isLoading: false);
|
||||
ref.read(_measureEndTimeProvider.notifier).set(
|
||||
DateTime.now().add(const Duration(seconds: _measureDurationSeconds)));
|
||||
|
||||
state = state.copyWith(
|
||||
isMeasuring: false,
|
||||
isMeasuringCountdown: true,
|
||||
measureRemainingSeconds: _measureDurationSeconds,
|
||||
);
|
||||
|
||||
_startCountdownTimer();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: _formatError(e),
|
||||
isMeasuring: false,
|
||||
errorEvent: HealthErrorEvent.measure,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _startCountdownTimer() {
|
||||
_measureTimer?.cancel();
|
||||
_measureTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (!ref.mounted) {
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
final remaining = state.measureRemainingSeconds - 1;
|
||||
if (remaining <= 0) {
|
||||
timer.cancel();
|
||||
_measureTimer = null;
|
||||
ref.read(_measureEndTimeProvider.notifier).set(null);
|
||||
state = state.copyWith(
|
||||
isMeasuringCountdown: false,
|
||||
measureRemainingSeconds: 0,
|
||||
);
|
||||
_init();
|
||||
} else {
|
||||
state = state.copyWith(measureRemainingSeconds: remaining);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,13 @@ import '../../domain/entities/oxygen_entity.dart';
|
||||
|
||||
part 'health_view_state.freezed.dart';
|
||||
|
||||
enum HealthErrorEvent {
|
||||
loadData,
|
||||
loadMore,
|
||||
measure,
|
||||
heartRateFrequency,
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class HealthStats with _$HealthStats {
|
||||
const factory HealthStats({
|
||||
@@ -33,6 +40,9 @@ abstract class HealthViewState with _$HealthViewState {
|
||||
@Default(null) DateTime? customEnd,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isLoadingMore,
|
||||
@Default('') String errorMessage,
|
||||
@Default(false) bool isMeasuring,
|
||||
@Default(false) bool isMeasuringCountdown,
|
||||
@Default(0) int measureRemainingSeconds,
|
||||
HealthErrorEvent? errorEvent,
|
||||
}) = _HealthViewState;
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ as int,
|
||||
/// @nodoc
|
||||
mixin _$HealthViewState {
|
||||
|
||||
List<HeartbeatEntity> get latestHeartbeats; List<OxygenEntity> get latestOxygens; List<HeartbeatEntity> get chartHeartbeats; List<OxygenEntity> get chartOxygens; List<HeartbeatEntity> get historyHeartbeats; List<OxygenEntity> get historyOxygens; int get currentHistoryPage; bool get hasMoreHistory; HealthStats get heartRateStats; HealthStats get oxygenStats; TimeRange get timeRange; DateTime? get customStart; DateTime? get customEnd; bool get isLoading; bool get isLoadingMore; String get errorMessage;
|
||||
List<HeartbeatEntity> get latestHeartbeats; List<OxygenEntity> get latestOxygens; List<HeartbeatEntity> get chartHeartbeats; List<OxygenEntity> get chartOxygens; List<HeartbeatEntity> get historyHeartbeats; List<OxygenEntity> get historyOxygens; int get currentHistoryPage; bool get hasMoreHistory; HealthStats get heartRateStats; HealthStats get oxygenStats; TimeRange get timeRange; DateTime? get customStart; DateTime? get customEnd; bool get isLoading; bool get isLoadingMore; bool get isMeasuring; bool get isMeasuringCountdown; int get measureRemainingSeconds; HealthErrorEvent? get errorEvent;
|
||||
/// Create a copy of HealthViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -288,16 +288,16 @@ $HealthViewStateCopyWith<HealthViewState> get copyWith => _$HealthViewStateCopyW
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is HealthViewState&&const DeepCollectionEquality().equals(other.latestHeartbeats, latestHeartbeats)&&const DeepCollectionEquality().equals(other.latestOxygens, latestOxygens)&&const DeepCollectionEquality().equals(other.chartHeartbeats, chartHeartbeats)&&const DeepCollectionEquality().equals(other.chartOxygens, chartOxygens)&&const DeepCollectionEquality().equals(other.historyHeartbeats, historyHeartbeats)&&const DeepCollectionEquality().equals(other.historyOxygens, historyOxygens)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.heartRateStats, heartRateStats) || other.heartRateStats == heartRateStats)&&(identical(other.oxygenStats, oxygenStats) || other.oxygenStats == oxygenStats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is HealthViewState&&const DeepCollectionEquality().equals(other.latestHeartbeats, latestHeartbeats)&&const DeepCollectionEquality().equals(other.latestOxygens, latestOxygens)&&const DeepCollectionEquality().equals(other.chartHeartbeats, chartHeartbeats)&&const DeepCollectionEquality().equals(other.chartOxygens, chartOxygens)&&const DeepCollectionEquality().equals(other.historyHeartbeats, historyHeartbeats)&&const DeepCollectionEquality().equals(other.historyOxygens, historyOxygens)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.heartRateStats, heartRateStats) || other.heartRateStats == heartRateStats)&&(identical(other.oxygenStats, oxygenStats) || other.oxygenStats == oxygenStats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.isMeasuring, isMeasuring) || other.isMeasuring == isMeasuring)&&(identical(other.isMeasuringCountdown, isMeasuringCountdown) || other.isMeasuringCountdown == isMeasuringCountdown)&&(identical(other.measureRemainingSeconds, measureRemainingSeconds) || other.measureRemainingSeconds == measureRemainingSeconds)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(latestHeartbeats),const DeepCollectionEquality().hash(latestOxygens),const DeepCollectionEquality().hash(chartHeartbeats),const DeepCollectionEquality().hash(chartOxygens),const DeepCollectionEquality().hash(historyHeartbeats),const DeepCollectionEquality().hash(historyOxygens),currentHistoryPage,hasMoreHistory,heartRateStats,oxygenStats,timeRange,customStart,customEnd,isLoading,isLoadingMore,errorMessage);
|
||||
int get hashCode => Object.hashAll([runtimeType,const DeepCollectionEquality().hash(latestHeartbeats),const DeepCollectionEquality().hash(latestOxygens),const DeepCollectionEquality().hash(chartHeartbeats),const DeepCollectionEquality().hash(chartOxygens),const DeepCollectionEquality().hash(historyHeartbeats),const DeepCollectionEquality().hash(historyOxygens),currentHistoryPage,hasMoreHistory,heartRateStats,oxygenStats,timeRange,customStart,customEnd,isLoading,isLoadingMore,isMeasuring,isMeasuringCountdown,measureRemainingSeconds,errorEvent]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HealthViewState(latestHeartbeats: $latestHeartbeats, latestOxygens: $latestOxygens, chartHeartbeats: $chartHeartbeats, chartOxygens: $chartOxygens, historyHeartbeats: $historyHeartbeats, historyOxygens: $historyOxygens, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, heartRateStats: $heartRateStats, oxygenStats: $oxygenStats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorMessage: $errorMessage)';
|
||||
return 'HealthViewState(latestHeartbeats: $latestHeartbeats, latestOxygens: $latestOxygens, chartHeartbeats: $chartHeartbeats, chartOxygens: $chartOxygens, historyHeartbeats: $historyHeartbeats, historyOxygens: $historyOxygens, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, heartRateStats: $heartRateStats, oxygenStats: $oxygenStats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, isMeasuring: $isMeasuring, isMeasuringCountdown: $isMeasuringCountdown, measureRemainingSeconds: $measureRemainingSeconds, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ abstract mixin class $HealthViewStateCopyWith<$Res> {
|
||||
factory $HealthViewStateCopyWith(HealthViewState value, $Res Function(HealthViewState) _then) = _$HealthViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage
|
||||
List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, bool isMeasuring, bool isMeasuringCountdown, int measureRemainingSeconds, HealthErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -325,7 +325,7 @@ class _$HealthViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of HealthViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? latestHeartbeats = null,Object? latestOxygens = null,Object? chartHeartbeats = null,Object? chartOxygens = null,Object? historyHeartbeats = null,Object? historyOxygens = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? heartRateStats = null,Object? oxygenStats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? latestHeartbeats = null,Object? latestOxygens = null,Object? chartHeartbeats = null,Object? chartOxygens = null,Object? historyHeartbeats = null,Object? historyOxygens = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? heartRateStats = null,Object? oxygenStats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? isMeasuring = null,Object? isMeasuringCountdown = null,Object? measureRemainingSeconds = null,Object? errorEvent = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
latestHeartbeats: null == latestHeartbeats ? _self.latestHeartbeats : latestHeartbeats // ignore: cast_nullable_to_non_nullable
|
||||
as List<HeartbeatEntity>,latestOxygens: null == latestOxygens ? _self.latestOxygens : latestOxygens // ignore: cast_nullable_to_non_nullable
|
||||
@@ -342,8 +342,11 @@ as TimeRange,customStart: freezed == customStart ? _self.customStart : customSta
|
||||
as DateTime?,customEnd: freezed == customEnd ? _self.customEnd : customEnd // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,isMeasuring: null == isMeasuring ? _self.isMeasuring : isMeasuring // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isMeasuringCountdown: null == isMeasuringCountdown ? _self.isMeasuringCountdown : isMeasuringCountdown // ignore: cast_nullable_to_non_nullable
|
||||
as bool,measureRemainingSeconds: null == measureRemainingSeconds ? _self.measureRemainingSeconds : measureRemainingSeconds // ignore: cast_nullable_to_non_nullable
|
||||
as int,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as HealthErrorEvent?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of HealthViewState
|
||||
@@ -446,10 +449,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, bool isMeasuring, bool isMeasuringCountdown, int measureRemainingSeconds, HealthErrorEvent? errorEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _HealthViewState() when $default != null:
|
||||
return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats,_that.chartOxygens,_that.historyHeartbeats,_that.historyOxygens,_that.currentHistoryPage,_that.hasMoreHistory,_that.heartRateStats,_that.oxygenStats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorMessage);case _:
|
||||
return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats,_that.chartOxygens,_that.historyHeartbeats,_that.historyOxygens,_that.currentHistoryPage,_that.hasMoreHistory,_that.heartRateStats,_that.oxygenStats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.isMeasuring,_that.isMeasuringCountdown,_that.measureRemainingSeconds,_that.errorEvent);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -467,10 +470,10 @@ return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, bool isMeasuring, bool isMeasuringCountdown, int measureRemainingSeconds, HealthErrorEvent? errorEvent) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _HealthViewState():
|
||||
return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats,_that.chartOxygens,_that.historyHeartbeats,_that.historyOxygens,_that.currentHistoryPage,_that.hasMoreHistory,_that.heartRateStats,_that.oxygenStats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorMessage);case _:
|
||||
return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats,_that.chartOxygens,_that.historyHeartbeats,_that.historyOxygens,_that.currentHistoryPage,_that.hasMoreHistory,_that.heartRateStats,_that.oxygenStats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.isMeasuring,_that.isMeasuringCountdown,_that.measureRemainingSeconds,_that.errorEvent);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -487,10 +490,10 @@ return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, bool isMeasuring, bool isMeasuringCountdown, int measureRemainingSeconds, HealthErrorEvent? errorEvent)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _HealthViewState() when $default != null:
|
||||
return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats,_that.chartOxygens,_that.historyHeartbeats,_that.historyOxygens,_that.currentHistoryPage,_that.hasMoreHistory,_that.heartRateStats,_that.oxygenStats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.errorMessage);case _:
|
||||
return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats,_that.chartOxygens,_that.historyHeartbeats,_that.historyOxygens,_that.currentHistoryPage,_that.hasMoreHistory,_that.heartRateStats,_that.oxygenStats,_that.timeRange,_that.customStart,_that.customEnd,_that.isLoading,_that.isLoadingMore,_that.isMeasuring,_that.isMeasuringCountdown,_that.measureRemainingSeconds,_that.errorEvent);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -502,7 +505,7 @@ return $default(_that.latestHeartbeats,_that.latestOxygens,_that.chartHeartbeats
|
||||
|
||||
|
||||
class _HealthViewState implements HealthViewState {
|
||||
const _HealthViewState({final List<HeartbeatEntity> latestHeartbeats = const [], final List<OxygenEntity> latestOxygens = const [], final List<HeartbeatEntity> chartHeartbeats = const [], final List<OxygenEntity> chartOxygens = const [], final List<HeartbeatEntity> historyHeartbeats = const [], final List<OxygenEntity> historyOxygens = const [], this.currentHistoryPage = 1, this.hasMoreHistory = false, this.heartRateStats = const HealthStats(), this.oxygenStats = const HealthStats(), this.timeRange = TimeRange.today, this.customStart = null, this.customEnd = null, this.isLoading = true, this.isLoadingMore = false, this.errorMessage = ''}): _latestHeartbeats = latestHeartbeats,_latestOxygens = latestOxygens,_chartHeartbeats = chartHeartbeats,_chartOxygens = chartOxygens,_historyHeartbeats = historyHeartbeats,_historyOxygens = historyOxygens;
|
||||
const _HealthViewState({final List<HeartbeatEntity> latestHeartbeats = const [], final List<OxygenEntity> latestOxygens = const [], final List<HeartbeatEntity> chartHeartbeats = const [], final List<OxygenEntity> chartOxygens = const [], final List<HeartbeatEntity> historyHeartbeats = const [], final List<OxygenEntity> historyOxygens = const [], this.currentHistoryPage = 1, this.hasMoreHistory = false, this.heartRateStats = const HealthStats(), this.oxygenStats = const HealthStats(), this.timeRange = TimeRange.today, this.customStart = null, this.customEnd = null, this.isLoading = true, this.isLoadingMore = false, this.isMeasuring = false, this.isMeasuringCountdown = false, this.measureRemainingSeconds = 0, this.errorEvent}): _latestHeartbeats = latestHeartbeats,_latestOxygens = latestOxygens,_chartHeartbeats = chartHeartbeats,_chartOxygens = chartOxygens,_historyHeartbeats = historyHeartbeats,_historyOxygens = historyOxygens;
|
||||
|
||||
|
||||
final List<HeartbeatEntity> _latestHeartbeats;
|
||||
@@ -556,7 +559,10 @@ class _HealthViewState implements HealthViewState {
|
||||
@override@JsonKey() final DateTime? customEnd;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isLoadingMore;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override@JsonKey() final bool isMeasuring;
|
||||
@override@JsonKey() final bool isMeasuringCountdown;
|
||||
@override@JsonKey() final int measureRemainingSeconds;
|
||||
@override final HealthErrorEvent? errorEvent;
|
||||
|
||||
/// Create a copy of HealthViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -568,16 +574,16 @@ _$HealthViewStateCopyWith<_HealthViewState> get copyWith => __$HealthViewStateCo
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HealthViewState&&const DeepCollectionEquality().equals(other._latestHeartbeats, _latestHeartbeats)&&const DeepCollectionEquality().equals(other._latestOxygens, _latestOxygens)&&const DeepCollectionEquality().equals(other._chartHeartbeats, _chartHeartbeats)&&const DeepCollectionEquality().equals(other._chartOxygens, _chartOxygens)&&const DeepCollectionEquality().equals(other._historyHeartbeats, _historyHeartbeats)&&const DeepCollectionEquality().equals(other._historyOxygens, _historyOxygens)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.heartRateStats, heartRateStats) || other.heartRateStats == heartRateStats)&&(identical(other.oxygenStats, oxygenStats) || other.oxygenStats == oxygenStats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HealthViewState&&const DeepCollectionEquality().equals(other._latestHeartbeats, _latestHeartbeats)&&const DeepCollectionEquality().equals(other._latestOxygens, _latestOxygens)&&const DeepCollectionEquality().equals(other._chartHeartbeats, _chartHeartbeats)&&const DeepCollectionEquality().equals(other._chartOxygens, _chartOxygens)&&const DeepCollectionEquality().equals(other._historyHeartbeats, _historyHeartbeats)&&const DeepCollectionEquality().equals(other._historyOxygens, _historyOxygens)&&(identical(other.currentHistoryPage, currentHistoryPage) || other.currentHistoryPage == currentHistoryPage)&&(identical(other.hasMoreHistory, hasMoreHistory) || other.hasMoreHistory == hasMoreHistory)&&(identical(other.heartRateStats, heartRateStats) || other.heartRateStats == heartRateStats)&&(identical(other.oxygenStats, oxygenStats) || other.oxygenStats == oxygenStats)&&(identical(other.timeRange, timeRange) || other.timeRange == timeRange)&&(identical(other.customStart, customStart) || other.customStart == customStart)&&(identical(other.customEnd, customEnd) || other.customEnd == customEnd)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingMore, isLoadingMore) || other.isLoadingMore == isLoadingMore)&&(identical(other.isMeasuring, isMeasuring) || other.isMeasuring == isMeasuring)&&(identical(other.isMeasuringCountdown, isMeasuringCountdown) || other.isMeasuringCountdown == isMeasuringCountdown)&&(identical(other.measureRemainingSeconds, measureRemainingSeconds) || other.measureRemainingSeconds == measureRemainingSeconds)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_latestHeartbeats),const DeepCollectionEquality().hash(_latestOxygens),const DeepCollectionEquality().hash(_chartHeartbeats),const DeepCollectionEquality().hash(_chartOxygens),const DeepCollectionEquality().hash(_historyHeartbeats),const DeepCollectionEquality().hash(_historyOxygens),currentHistoryPage,hasMoreHistory,heartRateStats,oxygenStats,timeRange,customStart,customEnd,isLoading,isLoadingMore,errorMessage);
|
||||
int get hashCode => Object.hashAll([runtimeType,const DeepCollectionEquality().hash(_latestHeartbeats),const DeepCollectionEquality().hash(_latestOxygens),const DeepCollectionEquality().hash(_chartHeartbeats),const DeepCollectionEquality().hash(_chartOxygens),const DeepCollectionEquality().hash(_historyHeartbeats),const DeepCollectionEquality().hash(_historyOxygens),currentHistoryPage,hasMoreHistory,heartRateStats,oxygenStats,timeRange,customStart,customEnd,isLoading,isLoadingMore,isMeasuring,isMeasuringCountdown,measureRemainingSeconds,errorEvent]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'HealthViewState(latestHeartbeats: $latestHeartbeats, latestOxygens: $latestOxygens, chartHeartbeats: $chartHeartbeats, chartOxygens: $chartOxygens, historyHeartbeats: $historyHeartbeats, historyOxygens: $historyOxygens, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, heartRateStats: $heartRateStats, oxygenStats: $oxygenStats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, errorMessage: $errorMessage)';
|
||||
return 'HealthViewState(latestHeartbeats: $latestHeartbeats, latestOxygens: $latestOxygens, chartHeartbeats: $chartHeartbeats, chartOxygens: $chartOxygens, historyHeartbeats: $historyHeartbeats, historyOxygens: $historyOxygens, currentHistoryPage: $currentHistoryPage, hasMoreHistory: $hasMoreHistory, heartRateStats: $heartRateStats, oxygenStats: $oxygenStats, timeRange: $timeRange, customStart: $customStart, customEnd: $customEnd, isLoading: $isLoading, isLoadingMore: $isLoadingMore, isMeasuring: $isMeasuring, isMeasuringCountdown: $isMeasuringCountdown, measureRemainingSeconds: $measureRemainingSeconds, errorEvent: $errorEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -588,7 +594,7 @@ abstract mixin class _$HealthViewStateCopyWith<$Res> implements $HealthViewState
|
||||
factory _$HealthViewStateCopyWith(_HealthViewState value, $Res Function(_HealthViewState) _then) = __$HealthViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, String errorMessage
|
||||
List<HeartbeatEntity> latestHeartbeats, List<OxygenEntity> latestOxygens, List<HeartbeatEntity> chartHeartbeats, List<OxygenEntity> chartOxygens, List<HeartbeatEntity> historyHeartbeats, List<OxygenEntity> historyOxygens, int currentHistoryPage, bool hasMoreHistory, HealthStats heartRateStats, HealthStats oxygenStats, TimeRange timeRange, DateTime? customStart, DateTime? customEnd, bool isLoading, bool isLoadingMore, bool isMeasuring, bool isMeasuringCountdown, int measureRemainingSeconds, HealthErrorEvent? errorEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -605,7 +611,7 @@ class __$HealthViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of HealthViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? latestHeartbeats = null,Object? latestOxygens = null,Object? chartHeartbeats = null,Object? chartOxygens = null,Object? historyHeartbeats = null,Object? historyOxygens = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? heartRateStats = null,Object? oxygenStats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? latestHeartbeats = null,Object? latestOxygens = null,Object? chartHeartbeats = null,Object? chartOxygens = null,Object? historyHeartbeats = null,Object? historyOxygens = null,Object? currentHistoryPage = null,Object? hasMoreHistory = null,Object? heartRateStats = null,Object? oxygenStats = null,Object? timeRange = null,Object? customStart = freezed,Object? customEnd = freezed,Object? isLoading = null,Object? isLoadingMore = null,Object? isMeasuring = null,Object? isMeasuringCountdown = null,Object? measureRemainingSeconds = null,Object? errorEvent = freezed,}) {
|
||||
return _then(_HealthViewState(
|
||||
latestHeartbeats: null == latestHeartbeats ? _self._latestHeartbeats : latestHeartbeats // ignore: cast_nullable_to_non_nullable
|
||||
as List<HeartbeatEntity>,latestOxygens: null == latestOxygens ? _self._latestOxygens : latestOxygens // ignore: cast_nullable_to_non_nullable
|
||||
@@ -622,8 +628,11 @@ as TimeRange,customStart: freezed == customStart ? _self.customStart : customSta
|
||||
as DateTime?,customEnd: freezed == customEnd ? _self.customEnd : customEnd // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isLoadingMore: null == isLoadingMore ? _self.isLoadingMore : isLoadingMore // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,isMeasuring: null == isMeasuring ? _self.isMeasuring : isMeasuring // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isMeasuringCountdown: null == isMeasuringCountdown ? _self.isMeasuringCountdown : isMeasuringCountdown // ignore: cast_nullable_to_non_nullable
|
||||
as bool,measureRemainingSeconds: null == measureRemainingSeconds ? _self.measureRemainingSeconds : measureRemainingSeconds // ignore: cast_nullable_to_non_nullable
|
||||
as int,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as HealthErrorEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class LocateDeviceScreen extends ConsumerWidget {
|
||||
title: context.translate(I18n.locateSF),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 15),
|
||||
horizontal: SizeUtils.getByScreen(small: 20, big: 18),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -46,7 +46,11 @@ class LocateDeviceViewModel extends Notifier<LocateDeviceViewState> {
|
||||
}
|
||||
}
|
||||
|
||||
void resetError() {
|
||||
state = state.copyWith(errorMessage: '');
|
||||
void reset() {
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
isComplete: false,
|
||||
errorMessage: '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,28 @@ class LocateDeviceDialog extends ConsumerWidget {
|
||||
final state = ref.watch(locateDeviceViewModelProvider);
|
||||
final vm = ref.read(locateDeviceViewModelProvider.notifier);
|
||||
|
||||
ref.listen(
|
||||
locateDeviceViewModelProvider.select((s) => s.isComplete),
|
||||
(previous, next) {
|
||||
if (next && !(previous ?? false)) {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
vm.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 32, big: 30),
|
||||
vertical: SizeUtils.getByScreen(small: 30, big: 28),
|
||||
vertical: SizeUtils.getByScreen(small: 40, big: 38),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
),
|
||||
width: SizeUtils.getByScreen(small: 360, big: 350),
|
||||
child: Column(
|
||||
@@ -44,7 +62,7 @@ class LocateDeviceDialog extends ConsumerWidget {
|
||||
PrimaryButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
vm.resetError();
|
||||
vm.reset();
|
||||
},
|
||||
text: context.translate(I18n.ok),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'picture_entity.freezed.dart';
|
||||
@@ -6,12 +8,13 @@ part 'picture_entity.freezed.dart';
|
||||
abstract class PictureEntity with _$PictureEntity {
|
||||
const factory PictureEntity({
|
||||
required String id,
|
||||
required String deviceIdentificator,
|
||||
@Default('') String deviceIdentificator,
|
||||
String? imgType,
|
||||
String? timestamp,
|
||||
required String fileId,
|
||||
@Default('') String fileId,
|
||||
String? fileName,
|
||||
String? contentType,
|
||||
required int createdAt,
|
||||
@Default(0) int createdAt,
|
||||
Uint8List? fileBytes,
|
||||
}) = _PictureEntity;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$PictureEntity {
|
||||
|
||||
String get id; String get deviceIdentificator; String? get imgType; String? get timestamp; String get fileId; String? get fileName; String? get contentType; int get createdAt;
|
||||
String get id; String get deviceIdentificator; String? get imgType; String? get timestamp; String get fileId; String? get fileName; String? get contentType; int get createdAt; Uint8List? get fileBytes;
|
||||
/// 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.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));
|
||||
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)&&const DeepCollectionEquality().equals(other.fileBytes, fileBytes));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt,const DeepCollectionEquality().hash(fileBytes));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PictureEntity(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
|
||||
return 'PictureEntity(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt, fileBytes: $fileBytes)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $PictureEntityCopyWith<$Res> {
|
||||
factory $PictureEntityCopyWith(PictureEntity value, $Res Function(PictureEntity) _then) = _$PictureEntityCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt
|
||||
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Uint8List? fileBytes
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ 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? deviceIdentificator = null,Object? imgType = freezed,Object? timestamp = freezed,Object? fileId = null,Object? fileName = freezed,Object? contentType = freezed,Object? createdAt = 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,Object? fileBytes = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,deviceIdentificator: null == deviceIdentificator ? _self.deviceIdentificator : deviceIdentificator // ignore: cast_nullable_to_non_nullable
|
||||
@@ -72,7 +72,8 @@ as String?,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullab
|
||||
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,
|
||||
as int,fileBytes: freezed == fileBytes ? _self.fileBytes : fileBytes // ignore: cast_nullable_to_non_nullable
|
||||
as Uint8List?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -157,10 +158,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Uint8List? fileBytes)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PictureEntity() when $default != null:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.fileBytes);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -178,10 +179,10 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Uint8List? fileBytes) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PictureEntity():
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.fileBytes);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -198,10 +199,10 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Uint8List? fileBytes)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PictureEntity() when $default != null:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt);case _:
|
||||
return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp,_that.fileId,_that.fileName,_that.contentType,_that.createdAt,_that.fileBytes);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -213,17 +214,18 @@ return $default(_that.id,_that.deviceIdentificator,_that.imgType,_that.timestamp
|
||||
|
||||
|
||||
class _PictureEntity implements PictureEntity {
|
||||
const _PictureEntity({required this.id, required this.deviceIdentificator, this.imgType, this.timestamp, required this.fileId, this.fileName, this.contentType, required this.createdAt});
|
||||
const _PictureEntity({required this.id, this.deviceIdentificator = '', this.imgType, this.timestamp, this.fileId = '', this.fileName, this.contentType, this.createdAt = 0, this.fileBytes});
|
||||
|
||||
|
||||
@override final String id;
|
||||
@override final String deviceIdentificator;
|
||||
@override@JsonKey() final String deviceIdentificator;
|
||||
@override final String? imgType;
|
||||
@override final String? timestamp;
|
||||
@override final String fileId;
|
||||
@override@JsonKey() final String fileId;
|
||||
@override final String? fileName;
|
||||
@override final String? contentType;
|
||||
@override final int createdAt;
|
||||
@override@JsonKey() final int createdAt;
|
||||
@override final Uint8List? fileBytes;
|
||||
|
||||
/// Create a copy of PictureEntity
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -235,16 +237,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.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));
|
||||
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)&&const DeepCollectionEquality().equals(other.fileBytes, fileBytes));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt);
|
||||
int get hashCode => Object.hash(runtimeType,id,deviceIdentificator,imgType,timestamp,fileId,fileName,contentType,createdAt,const DeepCollectionEquality().hash(fileBytes));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PictureEntity(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt)';
|
||||
return 'PictureEntity(id: $id, deviceIdentificator: $deviceIdentificator, imgType: $imgType, timestamp: $timestamp, fileId: $fileId, fileName: $fileName, contentType: $contentType, createdAt: $createdAt, fileBytes: $fileBytes)';
|
||||
}
|
||||
|
||||
|
||||
@@ -255,7 +257,7 @@ abstract mixin class _$PictureEntityCopyWith<$Res> implements $PictureEntityCopy
|
||||
factory _$PictureEntityCopyWith(_PictureEntity value, $Res Function(_PictureEntity) _then) = __$PictureEntityCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt
|
||||
String id, String deviceIdentificator, String? imgType, String? timestamp, String fileId, String? fileName, String? contentType, int createdAt, Uint8List? fileBytes
|
||||
});
|
||||
|
||||
|
||||
@@ -272,7 +274,7 @@ 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? deviceIdentificator = null,Object? imgType = freezed,Object? timestamp = freezed,Object? fileId = null,Object? fileName = freezed,Object? contentType = freezed,Object? createdAt = 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,Object? fileBytes = freezed,}) {
|
||||
return _then(_PictureEntity(
|
||||
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
|
||||
@@ -282,7 +284,8 @@ as String?,fileId: null == fileId ? _self.fileId : fileId // ignore: cast_nullab
|
||||
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,
|
||||
as int,fileBytes: freezed == fileBytes ? _self.fileBytes : fileBytes // ignore: cast_nullable_to_non_nullable
|
||||
as Uint8List?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/widgets/show_picture_dialog.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
@@ -17,10 +19,33 @@ class RemoteCameraScreen extends ConsumerWidget {
|
||||
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);
|
||||
remoteConnectionViewModelProvider.select((s) => s.errorEvent),
|
||||
(_, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
RemoteConnectionErrorEvent.takePicture =>
|
||||
context.translate(I18n.errorTakePicture),
|
||||
RemoteConnectionErrorEvent.fetchPhotos =>
|
||||
context.translate(I18n.errorFetchPhotos),
|
||||
RemoteConnectionErrorEvent.call =>
|
||||
context.translate(I18n.errorCall),
|
||||
RemoteConnectionErrorEvent.invalidPhone =>
|
||||
context.translate(I18n.errorMessagePhoneIsInvalid),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
remoteConnectionViewModelProvider.select((s) => s.successEvent),
|
||||
(_, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
RemoteConnectionSuccessEvent.photoTaken =>
|
||||
context.translate(I18n.photoTaken),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.success);
|
||||
ref.read(remoteConnectionViewModelProvider.notifier).clearSuccess();
|
||||
}
|
||||
},
|
||||
@@ -28,20 +53,38 @@ class RemoteCameraScreen extends ConsumerWidget {
|
||||
|
||||
final theme = ref.watch(themePortProvider);
|
||||
|
||||
final isLoadingPictures = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s)=>s.isLoadingPictures)
|
||||
final isLoading = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isLoadingPictures),
|
||||
);
|
||||
final isTakingPicture = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s)=>s.isTakingPicture)
|
||||
final isTaking = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isTakingPicture),
|
||||
);
|
||||
final isWaiting = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isWaitingForPhoto),
|
||||
);
|
||||
final countdown = ref.watch(
|
||||
remoteConnectionViewModelProvider.select((s) => s.photoCountdown),
|
||||
);
|
||||
|
||||
Widget body;
|
||||
if (isLoading || isTaking) {
|
||||
body = const Center(child: CircularProgressIndicator());
|
||||
} else if (isWaiting) {
|
||||
body = _WaitingForPhotoOverlay(
|
||||
remainingSeconds: countdown,
|
||||
theme: theme,
|
||||
);
|
||||
} else {
|
||||
body = const _GallerySection();
|
||||
}
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.remoteCamera),
|
||||
body: isLoadingPictures || isTakingPicture
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: const _GallerySection(),
|
||||
footer: _TakePictureSection(),
|
||||
body: body,
|
||||
footer: isWaiting
|
||||
? const SizedBox.shrink()
|
||||
: const _TakePictureSection(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -80,19 +123,19 @@ class _GallerySection extends ConsumerWidget {
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
height: SizeUtils.getByScreen(small: 110, big: 105),
|
||||
width: SizeUtils.getByScreen(small: 60, big: 58),
|
||||
height: SizeUtils.getByScreen(small: 120, big: 115),
|
||||
width: SizeUtils.getByScreen(small: 80, big: 78),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.fromBorderSide(BorderSide(
|
||||
color: theme.getColorFor(ThemeCode.textTertiary)
|
||||
))
|
||||
),
|
||||
child: Image.network(
|
||||
pictures[index].fileId,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) =>
|
||||
const Icon(Icons.broken_image, color: Colors.grey),
|
||||
)
|
||||
child: pictures[index].fileBytes != null
|
||||
? Image.memory(
|
||||
pictures[index].fileBytes!,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: const Icon(Icons.broken_image, color: Colors.grey),
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -120,9 +163,57 @@ class _TakePictureSection extends ConsumerWidget {
|
||||
onPressed: vm.takePicture,
|
||||
text: context.translate(I18n.takePicture),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
height: SizeUtils.getByScreen(small: 36, big: 35),
|
||||
height: SizeUtils.getByScreen(small: 36, big: 38),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WaitingForPhotoOverlay extends StatelessWidget {
|
||||
static const String _animationAsset =
|
||||
'assets/shared/animations/shooting_photo.json';
|
||||
|
||||
final int remainingSeconds;
|
||||
final ThemePort theme;
|
||||
|
||||
const _WaitingForPhotoOverlay({
|
||||
required this.remainingSeconds,
|
||||
required this.theme,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Lottie.asset(
|
||||
_animationAsset,
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
context.translate(I18n.takingPhoto),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'${remainingSeconds}s',
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'dart:async';
|
||||
|
||||
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/presentation/state/remote_connection_view_state.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
import '../../../../core/domain/repositories/pictures_repository.dart';
|
||||
|
||||
@@ -18,8 +19,10 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
late final TextEditingController phoneController;
|
||||
late final CommandsRepository _commandsRepository;
|
||||
late final PicturesRepository _picturesRepository;
|
||||
Timer? _photoTimer;
|
||||
|
||||
static final RegExp _phoneRegex = RegExp(r'^\+?\d{6,15}$');
|
||||
static const int _photoWaitSeconds = 5;
|
||||
|
||||
@override
|
||||
RemoteConnectionViewState build() {
|
||||
@@ -60,12 +63,12 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
final text = phoneController.text;
|
||||
if (text == state.phone) return;
|
||||
|
||||
state = state.copyWith(phone: text, errorMessage: '');
|
||||
state = state.copyWith(phone: text, errorEvent: null);
|
||||
}
|
||||
|
||||
void updateDialCode(String value) {
|
||||
if (value == state.dialCode) return;
|
||||
state = state.copyWith(dialCode: value, errorMessage: '');
|
||||
state = state.copyWith(dialCode: value, errorEvent: null);
|
||||
}
|
||||
|
||||
void prevPicture() {
|
||||
@@ -95,18 +98,17 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
}
|
||||
|
||||
void clearSuccess() {
|
||||
state = state.copyWith(
|
||||
successMessage: '',
|
||||
);
|
||||
state = state.copyWith(successEvent: null);
|
||||
}
|
||||
|
||||
Future<void> takePicture() async {
|
||||
try {
|
||||
state = state.copyWith(
|
||||
isTakingPicture: true,
|
||||
successMessage: '',
|
||||
successEvent: null,
|
||||
errorEvent: null,
|
||||
);
|
||||
|
||||
|
||||
final request = SendCommandRequestModel(
|
||||
device: state.deviceId,
|
||||
command: DeviceCommand.requestPhoto,
|
||||
@@ -115,21 +117,59 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
await _commandsRepository.send(request: request);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
isWaitingForPhoto: true,
|
||||
photoCountdown: _photoWaitSeconds,
|
||||
);
|
||||
|
||||
_startPhotoCountdown();
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
errorEvent: RemoteConnectionErrorEvent.takePicture,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _startPhotoCountdown() {
|
||||
_photoTimer?.cancel();
|
||||
_photoTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (!ref.mounted) {
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
final remaining = state.photoCountdown - 1;
|
||||
if (remaining <= 0) {
|
||||
timer.cancel();
|
||||
_photoTimer = null;
|
||||
_fetchPhotosAfterCapture();
|
||||
} else {
|
||||
state = state.copyWith(photoCountdown: remaining);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _fetchPhotosAfterCapture() async {
|
||||
try {
|
||||
final pictures = await _picturesRepository.getPictures(
|
||||
deviceId: state.deviceId,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
isWaitingForPhoto: false,
|
||||
photoCountdown: 0,
|
||||
pictures: pictures,
|
||||
successMessage: I18n.photoTaken,
|
||||
successEvent: RemoteConnectionSuccessEvent.photoTaken,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isTakingPicture: false,
|
||||
errorMessage: e.toString(),
|
||||
isWaitingForPhoto: false,
|
||||
photoCountdown: 0,
|
||||
errorEvent: RemoteConnectionErrorEvent.fetchPhotos,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -137,12 +177,8 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
Future<void> call() async {
|
||||
final phone = phoneController.text;
|
||||
final dialCode = state.dialCode;
|
||||
if (phone.isEmpty){
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
|
||||
return;
|
||||
}
|
||||
if (!_phoneRegex.hasMatch(phone)) {
|
||||
state = state.copyWith(errorMessage: I18n.errorMessagePhoneIsInvalid);
|
||||
if (phone.isEmpty || !_phoneRegex.hasMatch(phone)) {
|
||||
state = state.copyWith(errorEvent: RemoteConnectionErrorEvent.invalidPhone);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,7 +199,7 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isCalling: false,
|
||||
errorMessage: e.toString(),
|
||||
errorEvent: RemoteConnectionErrorEvent.call,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ import 'package:device_management/src/features/remote_connection/domain/entities
|
||||
|
||||
part 'remote_connection_view_state.freezed.dart';
|
||||
|
||||
enum RemoteConnectionErrorEvent { takePicture, fetchPhotos, call, invalidPhone }
|
||||
enum RemoteConnectionSuccessEvent { photoTaken }
|
||||
|
||||
@freezed
|
||||
abstract class RemoteConnectionViewState with _$RemoteConnectionViewState {
|
||||
const factory RemoteConnectionViewState({
|
||||
@@ -13,8 +16,10 @@ abstract class RemoteConnectionViewState with _$RemoteConnectionViewState {
|
||||
@Default(0) int pictureIndex,
|
||||
@Default(true) bool isLoadingPictures,
|
||||
@Default(false) bool isTakingPicture,
|
||||
@Default(false) bool isWaitingForPhoto,
|
||||
@Default(0) int photoCountdown,
|
||||
@Default(false) bool isCalling,
|
||||
@Default('') String errorMessage,
|
||||
@Default('') String successMessage
|
||||
RemoteConnectionErrorEvent? errorEvent,
|
||||
RemoteConnectionSuccessEvent? successEvent,
|
||||
}) = _RemoteConnectionViewState;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$RemoteConnectionViewState {
|
||||
|
||||
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;
|
||||
String get deviceId; String get dialCode; String get phone; List<PictureEntity> get pictures; int get pictureIndex; bool get isLoadingPictures; bool get isTakingPicture; bool get isWaitingForPhoto; int get photoCountdown; bool get isCalling; RemoteConnectionErrorEvent? get errorEvent; RemoteConnectionSuccessEvent? get successEvent;
|
||||
/// 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.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));
|
||||
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.isWaitingForPhoto, isWaitingForPhoto) || other.isWaitingForPhoto == isWaitingForPhoto)&&(identical(other.photoCountdown, photoCountdown) || other.photoCountdown == photoCountdown)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage,successMessage);
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(pictures),pictureIndex,isLoadingPictures,isTakingPicture,isWaitingForPhoto,photoCountdown,isCalling,errorEvent,successEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage, successMessage: $successMessage)';
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isWaitingForPhoto: $isWaitingForPhoto, photoCountdown: $photoCountdown, isCalling: $isCalling, errorEvent: $errorEvent, successEvent: $successEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $RemoteConnectionViewStateCopyWith<$Res> {
|
||||
factory $RemoteConnectionViewStateCopyWith(RemoteConnectionViewState value, $Res Function(RemoteConnectionViewState) _then) = _$RemoteConnectionViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ 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? 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,}) {
|
||||
@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? isWaitingForPhoto = null,Object? photoCountdown = null,Object? isCalling = null,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
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
|
||||
@@ -71,10 +71,12 @@ as String,pictures: null == pictures ? _self.pictures : pictures // ignore: cast
|
||||
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,
|
||||
as bool,isWaitingForPhoto: null == isWaitingForPhoto ? _self.isWaitingForPhoto : isWaitingForPhoto // ignore: cast_nullable_to_non_nullable
|
||||
as bool,photoCountdown: null == photoCountdown ? _self.photoCountdown : photoCountdown // ignore: cast_nullable_to_non_nullable
|
||||
as int,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionErrorEvent?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionSuccessEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -159,10 +161,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteConnectionViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isWaitingForPhoto,_that.photoCountdown,_that.isCalling,_that.errorEvent,_that.successEvent);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -180,10 +182,10 @@ return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.p
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteConnectionViewState():
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isWaitingForPhoto,_that.photoCountdown,_that.isCalling,_that.errorEvent,_that.successEvent);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -200,10 +202,10 @@ return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.p
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@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;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _RemoteConnectionViewState() when $default != null:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage,_that.successMessage);case _:
|
||||
return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isWaitingForPhoto,_that.photoCountdown,_that.isCalling,_that.errorEvent,_that.successEvent);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -215,7 +217,7 @@ return $default(_that.deviceId,_that.dialCode,_that.phone,_that.pictures,_that.p
|
||||
|
||||
|
||||
class _RemoteConnectionViewState implements RemoteConnectionViewState {
|
||||
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;
|
||||
const _RemoteConnectionViewState({this.deviceId = '', this.dialCode = '+34', this.phone = '', final List<PictureEntity> pictures = const [], this.pictureIndex = 0, this.isLoadingPictures = true, this.isTakingPicture = false, this.isWaitingForPhoto = false, this.photoCountdown = 0, this.isCalling = false, this.errorEvent, this.successEvent}): _pictures = pictures;
|
||||
|
||||
|
||||
@override@JsonKey() final String deviceId;
|
||||
@@ -231,9 +233,11 @@ class _RemoteConnectionViewState implements RemoteConnectionViewState {
|
||||
@override@JsonKey() final int pictureIndex;
|
||||
@override@JsonKey() final bool isLoadingPictures;
|
||||
@override@JsonKey() final bool isTakingPicture;
|
||||
@override@JsonKey() final bool isWaitingForPhoto;
|
||||
@override@JsonKey() final int photoCountdown;
|
||||
@override@JsonKey() final bool isCalling;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override@JsonKey() final String successMessage;
|
||||
@override final RemoteConnectionErrorEvent? errorEvent;
|
||||
@override final RemoteConnectionSuccessEvent? successEvent;
|
||||
|
||||
/// Create a copy of RemoteConnectionViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -245,16 +249,16 @@ _$RemoteConnectionViewStateCopyWith<_RemoteConnectionViewState> get copyWith =>
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
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));
|
||||
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.isWaitingForPhoto, isWaitingForPhoto) || other.isWaitingForPhoto == isWaitingForPhoto)&&(identical(other.photoCountdown, photoCountdown) || other.photoCountdown == photoCountdown)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(_pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage,successMessage);
|
||||
int get hashCode => Object.hash(runtimeType,deviceId,dialCode,phone,const DeepCollectionEquality().hash(_pictures),pictureIndex,isLoadingPictures,isTakingPicture,isWaitingForPhoto,photoCountdown,isCalling,errorEvent,successEvent);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage, successMessage: $successMessage)';
|
||||
return 'RemoteConnectionViewState(deviceId: $deviceId, dialCode: $dialCode, phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isWaitingForPhoto: $isWaitingForPhoto, photoCountdown: $photoCountdown, isCalling: $isCalling, errorEvent: $errorEvent, successEvent: $successEvent)';
|
||||
}
|
||||
|
||||
|
||||
@@ -265,7 +269,7 @@ abstract mixin class _$RemoteConnectionViewStateCopyWith<$Res> implements $Remot
|
||||
factory _$RemoteConnectionViewStateCopyWith(_RemoteConnectionViewState value, $Res Function(_RemoteConnectionViewState) _then) = __$RemoteConnectionViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage, String successMessage
|
||||
String deviceId, String dialCode, String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isWaitingForPhoto, int photoCountdown, bool isCalling, RemoteConnectionErrorEvent? errorEvent, RemoteConnectionSuccessEvent? successEvent
|
||||
});
|
||||
|
||||
|
||||
@@ -282,7 +286,7 @@ 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? 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,}) {
|
||||
@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? isWaitingForPhoto = null,Object? photoCountdown = null,Object? isCalling = null,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
||||
return _then(_RemoteConnectionViewState(
|
||||
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
|
||||
@@ -291,10 +295,12 @@ as String,pictures: null == pictures ? _self._pictures : pictures // ignore: cas
|
||||
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,
|
||||
as bool,isWaitingForPhoto: null == isWaitingForPhoto ? _self.isWaitingForPhoto : isWaitingForPhoto // ignore: cast_nullable_to_non_nullable
|
||||
as bool,photoCountdown: null == photoCountdown ? _self.photoCountdown : photoCountdown // ignore: cast_nullable_to_non_nullable
|
||||
as int,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionErrorEvent?,successEvent: freezed == successEvent ? _self.successEvent : successEvent // ignore: cast_nullable_to_non_nullable
|
||||
as RemoteConnectionSuccessEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,11 +41,12 @@ class ShowPictureDialog extends ConsumerWidget {
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: SizeUtils.getByScreen(small: 6, big: 4)),
|
||||
height: SizeUtils.getByScreen(small: 350, big: 340),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _PictureSection(fileId: picture.fileId),
|
||||
child: _PictureSection(picture: picture),
|
||||
),
|
||||
_MetadataSection(picture: picture),
|
||||
_ControlsSection(
|
||||
@@ -59,24 +60,22 @@ class ShowPictureDialog extends ConsumerWidget {
|
||||
}
|
||||
|
||||
class _PictureSection extends StatelessWidget {
|
||||
final String fileId;
|
||||
final PictureEntity picture;
|
||||
|
||||
const _PictureSection({required this.fileId});
|
||||
const _PictureSection({required this.picture});
|
||||
|
||||
@override
|
||||
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);
|
||||
},
|
||||
),
|
||||
if (picture.fileBytes != null) {
|
||||
return Center(
|
||||
child: Image.memory(
|
||||
picture.fileBytes!,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
);
|
||||
}
|
||||
return const Center(
|
||||
child: Icon(Icons.broken_image, size: 64, color: Colors.grey),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
|
||||
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
@@ -13,35 +14,49 @@ class SpyCallDialog extends ConsumerWidget {
|
||||
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);
|
||||
ref.listen(
|
||||
remoteConnectionViewModelProvider.select((s) => s.errorEvent),
|
||||
(_, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
RemoteConnectionErrorEvent.invalidPhone =>
|
||||
context.translate(I18n.errorMessagePhoneIsInvalid),
|
||||
RemoteConnectionErrorEvent.call =>
|
||||
context.translate(I18n.errorCall),
|
||||
RemoteConnectionErrorEvent.takePicture =>
|
||||
context.translate(I18n.errorTakePicture),
|
||||
RemoteConnectionErrorEvent.fetchPhotos =>
|
||||
context.translate(I18n.errorFetchPhotos),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
remoteConnectionViewModelProvider.select((s) => s.isCalling),
|
||||
(prev, isCalling) {
|
||||
if (prev == true && !isCalling) {
|
||||
final error = ref.read(remoteConnectionViewModelProvider).errorEvent;
|
||||
if (error == null && context.mounted) {
|
||||
Navigator.pop(context);
|
||||
showTopSnackbar(context,
|
||||
message: context.translate(I18n.remoteListening),
|
||||
type: MessageType.success);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: const EdgeInsets.symmetric(horizontal: 26, vertical: 20),
|
||||
big: const EdgeInsets.symmetric(horizontal: 24, vertical: 18)),
|
||||
small: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
big: const EdgeInsets.symmetric(horizontal: 14, vertical: 18),
|
||||
xl: const EdgeInsets.symmetric(horizontal: 12, vertical: 17)),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
color: theme.getColorFor(ThemeCode.backgroundSecondary)),
|
||||
width: SizeUtils.getByScreen(small: 390, big: 380),
|
||||
width: SizeUtils.getByScreen(small: 390, big: 380, xl: 390),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
||||
@@ -41,23 +41,28 @@ class RewardsScreen extends ConsumerWidget {
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.rewards),
|
||||
body: Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Icon(
|
||||
SFIcons.rewardsCircle,
|
||||
size: SizeUtils.getByScreen(small: 180, big: 160),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 20, big: 18, xl: 17)
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Icon(
|
||||
SFIcons.rewardsCircle,
|
||||
size: SizeUtils.getByScreen(small: 180, big: 160),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 32, big: 28)),
|
||||
Text(
|
||||
context.translate(I18n.rewardsMessage),
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
|
||||
_CounterSection(theme: theme),
|
||||
],
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 32, big: 28)),
|
||||
Text(
|
||||
context.translate(I18n.rewardsMessage),
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
|
||||
_CounterSection(theme: theme),
|
||||
],
|
||||
),
|
||||
),
|
||||
footer: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
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>());
|
||||
});
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
|
||||
import '../../data/device_update_datasource.dart';
|
||||
import '../../data/device_update_datasource_provider.dart';
|
||||
import 'volume_control_view_state.dart';
|
||||
|
||||
final volumeControlViewModelProvider =
|
||||
@@ -11,11 +10,11 @@ final volumeControlViewModelProvider =
|
||||
);
|
||||
|
||||
class VolumeControlViewModel extends Notifier<VolumeControlViewState> {
|
||||
late final DeviceUpdateDatasource _datasource;
|
||||
late final DeviceSettingsUpdateDatasource _datasource;
|
||||
|
||||
@override
|
||||
VolumeControlViewState build() {
|
||||
_datasource = ref.read(deviceUpdateDatasourceProvider);
|
||||
_datasource = ref.read(deviceSettingsUpdateProvider);
|
||||
Future.microtask(() => _load());
|
||||
return const VolumeControlViewState();
|
||||
}
|
||||
@@ -25,14 +24,14 @@ class VolumeControlViewModel extends Notifier<VolumeControlViewState> {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
final volume = device.settings['volume'] as Map<String, dynamic>? ?? {};
|
||||
final volume = device.settings.volume;
|
||||
|
||||
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,
|
||||
media: volume.media,
|
||||
ringtone: volume.ringtone,
|
||||
alarm: volume.alarm,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
@@ -51,19 +50,21 @@ class VolumeControlViewModel extends Notifier<VolumeControlViewState> {
|
||||
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,
|
||||
};
|
||||
final updatedSettings = device.settings.copyWith(
|
||||
volume: DeviceVolumeEntity(
|
||||
media: state.media,
|
||||
ringtone: state.ringtone,
|
||||
alarm: state.alarm,
|
||||
),
|
||||
);
|
||||
|
||||
await _datasource.updateDeviceSettings(
|
||||
device: device,
|
||||
updatedSettings: settings,
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isLoading: false, isComplete: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
|
||||
@@ -746,7 +746,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lottie:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: lottie
|
||||
sha256: "8ae0be46dbd9e19641791dc12ee480d34e1fd3f84c749adc05f3ad9342b71b95"
|
||||
|
||||
@@ -56,6 +56,7 @@ dependencies:
|
||||
uuid: ^4.5.1
|
||||
flutter_contacts: ^1.1.9+2
|
||||
fl_chart: ^1.1.1
|
||||
lottie: ^3.3.1
|
||||
image_picker: ^1.2.1
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
@@ -76,12 +77,6 @@ flutter:
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add Flutter specific assets to your application, add an assets section,
|
||||
# like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class LegacyIntroStepScreen extends ConsumerWidget {
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 8, big: 2)
|
||||
horizontal: SizeUtils.getByScreen(small: 8, big: 6)
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:location/src/features/location/presentation/state/location_view_model.dart';
|
||||
import 'package:location/src/features/location/presentation/state/location_view_state.dart';
|
||||
import 'package:location/src/features/location/presentation/widgets/location_map.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
|
||||
@@ -16,6 +17,55 @@ class LocationScreen extends ConsumerWidget {
|
||||
final controlPanelState = ref.watch(controlPanelViewModelProvider);
|
||||
final locationState = ref.watch(locationViewModelProvider);
|
||||
|
||||
ref.listen(
|
||||
locationViewModelProvider.select((s) => s.errorEvent),
|
||||
(previous, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
LocationErrorEvent.geofenceCreate => context.translate(I18n.errorGeofenceCreate),
|
||||
LocationErrorEvent.geofenceUpdate => context.translate(I18n.errorGeofenceUpdate),
|
||||
LocationErrorEvent.geofenceDelete => context.translate(I18n.errorGeofenceDelete),
|
||||
LocationErrorEvent.frequentPlaceCreate => context.translate(I18n.errorFrequentPlaceCreate),
|
||||
LocationErrorEvent.frequentPlaceUpdate => context.translate(I18n.errorFrequentPlaceUpdate),
|
||||
LocationErrorEvent.frequentPlaceDelete => context.translate(I18n.errorFrequentPlaceDelete),
|
||||
LocationErrorEvent.positionHistory => context.translate(I18n.errorPositionHistory),
|
||||
LocationErrorEvent.locationFrequency => context.translate(I18n.errorLocationFrequency),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
controlPanelViewModelProvider.select((s) => s.errorMessage),
|
||||
(previous, next) {
|
||||
if (next.isNotEmpty) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.errorGeneric),
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
locationViewModelProvider.select((s) => s.successMessage),
|
||||
(previous, next) {
|
||||
if (next != null) {
|
||||
final message = switch (next) {
|
||||
LocationSuccessEvent.geofenceCreated => context.translate(I18n.geofenceCreated),
|
||||
LocationSuccessEvent.geofenceUpdated => context.translate(I18n.geofenceUpdated),
|
||||
LocationSuccessEvent.geofenceDeleted => context.translate(I18n.geofenceDeleted),
|
||||
LocationSuccessEvent.frequentPlaceCreated => context.translate(I18n.frequentPlaceCreated),
|
||||
LocationSuccessEvent.frequentPlaceUpdated => context.translate(I18n.frequentPlaceUpdated),
|
||||
LocationSuccessEvent.frequentPlaceDeleted => context.translate(I18n.frequentPlaceDeleted),
|
||||
};
|
||||
showTopSnackbar(context, message: message, type: MessageType.success);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (controlPanelState.isLoading) {
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
|
||||
@@ -119,6 +119,14 @@ class LocationMapViewModel extends Notifier<LocationMapViewState> {
|
||||
state = state.copyWith(actionsExpanded: !state.actionsExpanded);
|
||||
}
|
||||
|
||||
void toggleFrequencyExpanded() {
|
||||
state = state.copyWith(frequencyExpanded: !state.frequencyExpanded);
|
||||
}
|
||||
|
||||
void collapseFrequency() {
|
||||
state = state.copyWith(frequencyExpanded: false);
|
||||
}
|
||||
|
||||
void updateMapZoom(double zoom) {
|
||||
state = state.copyWith(mapZoom: zoom);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ abstract class LocationMapViewState with _$LocationMapViewState {
|
||||
PositionEntity? selectedHistoryPosition,
|
||||
@Default(false) bool isFollowing,
|
||||
@Default(false) bool actionsExpanded,
|
||||
@Default(false) bool frequencyExpanded,
|
||||
@Default(_defaultZoom) double mapZoom,
|
||||
}) = _LocationMapViewState;
|
||||
}
|
||||
|
||||
@@ -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 isFollowing; bool get actionsExpanded; double get mapZoom;
|
||||
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; bool get frequencyExpanded; 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)&&(identical(other.isFollowing, isFollowing) || other.isFollowing == isFollowing)&&(identical(other.actionsExpanded, actionsExpanded) || other.actionsExpanded == actionsExpanded)&&(identical(other.mapZoom, mapZoom) || other.mapZoom == mapZoom));
|
||||
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.frequencyExpanded, frequencyExpanded) || other.frequencyExpanded == frequencyExpanded)&&(identical(other.mapZoom, mapZoom) || other.mapZoom == mapZoom));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition,isFollowing,actionsExpanded,mapZoom);
|
||||
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition,isFollowing,actionsExpanded,frequencyExpanded,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, isFollowing: $isFollowing, actionsExpanded: $actionsExpanded, mapZoom: $mapZoom)';
|
||||
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, frequencyExpanded: $frequencyExpanded, 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 isFollowing, bool actionsExpanded, double mapZoom
|
||||
bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition, bool isFollowing, bool actionsExpanded, bool frequencyExpanded, 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,Object? isFollowing = null,Object? actionsExpanded = null,Object? mapZoom = null,}) {
|
||||
@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? frequencyExpanded = 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
|
||||
@@ -76,6 +76,7 @@ as GeofenceEntity?,selectedFrequentPlace: freezed == selectedFrequentPlace ? _se
|
||||
as FrequentPlaceEntity?,selectedHistoryPosition: freezed == selectedHistoryPosition ? _self.selectedHistoryPosition : selectedHistoryPosition // ignore: cast_nullable_to_non_nullable
|
||||
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,frequencyExpanded: null == frequencyExpanded ? _self.frequencyExpanded : frequencyExpanded // ignore: cast_nullable_to_non_nullable
|
||||
as bool,mapZoom: null == mapZoom ? _self.mapZoom : mapZoom // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
));
|
||||
@@ -210,10 +211,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, bool isFollowing, bool actionsExpanded, double mapZoom)? $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, bool frequencyExpanded, 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,_that.isFollowing,_that.actionsExpanded,_that.mapZoom);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.frequencyExpanded,_that.mapZoom);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -231,10 +232,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, bool isFollowing, bool actionsExpanded, double mapZoom) $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, bool frequencyExpanded, 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,_that.isFollowing,_that.actionsExpanded,_that.mapZoom);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.frequencyExpanded,_that.mapZoom);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -251,10 +252,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, bool isFollowing, bool actionsExpanded, double mapZoom)? $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, bool frequencyExpanded, 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,_that.isFollowing,_that.actionsExpanded,_that.mapZoom);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.frequencyExpanded,_that.mapZoom);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -266,7 +267,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, this.isFollowing = false, this.actionsExpanded = false, this.mapZoom = _defaultZoom});
|
||||
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.frequencyExpanded = false, this.mapZoom = _defaultZoom});
|
||||
|
||||
|
||||
@override@JsonKey() final bool showGeofences;
|
||||
@@ -281,6 +282,7 @@ class _LocationMapViewState implements LocationMapViewState {
|
||||
@override final PositionEntity? selectedHistoryPosition;
|
||||
@override@JsonKey() final bool isFollowing;
|
||||
@override@JsonKey() final bool actionsExpanded;
|
||||
@override@JsonKey() final bool frequencyExpanded;
|
||||
@override@JsonKey() final double mapZoom;
|
||||
|
||||
/// Create a copy of LocationMapViewState
|
||||
@@ -293,16 +295,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)&&(identical(other.isFollowing, isFollowing) || other.isFollowing == isFollowing)&&(identical(other.actionsExpanded, actionsExpanded) || other.actionsExpanded == actionsExpanded)&&(identical(other.mapZoom, mapZoom) || other.mapZoom == mapZoom));
|
||||
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.frequencyExpanded, frequencyExpanded) || other.frequencyExpanded == frequencyExpanded)&&(identical(other.mapZoom, mapZoom) || other.mapZoom == mapZoom));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition,isFollowing,actionsExpanded,mapZoom);
|
||||
int get hashCode => Object.hash(runtimeType,showGeofences,showFrequentPlaces,placingMode,adjustingRadius,previewRadius,previewPoint,selectedGeofence,editingGeofence,selectedFrequentPlace,selectedHistoryPosition,isFollowing,actionsExpanded,frequencyExpanded,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, isFollowing: $isFollowing, actionsExpanded: $actionsExpanded, mapZoom: $mapZoom)';
|
||||
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, frequencyExpanded: $frequencyExpanded, mapZoom: $mapZoom)';
|
||||
}
|
||||
|
||||
|
||||
@@ -313,7 +315,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 isFollowing, bool actionsExpanded, double mapZoom
|
||||
bool showGeofences, bool showFrequentPlaces, PlacingMode placingMode, bool adjustingRadius, double previewRadius, LatLng? previewPoint, GeofenceEntity? selectedGeofence, GeofenceEntity? editingGeofence, FrequentPlaceEntity? selectedFrequentPlace, PositionEntity? selectedHistoryPosition, bool isFollowing, bool actionsExpanded, bool frequencyExpanded, double mapZoom
|
||||
});
|
||||
|
||||
|
||||
@@ -330,7 +332,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,Object? isFollowing = null,Object? actionsExpanded = null,Object? mapZoom = null,}) {
|
||||
@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? frequencyExpanded = 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
|
||||
@@ -344,6 +346,7 @@ as GeofenceEntity?,selectedFrequentPlace: freezed == selectedFrequentPlace ? _se
|
||||
as FrequentPlaceEntity?,selectedHistoryPosition: freezed == selectedHistoryPosition ? _self.selectedHistoryPosition : selectedHistoryPosition // ignore: cast_nullable_to_non_nullable
|
||||
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,frequencyExpanded: null == frequencyExpanded ? _self.frequencyExpanded : frequencyExpanded // ignore: cast_nullable_to_non_nullable
|
||||
as bool,mapZoom: null == mapZoom ? _self.mapZoom : mapZoom // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
));
|
||||
|
||||
@@ -24,7 +24,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
@override
|
||||
LocationViewState build() {
|
||||
_locationRepository = ref.read(locationRepositoryProvider);
|
||||
final device = ref.watch(selectedDeviceProvider);
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device != null) {
|
||||
_fetchData(device.id);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(isLoading: false, errorMessage: _formatError(e));
|
||||
state = state.copyWith(isLoading: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
required double longitude,
|
||||
required double radius,
|
||||
}) async {
|
||||
state = state.copyWith(isSubmitting: true, errorMessage: '');
|
||||
state = state.copyWith(isSubmitting: true, errorEvent: null, successMessage: null);
|
||||
try {
|
||||
final user = await ref.read(userInfoProvider.future);
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
@@ -78,10 +78,11 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
state = state.copyWith(
|
||||
geofences: [...state.geofences, created],
|
||||
isSubmitting: false,
|
||||
successMessage: LocationSuccessEvent.geofenceCreated,
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return _handleError(e);
|
||||
return _handleErrorEvent(LocationErrorEvent.geofenceCreate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +94,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
required double longitude,
|
||||
required double radius,
|
||||
}) async {
|
||||
state = state.copyWith(isSubmitting: true, errorMessage: '');
|
||||
state = state.copyWith(isSubmitting: true, errorEvent: null, successMessage: null);
|
||||
try {
|
||||
final request = UpdateGeofenceRequestModel(
|
||||
id: id,
|
||||
@@ -110,24 +111,26 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
geofences:
|
||||
state.geofences.map((g) => g.id == id ? updated : g).toList(),
|
||||
isSubmitting: false,
|
||||
successMessage: LocationSuccessEvent.geofenceUpdated,
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return _handleError(e);
|
||||
return _handleErrorEvent(LocationErrorEvent.geofenceUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteGeofence({required String id}) async {
|
||||
state = state.copyWith(errorMessage: '');
|
||||
state = state.copyWith(errorEvent: null, successMessage: null);
|
||||
try {
|
||||
await _locationRepository.deleteGeofence(geofenceId: id);
|
||||
if (!ref.mounted) return false;
|
||||
state = state.copyWith(
|
||||
geofences: state.geofences.where((g) => g.id != id).toList(),
|
||||
successMessage: LocationSuccessEvent.geofenceDeleted,
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return _handleError(e);
|
||||
return _handleErrorEvent(LocationErrorEvent.geofenceDelete);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +140,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
required double lng,
|
||||
List<WifiInfoEntity> wifiList = const [],
|
||||
}) async {
|
||||
state = state.copyWith(isSubmitting: true, errorMessage: '');
|
||||
state = state.copyWith(isSubmitting: true, errorEvent: null, successMessage: null);
|
||||
try {
|
||||
final user = await ref.read(userInfoProvider.future);
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
@@ -164,10 +167,11 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
state = state.copyWith(
|
||||
frequentPlaces: [...state.frequentPlaces, created],
|
||||
isSubmitting: false,
|
||||
successMessage: LocationSuccessEvent.frequentPlaceCreated,
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return _handleError(e);
|
||||
return _handleErrorEvent(LocationErrorEvent.frequentPlaceCreate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +182,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
required double lng,
|
||||
List<WifiInfoEntity> wifiList = const [],
|
||||
}) async {
|
||||
state = state.copyWith(isSubmitting: true, errorMessage: '');
|
||||
state = state.copyWith(isSubmitting: true, errorEvent: null, successMessage: null);
|
||||
try {
|
||||
final request = UpdateFrequentPlaceRequestModel(
|
||||
id: id,
|
||||
@@ -200,25 +204,27 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
frequentPlaces:
|
||||
state.frequentPlaces.map((f) => f.id == id ? updated : f).toList(),
|
||||
isSubmitting: false,
|
||||
successMessage: LocationSuccessEvent.frequentPlaceUpdated,
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return _handleError(e);
|
||||
return _handleErrorEvent(LocationErrorEvent.frequentPlaceUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteFrequentPlace({required String id}) async {
|
||||
state = state.copyWith(errorMessage: '');
|
||||
state = state.copyWith(errorEvent: null, successMessage: null);
|
||||
try {
|
||||
await _locationRepository.deleteFrequentPlace(frequentPlaceId: id);
|
||||
if (!ref.mounted) return false;
|
||||
state = state.copyWith(
|
||||
frequentPlaces:
|
||||
state.frequentPlaces.where((f) => f.id != id).toList(),
|
||||
successMessage: LocationSuccessEvent.frequentPlaceDeleted,
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return _handleError(e);
|
||||
return _handleErrorEvent(LocationErrorEvent.frequentPlaceDelete);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +235,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(isLoadingHistory: true, errorMessage: '');
|
||||
state = state.copyWith(isLoadingHistory: true, errorEvent: null);
|
||||
try {
|
||||
final positions = await _locationRepository.getPositionHistory(
|
||||
deviceIdentificator: device.identificator,
|
||||
@@ -246,7 +252,7 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isLoadingHistory: false,
|
||||
errorMessage: _formatError(e),
|
||||
errorEvent: LocationErrorEvent.positionHistory,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -262,17 +268,34 @@ class LocationViewModel extends Notifier<LocationViewState> {
|
||||
state = state.copyWith(showRouteTrail: !state.showRouteTrail);
|
||||
}
|
||||
|
||||
bool _handleError(Object e) {
|
||||
Future<bool> updateLocationFrequency({required int frequency}) async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return false;
|
||||
|
||||
state = state.copyWith(isSubmitting: true, errorEvent: null);
|
||||
|
||||
try {
|
||||
final updatedSettings = device.settings.copyWith(frequency: frequency);
|
||||
await ref.read(deviceSettingsUpdateProvider).updateDeviceSettings(
|
||||
device: device,
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
|
||||
if (!ref.mounted) return false;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isSubmitting: false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return _handleErrorEvent(LocationErrorEvent.locationFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
bool _handleErrorEvent(LocationErrorEvent event) {
|
||||
if (!ref.mounted) return false;
|
||||
state = state.copyWith(
|
||||
isSubmitting: false,
|
||||
errorMessage: _formatError(e),
|
||||
errorEvent: event,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
String _formatError(Object e) {
|
||||
final msg = e.toString();
|
||||
return msg.startsWith('Exception: ') ? msg.substring(11) : msg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,26 @@ import 'package:location/src/core/domain/entities/frequent_place_entity.dart';
|
||||
|
||||
part 'location_view_state.freezed.dart';
|
||||
|
||||
enum LocationSuccessEvent {
|
||||
geofenceCreated,
|
||||
geofenceUpdated,
|
||||
geofenceDeleted,
|
||||
frequentPlaceCreated,
|
||||
frequentPlaceUpdated,
|
||||
frequentPlaceDeleted,
|
||||
}
|
||||
|
||||
enum LocationErrorEvent {
|
||||
geofenceCreate,
|
||||
geofenceUpdate,
|
||||
geofenceDelete,
|
||||
frequentPlaceCreate,
|
||||
frequentPlaceUpdate,
|
||||
frequentPlaceDelete,
|
||||
positionHistory,
|
||||
locationFrequency,
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class LocationViewState with _$LocationViewState {
|
||||
const factory LocationViewState({
|
||||
@@ -15,6 +35,7 @@ abstract class LocationViewState with _$LocationViewState {
|
||||
@Default(false) bool isLoadingHistory,
|
||||
@Default(false) bool isSubmitting,
|
||||
@Default(false) bool showRouteTrail,
|
||||
@Default('') String errorMessage,
|
||||
LocationErrorEvent? errorEvent,
|
||||
LocationSuccessEvent? successMessage,
|
||||
}) = _LocationViewState;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$LocationViewState {
|
||||
|
||||
List<GeofenceEntity> get geofences; List<FrequentPlaceEntity> get frequentPlaces; List<PositionEntity> get positionHistory; bool get isLoading; bool get isLoadingHistory; bool get isSubmitting; bool get showRouteTrail; String get errorMessage;
|
||||
List<GeofenceEntity> get geofences; List<FrequentPlaceEntity> get frequentPlaces; List<PositionEntity> get positionHistory; bool get isLoading; bool get isLoadingHistory; bool get isSubmitting; bool get showRouteTrail; LocationErrorEvent? get errorEvent; LocationSuccessEvent? get successMessage;
|
||||
/// Create a copy of LocationViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -25,16 +25,16 @@ $LocationViewStateCopyWith<LocationViewState> get copyWith => _$LocationViewStat
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocationViewState&&const DeepCollectionEquality().equals(other.geofences, geofences)&&const DeepCollectionEquality().equals(other.frequentPlaces, frequentPlaces)&&const DeepCollectionEquality().equals(other.positionHistory, positionHistory)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingHistory, isLoadingHistory) || other.isLoadingHistory == isLoadingHistory)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.showRouteTrail, showRouteTrail) || other.showRouteTrail == showRouteTrail)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is LocationViewState&&const DeepCollectionEquality().equals(other.geofences, geofences)&&const DeepCollectionEquality().equals(other.frequentPlaces, frequentPlaces)&&const DeepCollectionEquality().equals(other.positionHistory, positionHistory)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingHistory, isLoadingHistory) || other.isLoadingHistory == isLoadingHistory)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.showRouteTrail, showRouteTrail) || other.showRouteTrail == showRouteTrail)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(geofences),const DeepCollectionEquality().hash(frequentPlaces),const DeepCollectionEquality().hash(positionHistory),isLoading,isLoadingHistory,isSubmitting,showRouteTrail,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(geofences),const DeepCollectionEquality().hash(frequentPlaces),const DeepCollectionEquality().hash(positionHistory),isLoading,isLoadingHistory,isSubmitting,showRouteTrail,errorEvent,successMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LocationViewState(geofences: $geofences, frequentPlaces: $frequentPlaces, positionHistory: $positionHistory, isLoading: $isLoading, isLoadingHistory: $isLoadingHistory, isSubmitting: $isSubmitting, showRouteTrail: $showRouteTrail, errorMessage: $errorMessage)';
|
||||
return 'LocationViewState(geofences: $geofences, frequentPlaces: $frequentPlaces, positionHistory: $positionHistory, isLoading: $isLoading, isLoadingHistory: $isLoadingHistory, isSubmitting: $isSubmitting, showRouteTrail: $showRouteTrail, errorEvent: $errorEvent, successMessage: $successMessage)';
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ abstract mixin class $LocationViewStateCopyWith<$Res> {
|
||||
factory $LocationViewStateCopyWith(LocationViewState value, $Res Function(LocationViewState) _then) = _$LocationViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, String errorMessage
|
||||
List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, LocationErrorEvent? errorEvent, LocationSuccessEvent? successMessage
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class _$LocationViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of LocationViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? geofences = null,Object? frequentPlaces = null,Object? positionHistory = null,Object? isLoading = null,Object? isLoadingHistory = null,Object? isSubmitting = null,Object? showRouteTrail = null,Object? errorMessage = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? geofences = null,Object? frequentPlaces = null,Object? positionHistory = null,Object? isLoading = null,Object? isLoadingHistory = null,Object? isSubmitting = null,Object? showRouteTrail = null,Object? errorEvent = freezed,Object? successMessage = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
geofences: null == geofences ? _self.geofences : geofences // ignore: cast_nullable_to_non_nullable
|
||||
as List<GeofenceEntity>,frequentPlaces: null == frequentPlaces ? _self.frequentPlaces : frequentPlaces // ignore: cast_nullable_to_non_nullable
|
||||
@@ -71,8 +71,9 @@ as List<PositionEntity>,isLoading: null == isLoading ? _self.isLoading : isLoadi
|
||||
as bool,isLoadingHistory: null == isLoadingHistory ? _self.isLoadingHistory : isLoadingHistory // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||
as bool,showRouteTrail: null == showRouteTrail ? _self.showRouteTrail : showRouteTrail // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as LocationErrorEvent?,successMessage: freezed == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as LocationSuccessEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -157,10 +158,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, LocationErrorEvent? errorEvent, LocationSuccessEvent? successMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _LocationViewState() when $default != null:
|
||||
return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that.isLoading,_that.isLoadingHistory,_that.isSubmitting,_that.showRouteTrail,_that.errorMessage);case _:
|
||||
return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that.isLoading,_that.isLoadingHistory,_that.isSubmitting,_that.showRouteTrail,_that.errorEvent,_that.successMessage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@@ -178,10 +179,10 @@ return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, String errorMessage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, LocationErrorEvent? errorEvent, LocationSuccessEvent? successMessage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _LocationViewState():
|
||||
return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that.isLoading,_that.isLoadingHistory,_that.isSubmitting,_that.showRouteTrail,_that.errorMessage);case _:
|
||||
return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that.isLoading,_that.isLoadingHistory,_that.isSubmitting,_that.showRouteTrail,_that.errorEvent,_that.successMessage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@@ -198,10 +199,10 @@ return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, String errorMessage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, LocationErrorEvent? errorEvent, LocationSuccessEvent? successMessage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _LocationViewState() when $default != null:
|
||||
return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that.isLoading,_that.isLoadingHistory,_that.isSubmitting,_that.showRouteTrail,_that.errorMessage);case _:
|
||||
return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that.isLoading,_that.isLoadingHistory,_that.isSubmitting,_that.showRouteTrail,_that.errorEvent,_that.successMessage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@@ -213,7 +214,7 @@ return $default(_that.geofences,_that.frequentPlaces,_that.positionHistory,_that
|
||||
|
||||
|
||||
class _LocationViewState implements LocationViewState {
|
||||
const _LocationViewState({final List<GeofenceEntity> geofences = const [], final List<FrequentPlaceEntity> frequentPlaces = const [], final List<PositionEntity> positionHistory = const [], this.isLoading = true, this.isLoadingHistory = false, this.isSubmitting = false, this.showRouteTrail = false, this.errorMessage = ''}): _geofences = geofences,_frequentPlaces = frequentPlaces,_positionHistory = positionHistory;
|
||||
const _LocationViewState({final List<GeofenceEntity> geofences = const [], final List<FrequentPlaceEntity> frequentPlaces = const [], final List<PositionEntity> positionHistory = const [], this.isLoading = true, this.isLoadingHistory = false, this.isSubmitting = false, this.showRouteTrail = false, this.errorEvent, this.successMessage}): _geofences = geofences,_frequentPlaces = frequentPlaces,_positionHistory = positionHistory;
|
||||
|
||||
|
||||
final List<GeofenceEntity> _geofences;
|
||||
@@ -241,7 +242,8 @@ class _LocationViewState implements LocationViewState {
|
||||
@override@JsonKey() final bool isLoadingHistory;
|
||||
@override@JsonKey() final bool isSubmitting;
|
||||
@override@JsonKey() final bool showRouteTrail;
|
||||
@override@JsonKey() final String errorMessage;
|
||||
@override final LocationErrorEvent? errorEvent;
|
||||
@override final LocationSuccessEvent? successMessage;
|
||||
|
||||
/// Create a copy of LocationViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -253,16 +255,16 @@ _$LocationViewStateCopyWith<_LocationViewState> get copyWith => __$LocationViewS
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocationViewState&&const DeepCollectionEquality().equals(other._geofences, _geofences)&&const DeepCollectionEquality().equals(other._frequentPlaces, _frequentPlaces)&&const DeepCollectionEquality().equals(other._positionHistory, _positionHistory)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingHistory, isLoadingHistory) || other.isLoadingHistory == isLoadingHistory)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.showRouteTrail, showRouteTrail) || other.showRouteTrail == showRouteTrail)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LocationViewState&&const DeepCollectionEquality().equals(other._geofences, _geofences)&&const DeepCollectionEquality().equals(other._frequentPlaces, _frequentPlaces)&&const DeepCollectionEquality().equals(other._positionHistory, _positionHistory)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isLoadingHistory, isLoadingHistory) || other.isLoadingHistory == isLoadingHistory)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.showRouteTrail, showRouteTrail) || other.showRouteTrail == showRouteTrail)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successMessage, successMessage) || other.successMessage == successMessage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_geofences),const DeepCollectionEquality().hash(_frequentPlaces),const DeepCollectionEquality().hash(_positionHistory),isLoading,isLoadingHistory,isSubmitting,showRouteTrail,errorMessage);
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_geofences),const DeepCollectionEquality().hash(_frequentPlaces),const DeepCollectionEquality().hash(_positionHistory),isLoading,isLoadingHistory,isSubmitting,showRouteTrail,errorEvent,successMessage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'LocationViewState(geofences: $geofences, frequentPlaces: $frequentPlaces, positionHistory: $positionHistory, isLoading: $isLoading, isLoadingHistory: $isLoadingHistory, isSubmitting: $isSubmitting, showRouteTrail: $showRouteTrail, errorMessage: $errorMessage)';
|
||||
return 'LocationViewState(geofences: $geofences, frequentPlaces: $frequentPlaces, positionHistory: $positionHistory, isLoading: $isLoading, isLoadingHistory: $isLoadingHistory, isSubmitting: $isSubmitting, showRouteTrail: $showRouteTrail, errorEvent: $errorEvent, successMessage: $successMessage)';
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +275,7 @@ abstract mixin class _$LocationViewStateCopyWith<$Res> implements $LocationViewS
|
||||
factory _$LocationViewStateCopyWith(_LocationViewState value, $Res Function(_LocationViewState) _then) = __$LocationViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, String errorMessage
|
||||
List<GeofenceEntity> geofences, List<FrequentPlaceEntity> frequentPlaces, List<PositionEntity> positionHistory, bool isLoading, bool isLoadingHistory, bool isSubmitting, bool showRouteTrail, LocationErrorEvent? errorEvent, LocationSuccessEvent? successMessage
|
||||
});
|
||||
|
||||
|
||||
@@ -290,7 +292,7 @@ class __$LocationViewStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of LocationViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? geofences = null,Object? frequentPlaces = null,Object? positionHistory = null,Object? isLoading = null,Object? isLoadingHistory = null,Object? isSubmitting = null,Object? showRouteTrail = null,Object? errorMessage = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? geofences = null,Object? frequentPlaces = null,Object? positionHistory = null,Object? isLoading = null,Object? isLoadingHistory = null,Object? isSubmitting = null,Object? showRouteTrail = null,Object? errorEvent = freezed,Object? successMessage = freezed,}) {
|
||||
return _then(_LocationViewState(
|
||||
geofences: null == geofences ? _self._geofences : geofences // ignore: cast_nullable_to_non_nullable
|
||||
as List<GeofenceEntity>,frequentPlaces: null == frequentPlaces ? _self._frequentPlaces : frequentPlaces // ignore: cast_nullable_to_non_nullable
|
||||
@@ -299,8 +301,9 @@ as List<PositionEntity>,isLoading: null == isLoading ? _self.isLoading : isLoadi
|
||||
as bool,isLoadingHistory: null == isLoadingHistory ? _self.isLoadingHistory : isLoadingHistory // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||
as bool,showRouteTrail: null == showRouteTrail ? _self.showRouteTrail : showRouteTrail // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as LocationErrorEvent?,successMessage: freezed == successMessage ? _self.successMessage : successMessage // ignore: cast_nullable_to_non_nullable
|
||||
as LocationSuccessEvent?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -124,8 +124,8 @@ class _FrequentPlaceSheetState extends ConsumerState<_FrequentPlaceSheet> {
|
||||
final isSubmitting = ref.watch(
|
||||
locationViewModelProvider.select((s) => s.isSubmitting),
|
||||
);
|
||||
final errorMessage = ref.watch(
|
||||
locationViewModelProvider.select((s) => s.errorMessage),
|
||||
final errorEvent = ref.watch(
|
||||
locationViewModelProvider.select((s) => s.errorEvent),
|
||||
);
|
||||
|
||||
return DraggableScrollableSheet(
|
||||
@@ -270,11 +270,11 @@ class _FrequentPlaceSheetState extends ConsumerState<_FrequentPlaceSheet> {
|
||||
);
|
||||
}),
|
||||
],
|
||||
if (errorMessage.isNotEmpty)
|
||||
if (errorEvent != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
errorMessage,
|
||||
context.translate(I18n.errorGeneric),
|
||||
style:
|
||||
const TextStyle(color: Colors.red, fontSize: 13),
|
||||
),
|
||||
|
||||
@@ -130,8 +130,8 @@ class _GeofenceSheetState extends ConsumerState<_GeofenceSheet> {
|
||||
final isSubmitting = ref.watch(
|
||||
locationViewModelProvider.select((s) => s.isSubmitting),
|
||||
);
|
||||
final errorMessage = ref.watch(
|
||||
locationViewModelProvider.select((s) => s.errorMessage),
|
||||
final errorEvent = ref.watch(
|
||||
locationViewModelProvider.select((s) => s.errorEvent),
|
||||
);
|
||||
|
||||
return DraggableScrollableSheet(
|
||||
@@ -252,11 +252,11 @@ class _GeofenceSheetState extends ConsumerState<_GeofenceSheet> {
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
),
|
||||
if (errorMessage.isNotEmpty)
|
||||
if (errorEvent != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
errorMessage,
|
||||
context.translate(I18n.errorGeneric),
|
||||
style: const TextStyle(color: Colors.red, fontSize: 13),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -80,6 +80,15 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
void initState() {
|
||||
super.initState();
|
||||
_mapController = MapController();
|
||||
_startMonitoring();
|
||||
}
|
||||
|
||||
void _startMonitoring() {
|
||||
_followTimer?.cancel();
|
||||
final frequency = widget.selectedDevice?.settings.frequency ?? 60;
|
||||
_followTimer = Timer.periodic(Duration(seconds: frequency), (_) {
|
||||
widget.onRefreshPosition();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -94,7 +103,7 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
void didUpdateWidget(LocationMap oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.selectedDevice?.id != oldWidget.selectedDevice?.id) {
|
||||
_stopFollowing();
|
||||
_startMonitoring();
|
||||
}
|
||||
if (widget.selectedPosition != null &&
|
||||
widget.selectedPosition != oldWidget.selectedPosition) {
|
||||
@@ -176,28 +185,6 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
_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;
|
||||
@@ -220,6 +207,33 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
Share.share(text.toString().trim());
|
||||
}
|
||||
|
||||
Future<void> _updateFrequency(int frequency) async {
|
||||
final success = await ref
|
||||
.read(locationViewModelProvider.notifier)
|
||||
.updateLocationFrequency(frequency: frequency);
|
||||
if (!mounted) return;
|
||||
if (success) {
|
||||
_followTimer?.cancel();
|
||||
_followTimer = Timer.periodic(Duration(seconds: frequency), (_) {
|
||||
widget.onRefreshPosition();
|
||||
});
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(
|
||||
I18n.locationFrequencyUpdated,
|
||||
args: {'minutes': '${frequency ~/ 60}'},
|
||||
),
|
||||
type: MessageType.success,
|
||||
);
|
||||
} else {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.errorLocationFrequency),
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _confirmPlacement() {
|
||||
final center = _mapController.camera.center;
|
||||
final mapState = ref.read(locationMapViewModelProvider);
|
||||
@@ -540,7 +554,27 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
}
|
||||
|
||||
return [
|
||||
Positioned(top: 12, left: 12, child: const MapStyleSelector()),
|
||||
Positioned(
|
||||
top: 12,
|
||||
left: 12,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const MapStyleSelector(),
|
||||
if (widget.selectedDevice?.capabilities?.location != null &&
|
||||
widget.selectedDevice!.capabilities!.location!.options.isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
FrequencySelector(
|
||||
currentFrequency:
|
||||
ref.watch(selectedDeviceProvider)?.settings.frequency ?? 60,
|
||||
options:
|
||||
widget.selectedDevice!.capabilities!.location!.options,
|
||||
onChanged: _updateFrequency,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 12,
|
||||
right: 12,
|
||||
@@ -561,14 +595,12 @@ class _LocationMapState extends ConsumerState<LocationMap>
|
||||
right: 12,
|
||||
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,
|
||||
),
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:location/src/features/location/presentation/state/location_map_view_state.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:location/src/features/location/presentation/state/location_map_view_model.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,
|
||||
});
|
||||
@@ -53,12 +50,6 @@ class MapActionsPanel extends StatelessWidget {
|
||||
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(),
|
||||
@@ -77,3 +68,96 @@ class MapActionsPanel extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FrequencySelector extends ConsumerWidget {
|
||||
final int currentFrequency;
|
||||
final List<int> options;
|
||||
final ValueChanged<int> onChanged;
|
||||
|
||||
const FrequencySelector({
|
||||
super.key,
|
||||
required this.currentFrequency,
|
||||
required this.options,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
String _formatSeconds(int seconds) {
|
||||
if (seconds >= 60) return '${seconds ~/ 60}m';
|
||||
return '${seconds}s';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final expanded = ref.watch(
|
||||
locationMapViewModelProvider.select((s) => s.frequencyExpanded),
|
||||
);
|
||||
final vm = ref.read(locationMapViewModelProvider.notifier);
|
||||
|
||||
return AnimatedSize(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
alignment: Alignment.topCenter,
|
||||
child: expanded
|
||||
? _buildSegmented(context, vm)
|
||||
: MapActionButton(
|
||||
icon: Icons.timer_outlined,
|
||||
onTap: vm.toggleFrequencyExpanded,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSegmented(BuildContext context, LocationMapViewModel vm) {
|
||||
return Material(
|
||||
elevation: 2,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...options.map((opt) {
|
||||
final selected = opt == currentFrequency;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onChanged(opt);
|
||||
vm.collapseFrequency();
|
||||
},
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 32,
|
||||
margin: const EdgeInsets.symmetric(vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: selected
|
||||
? Theme.of(context).primaryColor
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
_formatSeconds(opt),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: selected ? Theme.of(context).colorScheme.surface : Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: 2),
|
||||
GestureDetector(
|
||||
onTap: vm.collapseFrequency,
|
||||
child: const SizedBox(
|
||||
width: 40,
|
||||
height: 24,
|
||||
child: Icon(Icons.close, size: 16, color: Colors.black54),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ packages:
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
get_it:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: get_it
|
||||
sha256: "568d62f0e68666fb5d95519743b3c24a34c7f19d834b0658c46e26d778461f66"
|
||||
|
||||
@@ -24,6 +24,7 @@ dependencies:
|
||||
path: ../../../../packages/sf_shared
|
||||
utils:
|
||||
path: ../../../../packages/utils
|
||||
get_it: ^9.0.5
|
||||
flutter_riverpod: ^3.0.3
|
||||
go_router: ^17.0.0
|
||||
freezed_annotation: ^3.1.0
|
||||
|
||||
@@ -8,7 +8,7 @@ export 'src/features/language/language_builder.dart';
|
||||
export 'src/features/legacy_notifications/legacy_notifications_builder.dart';
|
||||
export 'src/features/remote_management/remote_management_builder.dart';
|
||||
export 'src/features/remote_on_off/remote_on_off_builder.dart';
|
||||
export 'src/features/sms_alert/sms_alert_builder.dart';
|
||||
export 'src/features/alerts/alerts_builder.dart';
|
||||
export 'src/features/sos_contacts/sos_contacts_builder.dart';
|
||||
export 'src/features/sound/sound_builder.dart';
|
||||
export 'src/features/sync_clock/sync_clock_builder.dart';
|
||||
|
||||
@@ -1,33 +1,22 @@
|
||||
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';
|
||||
|
||||
class LanguageRemoteDatasourceImpl implements LanguageRemoteDatasource {
|
||||
LanguageRemoteDatasourceImpl(this._repository);
|
||||
LanguageRemoteDatasourceImpl(this._datasource);
|
||||
|
||||
final QuestiaRepository _repository;
|
||||
final DeviceSettingsUpdateDatasource _datasource;
|
||||
|
||||
@override
|
||||
Future<void> updateDeviceLanguage({
|
||||
required DeviceEntity device,
|
||||
required String newLanguage,
|
||||
}) async {
|
||||
final settings = Map<String, dynamic>.from(device.settings);
|
||||
settings['language'] = newLanguage;
|
||||
|
||||
final csvBase64 = DeviceCsvBuilder.buildBase64Csv(
|
||||
final updatedSettings = device.settings.copyWith(language: newLanguage);
|
||||
await _datasource.updateDeviceSettings(
|
||||
device: device,
|
||||
settings: settings,
|
||||
);
|
||||
|
||||
await safeCall(
|
||||
() => _repository.put<dynamic>(
|
||||
'/devices',
|
||||
body: {'csv': csvBase64},
|
||||
),
|
||||
'Error updating device language',
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
import 'package:settings/src/core/data/datasources/language_remote_datasource.dart';
|
||||
import 'package:settings/src/core/data/datasources/language_remote_datasource_impl.dart';
|
||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
||||
|
||||
final languageRemoteDatasourceProvider =
|
||||
Provider<LanguageRemoteDatasource>((ref) {
|
||||
final questiaRepository = getIt<QuestiaRepository>();
|
||||
return LanguageRemoteDatasourceImpl(questiaRepository);
|
||||
final datasource = ref.read(deviceSettingsUpdateProvider);
|
||||
return LanguageRemoteDatasourceImpl(datasource);
|
||||
});
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'presentation/sms_alert_screen.dart';
|
||||
import 'presentation/alerts_screen.dart';
|
||||
|
||||
class SmsAlertBuilder {
|
||||
const SmsAlertBuilder();
|
||||
class AlertsBuilder {
|
||||
const AlertsBuilder();
|
||||
|
||||
Page<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: const SmsAlertScreen(),
|
||||
child: const AlertsScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
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 'package:utils/utils.dart';
|
||||
|
||||
import 'state/alerts_view_model.dart';
|
||||
|
||||
class AlertsScreen extends ConsumerWidget {
|
||||
const AlertsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final state = ref.watch(alertsViewModelProvider);
|
||||
final vm = ref.read(alertsViewModelProvider.notifier);
|
||||
|
||||
ref.listen(
|
||||
alertsViewModelProvider.select((s) => s.errorEvent),
|
||||
(previous, next) {
|
||||
if (next != null) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.errorAlertUpdate),
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
alertsViewModelProvider.select((s) => s.saveSuccess),
|
||||
(previous, next) {
|
||||
if (next && !(previous ?? false)) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.alertsUpdated),
|
||||
type: MessageType.success,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.alerts),
|
||||
body: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
vertical: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.alertsDescription),
|
||||
style: TextStyle(
|
||||
fontSize: SizeUtils.getByScreen(small: 14, big: 15),
|
||||
color: theme
|
||||
.getColorFor(ThemeCode.textPrimary)
|
||||
.withAlpha(178),
|
||||
),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 14)),
|
||||
...state.availableAlerts.map(
|
||||
(alert) => _AlertToggle(
|
||||
label: _alertLabel(context, alert),
|
||||
isActive: state.activeAlerts.contains(alert),
|
||||
onChanged: (_) => vm.toggleAlert(alert),
|
||||
theme: theme,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: state.isSaving ? null : () => vm.save(),
|
||||
text: state.isSaving ? '...' : context.translate(I18n.save),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _alertLabel(BuildContext context, String alert) {
|
||||
return switch (alert) {
|
||||
'sos' => context.translate(I18n.alertSos),
|
||||
'falldown' => context.translate(I18n.alertFalldown),
|
||||
'lowBattery' => context.translate(I18n.alertLowBattery),
|
||||
'geofenceIn' => context.translate(I18n.alertGeofenceIn),
|
||||
'geofenceOut' => context.translate(I18n.alertGeofenceOut),
|
||||
'disconnect' => context.translate(I18n.alertDisconnect),
|
||||
'braceletRemoved' => context.translate(I18n.alertBraceletRemoved),
|
||||
'standstill' => context.translate(I18n.alertStandstill),
|
||||
'abnormalHeartRate' => context.translate(I18n.alertAbnormalHeartRate),
|
||||
'reconnect' => context.translate(I18n.alertReconnect),
|
||||
'movement' => context.translate(I18n.alertMovement),
|
||||
_ => alert,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class _AlertToggle extends StatelessWidget {
|
||||
final String label;
|
||||
final bool isActive;
|
||||
final ValueChanged<bool> onChanged;
|
||||
final ThemePort theme;
|
||||
|
||||
const _AlertToggle({
|
||||
required this.label,
|
||||
required this.isActive,
|
||||
required this.onChanged,
|
||||
required this.theme,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Switch.adaptive(
|
||||
value: isActive,
|
||||
activeTrackColor: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
onChanged: onChanged,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 15),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
|
||||
import 'alerts_view_state.dart';
|
||||
|
||||
final alertsViewModelProvider =
|
||||
NotifierProvider.autoDispose<AlertsViewModel, AlertsViewState>(
|
||||
AlertsViewModel.new,
|
||||
);
|
||||
|
||||
class AlertsViewModel extends Notifier<AlertsViewState> {
|
||||
late final DeviceSettingsUpdateDatasource _datasource;
|
||||
|
||||
@override
|
||||
AlertsViewState build() {
|
||||
_datasource = ref.read(deviceSettingsUpdateProvider);
|
||||
Future.microtask(_load);
|
||||
return const AlertsViewState();
|
||||
}
|
||||
|
||||
void _load() {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
final available = device.capabilities?.alerts?.types ?? [];
|
||||
final active = device.settings.alerts;
|
||||
|
||||
state = state.copyWith(
|
||||
availableAlerts: available,
|
||||
activeAlerts: active,
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
void toggleAlert(String alert) {
|
||||
final current = List<String>.from(state.activeAlerts);
|
||||
if (current.contains(alert)) {
|
||||
current.remove(alert);
|
||||
} else {
|
||||
current.add(alert);
|
||||
}
|
||||
state = state.copyWith(activeAlerts: current);
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(isSaving: true, errorEvent: null, saveSuccess: false);
|
||||
|
||||
try {
|
||||
final updatedSettings = device.settings.copyWith(
|
||||
alerts: state.activeAlerts,
|
||||
);
|
||||
await _datasource.updateDeviceSettings(
|
||||
device: device,
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(isSaving: false, saveSuccess: true);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorEvent: AlertsErrorEvent.update,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'alerts_view_state.freezed.dart';
|
||||
|
||||
enum AlertsErrorEvent { load, update }
|
||||
|
||||
@freezed
|
||||
abstract class AlertsViewState with _$AlertsViewState {
|
||||
const factory AlertsViewState({
|
||||
@Default([]) List<String> availableAlerts,
|
||||
@Default([]) List<String> activeAlerts,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isSaving,
|
||||
AlertsErrorEvent? errorEvent,
|
||||
@Default(false) bool saveSuccess,
|
||||
}) = _AlertsViewState;
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
// 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 'alerts_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$AlertsViewState {
|
||||
|
||||
List<String> get availableAlerts; List<String> get activeAlerts; bool get isLoading; bool get isSaving; AlertsErrorEvent? get errorEvent; bool get saveSuccess;
|
||||
/// Create a copy of AlertsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$AlertsViewStateCopyWith<AlertsViewState> get copyWith => _$AlertsViewStateCopyWithImpl<AlertsViewState>(this as AlertsViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AlertsViewState&&const DeepCollectionEquality().equals(other.availableAlerts, availableAlerts)&&const DeepCollectionEquality().equals(other.activeAlerts, activeAlerts)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(availableAlerts),const DeepCollectionEquality().hash(activeAlerts),isLoading,isSaving,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlertsViewState(availableAlerts: $availableAlerts, activeAlerts: $activeAlerts, isLoading: $isLoading, isSaving: $isSaving, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $AlertsViewStateCopyWith<$Res> {
|
||||
factory $AlertsViewStateCopyWith(AlertsViewState value, $Res Function(AlertsViewState) _then) = _$AlertsViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
List<String> availableAlerts, List<String> activeAlerts, bool isLoading, bool isSaving, AlertsErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$AlertsViewStateCopyWithImpl<$Res>
|
||||
implements $AlertsViewStateCopyWith<$Res> {
|
||||
_$AlertsViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final AlertsViewState _self;
|
||||
final $Res Function(AlertsViewState) _then;
|
||||
|
||||
/// Create a copy of AlertsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? availableAlerts = null,Object? activeAlerts = null,Object? isLoading = null,Object? isSaving = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
availableAlerts: null == availableAlerts ? _self.availableAlerts : availableAlerts // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,activeAlerts: null == activeAlerts ? _self.activeAlerts : activeAlerts // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as AlertsErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [AlertsViewState].
|
||||
extension AlertsViewStatePatterns on AlertsViewState {
|
||||
/// 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( _AlertsViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlertsViewState() 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( _AlertsViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlertsViewState():
|
||||
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( _AlertsViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AlertsViewState() 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<String> availableAlerts, List<String> activeAlerts, bool isLoading, bool isSaving, AlertsErrorEvent? errorEvent, bool saveSuccess)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlertsViewState() when $default != null:
|
||||
return $default(_that.availableAlerts,_that.activeAlerts,_that.isLoading,_that.isSaving,_that.errorEvent,_that.saveSuccess);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<String> availableAlerts, List<String> activeAlerts, bool isLoading, bool isSaving, AlertsErrorEvent? errorEvent, bool saveSuccess) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlertsViewState():
|
||||
return $default(_that.availableAlerts,_that.activeAlerts,_that.isLoading,_that.isSaving,_that.errorEvent,_that.saveSuccess);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<String> availableAlerts, List<String> activeAlerts, bool isLoading, bool isSaving, AlertsErrorEvent? errorEvent, bool saveSuccess)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AlertsViewState() when $default != null:
|
||||
return $default(_that.availableAlerts,_that.activeAlerts,_that.isLoading,_that.isSaving,_that.errorEvent,_that.saveSuccess);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _AlertsViewState implements AlertsViewState {
|
||||
const _AlertsViewState({final List<String> availableAlerts = const [], final List<String> activeAlerts = const [], this.isLoading = true, this.isSaving = false, this.errorEvent, this.saveSuccess = false}): _availableAlerts = availableAlerts,_activeAlerts = activeAlerts;
|
||||
|
||||
|
||||
final List<String> _availableAlerts;
|
||||
@override@JsonKey() List<String> get availableAlerts {
|
||||
if (_availableAlerts is EqualUnmodifiableListView) return _availableAlerts;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_availableAlerts);
|
||||
}
|
||||
|
||||
final List<String> _activeAlerts;
|
||||
@override@JsonKey() List<String> get activeAlerts {
|
||||
if (_activeAlerts is EqualUnmodifiableListView) return _activeAlerts;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_activeAlerts);
|
||||
}
|
||||
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isSaving;
|
||||
@override final AlertsErrorEvent? errorEvent;
|
||||
@override@JsonKey() final bool saveSuccess;
|
||||
|
||||
/// Create a copy of AlertsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$AlertsViewStateCopyWith<_AlertsViewState> get copyWith => __$AlertsViewStateCopyWithImpl<_AlertsViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AlertsViewState&&const DeepCollectionEquality().equals(other._availableAlerts, _availableAlerts)&&const DeepCollectionEquality().equals(other._activeAlerts, _activeAlerts)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_availableAlerts),const DeepCollectionEquality().hash(_activeAlerts),isLoading,isSaving,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AlertsViewState(availableAlerts: $availableAlerts, activeAlerts: $activeAlerts, isLoading: $isLoading, isSaving: $isSaving, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$AlertsViewStateCopyWith<$Res> implements $AlertsViewStateCopyWith<$Res> {
|
||||
factory _$AlertsViewStateCopyWith(_AlertsViewState value, $Res Function(_AlertsViewState) _then) = __$AlertsViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
List<String> availableAlerts, List<String> activeAlerts, bool isLoading, bool isSaving, AlertsErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$AlertsViewStateCopyWithImpl<$Res>
|
||||
implements _$AlertsViewStateCopyWith<$Res> {
|
||||
__$AlertsViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _AlertsViewState _self;
|
||||
final $Res Function(_AlertsViewState) _then;
|
||||
|
||||
/// Create a copy of AlertsViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? availableAlerts = null,Object? activeAlerts = null,Object? isLoading = null,Object? isSaving = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_AlertsViewState(
|
||||
availableAlerts: null == availableAlerts ? _self._availableAlerts : availableAlerts // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,activeAlerts: null == activeAlerts ? _self._activeAlerts : activeAlerts // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as AlertsErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -3,6 +3,9 @@ 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 'package:utils/utils.dart';
|
||||
|
||||
import 'state/battery_view_model.dart';
|
||||
|
||||
class BatteryScreen extends ConsumerWidget {
|
||||
const BatteryScreen({super.key});
|
||||
@@ -10,13 +13,98 @@ class BatteryScreen extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
final state = ref.watch(batteryViewModelProvider);
|
||||
final vm = ref.read(batteryViewModelProvider.notifier);
|
||||
|
||||
ref.listen(
|
||||
batteryViewModelProvider.select((s) => s.errorEvent),
|
||||
(previous, next) {
|
||||
if (next != null) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.errorBatteryNightMode),
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
batteryViewModelProvider.select((s) => s.saveSuccess),
|
||||
(previous, next) {
|
||||
if (next && !(previous ?? false)) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.batteryNightModeUpdated),
|
||||
type: MessageType.success,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.battery),
|
||||
body: const Center(
|
||||
child: Text('Coming soon'),
|
||||
),
|
||||
title: context.translate(I18n.batteryNightSaving),
|
||||
body: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 24, big: 22),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 40, big: 36)),
|
||||
Icon(
|
||||
Icons.nightlight_round,
|
||||
size: SizeUtils.getByScreen(small: 120, big: 110),
|
||||
color: primaryColor,
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 60, big: 50)),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.batteryNightModeTitle),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
context.translate(I18n.batteryNightModeDescription),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey.shade600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Switch.adaptive(
|
||||
value: state.nightMode,
|
||||
activeTrackColor: primaryColor,
|
||||
onChanged: state.isSaving
|
||||
? null
|
||||
: (value) => vm.toggleNightMode(value),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:legacy_shared/legacy_shared.dart';
|
||||
|
||||
import 'battery_view_state.dart';
|
||||
|
||||
final batteryViewModelProvider =
|
||||
NotifierProvider.autoDispose<BatteryViewModel, BatteryViewState>(
|
||||
BatteryViewModel.new,
|
||||
);
|
||||
|
||||
class BatteryViewModel extends Notifier<BatteryViewState> {
|
||||
late final DeviceSettingsUpdateDatasource _datasource;
|
||||
|
||||
@override
|
||||
BatteryViewState build() {
|
||||
_datasource = ref.read(deviceSettingsUpdateProvider);
|
||||
Future.microtask(_load);
|
||||
return const BatteryViewState();
|
||||
}
|
||||
|
||||
void _load() {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(
|
||||
nightMode: device.settings.nightMode,
|
||||
isLoading: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> toggleNightMode(bool value) async {
|
||||
final device = ref.read(selectedDeviceProvider);
|
||||
if (device == null) return;
|
||||
|
||||
state = state.copyWith(isSaving: true, errorEvent: null, saveSuccess: false);
|
||||
|
||||
try {
|
||||
final updatedSettings = device.settings.copyWith(nightMode: value);
|
||||
await _datasource.updateDeviceSettings(
|
||||
device: device,
|
||||
updatedSettings: updatedSettings,
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
ref.syncDeviceSettings(device, updatedSettings);
|
||||
state = state.copyWith(
|
||||
nightMode: value,
|
||||
isSaving: false,
|
||||
saveSuccess: true,
|
||||
);
|
||||
} catch (e) {
|
||||
if (!ref.mounted) return;
|
||||
state = state.copyWith(
|
||||
isSaving: false,
|
||||
errorEvent: BatteryErrorEvent.update,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'battery_view_state.freezed.dart';
|
||||
|
||||
enum BatteryErrorEvent { update }
|
||||
|
||||
@freezed
|
||||
abstract class BatteryViewState with _$BatteryViewState {
|
||||
const factory BatteryViewState({
|
||||
@Default(false) bool nightMode,
|
||||
@Default(true) bool isLoading,
|
||||
@Default(false) bool isSaving,
|
||||
BatteryErrorEvent? errorEvent,
|
||||
@Default(false) bool saveSuccess,
|
||||
}) = _BatteryViewState;
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
// 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 'battery_view_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$BatteryViewState {
|
||||
|
||||
bool get nightMode; bool get isLoading; bool get isSaving; BatteryErrorEvent? get errorEvent; bool get saveSuccess;
|
||||
/// Create a copy of BatteryViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$BatteryViewStateCopyWith<BatteryViewState> get copyWith => _$BatteryViewStateCopyWithImpl<BatteryViewState>(this as BatteryViewState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is BatteryViewState&&(identical(other.nightMode, nightMode) || other.nightMode == nightMode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,nightMode,isLoading,isSaving,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BatteryViewState(nightMode: $nightMode, isLoading: $isLoading, isSaving: $isSaving, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $BatteryViewStateCopyWith<$Res> {
|
||||
factory $BatteryViewStateCopyWith(BatteryViewState value, $Res Function(BatteryViewState) _then) = _$BatteryViewStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool nightMode, bool isLoading, bool isSaving, BatteryErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$BatteryViewStateCopyWithImpl<$Res>
|
||||
implements $BatteryViewStateCopyWith<$Res> {
|
||||
_$BatteryViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final BatteryViewState _self;
|
||||
final $Res Function(BatteryViewState) _then;
|
||||
|
||||
/// Create a copy of BatteryViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? nightMode = null,Object? isLoading = null,Object? isSaving = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
nightMode: null == nightMode ? _self.nightMode : nightMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BatteryErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [BatteryViewState].
|
||||
extension BatteryViewStatePatterns on BatteryViewState {
|
||||
/// 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( _BatteryViewState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BatteryViewState() 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( _BatteryViewState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BatteryViewState():
|
||||
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( _BatteryViewState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _BatteryViewState() 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 nightMode, bool isLoading, bool isSaving, BatteryErrorEvent? errorEvent, bool saveSuccess)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BatteryViewState() when $default != null:
|
||||
return $default(_that.nightMode,_that.isLoading,_that.isSaving,_that.errorEvent,_that.saveSuccess);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool nightMode, bool isLoading, bool isSaving, BatteryErrorEvent? errorEvent, bool saveSuccess) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BatteryViewState():
|
||||
return $default(_that.nightMode,_that.isLoading,_that.isSaving,_that.errorEvent,_that.saveSuccess);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool nightMode, bool isLoading, bool isSaving, BatteryErrorEvent? errorEvent, bool saveSuccess)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _BatteryViewState() when $default != null:
|
||||
return $default(_that.nightMode,_that.isLoading,_that.isSaving,_that.errorEvent,_that.saveSuccess);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _BatteryViewState implements BatteryViewState {
|
||||
const _BatteryViewState({this.nightMode = false, this.isLoading = true, this.isSaving = false, this.errorEvent, this.saveSuccess = false});
|
||||
|
||||
|
||||
@override@JsonKey() final bool nightMode;
|
||||
@override@JsonKey() final bool isLoading;
|
||||
@override@JsonKey() final bool isSaving;
|
||||
@override final BatteryErrorEvent? errorEvent;
|
||||
@override@JsonKey() final bool saveSuccess;
|
||||
|
||||
/// Create a copy of BatteryViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$BatteryViewStateCopyWith<_BatteryViewState> get copyWith => __$BatteryViewStateCopyWithImpl<_BatteryViewState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BatteryViewState&&(identical(other.nightMode, nightMode) || other.nightMode == nightMode)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isSaving, isSaving) || other.isSaving == isSaving)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.saveSuccess, saveSuccess) || other.saveSuccess == saveSuccess));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,nightMode,isLoading,isSaving,errorEvent,saveSuccess);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BatteryViewState(nightMode: $nightMode, isLoading: $isLoading, isSaving: $isSaving, errorEvent: $errorEvent, saveSuccess: $saveSuccess)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$BatteryViewStateCopyWith<$Res> implements $BatteryViewStateCopyWith<$Res> {
|
||||
factory _$BatteryViewStateCopyWith(_BatteryViewState value, $Res Function(_BatteryViewState) _then) = __$BatteryViewStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool nightMode, bool isLoading, bool isSaving, BatteryErrorEvent? errorEvent, bool saveSuccess
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$BatteryViewStateCopyWithImpl<$Res>
|
||||
implements _$BatteryViewStateCopyWith<$Res> {
|
||||
__$BatteryViewStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _BatteryViewState _self;
|
||||
final $Res Function(_BatteryViewState) _then;
|
||||
|
||||
/// Create a copy of BatteryViewState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? nightMode = null,Object? isLoading = null,Object? isSaving = null,Object? errorEvent = freezed,Object? saveSuccess = null,}) {
|
||||
return _then(_BatteryViewState(
|
||||
nightMode: null == nightMode ? _self.nightMode : nightMode // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isSaving: null == isSaving ? _self.isSaving : isSaving // ignore: cast_nullable_to_non_nullable
|
||||
as bool,errorEvent: freezed == errorEvent ? _self.errorEvent : errorEvent // ignore: cast_nullable_to_non_nullable
|
||||
as BatteryErrorEvent?,saveSuccess: null == saveSuccess ? _self.saveSuccess : saveSuccess // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// dart format on
|
||||
@@ -3,6 +3,9 @@ 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 'package:utils/utils.dart';
|
||||
|
||||
import 'state/disable_functions_view_model.dart';
|
||||
|
||||
class DisableFunctionsScreen extends ConsumerWidget {
|
||||
const DisableFunctionsScreen({super.key});
|
||||
@@ -10,12 +13,117 @@ class DisableFunctionsScreen extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
final primaryColor = theme.getColorFor(ThemeCode.legacyPrimary);
|
||||
final state = ref.watch(disableFunctionsViewModelProvider);
|
||||
final vm = ref.read(disableFunctionsViewModelProvider.notifier);
|
||||
|
||||
ref.listen(
|
||||
disableFunctionsViewModelProvider.select((s) => s.errorEvent),
|
||||
(previous, next) {
|
||||
if (next != null) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.errorDisableFunctions),
|
||||
type: MessageType.error,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
disableFunctionsViewModelProvider.select((s) => s.saveSuccess),
|
||||
(previous, next) {
|
||||
if (next && !(previous ?? false)) {
|
||||
showTopSnackbar(
|
||||
context,
|
||||
message: context.translate(I18n.disableFunctionsUpdated),
|
||||
type: MessageType.success,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return LegacyPageLayout(
|
||||
theme: theme,
|
||||
title: context.translate(I18n.disableFunctions),
|
||||
body: const Center(
|
||||
child: Text('Coming soon'),
|
||||
body: state.isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
vertical: SizeUtils.getByScreen(small: 16, big: 14),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_FunctionCard(
|
||||
icon: Icons.dialpad,
|
||||
label: context.translate(I18n.disableFunctionsKeyboard),
|
||||
value: state.keyboard,
|
||||
onChanged: vm.toggleKeyboard,
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_FunctionCard(
|
||||
icon: Icons.gps_fixed,
|
||||
label: context.translate(I18n.disableFunctionsGps),
|
||||
value: state.gps,
|
||||
onChanged: vm.toggleGps,
|
||||
primaryColor: primaryColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
footer: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: state.isSaving ? null : () => vm.save(),
|
||||
text: state.isSaving ? '...' : context.translate(I18n.save),
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FunctionCard extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final bool value;
|
||||
final ValueChanged<bool> onChanged;
|
||||
final Color primaryColor;
|
||||
|
||||
const _FunctionCard({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
required this.primaryColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: primaryColor, size: 28),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
Switch.adaptive(
|
||||
value: value,
|
||||
activeTrackColor: primaryColor,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user