Added translations, fixed icons, created menu buttons, refactor screens

This commit is contained in:
2026-03-04 18:00:55 +01:00
parent 5a68dfb3df
commit 014ef1ad26
53 changed files with 1845 additions and 1506 deletions

1
.idea/modules.xml generated
View File

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

View File

@@ -20,7 +20,8 @@ class AccountSettingsScreen extends ConsumerWidget {
final selectedDevice = ref.watch(selectedDeviceProvider);
return PageLayout(
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.accountSettings),
body: SingleChildScrollView(child: Container(
padding: SizeUtils.getByScreen(
@@ -32,31 +33,31 @@ class AccountSettingsScreen extends ConsumerWidget {
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.personalData);},
icon: SFIcons.account,
text: I18n.legacyPersonalData
text: I18n.personalData
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.changePassword);},
icon: Icons.lock,
text: I18n.legacyChangePassword
text: I18n.changePassword
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: Icons.add_circle_outline,
text: I18n.legacyAddNewSF
text: I18n.addNewSF
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.linkedDevices);},
icon: Icons.account_circle_outlined,
text: I18n.legacyLinkedDevices
text: I18n.linkedDevices
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.appUsers);},
icon: Icons.groups_outlined,
text: I18n.legacyAppUsers
text: I18n.appUsers
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
@@ -67,7 +68,7 @@ class AccountSettingsScreen extends ConsumerWidget {
}
},
icon: SFIcons.privacy,
text: I18n.legacyPrivacyPolicy
text: I18n.privacyPolicy
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
@@ -82,13 +83,13 @@ class AccountSettingsScreen extends ConsumerWidget {
));
},
icon: Icons.qr_code,
text: I18n.legacyRegCode
text: I18n.regCode
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.deleteAccount);},
icon: Icons.no_accounts,
text: I18n.legacyDeleteAccount
text: I18n.deleteAccount
),
],
),
@@ -98,7 +99,7 @@ class AccountSettingsScreen extends ConsumerWidget {
small: EdgeInsets.symmetric(vertical: 12, horizontal: 30),
big: EdgeInsets.symmetric(vertical: 10, horizontal: 28)
),
child: PrimaryButton(text: context.translate(I18n.legacyLogOut), color: Color(0xFF588EA5)),
child: PrimaryButton(text: context.translate(I18n.logOut), color: theme.getColorFor(ThemeCode.legacyPrimary)),
)
);
}
@@ -118,7 +119,7 @@ class AppSectionButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final theme = ref.watch(themePortProvider);
return GestureDetector(
onTap: onPressed,
@@ -141,7 +142,7 @@ class AppSectionButton extends ConsumerWidget {
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
child: Icon(icon,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
),
@@ -206,11 +207,11 @@ class RegCodeDialog extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(context.translate(I18n.legacyDeviceIdLabel,
Text(context.translate(I18n.deviceIdLabel,
args: {'deviceId': deviceId})),
TextButton(
onPressed: (){Clipboard.setData(ClipboardData(text: deviceId));},
child: Text(context.translate(I18n.legacyCopy)),
child: Text(context.translate(I18n.copy)),
)
],
),
@@ -223,11 +224,11 @@ class RegCodeDialog extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(context.translate(I18n.legacyRegCodeLabel,
Text(context.translate(I18n.regCodeLabel,
args: {'regCode': regCode})),
TextButton(
onPressed: (){Clipboard.setData(ClipboardData(text: regCode));},
child: Text(context.translate(I18n.legacyCopy))
child: Text(context.translate(I18n.copy))
)
],
)

View File

@@ -19,10 +19,11 @@ class AppUsersScreen extends ConsumerWidget {
final theme = ref.watch(themePortProvider);
return PageLayout(
return LegacyPageLayout(
theme: theme,
showEdit: true,
onEditChange: vm.toggleIsEditing,
title: context.translate(I18n.legacyAppUsers),
title: context.translate(I18n.appUsers),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
@@ -42,8 +43,8 @@ class AppUsersScreen extends ConsumerWidget {
footer: state.isEditing ?
PrimaryButton(
onPressed: vm.toggleIsEditing,
text: context.translate(I18n.legacySave),
color: Color(0xFF588EA5),
text: context.translate(I18n.save),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 44, big: 42),
) : null
);
@@ -62,7 +63,7 @@ class AppUserCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final theme = ref.watch(themePortProvider);
return Container(
padding: SizeUtils.getByScreen(
@@ -83,7 +84,7 @@ class AppUserCard extends ConsumerWidget {
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
child: Icon(SFIcons.account,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
),
@@ -98,12 +99,12 @@ class AppUserCard extends ConsumerWidget {
fontWeight: FontWeight.w500
)
),
Text(context.translate(I18n.legacyUserAccount, args: {'email': user.email}),
Text(context.translate(I18n.userAccount, args: {'email': user.email}),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 13),
)
),
Text(context.translate(I18n.legacyUserRole, args: {'role': user.role}),
Text(context.translate(I18n.userRole, args: {'role': user.role}),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 13),
)
@@ -128,7 +129,7 @@ class AppUserCard extends ConsumerWidget {
height: SizeUtils.getByScreen(small: 195, big: 185),
child: Column(
children: [
Text(context.translate(I18n.legacyDeleteUserDialog),
Text(context.translate(I18n.deleteUserDialog),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
),
@@ -138,8 +139,8 @@ class AppUserCard extends ConsumerWidget {
children: [
Expanded(child: PrimaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.legacyCancel),
color: Color(0xFF588EA5),
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
@@ -148,8 +149,8 @@ class AppUserCard extends ConsumerWidget {
onPressed: (){
Navigator.pop(context);
},
text: context.translate(I18n.legacyDelete),
color: Color(0xFF588EA5),
text: context.translate(I18n.delete),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
))

View File

@@ -19,8 +19,9 @@ class ChangePasswordScreen extends ConsumerWidget {
final theme = ref.watch(themePortProvider);
return PageLayout(
title: context.translate(I18n.legacyChangePassword),
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.changePassword),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 28, vertical: 10),
@@ -68,7 +69,7 @@ class ChangePasswordScreen extends ConsumerWidget {
}
},
text: context.translate('OK'),
color: Color(0xFF588EA5)
color: theme.getColorFor(ThemeCode.legacyPrimary)
),
);
}

View File

@@ -1,4 +1,5 @@
import 'package:account/src/features/delete_account/presentation/state/delete_account_view_model.dart';
import 'package:account/src/features/delete_account/presentation/widgets/confirm_dialog.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -19,8 +20,9 @@ class DeleteAccountScreen extends ConsumerWidget {
final state = ref.watch(deleteAccountViewModelProvider);
final viewModel = ref.read(deleteAccountViewModelProvider.notifier);
return PageLayout(
title: context.translate(I18n.legacyDeleteAccount),
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.deleteAccount),
body: SingleChildScrollView(child: Container(
margin: EdgeInsets.symmetric(horizontal: 10),
child: Column(
@@ -49,18 +51,18 @@ class DeleteAccountScreen extends ConsumerWidget {
color: Colors.white,
),
),
Text(context.translate(I18n.legacyDeleteAccount),
Text(context.translate(I18n.deleteAccount),
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 20, big: 19)),
)
],
),
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
Text(context.translate(I18n.legacyDeleteAccountBody1),
Text(context.translate(I18n.deleteAccountBody1),
textAlign: TextAlign.start,
),
SizedBox(height: SizeUtils.getByScreen(small: 38, big: 36)),
Text(context.translate(I18n.legacyDeleteAccountBody2),
Text(context.translate(I18n.deleteAccountBody2),
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 12,
@@ -86,171 +88,11 @@ class DeleteAccountScreen extends ConsumerWidget {
);
}
},
text: context.translate(I18n.legacyRequestCancelButton),
color: Color(0xFF588EA5)
text: context.translate(I18n.requestCancelButton),
color: theme.getColorFor(ThemeCode.legacyPrimary)
),
),
);
}
}
class ConfirmDialog extends ConsumerWidget{
final NavigationContract navigationContract;
const ConfirmDialog({
super.key,
required this.navigationContract,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(deleteAccountViewModelProvider);
final viewModel = ref.read(deleteAccountViewModelProvider.notifier);
final steps = [
Container(
height: 210,
width: 500,
color: Colors.white,
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 11),
big: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(context.translate(I18n.legacyVerifyAccount),
style: TextStyle(
fontWeight: FontWeight.w500
),
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 16)),
Text('${context.translate(I18n.email)}: ${state.loggedUser!.email}'),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
Row(
children: [
Text('${context.translate(I18n.password)}: '),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: TextField(
controller: viewModel.passwordController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(hintText: context.translate(I18n.password)),
obscureText: true,
enableSuggestions: false,
autocorrect: true,
))
],
),
if (state.errorMessage.isNotEmpty)
Text(
state.errorMessage,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 13,
),
),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
Row(
children: [
Expanded(child: SecondaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.legacyCancel),
color: Color(0xFF588EA5),
height: 40,
radius: 20,
)),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: PrimaryButton(
onPressed: viewModel.nextStep,
text: context.translate(I18n.accept),
color: Color(0xFF588EA5),
height: 40,
radius: 20,
)),
],
)
],
),
),
Container(
height: 400,
width: 500,
color: Colors.white,
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 11),
big: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 12)),
Text(context.translate(I18n.legacyRequestCancelTitle),
style: TextStyle(
fontWeight: FontWeight.w500
),
),
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 12)),
Expanded(child: SingleChildScrollView(child: Column(
children: [
Text(context.translate(I18n.legacyRequestCancelBody),
style: TextStyle(height: 1.5),
),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
...List<Widget>.generate(state.devices.length, (int index) =>
CheckboxListTile(
contentPadding: EdgeInsets.zero,
title: Text(context.translate(I18n.legacyDeleteDeviceData,
args: {'name': state.devices[index].carrierName}
),
style: TextStyle(height: 0),
),
controlAffinity: ListTileControlAffinity.leading,
value: false,
onChanged: (_){
viewModel.updateDeleteDevice(index);
}
)
),
]
))),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
Row(
children: [
Expanded(child: SecondaryButton(
onPressed: (){
viewModel.resetConfirmStep();
Navigator.pop(context);
},
text: context.translate(I18n.legacyCancel),
color: Color(0xFF588EA5),
height: 50,
radius: 25,
)),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: PrimaryButton(
onPressed: () async {
viewModel.deleteAccount();
if (!context.mounted) return;
navigationContract.goTo(AppRoutes.login);
},
text: context.translate(I18n.legacyConfirm),
color: Color(0xFF588EA5),
height: 50,
radius: 25,
)),
],
)
],
),
)
];
return steps[state.confirmStep];
}
}

View File

@@ -27,29 +27,33 @@ class DeleteAccountViewModel extends Notifier<DeleteAccountViewState> {
passwordController = TextEditingController();
passwordController.addListener(_onPasswordChanged);
ref.read(loggedUserProvider.future)
.then((user){
setUser(user);
return _getLinkedDevicesUseCase.getLinkedDevices(userId: user.id);
}).then(setDevices);
ref.onDispose(disposeListeners);
Future.microtask(() => load());
return const DeleteAccountViewState();
}
Future<void> load() async {
final user = await ref.read(loggedUserProvider.future);
setUser(user);
final devices = await _getLinkedDevicesUseCase.getLinkedDevices(userId: user.id);
setDevices(devices);
}
void setUser(UserEntity user) {
state = state.copyWith(loggedUser: user);
}
void setDevices(List<DeviceEntity> devices) {
state = state.copyWith(
devices: devices,
deviceNames: devices.map((device) => device.carrierName).toList(),
deleteDevices: List<bool>.generate(devices.length, (_)=>false),
);
}
void updateDeleteDevice(int index) {
void toggleDeleteDevice(int index) {
List<bool> deleteDevices = state.deleteDevices;
deleteDevices[index] = !deleteDevices[index];

View File

@@ -11,7 +11,7 @@ abstract class DeleteAccountViewState with _$DeleteAccountViewState {
@Default(false) bool isLoading,
@Default(false) bool isDeleted,
@Default(0) int confirmStep,
@Default([]) List<DeviceEntity> devices,
@Default([]) List<String> deviceNames,
@Default([]) List<bool> deleteDevices,
@Default('') String errorMessage,
}) = _DeleteAccountViewState;

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$DeleteAccountViewState {
UserEntity? get loggedUser; String get password; bool get isLoading; bool get isDeleted; int get confirmStep; List<DeviceEntity> get devices; List<bool> get deleteDevices; String get errorMessage;
UserEntity? get loggedUser; String get password; bool get isLoading; bool get isDeleted; int get confirmStep; List<String> get deviceNames; List<bool> get deleteDevices; String get errorMessage;
/// Create a copy of DeleteAccountViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $DeleteAccountViewStateCopyWith<DeleteAccountViewState> get copyWith => _$Delete
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is DeleteAccountViewState&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.password, password) || other.password == password)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isDeleted, isDeleted) || other.isDeleted == isDeleted)&&(identical(other.confirmStep, confirmStep) || other.confirmStep == confirmStep)&&const DeepCollectionEquality().equals(other.devices, devices)&&const DeepCollectionEquality().equals(other.deleteDevices, deleteDevices)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is DeleteAccountViewState&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.password, password) || other.password == password)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isDeleted, isDeleted) || other.isDeleted == isDeleted)&&(identical(other.confirmStep, confirmStep) || other.confirmStep == confirmStep)&&const DeepCollectionEquality().equals(other.deviceNames, deviceNames)&&const DeepCollectionEquality().equals(other.deleteDevices, deleteDevices)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,loggedUser,password,isLoading,isDeleted,confirmStep,const DeepCollectionEquality().hash(devices),const DeepCollectionEquality().hash(deleteDevices),errorMessage);
int get hashCode => Object.hash(runtimeType,loggedUser,password,isLoading,isDeleted,confirmStep,const DeepCollectionEquality().hash(deviceNames),const DeepCollectionEquality().hash(deleteDevices),errorMessage);
@override
String toString() {
return 'DeleteAccountViewState(loggedUser: $loggedUser, password: $password, isLoading: $isLoading, isDeleted: $isDeleted, confirmStep: $confirmStep, devices: $devices, deleteDevices: $deleteDevices, errorMessage: $errorMessage)';
return 'DeleteAccountViewState(loggedUser: $loggedUser, password: $password, isLoading: $isLoading, isDeleted: $isDeleted, confirmStep: $confirmStep, deviceNames: $deviceNames, deleteDevices: $deleteDevices, errorMessage: $errorMessage)';
}
@@ -45,7 +45,7 @@ abstract mixin class $DeleteAccountViewStateCopyWith<$Res> {
factory $DeleteAccountViewStateCopyWith(DeleteAccountViewState value, $Res Function(DeleteAccountViewState) _then) = _$DeleteAccountViewStateCopyWithImpl;
@useResult
$Res call({
UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<DeviceEntity> devices, List<bool> deleteDevices, String errorMessage
UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<String> deviceNames, List<bool> deleteDevices, String errorMessage
});
@@ -62,15 +62,15 @@ class _$DeleteAccountViewStateCopyWithImpl<$Res>
/// Create a copy of DeleteAccountViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? loggedUser = freezed,Object? password = null,Object? isLoading = null,Object? isDeleted = null,Object? confirmStep = null,Object? devices = null,Object? deleteDevices = null,Object? errorMessage = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? loggedUser = freezed,Object? password = null,Object? isLoading = null,Object? isDeleted = null,Object? confirmStep = null,Object? deviceNames = null,Object? deleteDevices = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
as UserEntity?,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isDeleted: null == isDeleted ? _self.isDeleted : isDeleted // ignore: cast_nullable_to_non_nullable
as bool,confirmStep: null == confirmStep ? _self.confirmStep : confirmStep // ignore: cast_nullable_to_non_nullable
as int,devices: null == devices ? _self.devices : devices // ignore: cast_nullable_to_non_nullable
as List<DeviceEntity>,deleteDevices: null == deleteDevices ? _self.deleteDevices : deleteDevices // ignore: cast_nullable_to_non_nullable
as int,deviceNames: null == deviceNames ? _self.deviceNames : deviceNames // ignore: cast_nullable_to_non_nullable
as List<String>,deleteDevices: null == deleteDevices ? _self.deleteDevices : deleteDevices // ignore: cast_nullable_to_non_nullable
as List<bool>,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
@@ -169,10 +169,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<DeviceEntity> devices, List<bool> deleteDevices, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<String> deviceNames, List<bool> deleteDevices, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _DeleteAccountViewState() when $default != null:
return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,_that.confirmStep,_that.devices,_that.deleteDevices,_that.errorMessage);case _:
return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,_that.confirmStep,_that.deviceNames,_that.deleteDevices,_that.errorMessage);case _:
return orElse();
}
@@ -190,10 +190,10 @@ return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<DeviceEntity> devices, List<bool> deleteDevices, String errorMessage) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<String> deviceNames, List<bool> deleteDevices, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _DeleteAccountViewState():
return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,_that.confirmStep,_that.devices,_that.deleteDevices,_that.errorMessage);case _:
return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,_that.confirmStep,_that.deviceNames,_that.deleteDevices,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
@@ -210,10 +210,10 @@ return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<DeviceEntity> devices, List<bool> deleteDevices, String errorMessage)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<String> deviceNames, List<bool> deleteDevices, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _DeleteAccountViewState() when $default != null:
return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,_that.confirmStep,_that.devices,_that.deleteDevices,_that.errorMessage);case _:
return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,_that.confirmStep,_that.deviceNames,_that.deleteDevices,_that.errorMessage);case _:
return null;
}
@@ -225,7 +225,7 @@ return $default(_that.loggedUser,_that.password,_that.isLoading,_that.isDeleted,
class _DeleteAccountViewState implements DeleteAccountViewState {
const _DeleteAccountViewState({this.loggedUser, this.password = '', this.isLoading = false, this.isDeleted = false, this.confirmStep = 0, final List<DeviceEntity> devices = const [], final List<bool> deleteDevices = const [], this.errorMessage = ''}): _devices = devices,_deleteDevices = deleteDevices;
const _DeleteAccountViewState({this.loggedUser, this.password = '', this.isLoading = false, this.isDeleted = false, this.confirmStep = 0, final List<String> deviceNames = const [], final List<bool> deleteDevices = const [], this.errorMessage = ''}): _deviceNames = deviceNames,_deleteDevices = deleteDevices;
@override final UserEntity? loggedUser;
@@ -233,11 +233,11 @@ class _DeleteAccountViewState implements DeleteAccountViewState {
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool isDeleted;
@override@JsonKey() final int confirmStep;
final List<DeviceEntity> _devices;
@override@JsonKey() List<DeviceEntity> get devices {
if (_devices is EqualUnmodifiableListView) return _devices;
final List<String> _deviceNames;
@override@JsonKey() List<String> get deviceNames {
if (_deviceNames is EqualUnmodifiableListView) return _deviceNames;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_devices);
return EqualUnmodifiableListView(_deviceNames);
}
final List<bool> _deleteDevices;
@@ -259,16 +259,16 @@ _$DeleteAccountViewStateCopyWith<_DeleteAccountViewState> get copyWith => __$Del
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DeleteAccountViewState&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.password, password) || other.password == password)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isDeleted, isDeleted) || other.isDeleted == isDeleted)&&(identical(other.confirmStep, confirmStep) || other.confirmStep == confirmStep)&&const DeepCollectionEquality().equals(other._devices, _devices)&&const DeepCollectionEquality().equals(other._deleteDevices, _deleteDevices)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _DeleteAccountViewState&&(identical(other.loggedUser, loggedUser) || other.loggedUser == loggedUser)&&(identical(other.password, password) || other.password == password)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.isDeleted, isDeleted) || other.isDeleted == isDeleted)&&(identical(other.confirmStep, confirmStep) || other.confirmStep == confirmStep)&&const DeepCollectionEquality().equals(other._deviceNames, _deviceNames)&&const DeepCollectionEquality().equals(other._deleteDevices, _deleteDevices)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType,loggedUser,password,isLoading,isDeleted,confirmStep,const DeepCollectionEquality().hash(_devices),const DeepCollectionEquality().hash(_deleteDevices),errorMessage);
int get hashCode => Object.hash(runtimeType,loggedUser,password,isLoading,isDeleted,confirmStep,const DeepCollectionEquality().hash(_deviceNames),const DeepCollectionEquality().hash(_deleteDevices),errorMessage);
@override
String toString() {
return 'DeleteAccountViewState(loggedUser: $loggedUser, password: $password, isLoading: $isLoading, isDeleted: $isDeleted, confirmStep: $confirmStep, devices: $devices, deleteDevices: $deleteDevices, errorMessage: $errorMessage)';
return 'DeleteAccountViewState(loggedUser: $loggedUser, password: $password, isLoading: $isLoading, isDeleted: $isDeleted, confirmStep: $confirmStep, deviceNames: $deviceNames, deleteDevices: $deleteDevices, errorMessage: $errorMessage)';
}
@@ -279,7 +279,7 @@ abstract mixin class _$DeleteAccountViewStateCopyWith<$Res> implements $DeleteAc
factory _$DeleteAccountViewStateCopyWith(_DeleteAccountViewState value, $Res Function(_DeleteAccountViewState) _then) = __$DeleteAccountViewStateCopyWithImpl;
@override @useResult
$Res call({
UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<DeviceEntity> devices, List<bool> deleteDevices, String errorMessage
UserEntity? loggedUser, String password, bool isLoading, bool isDeleted, int confirmStep, List<String> deviceNames, List<bool> deleteDevices, String errorMessage
});
@@ -296,15 +296,15 @@ class __$DeleteAccountViewStateCopyWithImpl<$Res>
/// Create a copy of DeleteAccountViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? loggedUser = freezed,Object? password = null,Object? isLoading = null,Object? isDeleted = null,Object? confirmStep = null,Object? devices = null,Object? deleteDevices = null,Object? errorMessage = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? loggedUser = freezed,Object? password = null,Object? isLoading = null,Object? isDeleted = null,Object? confirmStep = null,Object? deviceNames = null,Object? deleteDevices = null,Object? errorMessage = null,}) {
return _then(_DeleteAccountViewState(
loggedUser: freezed == loggedUser ? _self.loggedUser : loggedUser // ignore: cast_nullable_to_non_nullable
as UserEntity?,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,isDeleted: null == isDeleted ? _self.isDeleted : isDeleted // ignore: cast_nullable_to_non_nullable
as bool,confirmStep: null == confirmStep ? _self.confirmStep : confirmStep // ignore: cast_nullable_to_non_nullable
as int,devices: null == devices ? _self._devices : devices // ignore: cast_nullable_to_non_nullable
as List<DeviceEntity>,deleteDevices: null == deleteDevices ? _self._deleteDevices : deleteDevices // ignore: cast_nullable_to_non_nullable
as int,deviceNames: null == deviceNames ? _self._deviceNames : deviceNames // ignore: cast_nullable_to_non_nullable
as List<String>,deleteDevices: null == deleteDevices ? _self._deleteDevices : deleteDevices // ignore: cast_nullable_to_non_nullable
as List<bool>,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));

View File

@@ -0,0 +1,229 @@
import 'package:account/src/features/delete_account/presentation/state/delete_account_view_model.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/app_routes.dart';
import 'package:navigation/navigation_contract.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class ConfirmDialog extends ConsumerWidget{
final NavigationContract navigationContract;
const ConfirmDialog({
super.key,
required this.navigationContract,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final state = ref.watch(deleteAccountViewModelProvider);
final viewModel = ref.read(deleteAccountViewModelProvider.notifier);
final steps = [
VerifyAccountStep(
theme: theme,
email: state.loggedUser!.email,
passwordController: viewModel.passwordController,
errorMessage: state.errorMessage,
nextStep: viewModel.nextStep,
),
ConfirmRequestStep(
theme: theme,
toggleDeleteDevice: viewModel.toggleDeleteDevice,
deviceNames: state.deviceNames,
onCancel: (){
viewModel.resetConfirmStep();
Navigator.pop(context);
},
onSubmit: () async {
viewModel.deleteAccount();
if (!context.mounted) return;
navigationContract.goTo(AppRoutes.login);
},
),
];
return steps[state.confirmStep];
}
}
class VerifyAccountStep extends StatelessWidget {
final String email;
final TextEditingController passwordController;
final String errorMessage;
final VoidCallback nextStep;
final ThemePort theme;
const VerifyAccountStep({
required this.email,
required this.passwordController,
required this.errorMessage,
required this.nextStep,
required this.theme,
});
@override
Widget build(BuildContext context) {
return Container(
height: 210,
width: 500,
color: Colors.white,
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 11),
big: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(context.translate(I18n.verifyAccount),
style: TextStyle(
fontWeight: FontWeight.w500
),
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 16)),
Text('${context.translate(I18n.email)}: ${email}'),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
Row(
children: [
Text('${context.translate(I18n.password)}: '),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: TextField(
controller: passwordController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(hintText: context.translate(I18n.password)),
obscureText: true,
enableSuggestions: false,
autocorrect: true,
))
],
),
if (errorMessage.isNotEmpty)
Text(
errorMessage,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 13,
),
),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
Row(
children: [
Expanded(child: SecondaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 40,
radius: 20,
)),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: PrimaryButton(
onPressed: nextStep,
text: context.translate(I18n.accept),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 40,
radius: 20,
)),
],
)
],
),
);
}
}
class ConfirmRequestStep extends StatelessWidget {
final ThemePort theme;
final Function toggleDeleteDevice;
final List<String> deviceNames;
final VoidCallback onCancel;
final VoidCallback onSubmit;
const ConfirmRequestStep({
required this.theme,
required this.toggleDeleteDevice,
required this.deviceNames,
required this.onCancel,
required this.onSubmit,
});
@override
Widget build(BuildContext context) {
return Container(
height: 400,
width: 500,
color: Colors.white,
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 11),
big: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 12)),
Text(context.translate(I18n.requestCancelTitle),
style: TextStyle(
fontWeight: FontWeight.w500
),
),
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 12)),
Expanded(child: SingleChildScrollView(child: Column(
children: [
Text(context.translate(I18n.requestCancelBody),
style: TextStyle(height: 1.5),
),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
...List<Widget>.generate(deviceNames.length, (int index) =>
CheckboxListTile(
contentPadding: EdgeInsets.zero,
title: Text(context.translate(I18n.deleteDeviceData,
args: {'name': deviceNames[index]}
),
style: TextStyle(height: 0),
),
controlAffinity: ListTileControlAffinity.leading,
value: false,
onChanged: (_){
toggleDeleteDevice(index);
}
)
),
]
))),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
Row(
children: [
Expanded(child: SecondaryButton(
onPressed: onCancel,
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 50,
radius: 25,
)),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: PrimaryButton(
onPressed: onSubmit,
text: context.translate(I18n.confirm),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 50,
radius: 25,
)),
],
)
],
),
);
}
}

View File

@@ -22,8 +22,9 @@ class EditLinkedDeviceScreen extends ConsumerWidget {
final theme = ref.watch(themePortProvider);
return /*PageLayout(
title: context.translate(I18n.legacyEditDeviceTitle),
return /*LegacyPageLayout(
theme: theme,
title: context.translate(I18n.editDeviceTitle),
showEdit: true,
onEditChange: vm.toggleIsEditing,
body: body
@@ -43,7 +44,7 @@ class EditLinkedDeviceScreen extends ConsumerWidget {
IconButton(onPressed: () {Navigator.pop(context);},
icon: Icon(Icons.arrow_back)),
Center(
child: Text(context.translate(I18n.legacyEditDeviceTitle),
child: Text(context.translate(I18n.editDeviceTitle),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 28, big: 27)
),
@@ -94,14 +95,14 @@ class EditLinkedDeviceScreen extends ConsumerWidget {
CustomTextField(
controller: vm.deviceNameController,
hint: device.carrierName,
label: context.translate(I18n.legacyName),
label: context.translate(I18n.name),
)
],
),
PrimaryButton(
onPressed: (){vm.updateDevice(device);},
text: context.translate(I18n.legacySave),
color: Color(0xFF588EA5)
text: context.translate(I18n.save),
color: theme.getColorFor(ThemeCode.legacyPrimary)
)
],
))

View File

@@ -21,8 +21,9 @@ class LinkedDevicesScreen extends ConsumerWidget {
final theme = ref.watch(themePortProvider);
return PageLayout(
title: context.translate(I18n.legacyLinkedDevices),
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.linkedDevices),
showEdit: true,
onEditChange: vm.toggleIsEditing,
body: Container(
@@ -60,7 +61,7 @@ class LinkedDeviceCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final theme = ref.watch(themePortProvider);
return Container(
padding: SizeUtils.getByScreen(
@@ -81,7 +82,7 @@ class LinkedDeviceCard extends ConsumerWidget {
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
child: Icon(SFIcons.watch,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
),
@@ -121,7 +122,7 @@ class LinkedDeviceCard extends ConsumerWidget {
height: SizeUtils.getByScreen(small: 195, big: 185),
child: Column(
children: [
Text(context.translate(I18n.legacyDeleteDeviceDialog),
Text(context.translate(I18n.deleteDeviceDialog),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
),
@@ -131,8 +132,8 @@ class LinkedDeviceCard extends ConsumerWidget {
children: [
Expanded(child: PrimaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.legacyCancel),
color: Color(0xFF588EA5),
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
@@ -142,8 +143,8 @@ class LinkedDeviceCard extends ConsumerWidget {
await onDelete();
Navigator.pop(context);
},
text: context.translate(I18n.legacyDelete),
color: Color(0xFF588EA5),
text: context.translate(I18n.delete),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
))
@@ -162,7 +163,7 @@ class LinkedDeviceCard extends ConsumerWidget {
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 14)),
DecoratedBox(
decoration: BoxDecoration(
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child:

View File

@@ -20,8 +20,9 @@ class PersonalDataScreen extends ConsumerWidget {
final theme = ref.watch(themePortProvider);
return PageLayout(
title: context.translate(I18n.legacyPersonalData),
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.personalData),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 48, vertical: 10),
@@ -86,8 +87,8 @@ class PersonalDataScreen extends ConsumerWidget {
),
footer: PrimaryButton(
onPressed: vm.updateUser,
text: context.translate(I18n.legacySubmit),
color: Color(0xFF588EA5)
text: context.translate(I18n.submit),
color: theme.getColorFor(ThemeCode.legacyPrimary)
),
);
}

View File

@@ -1,15 +0,0 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'send_email_request_entity.freezed.dart';
@freezed
abstract class SendEmailRequestEntity with _$SendEmailRequestEntity{
const factory SendEmailRequestEntity({
required String country,
required String channel,
required String name,
required String email,
required String subject,
required String body,
}) = _SendEmailRequestEntity;
}

View File

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

View File

@@ -1,5 +0,0 @@
import 'package:customer_service/src/domain/entities/send_email_request_entity.dart';
abstract class SendEmailUseCase {
Future<void> sendEmail({required SendEmailRequestEntity request});
}

View File

@@ -1,13 +0,0 @@
import 'package:customer_service/src/domain/entities/send_email_request_entity.dart';
import 'package:customer_service/src/domain/send_email_use_case.dart';
class SendEmailUseCaseImpl implements SendEmailUseCase {
//SignUpUseCaseImpl(this._repository);
//final AuthRepository _repository;
@override
Future<void> sendEmail({required SendEmailRequestEntity request}) async {
//return _repository.signUp(request: request);
}
}

View File

@@ -16,10 +16,45 @@ class ContactScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final vm = ref.read(contactViewModelProvider.notifier);
final viewState = ref.watch(contactViewModelProvider);
final viewModel = ref.read(contactViewModelProvider.notifier);
final List<String> country = [
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.contactTitle),
body: Padding(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(small: 38, big: 36)
),
child: SingleChildScrollView(child: Column(
children: [
const _CountrySection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _ChannelSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _NameSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _EmailSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _SubjectSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
_MessageSection(onSubmit: viewModel.sendEmail),
const _ErrorMessageSection(),
],
)),
),
footer: _SendSection(onSend: viewModel.sendEmail),
);
}
}
class _CountrySection extends ConsumerWidget {
const _CountrySection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final List<String> countries = [
'España',
'Portugal',
'France',
@@ -28,81 +63,163 @@ class ContactScreen extends ConsumerWidget {
context.translate(I18n.other),
];
final List<String> channel = [
final vm = ref.read(contactViewModelProvider.notifier);
return CustomDropdown(
items: countries.map(Text.new).toList(growable: false),
onChanged: (x){vm.setCountry(x);},
hint: context.translate(I18n.selectCountry)
);
}
}
class _ChannelSection extends ConsumerWidget {
const _ChannelSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final List<String> channels = [
context.translate(I18n.channelOnline),
context.translate(I18n.channelAmazon),
context.translate(I18n.channelStore),
context.translate(I18n.other),
];
return PageLayout(
title: context.translate(I18n.contactTitle),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 38),
big: EdgeInsets.symmetric(horizontal: 36)
),
child: SingleChildScrollView(child: Column(
children: [
CustomDropdown(
items: country.map(Text.new).toList(growable: false),
onChanged: (x){vm.setCountry(x);},
hint: context.translate(I18n.selectCountry)
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
CustomDropdown(
items: channel.map(Text.new).toList(growable: false),
onChanged: (x){vm.setChannel(x);},
hint: context.translate(I18n.selectChannel)
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
CustomTextField(
controller: vm.nameController,
hint: context.translate(I18n.enterName),
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
CustomTextField(
controller: vm.emailController,
keyboardType: TextInputType.emailAddress,
hint: context.translate(I18n.enterEmail),
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
CustomTextField(
controller: vm.subjectController,
hint: context.translate(I18n.enterSubject),
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
CustomTextField(
controller: vm.bodyController,
keyboardType: TextInputType.multiline,
hint: context.translate(I18n.enterMessage),
lines: 8,
),
if (viewState.errorMessage.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
viewState.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
),
],
],
)),
),
footer: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 38, vertical: 14),
big: EdgeInsets.symmetric(horizontal: 36, vertical: 12)
),
child: PrimaryButton(
onPressed: vm.sendEmail,
text: context.translate(I18n.sendEmail),
color: theme.getColorFor(ThemeCode.buttonPrimary)
)
),
final vm = ref.read(contactViewModelProvider.notifier);
return CustomDropdown(
items: channels.map(Text.new).toList(growable: false),
onChanged: (x){vm.setChannel(x);},
hint: context.translate(I18n.selectChannel)
);
}
}
class _NameSection extends ConsumerWidget {
const _NameSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
controller: vm.nameController,
hint: context.translate(I18n.enterName),
);
}
}
class _EmailSection extends ConsumerWidget {
const _EmailSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
controller: vm.emailController,
keyboardType: TextInputType.emailAddress,
hint: context.translate(I18n.enterEmail),
);
}
}
class _SubjectSection extends ConsumerWidget {
const _SubjectSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
controller: vm.subjectController,
hint: context.translate(I18n.enterSubject),
);
}
}
class _MessageSection extends ConsumerWidget {
final VoidCallback onSubmit;
const _MessageSection({
required this.onSubmit,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
controller: vm.bodyController,
keyboardType: TextInputType.multiline,
hint: context.translate(I18n.enterMessage),
lines: 8,
onSubmitted: (_) => onSubmit(),
);
}
}
class _ErrorMessageSection extends ConsumerWidget {
const _ErrorMessageSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewState = ref.watch(contactViewModelProvider);
if (viewState.errorMessage.isNotEmpty) {
return Column(
children: [
const SizedBox(height: 4),
Text(
viewState.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
),
],
);
} else return SizedBox.shrink();
}
}
class _SendSection extends ConsumerWidget {
final VoidCallback onSend;
const _SendSection({
required this.onSend,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 38, vertical: 14),
big: EdgeInsets.symmetric(horizontal: 36, vertical: 12)
),
child: PrimaryButton(
onPressed: onSend,
text: context.translate(I18n.sendEmail),
color: theme.getColorFor(ThemeCode.buttonPrimary)
)
);
}
}

View File

@@ -15,14 +15,15 @@ class CustomerServiceScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// final theme = ref.watch(themePortProvider);
final theme = ref.watch(themePortProvider);
return PageLayout(
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.customerService),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 18),
big: EdgeInsets.symmetric(horizontal: 16)
small: EdgeInsets.symmetric(horizontal: 18),
big: EdgeInsets.symmetric(horizontal: 16)
),
child: Column(
children: [
@@ -33,8 +34,17 @@ class CustomerServiceScreen extends ConsumerWidget {
throw Exception('Could not launch $url');
}
},
image: 'assets/images/ui/iso_sf.png',
text: context.translate(I18n.supportWebsite)
icon: Image.asset('assets/images/ui/iso_sf.png',
width: SizeUtils.getByScreen(small: 44, big: 48),
height: SizeUtils.getByScreen(small: 44, big: 48),
),
body: Text(context.translate(I18n.supportWebsite),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline
)
)
),
SizedBox(height: SizeUtils.getByScreen(small: 10, big: 9)),
SectionButton(
@@ -44,88 +54,43 @@ class CustomerServiceScreen extends ConsumerWidget {
throw Exception('Could not launch $url');
}
},
icon: SFIcons.handshake,
text: context.translate(I18n.supportHelp)
icon: Icon(SFIcons.handshake,
size: SizeUtils.getByScreen(small: 44, big: 48),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
body: Text(context.translate(I18n.supportHelp),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500,
decoration:TextDecoration.underline
)
)
),
SizedBox(height: SizeUtils.getByScreen(small: 10, big: 9)),
SectionButton(
onPressed: (){Navigator.push(context,
MaterialPageRoute(
builder: (_) => ContactScreen(navigationContract: navigationContract),
));},
icon: Icons.email_outlined,
text: context.translate(I18n.contactTitle)
onPressed: (){
Navigator.push(context,
MaterialPageRoute(
builder: (_) => ContactScreen(navigationContract: navigationContract),
)
);
},
icon: Icon(Icons.email_outlined,
size: SizeUtils.getByScreen(small: 44, big: 48),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
body: Text(context.translate(I18n.contactTitle),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500,
)
)
),
],
)
)
);
}
}
class SectionButton extends ConsumerWidget {
final GestureTapCallback onPressed;
final IconData? icon;
final String? image;
final String text;
const SectionButton({
required this.onPressed,
this.icon,
this.image,
required this.text,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return GestureDetector(
onTap: onPressed,
child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 16),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 12)
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(SizeUtils.getByScreen(small: 12, big: 18))),
color: theme.getColorFor(ThemeCode.backgroundSecondary),
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
padding: EdgeInsets.all(
SizeUtils.getByScreen(small: 12, big: 16)),
child: icon != null
?Icon(icon,
size: SizeUtils.getByScreen(small: 44, big: 48),
color: Color(0xFF588EA5),
weight: 30,
)
: Image.asset(image!,
width: SizeUtils.getByScreen(small: 44, big: 48),
height: SizeUtils.getByScreen(small: 44, big: 48),
),
),
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
Expanded(
child: Text(context.translate(text),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500
)
)
),
],
),
)
);
}
}

View File

@@ -1,9 +0,0 @@
import 'package:customer_service/src/domain/send_email_use_case.dart';
import 'package:customer_service/src/domain/send_email_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final sendEmailUseCaseProvider =
Provider.autoDispose<SendEmailUseCase>((ref) {
//final authRepository = ref.read(customerServiceRepositoryProvider);
return SendEmailUseCaseImpl();
});

View File

@@ -1,5 +1,3 @@
// import 'package:customer_service/src/domain/send_email_use_case.dart';
// import 'package:customer_service/src/presentation/providers/send_email_use_case_provider.dart';
import 'package:customer_service/src/presentation/state/contact_view_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -12,7 +10,6 @@ NotifierProvider.autoDispose<ContactViewModel, ContactViewState>(
);
class ContactViewModel extends Notifier<ContactViewState> {
//late final SendEmailUseCase _sendEmailUseCase;
late final TextEditingController nameController;
late final TextEditingController emailController;
@@ -26,7 +23,6 @@ class ContactViewModel extends Notifier<ContactViewState> {
@override
ContactViewState build() {
//_sendEmailUseCase = ref.read(sendEmailUseCaseProvider);
nameController = TextEditingController();
emailController = TextEditingController();

View File

@@ -1,8 +0,0 @@
/*
import 'package:flutter_riverpod/flutter_riverpod.dart';
final customerServiceRepositoryProvider = Provider<CustomerServiceRepository>((ref) {
final remote = ref.read(customerServiceRemoteDatasourceProvider);
return CustomerServiceRepositoryImpl(remote);
});
*/

View File

@@ -67,10 +67,10 @@ class FunctionsRemoteDatasourceImpl implements FunctionsRemoteDatasource {
throw Exception('Empty response from /users/:userId/contacts');
}
final model = GetContactsResponseModel.fromJson(data);
final model = GetPicturesResponseModel.fromJson(data);
return model.toEntity();
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error to get contacts');
throw _mapDioError(error, defaultMessage: 'Error to get pictures');
}*/
return [];
}
@@ -92,7 +92,13 @@ class FunctionsRemoteDatasourceImpl implements FunctionsRemoteDatasource {
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error to get contacts');
}*/
return PictureEntity(id: '1', userId: '1111', createdAt: 1111);
return PictureEntity(
id: '1',
deviceId: '1111',
createdAt: DateTime.now(),
takenAt: DateTime.now(),
asset: 'assets/images/ui/iso_sf.png'
);
}
@override

View File

@@ -40,7 +40,7 @@ class ContactsScreen extends ConsumerWidget {
if (!state.isEditing) ...[
DecoratedBox(
decoration: BoxDecoration(
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
shape: BoxShape.circle
),
child: IconButton(onPressed: vm.toggleIsEditing,
@@ -91,7 +91,7 @@ class ContactsScreen extends ConsumerWidget {
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
),
width: SizeUtils.getByScreen(small: 48, big: 46),
child: CustomTextButton(
@@ -120,7 +120,7 @@ class ContactCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final theme = ref.watch(themePortProvider);
return Container(
padding: SizeUtils.getByScreen(
@@ -141,7 +141,7 @@ class ContactCard extends ConsumerWidget {
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
child: Icon(SFIcons.account,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
),
@@ -181,7 +181,7 @@ class ContactCard extends ConsumerWidget {
height: SizeUtils.getByScreen(small: 195, big: 185),
child: Column(
children: [
Text(context.translate(I18n.legacyDeleteUserDialog),
Text(context.translate(I18n.deleteUserDialog),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
),
@@ -191,8 +191,8 @@ class ContactCard extends ConsumerWidget {
children: [
Expanded(child: PrimaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.legacyCancel),
color: Color(0xFF588EA5),
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
@@ -201,8 +201,8 @@ class ContactCard extends ConsumerWidget {
onPressed: (){
Navigator.pop(context);
},
text: context.translate(I18n.legacyDelete),
color: Color(0xFF588EA5),
text: context.translate(I18n.delete),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
))
@@ -221,7 +221,7 @@ class ContactCard extends ConsumerWidget {
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 14)),
DecoratedBox(
decoration: BoxDecoration(
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child:

View File

@@ -89,7 +89,7 @@ class EditContactScreen extends ConsumerWidget {
CustomTextField(
controller: vm.nameController,
hint: contact.name,
label: context.translate(I18n.legacyName),
label: context.translate(I18n.name),
),
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 26)),
Stack(
@@ -109,7 +109,7 @@ class EditContactScreen extends ConsumerWidget {
icon: DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF588EA5)
color: theme.getColorFor(ThemeCode.legacyPrimary)
),
child: Icon(
SFIcons.contactsCircle,
@@ -126,8 +126,8 @@ class EditContactScreen extends ConsumerWidget {
),
PrimaryButton(
onPressed: (){vm.updateContact(contact);},
text: context.translate(I18n.legacySave),
color: Color(0xFF588EA5)
text: context.translate(I18n.save),
color: theme.getColorFor(ThemeCode.legacyPrimary)
)
],
))

View File

@@ -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:functions/src/features/functions/widgets/call_watch_dialog.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
@@ -15,180 +16,120 @@ class FunctionsScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: SafeArea(
child: Column(
children: [
Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 8)
),
child: Stack(
children: [
IconButton(onPressed: () {Navigator.pop(context);},
icon: Icon(Icons.arrow_back)),
Center(
child: Text(context.translate('Device Features'),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 28, big: 27)
),
)
)
],
),
),
SizedBox(height: SizeUtils.getByScreen(small: 30, big: 28)),
Expanded(child: SingleChildScrollView(child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 8)
),
child: Column(
children: [
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.remoteConnection);},
icon: SFIcons.connection,
text: 'Remote connection'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.calendarCircle,
text: 'Calendar'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.contacts);},
icon: SFIcons.contactsCircle,
text: 'Contacts'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.doNotDisturbCircle,
text: 'Do not disturb'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.videoCallCircle,
text: 'Video call'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.healthCircle,
text: 'Health'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.healthCircle,
text: 'Activity meter'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.rewardsCircle,
text: 'Rewards'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
child: CallWatchDialog()
));},
icon: Icons.call_outlined,
text: 'Call watch'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.screenTime,
text: 'Apps use'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: Icons.app_registration_sharp,
text: 'Apps surveillance'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){},
icon: SFIcons.friendsCircle,
text: 'Make friends'
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.locateDevice);},
icon: SFIcons.locateSfCircle,
text: 'Locate your SaveFamily'
),
],
),
))),
],
)
),
);
}
}
class AppSectionButton extends ConsumerWidget {
final GestureTapCallback onPressed;
final IconData icon;
final String text;
const AppSectionButton({
required this.onPressed,
required this.icon,
required this.text,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return GestureDetector(
onTap: onPressed,
child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 14),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 12)
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.functions),
body: SingleChildScrollView(child: Container(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(big: 22, small: 21),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(SizeUtils.getByScreen(small: 12, big: 18))),
color: theme.getColorFor(ThemeCode.backgroundSecondary),
),
child: Row(
child: Column(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 0, big: 0)),
child: Icon(icon,
size: SizeUtils.getByScreen(small: 52, big: 48),
color: Color(0xFF588EA5),
weight: 30,
),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){navigationContract.pushTo(AppRoutes.remoteConnection);},
icon: SFIcons.connection,
text: context.translate(I18n.remoteConnection)
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){},
icon: SFIcons.calendarCircle,
negativeIcon: true,
text: context.translate(I18n.calendar)
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){navigationContract.pushTo(AppRoutes.contacts);},
icon: SFIcons.contactsCircle,
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.videoCallCircle,
negativeIcon: true,
text: context.translate(I18n.videoCall)
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){},
icon: SFIcons.healthCircle,
negativeIcon: true,
text: context.translate(I18n.health)
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){},
icon: SFIcons.healthCircle,
negativeIcon: true,
text: context.translate(I18n.activityMeter)
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){},
icon: SFIcons.rewardsCircle,
negativeIcon: true,
text: context.translate(I18n.rewards)
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
child: CallWatchDialog()
));},
icon: Icons.call_outlined,
iconSize: SizeUtils.getByScreen(small: 42, big: 40),
text: context.translate(I18n.callWatch)
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppMenuButton(
color: theme.getColorFor(ThemeCode.legacyPrimary),
onPressed: (){},
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: (){navigationContract.pushTo(AppRoutes.locateDevice);},
icon: SFIcons.locateSfCircle,
negativeIcon: true,
text: context.translate(I18n.locateSF)
),
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
Expanded(
child: Text(context.translate(text),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500
)
)
)
],
),
)
))
);
}
}

View File

@@ -9,6 +9,7 @@ class CallWatchDialog extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewModel = ref.read(callWatchViewModelProvider.notifier);
final viewState = ref.watch(callWatchViewModelProvider);
@@ -32,7 +33,7 @@ class CallWatchDialog extends ConsumerWidget {
alignment: Alignment.centerRight,
child: IconButton(
onPressed: (){Navigator.pop(context);},
icon: Icon(Icons.close, color: Color(0xFF588EA5)),
icon: Icon(Icons.close, color: theme.getColorFor(ThemeCode.legacyPrimary)),
)
)
],
@@ -59,7 +60,7 @@ class CallWatchDialog extends ConsumerWidget {
PrimaryButton(
onPressed: viewModel.call,
text: context.translate('Call me'),
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
),

View File

@@ -1,7 +1,7 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/features/locate_device/presentation/state/locate_device_view_model.dart';
import 'package:functions/src/features/locate_device/presentation/widgets/locate_device_dialog.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
@@ -15,8 +15,11 @@ class LocateDeviceScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return PageLayout(
title: context.translate('Find your device'),
final theme = ref.watch(themePortProvider);
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.locateSF),
body: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 16),
@@ -25,14 +28,14 @@ class LocateDeviceScreen extends ConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(context.translate('Locate your SaveFamily in nearby locations'),
Text(context.translate(I18n.locateSFBody1),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 17),
fontWeight: FontWeight.w500,
)
),
SizedBox(height: SizeUtils.getByScreen(small: 10, big: 8)),
Text('This function allows you to find the device indoors or in places close to you. When you press the button, the smartwatch will start beeping and you will be able to hear it.',
Text(context.translate(I18n.locateSFBody2),
style: TextStyle(
fontSize: 16
))
@@ -50,99 +53,11 @@ class LocateDeviceScreen extends ConsumerWidget {
child: LocateDialog(),
));
},
text: context.translate('Locate my SaveFamily'),
color: Color(0xFF588EA5),
text: context.translate(I18n.locateSFAction),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 36, big: 35),
),
)
);
}
}
class LocateDialog extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewModel = ref.read(locateDeviceViewModelProvider.notifier);
final viewState = ref.watch(locateDeviceViewModelProvider);
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
),
width: SizeUtils.getByScreen(small: 360, big: 350),
height: SizeUtils.getByScreen(small: 210, big: 205),
child: Column(
children: [
if (viewState.isLoading)
Expanded(child: Center(child: Text(context.translate('Sending...'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 25, big: 24),
),
))),
if (viewState.isComplete)
Expanded(child: Center(child: Text(context.translate('Sent successfully'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 25, big: 24),
),
))),
if (viewState.errorMessage.isNotEmpty) ...[
Expanded(child: Center(child: Text(context.translate('The device is not connected to the Internet'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 20, big: 19),
),
))),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 23)),
Expanded(child: PrimaryButton(
onPressed: (){
Navigator.pop(context);
viewModel.endLocation();
},
text: context.translate('OK'),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
],
if (!viewState.isComplete && !viewState.isLoading && viewState.errorMessage.isEmpty) ...[
Text(context.translate('You are going to activate the remote location of your device. It will start ringing.'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 19, big: 18),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 23)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: PrimaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.legacyCancel),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
SizedBox(width: SizeUtils.getByScreen(small: 4, big: 16)),
Expanded(child: PrimaryButton(
onPressed: () {
viewModel.locateDevice();
},
text: context.translate(I18n.accept),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
))
],
)
]
],
),
);
}
}

View File

@@ -0,0 +1,95 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/features/locate_device/presentation/state/locate_device_view_model.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class LocateDialog extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewModel = ref.read(locateDeviceViewModelProvider.notifier);
final viewState = ref.watch(locateDeviceViewModelProvider);
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
),
width: SizeUtils.getByScreen(small: 360, big: 350),
height: SizeUtils.getByScreen(small: 210, big: 205),
child: Column(
children: [
if (viewState.isLoading)
Expanded(child: Center(child: Text(context.translate('Sending...'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 25, big: 24),
),
))),
if (viewState.isComplete)
Expanded(child: Center(child: Text(context.translate('Sent successfully'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 25, big: 24),
),
))),
if (viewState.errorMessage.isNotEmpty) ...[
Expanded(child: Center(child: Text(context.translate('The device is not connected to the Internet'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 20, big: 19),
),
))),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 23)),
Expanded(child: PrimaryButton(
onPressed: (){
Navigator.pop(context);
viewModel.endLocation();
},
text: context.translate('OK'),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
],
if (!viewState.isComplete && !viewState.isLoading && viewState.errorMessage.isEmpty) ...[
Text(context.translate('You are going to activate the remote location of your device. It will start ringing.'),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 19, big: 18),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 23)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: PrimaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
)),
SizedBox(width: SizeUtils.getByScreen(small: 4, big: 16)),
Expanded(child: PrimaryButton(
onPressed: () {
viewModel.locateDevice();
},
text: context.translate(I18n.accept),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
))
],
)
]
],
),
);
}
}

