Refactor TwoGangGlassSwitchBloc to integrate new service dependencies and utilize a factory for instantiation. Enhanced event handling methods for improved error management and state updates, including real-time status listening from Firebase.

This commit is contained in:
Faris Armoush
2025-06-03 10:33:33 +03:00
parent f58ddf76da
commit 88a7607395
5 changed files with 142 additions and 176 deletions

View File

@ -1,26 +1,33 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.dart'; import 'package:flutter/foundation.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.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'; import 'package:syncrow_web/services/devices_mang_api.dart';
part 'two_gang_glass_switch_event.dart'; part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart'; part 'two_gang_glass_switch_state.dart';
class TwoGangGlassSwitchBloc class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> { extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
TwoGangGlassStatusModel deviceStatus; final String deviceId;
Timer? _timer; final ControlDeviceService controlDeviceService;
TwoGangGlassSwitchBloc({required String deviceId}) final BatchControlDevicesService batchControlDevicesService;
: deviceStatus = TwoGangGlassStatusModel(
uuid: deviceId, late TwoGangGlassStatusModel deviceStatus;
switch1: false,
countDown1: 0, TwoGangGlassSwitchBloc({
switch2: false, required this.deviceId,
countDown2: 0), required this.controlDeviceService,
super(TwoGangGlassSwitchInitial()) { required this.batchControlDevicesService,
}) : super(TwoGangGlassSwitchInitial()) {
on<TwoGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<TwoGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<TwoGangGlassSwitchControl>(_onControl); on<TwoGangGlassSwitchControl>(_onControl);
on<TwoGangGlassSwitchBatchControl>(_onBatchControl); on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
@ -29,14 +36,14 @@ class TwoGangGlassSwitchBloc
on<StatusUpdated>(_onStatusUpdated); on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event, Future<void> _onFetchDeviceStatus(
Emitter<TwoGangGlassSwitchState> emit) async { TwoGangGlassSwitchFetchDeviceEvent event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading()); emit(TwoGangGlassSwitchLoading());
try { try {
final status = final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
await DevicesManagementApi().getDeviceStatus(event.deviceId); deviceStatus = TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
deviceStatus =
TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId); _listenToChanges(event.deviceId);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -46,200 +53,121 @@ class TwoGangGlassSwitchBloc
void _listenToChanges(String deviceId) { void _listenToChanges(String deviceId) {
try { try {
DatabaseReference ref = final ref = FirebaseDatabase.instance.ref(
FirebaseDatabase.instance.ref('device-status/$deviceId'); 'device-status/$deviceId',
ref.onValue.listen((DatabaseEvent event) { );
if (event.snapshot.value == null) return;
ref.onValue.listen((event) {
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
Map<dynamic, dynamic> data =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = []; List<Status> statusList = [];
eventsMap['status'].forEach((element) {
data['status'].forEach((element) { statusList.add(Status(code: element['code'], value: element['value']));
statusList
.add(Status(code: element['code'], value: element['value']));
}); });
// Parse the new status and add the event deviceStatus = TwoGangGlassStatusModel.fromJson(deviceId, statusList);
final updatedStatus = add(StatusUpdated(deviceStatus));
TwoGangGlassStatusModel.fromJson(data['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(updatedStatus));
}
}); });
} catch (e) { } catch (_) {
// Handle errors and emit an error state if necessary log(
if (!isClosed) { 'Error listening to changes',
// add(TwoGangGlassSwitchError('Error listening to updates: $e')); name: 'TwoGangGlassSwitchBloc._listenToChanges',
} );
} }
} }
Future<void> _onControl(TwoGangGlassSwitchControl event, Future<void> _onControl(
Emitter<TwoGangGlassSwitchState> emit) async { TwoGangGlassSwitchControl event,
final oldValue = _getValueByCode(event.code); Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
await _runDebounce( try {
deviceId: event.deviceId, await controlDeviceService.controlDevice(
code: event.code, deviceUuid: event.deviceId,
value: event.value, status: Status(code: event.code, value: event.value),
oldValue: oldValue, );
emit: emit, } catch (e) {
isBatch: false, _updateLocalValue(event.code, !event.value);
); emit(TwoGangGlassSwitchError(e.toString()));
}
} }
Future<void> _onBatchControl(TwoGangGlassSwitchBatchControl event, Future<void> _onBatchControl(
Emitter<TwoGangGlassSwitchState> emit) async { TwoGangGlassSwitchBatchControl event,
final oldValue = _getValueByCode(event.code); Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading());
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
await _runDebounce( try {
deviceId: event.deviceIds, await batchControlDevicesService.batchControlDevices(
code: event.code, uuids: event.deviceIds,
value: event.value, code: event.code,
oldValue: oldValue, value: event.value,
emit: emit, );
isBatch: true, } catch (e) {
); _updateLocalValue(event.code, !event.value);
emit(TwoGangGlassSwitchError(e.toString()));
}
} }
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
TwoGangGlassSwitchFetchBatchStatusEvent event, TwoGangGlassSwitchFetchBatchStatusEvent event,
Emitter<TwoGangGlassSwitchState> emit) async { Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading()); emit(TwoGangGlassSwitchLoading());
try { try {
final status = final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = TwoGangGlassStatusModel.fromJson( deviceStatus = TwoGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status); event.deviceIds.first,
status.status,
);
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(TwoGangGlassSwitchError(e.toString())); emit(TwoGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onFactoryReset(TwoGangGlassFactoryReset event, Future<void> _onFactoryReset(
Emitter<TwoGangGlassSwitchState> emit) async { TwoGangGlassFactoryReset event,
Emitter<TwoGangGlassSwitchState> emit,
) async {
emit(TwoGangGlassSwitchLoading()); emit(TwoGangGlassSwitchLoading());
try { try {
final response = await DevicesManagementApi() final response = await DevicesManagementApi().factoryReset(
.factoryReset(event.factoryReset, event.deviceId); event.factoryReset,
event.deviceId,
);
if (!response) { if (!response) {
emit(TwoGangGlassSwitchError('Failed')); emit(TwoGangGlassSwitchError('Failed to reset device'));
} else { } else {
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); add(TwoGangGlassSwitchFetchDeviceEvent(event.deviceId));
} }
} catch (e) { } catch (e) {
emit(TwoGangGlassSwitchError(e.toString())); emit(TwoGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _runDebounce({ void _onStatusUpdated(
required dynamic deviceId, StatusUpdated event,
required String code, Emitter<TwoGangGlassSwitchState> emit,
required bool value, ) {
required bool oldValue, deviceStatus = event.deviceStatus;
required Emitter<TwoGangGlassSwitchState> emit,
required bool isBatch,
}) async {
late String id;
if (deviceId is List) {
id = deviceId.first;
} else {
id = deviceId;
}
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(const Duration(milliseconds: 500), () async {
try {
late bool response;
if (isBatch) {
response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else {
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
}
if (!response) {
_revertValueAndEmit(id, code, oldValue, emit);
}
} catch (e) {
_revertValueAndEmit(id, code, oldValue, emit);
}
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<TwoGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} }
void _updateLocalValue(String code, bool value) { void _updateLocalValue(String code, bool value) {
if (code == 'switch_1') {
deviceStatus = deviceStatus.copyWith(switch1: value);
} else if (code == 'switch_2') {
deviceStatus = deviceStatus.copyWith(switch2: value);
}
}
bool _getValueByCode(String code) {
switch (code) { switch (code) {
case 'switch_1': case 'switch_1':
return deviceStatus.switch1; deviceStatus = deviceStatus.copyWith(switch1: value);
break;
case 'switch_2': case 'switch_2':
return deviceStatus.switch2; deviceStatus = deviceStatus.copyWith(switch2: value);
default: break;
return false;
} }
} }
@override
Future<void> close() {
_timer?.cancel();
return super.close();
}
// _listenToChanges(deviceId) {
// try {
// DatabaseReference ref =
// FirebaseDatabase.instance.ref('device-status/$deviceId');
// Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap =
// event.snapshot.value as Map<dynamic, dynamic>;
// List<Status> statusList = [];
// usersMap['status'].forEach((element) {
// statusList
// .add(Status(code: element['code'], value: element['value']));
// });
// deviceStatus = TwoGangGlassStatusModel.fromJson(
// usersMap['productUuid'], statusList);
// if (!isClosed) {
// add(StatusUpdated(deviceStatus));
// }
// });
// } catch (_) {}
// }
void _onStatusUpdated(
StatusUpdated event, Emitter<TwoGangGlassSwitchState> emit) {
// Update the local deviceStatus with the new status from the event
deviceStatus = event.deviceStatus;
// Emit the new state with the updated status
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
}
} }

View File

@ -1,12 +1,17 @@
part of 'two_gang_glass_switch_bloc.dart'; part of 'two_gang_glass_switch_bloc.dart';
@immutable @immutable
abstract class TwoGangGlassSwitchEvent {} abstract class TwoGangGlassSwitchEvent extends Equatable {
const TwoGangGlassSwitchEvent();
}
class TwoGangGlassSwitchFetchDeviceEvent extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchFetchDeviceEvent extends TwoGangGlassSwitchEvent {
final String deviceId; final String deviceId;
TwoGangGlassSwitchFetchDeviceEvent(this.deviceId); const TwoGangGlassSwitchFetchDeviceEvent(this.deviceId);
@override
List<Object> get props => [deviceId];
} }
class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent {
@ -14,11 +19,14 @@ class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent {
final String code; final String code;
final bool value; final bool value;
TwoGangGlassSwitchControl({ const TwoGangGlassSwitchControl({
required this.deviceId, required this.deviceId,
required this.code, required this.code,
required this.value, required this.value,
}); });
@override
List<Object> get props => [deviceId, code, value];
} }
class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent {
@ -26,33 +34,43 @@ class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent {
final String code; final String code;
final bool value; final bool value;
TwoGangGlassSwitchBatchControl({ const TwoGangGlassSwitchBatchControl({
required this.deviceIds, required this.deviceIds,
required this.code, required this.code,
required this.value, required this.value,
}); });
@override
List<Object> get props => [deviceIds, code, value];
} }
class TwoGangGlassSwitchFetchBatchStatusEvent extends TwoGangGlassSwitchEvent { class TwoGangGlassSwitchFetchBatchStatusEvent extends TwoGangGlassSwitchEvent {
final List<String> deviceIds; final List<String> deviceIds;
TwoGangGlassSwitchFetchBatchStatusEvent(this.deviceIds); const TwoGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
@override
List<Object> get props => [deviceIds];
} }
class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent { class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent {
final String deviceId; final String deviceId;
final FactoryResetModel factoryReset; final FactoryResetModel factoryReset;
TwoGangGlassFactoryReset({ const TwoGangGlassFactoryReset({
required this.deviceId, required this.deviceId,
required this.factoryReset, required this.factoryReset,
}); });
@override
List<Object> get props => [deviceId, factoryReset];
} }
class StatusUpdated extends TwoGangGlassSwitchEvent { class StatusUpdated extends TwoGangGlassSwitchEvent {
final TwoGangGlassStatusModel deviceStatus; final TwoGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
const StatusUpdated(this.deviceStatus);
@override @override
List<Object> get props => [deviceStatus]; List<Object> get props => [deviceStatus];
} }

View File

@ -0,0 +1,18 @@
import 'package:syncrow_web/pages/device_managment/factories/device_bloc_dependencies_factory.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart';
abstract final class TwoGangGlassSwitchBlocFactory {
const TwoGangGlassSwitchBlocFactory._();
static TwoGangGlassSwitchBloc create({
required String deviceId,
}) {
return TwoGangGlassSwitchBloc(
deviceId: deviceId,
controlDeviceService:
DeviceBlocDependenciesFactory.createControlDeviceService(),
batchControlDevicesService:
DeviceBlocDependenciesFactory.createBatchControlDevicesService(),
);
}
}

View File

@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_
// import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart'; // import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/factories/two_gang_glass_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -16,7 +17,7 @@ class TwoGangGlassSwitchBatchControlView extends StatelessWidget with HelperResp
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceIds.first) create: (context) => TwoGangGlassSwitchBlocFactory.create(deviceId: deviceIds.first)
..add(TwoGangGlassSwitchFetchBatchStatusEvent(deviceIds)), ..add(TwoGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>( child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart'; import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/factories/two_gang_glass_switch_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -15,7 +16,7 @@ class TwoGangGlassSwitchControlView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceId) create: (context) => TwoGangGlassSwitchBlocFactory.create(deviceId: deviceId)
..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)), ..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>( child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {