From 77d39bfc53e8fed0a3bdac2c98ecd58fd1c95c40 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Mon, 2 Jun 2025 11:26:30 +0300 Subject: [PATCH] Refactor `CurtainBloc` to use new service dependencies and implement a factory for instantiation. Updated event handling methods for improved error management and state updates. --- .../curtain/bloc/curtain_bloc.dart | 162 +++++++----------- .../factories/curtain_bloc_factory.dart | 18 ++ .../view/curtain_batch_status_view.dart | 3 +- .../curtain/view/curtain_status_view.dart | 3 +- 4 files changed, 86 insertions(+), 100 deletions(-) create mode 100644 lib/pages/device_managment/curtain/factories/curtain_bloc_factory.dart diff --git a/lib/pages/device_managment/curtain/bloc/curtain_bloc.dart b/lib/pages/device_managment/curtain/bloc/curtain_bloc.dart index 251d999f..749a7729 100644 --- a/lib/pages/device_managment/curtain/bloc/curtain_bloc.dart +++ b/lib/pages/device_managment/curtain/bloc/curtain_bloc.dart @@ -1,17 +1,25 @@ import 'dart:async'; + import 'package:firebase_database/firebase_database.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart'; +import 'package:syncrow_web/services/batch_control_devices_service.dart'; +import 'package:syncrow_web/services/control_device_service.dart'; import 'package:syncrow_web/services/devices_mang_api.dart'; class CurtainBloc extends Bloc { late bool deviceStatus; final String deviceId; - Timer? _timer; + final ControlDeviceService controlDeviceService; + final BatchControlDevicesService batchControlDevicesService; - CurtainBloc({required this.deviceId}) : super(CurtainInitial()) { + CurtainBloc({ + required this.deviceId, + required this.controlDeviceService, + required this.batchControlDevicesService, + }) : super(CurtainInitial()) { on(_onFetchDeviceStatus); on(_onFetchBatchStatus); on(_onCurtainControl); @@ -20,32 +28,31 @@ class CurtainBloc extends Bloc { on(_onStatusUpdated); } - FutureOr _onFetchDeviceStatus( - CurtainFetchDeviceStatus event, Emitter emit) async { + Future _onFetchDeviceStatus( + CurtainFetchDeviceStatus event, + Emitter emit, + ) async { emit(CurtainStatusLoading()); try { - final status = - await DevicesManagementApi().getDeviceStatus(event.deviceId); - _listenToChanges(event.deviceId); + final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); + _listenToChanges(event.deviceId, emit); deviceStatus = _checkStatus(status.status[0].value); - emit(CurtainStatusLoaded(deviceStatus)); } catch (e) { emit(CurtainError(e.toString())); } } - void _listenToChanges(String deviceId) { + void _listenToChanges(String deviceId, Emitter emit) { try { - DatabaseReference ref = - FirebaseDatabase.instance.ref('device-status/$deviceId'); - Stream stream = ref.onValue; + final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); + final stream = ref.onValue; stream.listen((DatabaseEvent event) { final data = event.snapshot.value as Map?; if (data == null) return; - List statusList = []; + final statusList = []; if (data['status'] != null) { for (var element in data['status']) { statusList.add( @@ -57,7 +64,7 @@ class CurtainBloc extends Bloc { } } if (statusList.isNotEmpty) { - bool newStatus = _checkStatus(statusList[0].value); + final newStatus = _checkStatus(statusList[0].value); if (newStatus != deviceStatus) { deviceStatus = newStatus; if (!isClosed) { @@ -71,76 +78,32 @@ class CurtainBloc extends Bloc { } } - void _onStatusUpdated(StatusUpdated event, Emitter emit) { + void _onStatusUpdated( + StatusUpdated event, + Emitter emit, + ) { emit(CurtainStatusLoading()); deviceStatus = event.deviceStatus; emit(CurtainStatusLoaded(deviceStatus)); } - FutureOr _onCurtainControl( - CurtainControl event, Emitter emit) async { - final oldValue = deviceStatus; - + Future _onCurtainControl( + CurtainControl event, + Emitter emit, + ) async { + emit(CurtainStatusLoading()); _updateLocalValue(event.value, emit); - emit(CurtainStatusLoaded(deviceStatus)); - - await _runDebounce( - deviceId: event.deviceId, - code: event.code, - value: event.value, - oldValue: oldValue, - emit: emit, - isBatch: false, - ); - } - - Future _runDebounce({ - required dynamic deviceId, - required String code, - required bool value, - required bool oldValue, - required Emitter emit, - required bool isBatch, - }) async { - late String id; - - if (deviceId is List) { - id = deviceId.first; - } else { - id = deviceId; + try { + final controlValue = event.value ? 'open' : 'close'; + await controlDeviceService.controlDevice( + deviceUuid: event.deviceId, + status: Status(code: event.code, value: controlValue), + ); + } catch (e) { + _updateLocalValue(!event.value, emit); + emit(CurtainControlError(e.toString())); } - - if (_timer != null) { - _timer!.cancel(); - } - _timer = Timer(const Duration(seconds: 1), () async { - try { - final controlValue = value ? 'open' : 'close'; - - late bool response; - if (isBatch) { - response = await DevicesManagementApi() - .deviceBatchControl(deviceId, code, controlValue); - } else { - response = await DevicesManagementApi() - .deviceControl(deviceId, Status(code: code, value: controlValue)); - } - - if (!response) { - _revertValueAndEmit(id, oldValue, emit); - } - } catch (e) { - _revertValueAndEmit(id, oldValue, emit); - } - }); - } - - void _revertValueAndEmit( - String deviceId, bool oldValue, Emitter emit) { - _updateLocalValue(oldValue, emit); - emit(CurtainStatusLoaded(deviceStatus)); - emit(const CurtainControlError('Failed to control the device.')); } void _updateLocalValue(bool value, Emitter emit) { @@ -152,41 +115,44 @@ class CurtainBloc extends Bloc { return command.toLowerCase() == 'open'; } - FutureOr _onFetchBatchStatus( - CurtainFetchBatchStatus event, Emitter emit) async { + Future _onFetchBatchStatus( + CurtainFetchBatchStatus event, + Emitter emit, + ) async { emit(CurtainStatusLoading()); try { - final status = - await DevicesManagementApi().getBatchStatus(event.devicesIds); - + final status = await DevicesManagementApi().getBatchStatus(event.devicesIds); deviceStatus = _checkStatus(status.status[0].value); - emit(CurtainStatusLoaded(deviceStatus)); } catch (e) { emit(CurtainError(e.toString())); } } - FutureOr _onCurtainBatchControl( - CurtainBatchControl event, Emitter emit) async { - final oldValue = deviceStatus; - + Future _onCurtainBatchControl( + CurtainBatchControl event, + Emitter emit, + ) async { + emit(CurtainStatusLoading()); _updateLocalValue(event.value, emit); - emit(CurtainStatusLoaded(deviceStatus)); - - await _runDebounce( - deviceId: event.devicesIds, - code: event.code, - value: event.value, - oldValue: oldValue, - emit: emit, - isBatch: true, - ); + try { + final controlValue = event.value ? 'open' : 'stop'; + await batchControlDevicesService.batchControlDevices( + uuids: event.devicesIds, + code: event.code, + value: controlValue, + ); + } catch (e) { + _updateLocalValue(!event.value, emit); + emit(CurtainControlError(e.toString())); + } } - FutureOr _onFactoryReset( - CurtainFactoryReset event, Emitter emit) async { + Future _onFactoryReset( + CurtainFactoryReset event, + Emitter emit, + ) async { emit(CurtainStatusLoading()); try { final response = await DevicesManagementApi().factoryReset( diff --git a/lib/pages/device_managment/curtain/factories/curtain_bloc_factory.dart b/lib/pages/device_managment/curtain/factories/curtain_bloc_factory.dart new file mode 100644 index 00000000..f6257b0a --- /dev/null +++ b/lib/pages/device_managment/curtain/factories/curtain_bloc_factory.dart @@ -0,0 +1,18 @@ +import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/factories/device_bloc_dependencies_factory.dart'; + +abstract final class CurtainBlocFactory { + const CurtainBlocFactory._(); + + static CurtainBloc create({ + required String deviceId, + }) { + return CurtainBloc( + deviceId: deviceId, + controlDeviceService: + DeviceBlocDependenciesFactory.createControlDeviceService(), + batchControlDevicesService: + DeviceBlocDependenciesFactory.createBatchControlDevicesService(), + ); + } +} diff --git a/lib/pages/device_managment/curtain/view/curtain_batch_status_view.dart b/lib/pages/device_managment/curtain/view/curtain_batch_status_view.dart index 7c873e20..41dcaf9e 100644 --- a/lib/pages/device_managment/curtain/view/curtain_batch_status_view.dart +++ b/lib/pages/device_managment/curtain/view/curtain_batch_status_view.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart'; +import 'package:syncrow_web/pages/device_managment/curtain/factories/curtain_bloc_factory.dart'; import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart'; // import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; @@ -18,7 +19,7 @@ class CurtainBatchStatusView extends StatelessWidget with HelperResponsiveLayout Widget build(BuildContext context) { return BlocProvider( create: (context) => - CurtainBloc(deviceId: devicesIds.first)..add(CurtainFetchBatchStatus(devicesIds)), + CurtainBlocFactory.create(deviceId: devicesIds.first)..add(CurtainFetchBatchStatus(devicesIds)), child: BlocBuilder( builder: (context, state) { if (state is CurtainStatusLoading) { diff --git a/lib/pages/device_managment/curtain/view/curtain_status_view.dart b/lib/pages/device_managment/curtain/view/curtain_status_view.dart index 2afe49f4..84b0a943 100644 --- a/lib/pages/device_managment/curtain/view/curtain_status_view.dart +++ b/lib/pages/device_managment/curtain/view/curtain_status_view.dart @@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/common/curtain_toggle.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart'; import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart'; +import 'package:syncrow_web/pages/device_managment/curtain/factories/curtain_bloc_factory.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; class CurtainStatusControlsView extends StatelessWidget @@ -15,7 +16,7 @@ class CurtainStatusControlsView extends StatelessWidget @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => CurtainBloc(deviceId: deviceId) + create: (context) => CurtainBlocFactory.create(deviceId: deviceId) ..add(CurtainFetchDeviceStatus(deviceId)), child: BlocBuilder( builder: (context, state) {