View File

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

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$PictureEntity {
String get id; String? get userId; int get createdAt;
String get id; String? get deviceId; DateTime get createdAt; DateTime get takenAt; String get asset;
/// 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.userId, userId) || other.userId == userId)&&(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.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.takenAt, takenAt) || other.takenAt == takenAt)&&(identical(other.asset, asset) || other.asset == asset));
}
@override
int get hashCode => Object.hash(runtimeType,id,userId,createdAt);
int get hashCode => Object.hash(runtimeType,id,deviceId,createdAt,takenAt,asset);
@override
String toString() {
return 'PictureEntity(id: $id, userId: $userId, createdAt: $createdAt)';
return 'PictureEntity(id: $id, deviceId: $deviceId, createdAt: $createdAt, takenAt: $takenAt, asset: $asset)';
}
@@ -45,7 +45,7 @@ abstract mixin class $PictureEntityCopyWith<$Res> {
factory $PictureEntityCopyWith(PictureEntity value, $Res Function(PictureEntity) _then) = _$PictureEntityCopyWithImpl;
@useResult
$Res call({
String id, String? userId, int createdAt
String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset
});
@@ -62,12 +62,14 @@ 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? userId = freezed,Object? createdAt = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? deviceId = freezed,Object? createdAt = null,Object? takenAt = null,Object? asset = null,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,userId: freezed == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,deviceId: freezed == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
as DateTime,takenAt: null == takenAt ? _self.takenAt : takenAt // ignore: cast_nullable_to_non_nullable
as DateTime,asset: null == asset ? _self.asset : asset // ignore: cast_nullable_to_non_nullable
as String,
));
}
@@ -152,10 +154,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? userId, int createdAt)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PictureEntity() when $default != null:
return $default(_that.id,_that.userId,_that.createdAt);case _:
return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asset);case _:
return orElse();
}
@@ -173,10 +175,10 @@ return $default(_that.id,_that.userId,_that.createdAt);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? userId, int createdAt) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset) $default,) {final _that = this;
switch (_that) {
case _PictureEntity():
return $default(_that.id,_that.userId,_that.createdAt);case _:
return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asset);case _:
throw StateError('Unexpected subclass');
}
@@ -193,10 +195,10 @@ return $default(_that.id,_that.userId,_that.createdAt);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? userId, int createdAt)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset)? $default,) {final _that = this;
switch (_that) {
case _PictureEntity() when $default != null:
return $default(_that.id,_that.userId,_that.createdAt);case _:
return $default(_that.id,_that.deviceId,_that.createdAt,_that.takenAt,_that.asset);case _:
return null;
}
@@ -208,12 +210,14 @@ return $default(_that.id,_that.userId,_that.createdAt);case _:
class _PictureEntity implements PictureEntity {
const _PictureEntity({required this.id, required this.userId, required this.createdAt});
const _PictureEntity({required this.id, required this.deviceId, required this.createdAt, required this.takenAt, required this.asset});
@override final String id;
@override final String? userId;
@override final int createdAt;
@override final String? deviceId;
@override final DateTime createdAt;
@override final DateTime takenAt;
@override final String asset;
/// Create a copy of PictureEntity
/// with the given fields replaced by the non-null parameter values.
@@ -225,16 +229,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.userId, userId) || other.userId == userId)&&(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.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.takenAt, takenAt) || other.takenAt == takenAt)&&(identical(other.asset, asset) || other.asset == asset));
}
@override
int get hashCode => Object.hash(runtimeType,id,userId,createdAt);
int get hashCode => Object.hash(runtimeType,id,deviceId,createdAt,takenAt,asset);
@override
String toString() {
return 'PictureEntity(id: $id, userId: $userId, createdAt: $createdAt)';
return 'PictureEntity(id: $id, deviceId: $deviceId, createdAt: $createdAt, takenAt: $takenAt, asset: $asset)';
}
@@ -245,7 +249,7 @@ abstract mixin class _$PictureEntityCopyWith<$Res> implements $PictureEntityCopy
factory _$PictureEntityCopyWith(_PictureEntity value, $Res Function(_PictureEntity) _then) = __$PictureEntityCopyWithImpl;
@override @useResult
$Res call({
String id, String? userId, int createdAt
String id, String? deviceId, DateTime createdAt, DateTime takenAt, String asset
});
@@ -262,12 +266,14 @@ 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? userId = freezed,Object? createdAt = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? deviceId = freezed,Object? createdAt = null,Object? takenAt = null,Object? asset = null,}) {
return _then(_PictureEntity(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,userId: freezed == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,deviceId: freezed == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
as String?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as int,
as DateTime,takenAt: null == takenAt ? _self.takenAt : takenAt // ignore: cast_nullable_to_non_nullable
as DateTime,asset: null == asset ? _self.asset : asset // ignore: cast_nullable_to_non_nullable
as String,
));
}

View File

@@ -8,7 +8,13 @@ class GetPicturesUseCaseImpl implements GetPicturesUseCase {
final FunctionsRepository _repository;
@override
Future<List<PictureEntity>> getPictures({required String userId}) {
return _repository.getPictures(userId: userId);
Future<List<PictureEntity>> getPictures({required String userId}) async {
// return _repository.getPictures(userId: userId);
return [
PictureEntity(id: '1', deviceId: '1111', createdAt: DateTime.now(), asset: 'assets/images/ui/iso_sf.png', takenAt: DateTime.now()),
PictureEntity(id: '2', deviceId: '1111', createdAt: DateTime.now(), asset: 'assets/images/ui/iso_sf.png', takenAt: DateTime.now()),
PictureEntity(id: '3', deviceId: '1111', createdAt: DateTime.now(), asset: 'assets/images/ui/iso_sf.png', takenAt: DateTime.now()),
PictureEntity(id: '4', deviceId: '1111', createdAt: DateTime.now(), asset: 'assets/images/ui/iso_sf.png', takenAt: DateTime.now()),
];
}
}

View File

@@ -1,9 +1,8 @@
// import 'package:account/src/features/linked_devices/presentation/app_users_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:functions/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
import 'package:functions/src/features/remote_connection/presentation/widgets/show_picture_dialog.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
@@ -18,59 +17,115 @@ class RemoteCameraScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewModel = ref.read(remoteConnectionViewModelProvider.notifier);
final viewState = ref.watch(remoteConnectionViewModelProvider);
final isLoadingPictures = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.isLoadingPictures)
);
return PageLayout(
title: context.translate('Remote camera'),
body: Expanded(child: GridView.count(
primary: false,
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
big: EdgeInsets.symmetric(horizontal: 23, vertical: 11)
),
crossAxisSpacing: 11,
mainAxisSpacing: 11,
crossAxisCount: 3,
childAspectRatio: 0.8,
children: List<Widget>.generate(12, (int index)=>
Container(
height: SizeUtils.getByScreen(small: 110, big: 105),
width: SizeUtils.getByScreen(small: 60, big: 58),
color: Colors.grey[200],
child: SvgPicture.asset('assets/images/ui/face.svg'),
)
),
)),
footer: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(vertical: 12, horizontal: 26),
big: EdgeInsets.symmetric(vertical: 10, horizontal: 25)
),
child: PrimaryButton(
onPressed: () async {
showDialog(context: context, builder: (context)=>Dialog(
child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
),
width: SizeUtils.getByScreen(small: 360, big: 350),
height: SizeUtils.getByScreen(small: 195, big: 185),
child: Center(child: Text(context.translate('Loading photo...'),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 26, big: 25)),
)),
),
));
await viewModel.takePicture();
Navigator.pop(context);
},
text: context.translate('Take a picture'),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 36, big: 35),
),
)
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.remoteCamera),
body: Expanded(child: isLoadingPictures
? const Center(child: CircularProgressIndicator())
: const _GallerySection()
),
footer: _TakePictureSection(),
);
}
}
class _GallerySection extends ConsumerWidget {
const _GallerySection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
final pictures = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.pictures)
);
return GridView.count(
primary: false,
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
big: EdgeInsets.symmetric(horizontal: 23, vertical: 11)
),
crossAxisSpacing: 11,
mainAxisSpacing: 11,
crossAxisCount: 3,
childAspectRatio: 0.8,
children: List<Widget>.generate(pictures.length, (int index)=>
TextButton(
onPressed: (){
vm.setPictureIndex(index);
showDialog(context: context, builder: (context)=>Dialog(
child: ShowPictureDialog(),
));
},
child: Container(
height: SizeUtils.getByScreen(small: 110, big: 105),
width: SizeUtils.getByScreen(small: 60, big: 58),
decoration: BoxDecoration(
border: Border.fromBorderSide(BorderSide(
color: theme.getColorFor(ThemeCode.textTertiary)
))
),
child: Column(
children: [
Image.asset(pictures[index].asset),
],
)
)
)
),
);
}
}
class _TakePictureSection extends ConsumerWidget {
const _TakePictureSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
return Padding(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(vertical: 12, horizontal: 26),
big: EdgeInsets.symmetric(vertical: 10, horizontal: 25)
),
child: PrimaryButton(
onPressed: () async {
showDialog(context: context, builder: (context)=>Dialog(
child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
),
width: SizeUtils.getByScreen(small: 360, big: 350),
height: SizeUtils.getByScreen(small: 195, big: 185),
child: Center(child: Text(context.translate('Loading photo...'),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 26, big: 25)),
)),
),
));
await vm.takePicture();
Navigator.pop(context);
},
text: context.translate(I18n.takePicture),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 36, big: 35),
),
);
}
}

View File

@@ -1,9 +1,8 @@
// import 'package:account/src/features/linked_devices/presentation/app_users_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/features/remote_connection/presentation/remote_camera_screen.dart';
import 'package:functions/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
import 'package:functions/src/features/remote_connection/presentation/widgets/spy_call_dialog.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
@@ -16,35 +15,32 @@ class RemoteConnectionScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// final theme = ref.watch(themePortProvider);
final theme = ref.watch(themePortProvider);
final viewModel = ref.read(remoteConnectionViewModelProvider.notifier);
final viewState = ref.watch(remoteConnectionViewModelProvider);
return PageLayout(
title: 'Remote Connection',
body: SingleChildScrollView(child: Container(
return LegacyPageLayout(
theme: theme,
title: context.translate(I18n.remoteConnection),
body: SingleChildScrollView(child: Padding(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 8)
),
child: Column(
children: [
AppSectionButton(
_SectionButton(
onPressed: (){Navigator.push(context, MaterialPageRoute(
builder: (_)=>RemoteCameraScreen(navigationContract: navigationContract)
));},
icon: Icons.photo_camera_outlined,
text: 'Remote camera'
text: I18n.remoteCamera
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 15)),
AppSectionButton(
_SectionButton(
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
child: CallDialog(
)
child: SpyCallDialog()
));},
icon: SFIcons.listen,
text: 'Remote listening'
text: I18n.remoteListening
),
],
),
@@ -53,82 +49,13 @@ class RemoteConnectionScreen extends ConsumerWidget {
}
}
class CallDialog extends ConsumerWidget {
class _SectionButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewModel = ref.read(remoteConnectionViewModelProvider.notifier);
final viewState = ref.watch(remoteConnectionViewModelProvider);
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 26, vertical: 20),
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18)
),
width: SizeUtils.getByScreen(small: 390, big: 380),
height: SizeUtils.getByScreen(small: 250, big: 243),
child: Column(
children: [
Stack(
children: [
Center(child: Text(context.translate('Remote listening'),
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: Color(0xFF588EA5)),
)
)
],
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
CustomTextField(
controller: viewModel.phoneController,
hint: context.translate('Insert your phone number'),
keyboardType: TextInputType.number,
),
if (viewState.errorMessage.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
viewState.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
),
],
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
PrimaryButton(
onPressed: () async {
await viewModel.call();
if (viewState.errorMessage.isEmpty){
Navigator.pop(context);
}
},
text: context.translate('Call me'),
color: Color(0xFF588EA5),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
),
],
),
);
}
}
class AppSectionButton extends ConsumerWidget {
final GestureTapCallback onPressed;
final VoidCallback onPressed;
final IconData icon;
final String text;
const AppSectionButton({
const _SectionButton({
required this.onPressed,
required this.icon,
required this.text,
@@ -136,45 +63,23 @@ class AppSectionButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return GestureDetector(
onTap: onPressed,
child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 20),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 18)
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(SizeUtils.getByScreen(small: 12, big: 18))),
color: theme.getColorFor(ThemeCode.backgroundSecondary),
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
child: Icon(icon,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: Color(0xFF588EA5),
weight: 30,
),
),
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
Expanded(
child: Text(context.translate(text),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500
)
)
)
],
),
return SectionButton(
onPressed: onPressed,
icon: Icon(icon,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
body: Text(context.translate(text),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500
)
)
);
}
}

View File

@@ -6,9 +6,7 @@ import 'package:functions/src/features/remote_connection/domain/take_picture_use
import 'package:functions/src/features/remote_connection/presentation/providers/get_pictures_use_case_provider.dart';
import 'package:functions/src/features/remote_connection/presentation/providers/take_picture_use_case_provider.dart';
import 'package:functions/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
// import 'package:legacy_shared/src/providers/logged_user_provider.dart';
// import 'package:legacy_shared/src/data/models/entities/user_entity.dart';
// import 'package:sf_localizations/sf_localizations.dart';
import 'package:legacy_shared/legacy_shared.dart';
final remoteConnectionViewModelProvider =
NotifierProvider.autoDispose<RemoteConnectionViewModel, RemoteConnectionViewState>(
@@ -21,8 +19,6 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
late final TextEditingController phoneController;
// late final UserEntity loggedUser;
static final RegExp _phoneRegex = RegExp(r'^\+?\d{6,15}$');
@override
@@ -30,22 +26,27 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
_getPicturesUseCase = ref.read(getPicturesUseCaseProvider);
_takePictureUseCase = ref.read(takePictureUseCaseProvider);
// loggedUser = ref.read(loggedUserProvider);
phoneController = TextEditingController();
phoneController.addListener(_onPhoneChanged);
_getPicturesUseCase.getPictures(userId: '').then(setImages);
ref.onDispose(disposeControllers);
Future.microtask(load);
return const RemoteConnectionViewState();
}
Future<void> load() async {
final loggedUser = await ref.read(loggedUserProvider.future);
final pictures = await _getPicturesUseCase.getPictures(userId: loggedUser.id);
setImages(pictures);
}
void setImages(List<PictureEntity> pictures) {
state = state.copyWith(
pictures: pictures
pictures: pictures,
isLoadingPictures: false,
);
}
@@ -56,6 +57,32 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
state = state.copyWith(phone: text, errorMessage: '');
}
void prevPicture() {
int pictureIndex = state.pictureIndex - 1;
if (pictureIndex < 0) {
pictureIndex = state.pictures.length - 1;
}
state = state.copyWith(
pictureIndex: pictureIndex
);
}
void nextPicture() {
int pictureIndex = (state.pictureIndex + 1) % state.pictures.length;
state = state.copyWith(
pictureIndex: pictureIndex
);
}
void setPictureIndex(int value) {
state = state.copyWith(
pictureIndex: value,
);
}
Future<void> takePicture() async {
try {
state = state.copyWith(isTakingPicture: true);
@@ -63,7 +90,7 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
await _takePictureUseCase.takePicture(userId: '')
.then((picture) {
List<PictureEntity> pictures = state.pictures;
//pictures.add(picture);
pictures.add(picture);
state = state.copyWith(
isTakingPicture: true,
);
@@ -86,7 +113,6 @@ class RemoteConnectionViewModel extends Notifier<RemoteConnectionViewState> {
state = state.copyWith(errorMessage: 'errorMessagePhoneIsInvalid');
return;
}
}
void disposeControllers() {

View File

@@ -8,7 +8,8 @@ abstract class RemoteConnectionViewState with _$RemoteConnectionViewState {
const factory RemoteConnectionViewState({
@Default('') String phone,
@Default([]) List<PictureEntity> pictures,
@Default(true) bool isLoadingImages,
@Default(0) int pictureIndex,
@Default(true) bool isLoadingPictures,
@Default(false) bool isTakingPicture,
@Default(false) bool isCalling,
@Default('') String errorMessage

View File

@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$RemoteConnectionViewState {
String get phone; List<PictureEntity> get pictures; bool get isLoadingImages; bool get isTakingPicture; bool get isCalling; String get errorMessage;
String get phone; List<PictureEntity> get pictures; int get pictureIndex; bool get isLoadingPictures; bool get isTakingPicture; bool get isCalling; String get errorMessage;
/// Create a copy of RemoteConnectionViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -25,16 +25,16 @@ $RemoteConnectionViewStateCopyWith<RemoteConnectionViewState> get copyWith => _$
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteConnectionViewState&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other.pictures, pictures)&&(identical(other.isLoadingImages, isLoadingImages) || other.isLoadingImages == isLoadingImages)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is RemoteConnectionViewState&&(identical(other.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));
}
@override
int get hashCode => Object.hash(runtimeType,phone,const DeepCollectionEquality().hash(pictures),isLoadingImages,isTakingPicture,isCalling,errorMessage);
int get hashCode => Object.hash(runtimeType,phone,const DeepCollectionEquality().hash(pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage);
@override
String toString() {
return 'RemoteConnectionViewState(phone: $phone, pictures: $pictures, isLoadingImages: $isLoadingImages, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage)';
return 'RemoteConnectionViewState(phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage)';
}
@@ -45,7 +45,7 @@ abstract mixin class $RemoteConnectionViewStateCopyWith<$Res> {
factory $RemoteConnectionViewStateCopyWith(RemoteConnectionViewState value, $Res Function(RemoteConnectionViewState) _then) = _$RemoteConnectionViewStateCopyWithImpl;
@useResult
$Res call({
String phone, List<PictureEntity> pictures, bool isLoadingImages, bool isTakingPicture, bool isCalling, String errorMessage
String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage
});
@@ -62,11 +62,12 @@ class _$RemoteConnectionViewStateCopyWithImpl<$Res>
/// Create a copy of RemoteConnectionViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? phone = null,Object? pictures = null,Object? isLoadingImages = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,}) {
return _then(_self.copyWith(
phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,pictures: null == pictures ? _self.pictures : pictures // ignore: cast_nullable_to_non_nullable
as List<PictureEntity>,isLoadingImages: null == isLoadingImages ? _self.isLoadingImages : isLoadingImages // ignore: cast_nullable_to_non_nullable
as List<PictureEntity>,pictureIndex: null == pictureIndex ? _self.pictureIndex : pictureIndex // ignore: cast_nullable_to_non_nullable
as int,isLoadingPictures: null == isLoadingPictures ? _self.isLoadingPictures : isLoadingPictures // ignore: cast_nullable_to_non_nullable
as bool,isTakingPicture: null == isTakingPicture ? _self.isTakingPicture : isTakingPicture // ignore: cast_nullable_to_non_nullable
as bool,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
@@ -155,10 +156,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String phone, List<PictureEntity> pictures, bool isLoadingImages, bool isTakingPicture, bool isCalling, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _RemoteConnectionViewState() when $default != null:
return $default(_that.phone,_that.pictures,_that.isLoadingImages,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return orElse();
}
@@ -176,10 +177,10 @@ return $default(_that.phone,_that.pictures,_that.isLoadingImages,_that.isTakingP
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String phone, List<PictureEntity> pictures, bool isLoadingImages, bool isTakingPicture, bool isCalling, String errorMessage) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage) $default,) {final _that = this;
switch (_that) {
case _RemoteConnectionViewState():
return $default(_that.phone,_that.pictures,_that.isLoadingImages,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
throw StateError('Unexpected subclass');
}
@@ -196,10 +197,10 @@ return $default(_that.phone,_that.pictures,_that.isLoadingImages,_that.isTakingP
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String phone, List<PictureEntity> pictures, bool isLoadingImages, bool isTakingPicture, bool isCalling, String errorMessage)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage)? $default,) {final _that = this;
switch (_that) {
case _RemoteConnectionViewState() when $default != null:
return $default(_that.phone,_that.pictures,_that.isLoadingImages,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return $default(_that.phone,_that.pictures,_that.pictureIndex,_that.isLoadingPictures,_that.isTakingPicture,_that.isCalling,_that.errorMessage);case _:
return null;
}
@@ -211,7 +212,7 @@ return $default(_that.phone,_that.pictures,_that.isLoadingImages,_that.isTakingP
class _RemoteConnectionViewState implements RemoteConnectionViewState {
const _RemoteConnectionViewState({this.phone = '', final List<PictureEntity> pictures = const [], this.isLoadingImages = true, this.isTakingPicture = false, this.isCalling = false, this.errorMessage = ''}): _pictures = pictures;
const _RemoteConnectionViewState({this.phone = '', final List<PictureEntity> pictures = const [], this.pictureIndex = 0, this.isLoadingPictures = true, this.isTakingPicture = false, this.isCalling = false, this.errorMessage = ''}): _pictures = pictures;
@override@JsonKey() final String phone;
@@ -222,7 +223,8 @@ class _RemoteConnectionViewState implements RemoteConnectionViewState {
return EqualUnmodifiableListView(_pictures);
}
@override@JsonKey() final bool isLoadingImages;
@override@JsonKey() final int pictureIndex;
@override@JsonKey() final bool isLoadingPictures;
@override@JsonKey() final bool isTakingPicture;
@override@JsonKey() final bool isCalling;
@override@JsonKey() final String errorMessage;
@@ -237,16 +239,16 @@ _$RemoteConnectionViewStateCopyWith<_RemoteConnectionViewState> get copyWith =>
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteConnectionViewState&&(identical(other.phone, phone) || other.phone == phone)&&const DeepCollectionEquality().equals(other._pictures, _pictures)&&(identical(other.isLoadingImages, isLoadingImages) || other.isLoadingImages == isLoadingImages)&&(identical(other.isTakingPicture, isTakingPicture) || other.isTakingPicture == isTakingPicture)&&(identical(other.isCalling, isCalling) || other.isCalling == isCalling)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _RemoteConnectionViewState&&(identical(other.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));
}
@override
int get hashCode => Object.hash(runtimeType,phone,const DeepCollectionEquality().hash(_pictures),isLoadingImages,isTakingPicture,isCalling,errorMessage);
int get hashCode => Object.hash(runtimeType,phone,const DeepCollectionEquality().hash(_pictures),pictureIndex,isLoadingPictures,isTakingPicture,isCalling,errorMessage);
@override
String toString() {
return 'RemoteConnectionViewState(phone: $phone, pictures: $pictures, isLoadingImages: $isLoadingImages, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage)';
return 'RemoteConnectionViewState(phone: $phone, pictures: $pictures, pictureIndex: $pictureIndex, isLoadingPictures: $isLoadingPictures, isTakingPicture: $isTakingPicture, isCalling: $isCalling, errorMessage: $errorMessage)';
}
@@ -257,7 +259,7 @@ abstract mixin class _$RemoteConnectionViewStateCopyWith<$Res> implements $Remot
factory _$RemoteConnectionViewStateCopyWith(_RemoteConnectionViewState value, $Res Function(_RemoteConnectionViewState) _then) = __$RemoteConnectionViewStateCopyWithImpl;
@override @useResult
$Res call({
String phone, List<PictureEntity> pictures, bool isLoadingImages, bool isTakingPicture, bool isCalling, String errorMessage
String phone, List<PictureEntity> pictures, int pictureIndex, bool isLoadingPictures, bool isTakingPicture, bool isCalling, String errorMessage
});
@@ -274,11 +276,12 @@ class __$RemoteConnectionViewStateCopyWithImpl<$Res>
/// Create a copy of RemoteConnectionViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? phone = null,Object? pictures = null,Object? isLoadingImages = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? phone = null,Object? pictures = null,Object? pictureIndex = null,Object? isLoadingPictures = null,Object? isTakingPicture = null,Object? isCalling = null,Object? errorMessage = null,}) {
return _then(_RemoteConnectionViewState(
phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,pictures: null == pictures ? _self._pictures : pictures // ignore: cast_nullable_to_non_nullable
as List<PictureEntity>,isLoadingImages: null == isLoadingImages ? _self.isLoadingImages : isLoadingImages // ignore: cast_nullable_to_non_nullable
as List<PictureEntity>,pictureIndex: null == pictureIndex ? _self.pictureIndex : pictureIndex // ignore: cast_nullable_to_non_nullable
as int,isLoadingPictures: null == isLoadingPictures ? _self.isLoadingPictures : isLoadingPictures // ignore: cast_nullable_to_non_nullable
as bool,isTakingPicture: null == isTakingPicture ? _self.isTakingPicture : isTakingPicture // ignore: cast_nullable_to_non_nullable
as bool,isCalling: null == isCalling ? _self.isCalling : isCalling // ignore: cast_nullable_to_non_nullable
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable

View File

@@ -0,0 +1,105 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:functions/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
import 'package:utils/utils.dart';
class ShowPictureDialog extends ConsumerWidget {
const ShowPictureDialog();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final viewModel = ref.read(remoteConnectionViewModelProvider.notifier);
final pictures = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.pictures)
);
final pictureIndex = ref.watch(
remoteConnectionViewModelProvider.select((s)=>s.pictureIndex)
);
return Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(8))
),
height: SizeUtils.getByScreen(small: 350, big: 340),
child: Column(
children: [
_PictureSection(asset: pictures[pictureIndex].asset),
_MetadataSection(picture: pictures[pictureIndex]),
_ControlsSection(
prev: viewModel.prevPicture,
next: viewModel.nextPicture,
),
],
),
);
}
}
class _PictureSection extends ConsumerWidget {
final String asset;
const _PictureSection({
required this.asset,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Expanded(
child: Center(
child: Image.asset(asset),
)
);
}
}
class _MetadataSection extends ConsumerWidget {
final PictureEntity picture;
const _MetadataSection({
required this.picture,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Column(
children: [
Text(picture.createdAt.toString())
],
);
}
}
class _ControlsSection extends ConsumerWidget {
final VoidCallback prev;
final VoidCallback next;
const _ControlsSection({
required this.prev,
required this.next
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(onPressed: prev, icon: Icon(Icons.arrow_back_ios_new_rounded)),
IconButton(onPressed: next, icon: Icon(Icons.arrow_forward_ios_rounded)),
],
);
}
}

View File

@@ -0,0 +1,156 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:functions/src/features/remote_connection/presentation/state/remote_connection_view_model.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class SpyCallDialog extends ConsumerWidget {
Future<void> _onCall(BuildContext context, WidgetRef ref) async {
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
await vm.call();
if (!context.mounted) return;
final errorMessage = ref.read(
remoteConnectionViewModelProvider.select((s)=>s.errorMessage)
);
if (errorMessage.isNotEmpty) return;
Navigator.pop(context);
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 26, vertical: 20),
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18)
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
color: theme.getColorFor(ThemeCode.backgroundSecondary)
),
width: SizeUtils.getByScreen(small: 390, big: 380),
height: SizeUtils.getByScreen(small: 250, big: 243),
child: Column(
children: [
_Header(theme: theme),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_PhoneSection(onSubmit: () {_onCall(context, ref);}),
const _ErrorMessageSection(),
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
_CallSection(onPressed: () {_onCall(context, ref);}),
],
),
);
}
}
class _Header extends StatelessWidget {
final ThemePort theme;
const _Header({
required this.theme,
});
@override
Widget build(BuildContext context) {
return Stack(
children: [
Center(child: Text(context.translate('Remote listening'),
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)),
)
)
],
);
}
}
class _PhoneSection extends ConsumerWidget {
final VoidCallback onSubmit;
const _PhoneSection({
required this.onSubmit,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(remoteConnectionViewModelProvider.notifier);
return CustomTextField(
controller: vm.phoneController,
hint: context.translate(I18n.insertPhone),
keyboardType: TextInputType.number,
onSubmitted: (_) => onSubmit(),
);
}
}
class _ErrorMessageSection extends ConsumerWidget {
const _ErrorMessageSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewState = ref.watch(remoteConnectionViewModelProvider);
if (viewState.errorMessage.isNotEmpty) {
return Column(
children: [
const SizedBox(height: 4),
Text(
viewState.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
),
],
);
} else return SizedBox.shrink();
}
}
class _CallSection extends ConsumerWidget {
final VoidCallback onPressed;
const _CallSection({
required this.onPressed,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return PrimaryButton(
onPressed: onPressed,
text: context.translate(I18n.call),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: SizeUtils.getByScreen(small: 38, big: 36),
radius: SizeUtils.getByScreen(small: 32, big: 34),
);
}
}

View File

@@ -16,7 +16,7 @@ class HomeRemoteDatasourceImpl implements HomeRemoteDatasource {
@override
Future<List<DeviceEntity>> getDevices({required String userId}) async {
try {
final response = await _repository.get<Map<String, dynamic>>(
/*final response = await _repository.get<Map<String, dynamic>>(
'/$userId/devices',
);
final data = response.data!['items'];
@@ -24,17 +24,57 @@ class HomeRemoteDatasourceImpl implements HomeRemoteDatasource {
throw Exception('Empty response from /:userId/devices');
}
final model = GetDevicesResponseModel.fromJson(data);
/*final model = GetDevicesResponseModel(items: [
final model = GetDevicesResponseModel.fromJson(data);*/
final model = GetDevicesResponseModel(items: [
GetDevicesItemResponseModel(
id: '1',
identificator: '1111',
carrierName: 'Carlos'),
carrierName: 'Carlos',
phone: '111111111',
settings: GetDevicesSettingsResponseModel(
frequency: 0,
frequencyHeartRate: 0,
timezone: 0,
pedometer: true,
language: 'es',
alerts: []
),
protocol: '',
type: '',
connectionServer: '',
createdAt: 0,
flags: GetDevicesFlagsResponseModel(
isInOrOut: 'out',
geofenceId: '1',
isBatteryLow: false,
isDisconnect: false
)
),
GetDevicesItemResponseModel(
id: '2',
identificator: '1112',
carrierName: 'Ana'),
]);*/
id: '2',
identificator: '1112',
carrierName: 'Ana',
phone: '222222222',
settings: GetDevicesSettingsResponseModel(
frequency: 0,
frequencyHeartRate: 0,
timezone: 0,
pedometer: true,
language: 'es',
alerts: []
),
protocol: '',
type: '',
connectionServer: '',
createdAt: 0,
flags: GetDevicesFlagsResponseModel(
isInOrOut: 'out',
geofenceId: '2',
isBatteryLow: false,
isDisconnect: false
)
),
]);
return model.toEntity();
} on DioException catch (error) {
throw _mapDioError(

View File

@@ -25,9 +25,6 @@ class HubScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewState = ref.watch(hubViewModelProvider);
final viewModel = ref.read(hubViewModelProvider.notifier);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: SafeArea(
@@ -36,83 +33,15 @@ class HubScreen extends ConsumerWidget {
child: Column(
children: [
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 4)),
Stack(
children: [
SizedBox(
height: SizeUtils.getByScreen(small: 36, big: 36),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset('assets/images/ui/iso_sf.png',
height: SizeUtils.getByScreen(small: 18, big: 18)),
SizedBox(width: SizeUtils.getByScreen(small: 8, big: 4)),
SizedBox(
width: SizeUtils.getByScreen(small: 104, big: 100),
height: 32,
child: CustomDropdown(
items: viewState.devices.map((DeviceEntity device)=>
Text(device.carrierName)
).toList(),
values: viewState.devices,
value: viewState.selectedDevice,
onChanged: (device){viewModel.setSelectedDevice(device);},
height: 32,
color: Colors.transparent,
padding: EdgeInsets.zero,
),
),
]
)
),
Center(
child: SvgPicture.asset('assets/images/ui/logo_sf.svg',
height: SizeUtils.getByScreen(small: 36, big: 36))
),
],
),
_Header(),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 14)),
Expanded(child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.customerService);},
icon: SFIcons.customerService,
text: I18n.customerService),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.dashboardHome);},
icon: SFIcons.payments,
text: I18n.sfPay),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.dashboardFunctions);},
icon: SFIcons.functions,
text: I18n.functions),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
AppSectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.accountSettings);},
icon: Icons.manage_accounts_outlined,
text: I18n.accountSettings),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
AppSectionButton(
onPressed: (){},
icon: Icons.settings_outlined,
text: I18n.deviceSettings),
_MenuSection(navigationContract: navigationContract),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 22)),
Text(context.translate(I18n.watchesOnMap),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 20, big: 19),
fontWeight: FontWeight.bold,
color: Color(0xFF588EA5),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 4, big: 8)),
SizedBox(
height: SizeUtils.getByScreen(small: 200, big: 300),
child: Minimap(),
),
_MapSection(),
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 13)),
],
),
@@ -125,76 +54,165 @@ class HubScreen extends ConsumerWidget {
}
}
class AppSectionButton extends ConsumerWidget {
class _Header extends ConsumerWidget {
final GestureTapCallback onPressed;
final IconData icon;
final String text;
const _Header();
const AppSectionButton({
required this.onPressed,
required this.icon,
required this.text,
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(hubViewModelProvider);
final vm = ref.read(hubViewModelProvider.notifier);
return Stack(
children: [
SizedBox(
height: SizeUtils.getByScreen(small: 36, big: 36),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset('assets/images/ui/iso_sf.png',
height: SizeUtils.getByScreen(small: 18, big: 18)),
SizedBox(width: SizeUtils.getByScreen(small: 8, big: 4)),
SizedBox(
width: SizeUtils.getByScreen(small: 104, big: 100),
height: 32,
child: CustomDropdown(
items: state.devices.map((DeviceEntity device)=>
Text(
device.carrierName,
overflow: TextOverflow.ellipsis)
).toList(),
values: state.devices,
value: state.selectedDevice,
onChanged: (device){vm.setSelectedDevice(device);},
height: 32,
color: Colors.transparent,
padding: EdgeInsets.zero,
),
),
]
)
),
Center(
child: SvgPicture.asset('assets/images/ui/logo_sf.svg',
height: SizeUtils.getByScreen(small: 36, big: 36))
),
],
);
}
}
class _MenuSection extends ConsumerWidget {
final NavigationContract navigationContract;
const _MenuSection({
required this.navigationContract,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Column(
children: [
_SectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.customerService);},
icon: SFIcons.customerService,
text: I18n.customerService),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_SectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.dashboardHome);},
icon: SFIcons.payments,
text: I18n.sfPay),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_SectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.dashboardFunctions);},
icon: SFIcons.functions,
text: I18n.functions),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_SectionButton(
onPressed: (){navigationContract.pushTo(AppRoutes.accountSettings);},
icon: Icons.manage_accounts_outlined,
text: I18n.accountSettings),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
_SectionButton(
onPressed: (){},
icon: Icons.settings_outlined,
text: I18n.deviceSettings),
],
);
}
}
class _SectionButton extends ConsumerWidget {
final VoidCallback onPressed;
final IconData icon;
final String text;
const _SectionButton({
required this.onPressed,
required this.icon,
required this.text
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return GestureDetector(
onTap: onPressed,
child: Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 8)
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(SizeUtils.getByScreen(small: 12, big: 18))),
color: theme.getColorFor(ThemeCode.backgroundSecondary),
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
child: Icon(icon,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: Color(0xFF588EA5),
weight: 30,
),
),
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
Expanded(
child: Text(context.translate(text),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500
)
)
)
],
),
return SectionButton(
onPressed: onPressed,
icon: Icon(icon,
size: SizeUtils.getByScreen(small: 40, big: 44),
color: theme.getColorFor(ThemeCode.legacyPrimary),
weight: 30,
),
iconPadding: SizeUtils.getByScreen(small: 14, big: 12),
body: Text(context.translate(text),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500
)
)
);
}
}
class Minimap extends ConsumerWidget {
class _MapSection extends ConsumerWidget {
IconData batteryToIcon(int battery) {
if (battery < 15) return Icons.battery_0_bar;
if (battery < 30) return Icons.battery_1_bar;
if (battery < 45) return Icons.battery_2_bar;
if (battery < 60) return Icons.battery_3_bar;
if (battery < 75) return Icons.battery_4_bar;
if (battery < 90) return Icons.battery_5_bar;
return Icons.battery_6_bar;
const _MapSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(context.translate(I18n.watchesOnMap),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 20, big: 19),
fontWeight: FontWeight.bold,
color: theme.getColorFor(ThemeCode.legacyPrimary),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 4, big: 8)),
SizedBox(
height: SizeUtils.getByScreen(small: 200, big: 300),
child: _Minimap(),
)
],
);
}
}
class _Minimap extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
@@ -202,11 +220,6 @@ class Minimap extends ConsumerWidget {
final viewState = ref.watch(hubViewModelProvider);
final viewModel = ref.read(hubViewModelProvider.notifier);
final battery = viewState.selectedPosition?.ncell ?? 0;
final IconData batteryIcon = batteryToIcon(battery);
final positionDate = DateTime.fromMillisecondsSinceEpoch(viewState.selectedPosition?.positionDate ?? 0);
return FlutterMap(
mapController: viewModel.mapController,
options: MapOptions(
@@ -235,64 +248,96 @@ class Minimap extends ConsumerWidget {
if (viewState.selectedPosition != null)
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: SizeUtils.getByScreen(small: 60, big: 58),
width: SizeUtils.getByScreen(small: 300, big: 298),
margin: EdgeInsets.only(bottom: SizeUtils.getByScreen(small: 20, big: 16)),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(SizeUtils.getByScreen(small: 9, big: 8)))
),
child: Row(
children: [
Icon(SFIcons.location,
size: SizeUtils.getByScreen(small: 40, big: 38),
color: Color(0xFF588EA5),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: SizeUtils.getByScreen(small: 250, big: 248),
child: Text('${viewState.selectedPosition!.address?.street}, '
'${viewState.selectedPosition!.address?.province}, '
'${viewState.selectedPosition!.address?.country}',
overflow: TextOverflow.ellipsis,
)
),
Row(
children: [
Text('${positionDate.month.toString().padLeft(2, '0')}-'
'${positionDate.day.toString().padLeft(2, '0')} '
'${positionDate.hour.toString().padLeft(2, '0')}:'
'${positionDate.minute.toString().padLeft(2, '0')}:'
'${positionDate.second.toString().padLeft(2, '0')}',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11)
),
),
if (viewState.selectedPosition!.networks.isNotEmpty)
Text(' | ${viewState.selectedPosition!.networks.first.signal}',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11)
),
),
Icon(batteryIcon),
Text('${viewState.selectedPosition!.ncell ?? 0}%',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11)
),
)
],
)
],
)
],
),
),
child: _LocationBanner(),
)
],
);
}
}
class _LocationBanner extends ConsumerWidget {
IconData toBatteryIcon(int battery) {
if (battery < 15) return Icons.battery_0_bar;
if (battery < 30) return Icons.battery_1_bar;
if (battery < 45) return Icons.battery_2_bar;
if (battery < 60) return Icons.battery_3_bar;
if (battery < 75) return Icons.battery_4_bar;
if (battery < 90) return Icons.battery_5_bar;
return Icons.battery_6_bar;
}
const _LocationBanner();
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
final viewState = ref.watch(hubViewModelProvider);
final battery = viewState.selectedPosition?.ncell ?? 0;
final IconData batteryIcon = toBatteryIcon(battery);
final positionDate = DateTime.fromMillisecondsSinceEpoch(viewState.selectedPosition?.positionDate ?? 0);
return Container(
height: SizeUtils.getByScreen(small: 60, big: 58),
width: SizeUtils.getByScreen(small: 300, big: 298),
margin: EdgeInsets.only(bottom: SizeUtils.getByScreen(small: 20, big: 16)),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(SizeUtils.getByScreen(small: 9, big: 8)))
),
child: Row(
children: [
Icon(SFIcons.location,
size: SizeUtils.getByScreen(small: 40, big: 38),
color: theme.getColorFor(ThemeCode.legacyPrimary),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: SizeUtils.getByScreen(small: 250, big: 248),
child: Text('${viewState.selectedPosition!.address?.street}, '
'${viewState.selectedPosition!.address?.province}, '
'${viewState.selectedPosition!.address?.country}',
overflow: TextOverflow.ellipsis,
)
),
Row(
children: [
Text('${positionDate.month.toString().padLeft(2, '0')}-'
'${positionDate.day.toString().padLeft(2, '0')} '
'${positionDate.hour.toString().padLeft(2, '0')}:'
'${positionDate.minute.toString().padLeft(2, '0')}:'
'${positionDate.second.toString().padLeft(2, '0')}',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11)
),
),
if (viewState.selectedPosition!.networks.isNotEmpty)
Text(' | ${viewState.selectedPosition!.networks.first.signal}',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11)
),
),
Icon(batteryIcon),
Text('${viewState.selectedPosition!.ncell ?? 0}%',
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 12, big: 11)
),
)
],
)
],
)
],
),
);
}
}

