diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart index 97c335c..d141214 100644 --- a/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart +++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_bloc.dart @@ -18,6 +18,7 @@ import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/services/api/home_management_api.dart'; import 'package:syncrow_app/services/api/scene_api.dart'; import 'package:syncrow_app/services/api/spaces_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; class FourSceneBloc extends Bloc { final String fourSceneId; @@ -27,6 +28,7 @@ class FourSceneBloc extends Bloc { on(_fetchStatus); on(fetchDeviceData); on(fetchLogsForLastMonth); + on(saveName); on(_toggleNotification); on(_changeName); on(_onSearchFaq); @@ -40,6 +42,7 @@ class FourSceneBloc extends Bloc { on(_addDeviceToGroup); on(_removeDeviceFromGroup); on(_onFourSceneInitial); + on(_fetchDeviceScene); } final TextEditingController nameController = @@ -93,6 +96,47 @@ class FourSceneBloc extends Bloc { ), ); + // void changeDeviceName( + // SaveNameEvent event, Emitter emit) async { + // emit(FourSceneLoadingState()); + // try { + // var response = await DevicesAPI.putDeviceName( + // deviceId: fourSceneId, deviceName: event.deviceName!); + // List statusModelList = []; + // for (var status in response['status']) { + // statusModelList.add(StatusModel.fromJson(status)); + // } + // deviceStatus = FourSceneModel.fromJson( + // statusModelList, + // ); + // emit(UpdateState(sensor: deviceStatus)); + // Future.delayed(const Duration(milliseconds: 500)); + // // _listenToChanges(); + // } catch (e) { + // emit(FourSceneFailedState(errorMessage: e.toString())); + // return; + // } + // } + + Future saveName( + SaveNameEvent event, Emitter emit) async { + if (_validateInputs()) return; + try { + add(const ChangeNameEvent(value: false)); + isSaving = true; + emit(FourSceneLoadingState()); + var response = await DevicesAPI.putDeviceName( + deviceId: fourSceneId, deviceName: nameController.text); + add(FourSceneInitialInfo()); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveState()); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + } finally { + isSaving = false; + } + } + void _fetchStatus( FourSceneInitial event, Emitter emit) async { emit(FourSceneLoadingState()); @@ -114,6 +158,27 @@ class FourSceneBloc extends Bloc { } } + void _fetchDeviceScene( + FetchDeviceScene event, Emitter emit) async { + emit(FourSceneLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(fourSceneId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = FourSceneModel.fromJson( + statusModelList, + ); + emit(UpdateState(sensor: deviceStatus)); + Future.delayed(const Duration(milliseconds: 500)); + // _listenToChanges(); + } catch (e) { + emit(FourSceneFailedState(errorMessage: e.toString())); + return; + } + } + Future fetchDeviceData( FourSceneInitialInfo event, Emitter emit) async { emit(FourSceneLoadingState()); @@ -369,6 +434,8 @@ class FourSceneBloc extends Bloc { allDevices.forEach((element) { allDevicesIds.add(element.uuid!); }); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveSelectionSuccessState()); } } catch (e) { @@ -376,5 +443,27 @@ class FourSceneBloc extends Bloc { return; } } - + + bool _validateInputs() { + final nameError = fullNameValidator(nameController.text); + if (nameError != null) { + CustomSnackBar.displaySnackBar(nameError); + return true; + } + return false; + } + + String? fullNameValidator(String? value) { + if (value == null) return 'name is required'; + final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim(); + if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) { + return 'Full name must be between 2 and 30 characters long'; + } + // Test if it contains anything but alphanumeric spaces and single quote + if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) { + return 'Only alphanumeric characters, space, dash and single quote are allowed'; + } + + return null; + } } diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_event.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_event.dart index ae89a6a..da82021 100644 --- a/lib/features/devices/bloc/four_scene_bloc/four_scene_event.dart +++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_event.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/devices/model/group_devices_model.dart'; @@ -23,12 +24,19 @@ class FourSceneSwitch extends FourSceneEvent { } class FourSceneUpdated extends FourSceneEvent {} + class FourSceneInitialInfo extends FourSceneEvent {} class FourSceneInitial extends FourSceneEvent { const FourSceneInitial(); } +class SaveNameEvent extends FourSceneEvent { + final String? deviceName; + + const SaveNameEvent({this.deviceName}); +} + class ReportLogsInitial extends FourSceneEvent { const ReportLogsInitial(); } @@ -173,19 +181,25 @@ class RemoveDeviceFromGroup extends FourSceneEvent { RemoveDeviceFromGroup(this.device, this.icon); } - class AssignRoomEvent extends FourSceneEvent { final String roomId; final SpaceModel unit; + final BuildContext context; const AssignRoomEvent({ required this.roomId, required this.unit, + required this.context, }); @override List get props => [ roomId, unit, + context, ]; } + +class FetchDeviceScene extends FourSceneEvent { + const FetchDeviceScene(); +} diff --git a/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart b/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart index 9645459..951ea3f 100644 --- a/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart +++ b/lib/features/devices/bloc/four_scene_bloc/four_scene_state.dart @@ -97,6 +97,7 @@ class SceneLoaded extends FourSceneState { class SelectedSceneState extends FourSceneState {} class SearchResultsState extends FourSceneState {} +class SaveState extends FourSceneState {} class SaveSelectionSuccessState extends FourSceneState {} diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_profile_page.dart b/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_profile_page.dart index aa05cfe..da0fa3d 100644 --- a/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_profile_page.dart +++ b/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_profile_page.dart @@ -25,6 +25,11 @@ class FourSceneProfilePage extends StatelessWidget { return DefaultScaffold( title: 'Device Settings', + leading: IconButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + icon: const Icon(Icons.arrow_back_ios)), child: BlocProvider( create: (context) => FourSceneBloc(fourSceneId: device?.uuid ?? '') ..add(const FourSceneInitial()) @@ -93,7 +98,7 @@ class FourSceneProfilePage extends StatelessWidget { controller: _bloc.nameController, enabled: _bloc.editName, onEditingComplete: () { - // sensor.add(SaveNameEvent(context: context)); + _bloc.add(const SaveNameEvent()); }, decoration: const InputDecoration( hintText: "Your Name", @@ -131,19 +136,22 @@ class FourSceneProfilePage extends StatelessWidget { DefaultContainer( padding: const EdgeInsets.all(20), child: InkWell( - onTap: () { - Navigator.of(context).push( + onTap: () async { + bool val = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => LocationFourScenePage( space: spaces!.first, deviceId: device?.uuid ?? '', )), ); + if (val == true) { + _bloc.add(FourSceneInitialInfo()); + } }, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox( + const SizedBox( child: Text('Location'), ), Row( @@ -155,7 +163,7 @@ class FourSceneProfilePage extends StatelessWidget { fontColor: ColorsManager.textGray, ), ), - Icon( + const Icon( Icons.arrow_forward_ios, size: 15, color: ColorsManager.textGray, diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_settings.dart b/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_settings.dart index 007a42f..ee0a5af 100644 --- a/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_settings.dart +++ b/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/four_scene_settings.dart @@ -69,14 +69,17 @@ class FourSceneSettings extends StatelessWidget { vertical: 10, ), child: InkWell( - onTap: () { - Navigator.of(context).push( + onTap: () async { + bool val = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => FourSceneProfilePage( device: device, ), ), ); + if (val == true) { + _bloc.add(FourSceneInitialInfo()); + } }, child: Stack( children: [ @@ -161,7 +164,9 @@ class FourSceneSettings extends StatelessWidget { onTap: () { Navigator.of(context).push( MaterialPageRoute( - builder: (context) => FourSceneInfoPage( device: device!,)), + builder: (context) => FourSceneInfoPage( + device: device!, + )), ); }, text: 'Device Information', diff --git a/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/location_setting_four_scene.dart b/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/location_setting_four_scene.dart index 71de7e3..43b8dfd 100644 --- a/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/location_setting_four_scene.dart +++ b/lib/features/devices/view/widgets/four_scene_switch/four_scene_setting/location_setting_four_scene.dart @@ -41,6 +41,12 @@ class LocationFourScenePage extends StatelessWidget { scene_4: '', scene_id_group_id: '', switch_backlight: ''); + if (state is SaveSelectionSuccessState) { + new Future.delayed(const Duration(microseconds: 500), () { + _bloc.add(FourSceneInitialInfo()); + Navigator.of(context).pop(true); + }); + } return state is FourSceneLoadingState ? const Center( child: DefaultContainer( @@ -60,6 +66,7 @@ class LocationFourScenePage extends StatelessWidget { ? () { context.read().add( AssignRoomEvent( + context: context, roomId: roomIdSelected, unit: space!)); } @@ -77,6 +84,7 @@ class LocationFourScenePage extends StatelessWidget { ), const SizedBox(width: 20), ], + child: RefreshIndicator( onRefresh: () async { // sensor.add(const SosInitial()); diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 3298c96..ac18192 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -31,6 +31,22 @@ class DevicesAPI { } } + static Future> putDeviceName( + {required String deviceId, required String deviceName}) async { + try { + final response = await _httpService.put( + path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId), + body: {"deviceName": deviceName}, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } catch (e) { + rethrow; + } + } + static Future> controlDevice( DeviceControlModel controlModel, String deviceId) async { try {