delete device command and device setup flow
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
|
||||
abstract class DevicesRemoteDatasource {
|
||||
Future<void> deleteDevice({required String userId, required String deviceId});
|
||||
Future<void> deleteDevice({required String deviceId});
|
||||
|
||||
Future<void> updateDevice({required String userId, required UpdateDeviceRequestEntity request});
|
||||
Future<void> updateDevice({required UpdateDeviceRequestEntity request});
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class DevicesRemoteDatasourceImpl implements DevicesRemoteDatasource {
|
||||
final QuestiaRepository _repository;
|
||||
|
||||
@override
|
||||
Future<void> deleteDevice({required String userId, required String deviceId}) async {
|
||||
Future<void> deleteDevice({required String deviceId}) async {
|
||||
try {
|
||||
await _repository.delete<void>(
|
||||
'/devices/$deviceId',
|
||||
@@ -23,7 +23,7 @@ class DevicesRemoteDatasourceImpl implements DevicesRemoteDatasource {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateDevice({required String userId, required UpdateDeviceRequestEntity request}) async {
|
||||
Future<void> updateDevice({required UpdateDeviceRequestEntity request}) async {
|
||||
try {
|
||||
final body = request.toModel().toJson();
|
||||
await _repository.put<void>(
|
||||
|
||||
@@ -9,12 +9,12 @@ class DevicesRepositoryImpl implements DevicesRepository {
|
||||
final DevicesRemoteDatasource _remote;
|
||||
|
||||
@override
|
||||
Future<void> deleteDevice({required String userId, required String deviceId}) {
|
||||
return _remote.deleteDevice(userId: userId, deviceId: deviceId);
|
||||
Future<void> deleteDevice({required String deviceId}) {
|
||||
return _remote.deleteDevice(deviceId: deviceId);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateDevice({required String userId, required UpdateDeviceRequestEntity request}) {
|
||||
return _remote.updateDevice(userId: userId, request: request);
|
||||
Future<void> updateDevice({required UpdateDeviceRequestEntity request}) {
|
||||
return _remote.updateDevice(request: request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import 'package:account/src/features/linked_devices/domain/entities/update_device_request_entity.dart';
|
||||
|
||||
abstract class DevicesRepository {
|
||||
Future<void> deleteDevice({required String userId, required String deviceId});
|
||||
Future<void> deleteDevice({required String deviceId});
|
||||
|
||||
Future<void> updateDevice({
|
||||
required String userId,
|
||||
required UpdateDeviceRequestEntity request
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,16 +27,20 @@ class LinkedDevicesScreen extends ConsumerWidget {
|
||||
title: context.translate(I18n.linkedDevices),
|
||||
showEdit: true,
|
||||
onEditChange: vm.toggleIsEditing,
|
||||
body: ListView.separated(
|
||||
itemBuilder: (BuildContext context, int index)=>_LinkedDeviceCard(
|
||||
device: state.linkedDevices[index],
|
||||
isEditing: state.isEditing,
|
||||
onDelete: ()=>vm.deleteDevice(state.linkedDevices[index]),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: SizeUtils.getByScreen(small: 10, big: 12)),
|
||||
child: ListView.separated(
|
||||
itemBuilder: (BuildContext context, int index)=>_LinkedDeviceCard(
|
||||
navigationContract: navigationContract,
|
||||
device: state.linkedDevices[index],
|
||||
isEditing: state.isEditing,
|
||||
onDelete: ()=>vm.deleteDevice(state.linkedDevices[index]),
|
||||
),
|
||||
separatorBuilder: (BuildContext context, int index)=>SizedBox(
|
||||
height: SizeUtils.getByScreen(small: 18, big: 17)
|
||||
),
|
||||
itemCount: state.linkedDevices.length
|
||||
),
|
||||
separatorBuilder: (BuildContext context, int index)=>SizedBox(
|
||||
height: SizeUtils.getByScreen(small: 18, big: 17)
|
||||
),
|
||||
itemCount: state.linkedDevices.length
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -44,11 +48,13 @@ class LinkedDevicesScreen extends ConsumerWidget {
|
||||
|
||||
class _LinkedDeviceCard extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
final DeviceEntity device;
|
||||
final bool isEditing;
|
||||
final Function onDelete;
|
||||
|
||||
const _LinkedDeviceCard({
|
||||
required this.navigationContract,
|
||||
required this.device,
|
||||
required this.isEditing,
|
||||
required this.onDelete,
|
||||
@@ -76,7 +82,7 @@ class _LinkedDeviceCard extends ConsumerWidget {
|
||||
shape: BoxShape.circle,
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
),
|
||||
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 4, big: 12)),
|
||||
padding: EdgeInsets.all(SizeUtils.getByScreen(small: 8, big: 12)),
|
||||
child: Icon(SFIcons.watch,
|
||||
size: SizeUtils.getByScreen(small: 40, big: 44),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
@@ -110,7 +116,10 @@ class _LinkedDeviceCard extends ConsumerWidget {
|
||||
),
|
||||
child: IconButton(
|
||||
onPressed: (){showDialog(context: context, builder: (context)=>Dialog(
|
||||
child: DeleteDeviceDialog(device: device),
|
||||
child: DeleteDeviceDialog(
|
||||
navigationContract: navigationContract,
|
||||
device: device,
|
||||
),
|
||||
));},
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
|
||||
@@ -70,19 +70,32 @@ class LinkedDevicesViewModel extends Notifier<LinkedDevicesViewState> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> deleteDevice(DeviceEntity device) async {
|
||||
Future<void> deleteDevice(DeviceEntity device) async {
|
||||
try {
|
||||
await _devicesRepository.deleteDevice(userId: state.loggedUser!.id, deviceId: device.identificator);
|
||||
List<DeviceEntity> newList = state.linkedDevices;
|
||||
newList.remove(device);
|
||||
|
||||
state = state.copyWith(
|
||||
linkedDevices: newList
|
||||
isLoading: true,
|
||||
isComplete: false,
|
||||
);
|
||||
|
||||
return true;
|
||||
await _devicesRepository.deleteDevice(deviceId: device.id);
|
||||
List<DeviceEntity> newList = state.linkedDevices.toList();
|
||||
newList.remove(device);
|
||||
|
||||
if (device == state.selectedDevice) {
|
||||
ref.invalidate(selectedDeviceProvider);
|
||||
}
|
||||
|
||||
state = state.copyWith(
|
||||
linkedDevices: newList,
|
||||
isLoading: false,
|
||||
isComplete: true,
|
||||
);
|
||||
} catch (e) {
|
||||
return false;
|
||||
state = state.copyWith(
|
||||
isLoading: false,
|
||||
errorMessage: e.toString(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,9 +115,7 @@ class LinkedDevicesViewModel extends Notifier<LinkedDevicesViewState> {
|
||||
if (deviceName.isEmpty) return;
|
||||
|
||||
try {
|
||||
final userId = state.loggedUser!.id;
|
||||
_devicesRepository.updateDevice(
|
||||
userId: userId,
|
||||
request: _toRequest(device));
|
||||
|
||||
} catch(e) {
|
||||
@@ -114,8 +125,6 @@ class LinkedDevicesViewModel extends Notifier<LinkedDevicesViewState> {
|
||||
errorMessage: e.toString()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void disposeControllers() {
|
||||
|
||||
@@ -2,15 +2,20 @@ import 'package:account/src/features/linked_devices/presentation/state/linked_de
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:navigation/navigation.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:sf_shared/sf_shared.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
class DeleteDeviceDialog extends ConsumerWidget {
|
||||
|
||||
final NavigationContract navigationContract;
|
||||
final DeviceEntity device;
|
||||
|
||||
const DeleteDeviceDialog({required this.device});
|
||||
const DeleteDeviceDialog({
|
||||
required this.navigationContract,
|
||||
required this.device
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -21,15 +26,15 @@ class DeleteDeviceDialog extends ConsumerWidget {
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 32, vertical: 30),
|
||||
big: EdgeInsets.symmetric(horizontal: 30, vertical: 28)
|
||||
big: EdgeInsets.symmetric(horizontal: 24, vertical: 18)
|
||||
),
|
||||
width: SizeUtils.getByScreen(small: 360, big: 350),
|
||||
height: SizeUtils.getByScreen(small: 195, big: 185),
|
||||
height: SizeUtils.getByScreen(small: 195, big: 160),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(context.translate(I18n.deleteDeviceDialog),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 18)),
|
||||
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 19, big: 16)),
|
||||
),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
|
||||
Row(
|
||||
@@ -46,7 +51,20 @@ class DeleteDeviceDialog extends ConsumerWidget {
|
||||
Expanded(child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
await vm.deleteDevice(device);
|
||||
Navigator.pop(context);
|
||||
|
||||
final isComplete = ref.read(
|
||||
linkedDevicesViewModelProvider.select((s)=>s.isComplete)
|
||||
);
|
||||
if (isComplete) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
final noMoreDevices = ref.read(
|
||||
linkedDevicesViewModelProvider.select((s)=>s.linkedDevices)
|
||||
).isEmpty;
|
||||
if (noMoreDevices) {
|
||||
navigationContract.goTo(AppRoutes.legacyDeviceSetup);
|
||||
}
|
||||
},
|
||||
text: context.translate(I18n.delete),
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
|
||||
@@ -8,6 +8,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:navigation/navigation_contract.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
import 'widgets/activation_code_dialog.dart';
|
||||
|
||||
class LegacyDeviceSetupScreen extends ConsumerWidget {
|
||||
final NavigationContract navigationContract;
|
||||
@@ -54,7 +57,7 @@ class LegacyDeviceSetupScreen extends ConsumerWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12, left: 8, right: 8),
|
||||
padding: EdgeInsets.only(top: SizeUtils.getByScreen(small: 4, big: 12), left: 8, right: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
if (isIntro)
|
||||
@@ -114,6 +117,9 @@ class LegacyDeviceSetupScreen extends ConsumerWidget {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (state.step == LegacyAddKidStep.scanWatch) {
|
||||
showActivationCodeDialog(context);
|
||||
}
|
||||
await vm.next();
|
||||
},
|
||||
theme: theme,
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
class LegacyIntroStepScreen extends ConsumerWidget {
|
||||
const LegacyIntroStepScreen({super.key});
|
||||
@@ -11,29 +12,34 @@ class LegacyIntroStepScreen extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.deviceSetup_intro_title),
|
||||
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
context.translate(I18n.deviceSetup_intro_subtitle),
|
||||
style: TextStyle(fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
LegacyNumberedSteps(
|
||||
steps: [
|
||||
context.translate(I18n.deviceSetup_intro_step_1),
|
||||
context.translate(I18n.deviceSetup_intro_step_2),
|
||||
],
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
],
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SizeUtils.getByScreen(small: 8, big: 2)
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
context.translate(I18n.deviceSetup_intro_title),
|
||||
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
context.translate(I18n.deviceSetup_intro_subtitle),
|
||||
style: TextStyle(fontSize: 18),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 40),
|
||||
LegacyNumberedSteps(
|
||||
steps: [
|
||||
context.translate(I18n.deviceSetup_intro_step_1),
|
||||
context.translate(I18n.deviceSetup_intro_step_2),
|
||||
],
|
||||
color: theme.getColorFor(ThemeCode.legacyPrimary),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
class LegacyLinkInfoStepScreen extends ConsumerWidget {
|
||||
const LegacyLinkInfoStepScreen({super.key});
|
||||
@@ -13,7 +14,7 @@ class LegacyLinkInfoStepScreen extends ConsumerWidget {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 30),
|
||||
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 30)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 65),
|
||||
child: Text(
|
||||
|
||||
@@ -147,7 +147,7 @@ class LegacyProfileStepScreen extends ConsumerWidget {
|
||||
|
||||
CustomTextField(
|
||||
label: context.translate(I18n.activationKeyLabel),
|
||||
hint: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
|
||||
hint: 'XXXXXXXX',
|
||||
controller: vm.activationKeyController,
|
||||
),
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sf_localizations/sf_localizations.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
void showActivationCodeDialog(BuildContext context) {
|
||||
showDialog(context: context, builder: (context) =>
|
||||
Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: _ActivationCodeDialog(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
class _ActivationCodeDialog extends StatelessWidget {
|
||||
|
||||
const _ActivationCodeDialog();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: SizeUtils.getByScreen(
|
||||
small: EdgeInsets.symmetric(horizontal: 14, vertical: 24),
|
||||
big: EdgeInsets.symmetric(horizontal: 12, vertical: 20),
|
||||
),
|
||||
color: Colors.white,
|
||||
child: Text(context.translate(I18n.activationCodeMessage),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 16, big: 15)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:legacy_auth/src/features/login/presentation/widgets/otp_code_fields.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:utils/utils.dart';
|
||||
|
||||
class LegacyTwoFactorBottomSheetView extends StatelessWidget {
|
||||
const LegacyTwoFactorBottomSheetView({
|
||||
@@ -88,6 +89,7 @@ class LegacyTwoFactorBottomSheetView extends StatelessWidget {
|
||||
if (isOtpLoading || !_isValidOtp) return;
|
||||
unawaited(onVerify());
|
||||
},
|
||||
gap: SizeUtils.getByScreen(small: 10, big: 8),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
<versions>
|
||||
<version>2.6.4</version>
|
||||
</versions>
|
||||
<lastUpdated>20260316000000</lastUpdated>
|
||||
<lastUpdated>20260318000000</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
|
||||
@@ -1 +1 @@
|
||||
a0ed8b315dd3aaa92839422686f00f9d
|
||||
f9e85f64806f37132f0c0cc4ef8a67ec
|
||||
@@ -1 +1 @@
|
||||
4888c373e3701bd965ce8ff0daaa19071f7d9a9e
|
||||
b7a72907f1f917f7b7d0cd57cb170901965b4113
|
||||
@@ -582,7 +582,7 @@
|
||||
"deviceSetup_weightHint": "30",
|
||||
"deviceSetup_heightLabel": "Height (cm)",
|
||||
"deviceSetup_heightHint": "120",
|
||||
"activationKeyLabel": "Activation key",
|
||||
"activationKeyLabel": "Activation key (check your email)",
|
||||
"rewardsMessage": "*Using this feature you can reward your child for goals achieved or good actions.",
|
||||
"sendRewards": "Send rewards!",
|
||||
"rewardsSent": "Rewards sent!",
|
||||
@@ -705,5 +705,6 @@
|
||||
"wifiBssidHint": "e.g. 0c:80:63:e4:cb:e1",
|
||||
"editChildProfile": "Edit profile",
|
||||
"editChildProfileSaveSuccess": "Child profile updated successfully",
|
||||
"editChildProfileTitle": "Edit child profile"
|
||||
"editChildProfileTitle": "Edit child profile",
|
||||
"activationCodeMessage": "An activation key has been sent to your email"
|
||||
}
|
||||
@@ -580,7 +580,7 @@
|
||||
"deviceSetup_weightHint": "30",
|
||||
"deviceSetup_heightLabel": "Altura (cm)",
|
||||
"deviceSetup_heightHint": "120",
|
||||
"activationKeyLabel": "Clave de activación",
|
||||
"activationKeyLabel": "Clave de activación (consulta tu correo electrónico)",
|
||||
"rewardsMessage": "*Usando esta función puedes recompensar a tu hijo por metas alcanzadas o buenas acciones.",
|
||||
"sendRewards": "¡Enviar recompensas!",
|
||||
"rewardsSent": "¡Recompensas enviadas!",
|
||||
@@ -703,5 +703,6 @@
|
||||
"wifiBssidHint": "ej. 0c:80:63:e4:cb:e1",
|
||||
"editChildProfile": "Editar perfil",
|
||||
"editChildProfileTitle": "Editar perfil del niño",
|
||||
"editChildProfileSaveSuccess": "Perfil del niño actualizado correctamente"
|
||||
"editChildProfileSaveSuccess": "Perfil del niño actualizado correctamente",
|
||||
"activationCodeMessage": "Se ha enviado un código de activación a tu correo electrónico"
|
||||
}
|
||||
@@ -829,4 +829,5 @@ class I18n {
|
||||
static const String editChildProfile = 'editChildProfile';
|
||||
static const String editChildProfileTitle = 'editChildProfileTitle';
|
||||
static const String editChildProfileSaveSuccess = 'editChildProfileSaveSuccess';
|
||||
static const String activationCodeMessage = 'activationCodeMessage';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user