View File

@@ -595,6 +595,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
legacy_dashboard_shell:
dependency: "direct main"
description:
path: "../legacy_dashboard_shell"
relative: true
source: path
version: "0.0.1"
legacy_shared:
dependency: "direct main"
description:

View File

@@ -34,6 +34,8 @@ dependencies:
#modules dependencies go here
dashboard_shell:
path: ../../../../modules/dashboard_shell
legacy_dashboard_shell:
path: ../../modules/legacy_dashboard_shell
#packages dependencies go here
design_system:
path: ../../../../packages/design_system

View File

@@ -1,3 +1,4 @@
# melos_managed_dependency_overrides: legacy_dashboard_shell
# melos_managed_dependency_overrides: legacy_shared
# melos_managed_dependency_overrides: design_system,fonts,navigation,sf_infrastructure,sf_localizations,utils,auth,dashboard_shell,home,notifications,sf_shared,profile
dependency_overrides:
@@ -11,6 +12,8 @@ dependency_overrides:
path: ..\\..\\..\\..\\packages\\fonts
home:
path: ..\\..\\..\\home
legacy_dashboard_shell:
path: ..\\legacy_dashboard_shell
legacy_shared:
path: ..\\..\\packages\\legacy_shared
navigation:

View File

@@ -0,0 +1,64 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:utils/utils.dart';
class AppMenuButton extends StatelessWidget {
final GestureTapCallback onPressed;
final IconData icon;
final bool negativeIcon;
final double? iconSize;
final String text;
final Color color;
const AppMenuButton({
required this.onPressed,
required this.icon,
this.negativeIcon = false,
this.iconSize,
required this.text,
required this.color,
});
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: onPressed,
style: ButtonStyle(
overlayColor: WidgetStatePropertyAll(Color(0xFFF7F7F7))
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: negativeIcon
? Colors.white
: color,
),
height: SizeUtils.getByScreen(small: 52, big: 48),
width: SizeUtils.getByScreen(small: 52, big: 48),
child: Icon(icon,
size: iconSize ?? SizeUtils.getByScreen(small: 52, big: 48),
color: negativeIcon
? color
: Colors.white,
weight: 30,
),
),
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
Expanded(
child: Text(text,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500,
color: Color(0xFF4B4B4B)
)
)
)
],
),
);
}
}

View File

@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:utils/utils.dart';
class SectionButton extends StatelessWidget {
final GestureTapCallback onPressed;
final Widget icon;
final double? iconPadding;
final Widget body;
const SectionButton({
required this.onPressed,
required this.icon,
this.iconPadding,
required this.body,
});
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: onPressed,
style: ButtonStyle(
padding: WidgetStatePropertyAll(SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 22, vertical: 16),
big: EdgeInsets.symmetric(horizontal: 21, vertical: 12)
)),
backgroundColor: WidgetStatePropertyAll(Color(0xFFF7F7F7)),
foregroundColor: WidgetStatePropertyAll(Color(0xFF4B4B4B)),
shape: WidgetStatePropertyAll<OutlinedBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(SizeUtils.getByScreen(small: 12, big: 18))),
),
),
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
padding: EdgeInsets.all(
iconPadding ?? SizeUtils.getByScreen(small: 12, big: 16)),
child: icon,
),
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
Expanded(
child: body
),
],
),
);
}
}

View File

@@ -1,21 +1,24 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:utils/src/size_utils.dart';
import 'package:utils/utils.dart';
class PageLayout extends StatelessWidget{
class LegacyPageLayout extends StatelessWidget{
final String title;
final Widget body;
final Widget? footer;
final bool showEdit;
final VoidCallback? onEditChange;
final ThemePort theme;
const PageLayout({
const LegacyPageLayout({
super.key,
required this.title,
required this.body,
this.footer,
this.showEdit = false,
this.onEditChange
this.onEditChange,
required this.theme,
});
@override
@@ -37,7 +40,7 @@ class PageLayout extends StatelessWidget{
children: [
IconButton(onPressed: () {Navigator.pop(context);},
icon: Icon(Icons.arrow_back,
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
size: 32,
),
padding: EdgeInsets.zero,
@@ -45,7 +48,7 @@ class PageLayout extends StatelessWidget{
if (showEdit)
DecoratedBox(
decoration: BoxDecoration(
color: Color(0xFF588EA5),
color: theme.getColorFor(ThemeCode.legacyPrimary),
shape: BoxShape.circle
),
child: IconButton(onPressed: onEditChange,
@@ -65,7 +68,7 @@ class PageLayout extends StatelessWidget{
fontSize: SizeUtils.getByScreen(small: 20, big: 19),
fontWeight: FontWeight.w500,
letterSpacing: 0,
color: Color(0xFF588EA5)
color: theme.getColorFor(ThemeCode.legacyPrimary)
),
)
)

View File

@@ -10,4 +10,4 @@ export 'src/buttons/primary_button.dart';
export 'src/buttons/secondary_button.dart';
export 'src/buttons/custom_text_button.dart';
export 'src/dropdowns/dropdown.dart';
export 'src/dropdowns/country_prefix_picker.dart';
export 'src/dropdowns/country_prefix_picker.dart';

View File

@@ -20,6 +20,7 @@ enum ThemeCode {
textTertiary,
buttonPrimary,
buttonSecondary,
legacyPrimary,
}
abstract class ThemePort {

View File

@@ -13,6 +13,7 @@ class ThemeSfAdapter extends ThemePort {
ThemeCode.textTertiary: Color(0xFFE0E0E0),
ThemeCode.buttonPrimary: Color(0xFF329e95),
ThemeCode.buttonSecondary: Color(0xFF4B4B4B),
ThemeCode.legacyPrimary: Color(0xFF588EA5),
};
@override

View File

@@ -178,5 +178,34 @@
"userRole": "Role: {role}",
"copy": "copy",
"deviceIdLabel": "ID: {deviceId}",
"regCodeLabel": "Registration code: {regCode}"
"regCodeLabel": "Registration code: {regCode}",
"deleteAccountBody1": "Canceling an account is an irreversible operation. Please confirm that all services related to the account have been properly managed.\n\nAfter canceling your account, you will no longer be able to use this account or recover any content or information you have added or linked (even if you use the same account to register and reuse), including, but not limited to:\n1. You will not be able to log in and use this account.\n2. Your personal information and account history will not be recovered.\n3. All records of third-party services you use through account linking or account binding cannot be recovered. You will no longer be able to log in and use the above services. Love, gold coins, third-party points, orders, tickets, and other coupons you have received will be considered abandoned and will not be used.\n\nPlease note that canceling your account does not mean that the account behavior and related responsibilities prior to the cancellation of this account are exempt or reduced.",
"deleteAccountBody2": "Clicking \"Request account cancellation\" means that you have read and agree to the above description.",
"requestCancelButton": "Request account cancellation",
"verifyAccount": "Verify account",
"requestCancelTitle": "Request account cancellation",
"requestCancelBody": "1. Account cancellation does not mean operational recovery, so please ensure that the account is no longer in use before operating it.\n2. Once an account cancellation request has been successfully submitted, the platform will delete all information related to your account within one hour.",
"deleteDeviceData": "Delete all information related to the {name} device",
"confirm": "Confirm",
"remoteConnection": "Remote connection",
"calendar": "Calendar",
"contacts": "Contacts",
"doNotDisturb": "Do not disturb",
"videoCall": "Video call",
"health": "Health",
"activityMeter": "Activity meter",
"rewards": "Rewards",
"callWatch": "Call watch",
"appsUse": "Apps use",
"appsSurveillance": "Apps surveillance",
"makeFriends": "Make friends",
"locateSF": "Play sound on device",
"locateSFBody1": "Locate your device with a sound.",
"locateSFBody2": "1. Press the Play Sound button to activate the sound on the device.\n2. Press OK.\n3. The device will play a sound.\n4. The device will start sounding after receiving the instructions.",
"locateSFAction": "Locate my SaveFamily",
"insertPhone": "Insert your phone number",
"call": "Call me",
"takePicture": "Take a picture",
"remoteCamera": "Remote Camera",
"remoteListening": "Remote Listening"
}

View File

@@ -184,5 +184,26 @@
"requestCancelTitle": "Solicitud de cancelación de la cuenta",
"requestCancelBody": "1. La cancelación de la cuenta no es la recuperación operacional, asegúrate de que antes de operar la cuenta ya no se utiliza.\n2. Enviado correctamente una cancelación de la cuenta de la aplicación, la plataforma se eliminará toda la información relacionada con tu cuenta dentro de 1 hora.",
"deleteDeviceData": "Borrar toda la información relacionada con el dispositivo de {name}",
"confirm": "Confirmar"
"confirm": "Confirmar",
"remoteConnection": "Conexión remota",
"calendar": "Horario de actividades",
"contacts": "Agenda",
"doNotDisturb": "No molestar",
"videoCall": "Video llamada",
"health": "Salud",
"activityMeter": "Medidor de actividad",
"rewards": "Enviar recompensas",
"callWatch": "Llamar al reloj",
"appsUse": "Uso de las aplicaciones",
"appsSurveillance": "Supervisión de las aplicaciones",
"makeFriends": "Hacer amigos",
"locateSF": "Reproducir sonido en dispositivo",
"locateSFBody1": "Localiza tu dispositivo con un sonido.",
"locateSFBody2": "1. Pulsa el botón Reproducir Sonido para activar el sonido en el dispositivo.\n2. Pulsa OK\n3. El dispositivo reproducirá un sonido\n4. El dispositivo comenzará a sonar después de recibir las instrucciones",
"locateSFAction": "Ubica mi Savefamily",
"insertPhone": "Inserta tu número de teléfono",
"call": "Llámame",
"takePicture": "Tomar fotografía",
"remoteCamera": "Foto remota",
"remoteListening": "Escucha remota"
}

View File

@@ -184,35 +184,56 @@ class I18n {
static const String enterSubject = 'enterSubject';
static const String enterMessage = 'enterMessage';
static const String sendEmail = 'sendEmail';
static const String legacyPersonalData = 'personalData';
static const String legacyChangePassword = 'changePassword';
static const String legacyAddNewSF = 'addNewSF';
static const String legacyLinkedDevices = 'linkedDevices';
static const String legacyAppUsers = 'appUsers';
static const String legacyPrivacyPolicy = 'privacyPolicy';
static const String legacyLogOut = 'logOut';
static const String personalData = 'personalData';
static const String changePassword = 'changePassword';
static const String addNewSF = 'addNewSF';
static const String linkedDevices = 'linkedDevices';
static const String appUsers = 'appUsers';
static const String privacyPolicy = 'privacyPolicy';
static const String logOut = 'logOut';
static const String passwordLabel = 'passwordLabel';
static const String legacySubmit = 'submit';
static const String legacySave = 'save';
static const String legacyEditDeviceTitle = 'editDeviceTitle';
static const String legacyName = 'name';
static const String legacyDeleteDeviceDialog = 'deleteDeviceDialog';
static const String legacyDeleteUserDialog = 'deleteUserDialog';
static const String legacyCancel = 'cancel';
static const String legacyDelete = 'delete';
static const String legacyUserAccount = 'userAccount';
static const String legacyUserRole = 'userRole';
static const String legacyCopy = 'copy';
static const String legacyDeviceIdLabel = 'deviceIdLabel';
static const String legacyRegCodeLabel = 'regCodeLabel';
static const String legacyRegCode = 'regCode';
static const String legacyDeleteAccount = 'deleteAccount';
static const String legacyDeleteAccountBody1 = 'deleteAccountBody1';
static const String legacyDeleteAccountBody2 = 'deleteAccountBody2';
static const String legacyRequestCancelButton = 'requestCancelButton';
static const String legacyVerifyAccount = 'verifyAccount';
static const String legacyRequestCancelTitle = 'requestCancelTitle';
static const String legacyRequestCancelBody = 'requestCancelBody';
static const String legacyDeleteDeviceData = 'deleteDeviceData';
static const String legacyConfirm = 'confirm';
static const String submit = 'submit';
static const String save = 'save';
static const String editDeviceTitle = 'editDeviceTitle';
static const String name = 'name';
static const String deleteDeviceDialog = 'deleteDeviceDialog';
static const String deleteUserDialog = 'deleteUserDialog';
static const String cancel = 'cancel';
static const String delete = 'delete';
static const String userAccount = 'userAccount';
static const String userRole = 'userRole';
static const String copy = 'copy';
static const String deviceIdLabel = 'deviceIdLabel';
static const String regCodeLabel = 'regCodeLabel';
static const String regCode = 'regCode';
static const String deleteAccount = 'deleteAccount';
static const String deleteAccountBody1 = 'deleteAccountBody1';
static const String deleteAccountBody2 = 'deleteAccountBody2';
static const String requestCancelButton = 'requestCancelButton';
static const String verifyAccount = 'verifyAccount';
static const String requestCancelTitle = 'requestCancelTitle';
static const String requestCancelBody = 'requestCancelBody';
static const String deleteDeviceData = 'deleteDeviceData';
static const String confirm = 'confirm';
static const String remoteConnection = 'remoteConnection';
static const String calendar = 'calendar';
static const String contacts = 'contacts';
static const String doNotDisturb = 'doNotDisturb';
static const String videoCall = 'videoCall';
static const String health = 'health';
static const String activityMeter = 'activityMeter';
static const String rewards = 'rewards';
static const String callWatch = 'callWatch';
static const String appsUse = 'appsUse';
static const String appsSurveillance = 'appsSurveillance';
static const String makeFriends = 'makeFriends';
static const String locateSF = 'locateSF';
static const String locateSFBody1 = 'locateSFBody1';
static const String locateSFBody2 = 'locateSFBody2';
static const String locateSFAction = 'locateSFAction';
static const String insertPhone = 'insertPhone';
static const String call = 'call';
static const String takePicture = 'takePicture';
static const String remoteCamera = 'remoteCamera';
static const String remoteListening = 'remoteListening';
}