push living room refactor

This commit is contained in:
ashrafzarkanisala
2024-08-26 21:46:01 +03:00
parent 191801d9a8
commit 2dc5a1e7ed
12 changed files with 287 additions and 69 deletions

View File

@ -12,7 +12,7 @@ mixin RouteControlsBasedCode {
switch (device.productType) { switch (device.productType) {
case '3G': case '3G':
return LivingRoomDeviceControl( return LivingRoomDeviceControl(
device: device, deviceId: device.uuid!,
); );
case 'GW': case 'GW':
return GateWayControls( return GateWayControls(

View File

@ -11,7 +11,8 @@ import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presen
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';
class CeilingSensorControls extends StatelessWidget with HelperResponsiveLayout { class CeilingSensorControls extends StatelessWidget
with HelperResponsiveLayout {
const CeilingSensorControls({super.key, required this.device}); const CeilingSensorControls({super.key, required this.device});
final AllDevicesModel device; final AllDevicesModel device;
@ -21,8 +22,8 @@ class CeilingSensorControls extends StatelessWidget with HelperResponsiveLayout
final isLarge = isLargeScreenSize(context); final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => CeilingSensorBloc(deviceId: device.uuid ?? '')
CeilingSensorBloc(deviceId: device.uuid ?? '')..add(CeilingInitialEvent()), ..add(CeilingInitialEvent()),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>( child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
builder: (context, state) { builder: (context, state) {
if (state is CeilingLoadingInitialState) { if (state is CeilingLoadingInitialState) {
@ -86,17 +87,20 @@ class CeilingSensorControls extends StatelessWidget with HelperResponsiveLayout
), ),
), ),
PresenceUpdateData( PresenceUpdateData(
value: (state.ceilingSensorModel.noBodyTime.toDouble() / 3600).roundToDouble(), value:
(state.ceilingSensorModel.noBodyTime.toDouble() / 3600)
.roundToDouble(),
title: 'Nobody Time:', title: 'Nobody Time:',
minValue: 0, minValue: 0,
maxValue: 300000, maxValue: 300000,
steps: 5000, steps: 5000,
description: 'hr', description: 'hr',
action: (int value) => action: (int value) => context
context.read<CeilingSensorBloc>().add(CeilingChangeValueEvent( .read<CeilingSensorBloc>()
code: 'none_body_time', .add(CeilingChangeValueEvent(
value: value, code: 'none_body_time',
))), value: value,
))),
GestureDetector( GestureDetector(
onTap: () {}, onTap: () {},
child: const PresenceStaticWidget( child: const PresenceStaticWidget(

View File

@ -34,7 +34,7 @@ class GateWayControls extends StatelessWidget with HelperResponsiveLayout {
: isMedium : isMedium
? 2 ? 2
: 1, : 1,
mainAxisExtent: 150, mainAxisExtent: 133,
crossAxisSpacing: 12, crossAxisSpacing: 12,
mainAxisSpacing: 12, mainAxisSpacing: 12,
), ),

View File

@ -1,15 +1,21 @@
import 'dart:async'; // ignore_for_file: invalid_use_of_visible_for_testing_member
import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.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/living_room_switch/models/living_room_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
part 'living_room_event.dart'; part 'living_room_event.dart';
part 'living_room_state.dart'; part 'living_room_state.dart';
class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> { class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
LivingRoomBloc() : super(LivingRoomInitial()) { late LivingRoomStatusModel deviceStatus;
final String deviceId;
Timer? _timer;
LivingRoomBloc({required this.deviceId}) : super(LivingRoomInitial()) {
on<LivingRoomFetchDeviceStatus>(_onFetchDeviceStatus); on<LivingRoomFetchDeviceStatus>(_onFetchDeviceStatus);
on<LivingRoomControl>(_livingRoomControl); on<LivingRoomControl>(_livingRoomControl);
} }
@ -20,7 +26,9 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
try { try {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
emit(LivingRoomDeviceStatusLoaded(status)); deviceStatus =
LivingRoomStatusModel.fromJson(event.deviceId, status.status);
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
} }
@ -28,22 +36,84 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
FutureOr<void> _livingRoomControl( FutureOr<void> _livingRoomControl(
LivingRoomControl event, Emitter<LivingRoomState> emit) async { LivingRoomControl event, Emitter<LivingRoomState> emit) async {
emit(LivingRoomControlLoading()); final oldValue = _getValueByCode(event.code);
try {
final status = Status(code: event.code, value: event.value);
final response =
await DevicesManagementApi().deviceControl(event.deviceId, status);
if (response) { _updateLocalValue(event.code, event.value);
await Future.delayed(const Duration(milliseconds: 500));
final newStatus = emit(LivingRoomDeviceStatusLoaded(deviceStatus));
await DevicesManagementApi().getDeviceStatus(event.deviceId);
emit(LivingRoomDeviceStatusLoaded(newStatus)); await _runDebounce(
} else { deviceId: event.deviceId,
emit(const LivingRoomControlError('Failed to control the device.')); code: event.code,
value: event.value,
oldValue: oldValue,
emit: emit,
);
}
Future<void> _runDebounce({
required String deviceId,
required String code,
required dynamic value,
required dynamic oldValue,
required Emitter<LivingRoomState> emit,
}) async {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(const Duration(seconds: 1), () async {
try {
final response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
if (!response) {
_revertValueAndEmit(deviceId, code, oldValue, emit);
}
} catch (e) {
_revertValueAndEmit(deviceId, code, oldValue, emit);
} }
} catch (e) { });
emit(LivingRoomControlError('Error controlling the device: $e')); }
void _revertValueAndEmit(String deviceId, String code, dynamic oldValue,
Emitter<LivingRoomState> emit) {
_updateLocalValue(code, oldValue);
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
emit(const LivingRoomControlError('Failed to control the device.'));
}
void _updateLocalValue(String code, dynamic value) {
switch (code) {
case 'switch_1':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(switch1: value);
}
break;
case 'switch_2':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(switch2: value);
}
break;
case 'switch_3':
if (value is bool) {
deviceStatus = deviceStatus.copyWith(switch3: value);
}
break;
default:
break;
}
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
}
dynamic _getValueByCode(String code) {
switch (code) {
case 'switch_1':
return deviceStatus.switch1;
case 'switch_2':
return deviceStatus.switch2;
case 'switch_3':
return deviceStatus.switch3;
default:
return null;
} }
} }
} }

View File

@ -12,7 +12,7 @@ final class LivingRoomInitial extends LivingRoomState {}
class LivingRoomDeviceStatusLoading extends LivingRoomState {} class LivingRoomDeviceStatusLoading extends LivingRoomState {}
class LivingRoomDeviceStatusLoaded extends LivingRoomState { class LivingRoomDeviceStatusLoaded extends LivingRoomState {
final DeviceStatus status; final LivingRoomStatusModel status;
const LivingRoomDeviceStatusLoaded(this.status); const LivingRoomDeviceStatusLoaded(this.status);
@ -29,8 +29,6 @@ class LivingRoomDeviceManagementError extends LivingRoomState {
List<Object> get props => [message]; List<Object> get props => [message];
} }
class LivingRoomControlSuccess extends LivingRoomState {}
class LivingRoomControlError extends LivingRoomState { class LivingRoomControlError extends LivingRoomState {
final String message; final String message;
@ -39,5 +37,3 @@ class LivingRoomControlError extends LivingRoomState {
@override @override
List<Object> get props => [message]; List<Object> get props => [message];
} }
class LivingRoomControlLoading extends LivingRoomState {}

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/living_room_switch/control_list/cieling_light.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/cieling_light.dart';
import 'package:syncrow_web/pages/device_managment/living_room_switch/control_list/spot_light.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/spot_light.dart';
import 'package:syncrow_web/pages/device_managment/living_room_switch/control_list/wall_light.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/wall_light.dart';
mixin LivingRoomHelper { mixin LivingRoomHelper {
Widget livingRoomControlWidgets( Widget livingRoomControlWidgets(

View File

@ -0,0 +1,56 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
class LivingRoomStatusModel {
final String uuid;
final bool switch1;
final bool switch2;
final bool switch3;
LivingRoomStatusModel({
required this.uuid,
required this.switch1,
required this.switch2,
required this.switch3,
});
factory LivingRoomStatusModel.fromJson(String id, List<Status> jsonList) {
late bool switch1;
late bool switch2;
late bool switch3;
for (var status in jsonList) {
switch (status.code) {
case 'switch_1':
switch1 = status.value ?? false; // default to false if null
break;
case 'switch_2':
switch2 = status.value ?? false; // default to false if null
break;
case 'switch_3':
switch3 = status.value ?? false; // default to false if null
break;
}
}
return LivingRoomStatusModel(
uuid: id,
switch1: switch1,
switch2: switch2,
switch3: switch3,
);
}
LivingRoomStatusModel copyWith({
String? uuid,
bool? switch1,
bool? switch2,
bool? switch3,
}) {
return LivingRoomStatusModel(
uuid: uuid ?? this.uuid,
switch1: switch1 ?? this.switch1,
switch2: switch2 ?? this.switch2,
switch3: switch3 ?? this.switch3,
);
}
}

View File

@ -1,30 +1,30 @@
import 'package:flutter/material.dart'; 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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart';
import 'package:syncrow_web/pages/device_managment/living_room_switch/helper/living_room_helper.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/models/living_room_model.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/pages/device_managment/living_room_switch/widgets/living_toggle_widget.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class LivingRoomDeviceControl extends StatelessWidget with LivingRoomHelper { class LivingRoomDeviceControl extends StatelessWidget
const LivingRoomDeviceControl({super.key, required this.device}); with HelperResponsiveLayout {
final String deviceId;
final AllDevicesModel device; const LivingRoomDeviceControl({super.key, required this.deviceId});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => LivingRoomBloc(deviceId: deviceId)
LivingRoomBloc()..add(LivingRoomFetchDeviceStatus(device.uuid!)), ..add(LivingRoomFetchDeviceStatus(deviceId)),
child: BlocBuilder<LivingRoomBloc, LivingRoomState>( child: BlocBuilder<LivingRoomBloc, LivingRoomState>(
builder: (context, state) { builder: (context, state) {
if (state is LivingRoomDeviceStatusLoading) { if (state is LivingRoomDeviceStatusLoading) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (state is LivingRoomDeviceStatusLoaded) { } else if (state is LivingRoomDeviceStatusLoaded) {
return _buildStatusControls(state.status.status); return _buildStatusControls(context, state.status);
} else if ((state is LivingRoomDeviceManagementError) || } else if (state is LivingRoomDeviceManagementError ||
(state is LivingRoomControlError)) { state is LivingRoomControlError) {
return const Center(child: Text('Error fetching status')); return Center(child: Text(state.toString()));
} else { } else {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
@ -33,32 +33,44 @@ class LivingRoomDeviceControl extends StatelessWidget with LivingRoomHelper {
); );
} }
Widget _buildStatusControls(List<Status> statuses) { Widget _buildStatusControls(
return GridView.builder( BuildContext context, LivingRoomStatusModel status) {
padding: const EdgeInsets.symmetric(horizontal: 40), final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, crossAxisCount: isLarge
? 3
: isMedium
? 2
: 1,
mainAxisExtent: 133, mainAxisExtent: 133,
crossAxisSpacing: 12, crossAxisSpacing: 12,
mainAxisSpacing: 12, mainAxisSpacing: 12,
), ),
itemCount: 3, children: [
itemBuilder: (context, index) { ToggleWidget(
final status = statuses[index]; value: status.switch1,
code: 'switch_1',
return Container( deviceId: deviceId,
decoration: BoxDecoration( label: 'Wall Light',
borderRadius: BorderRadius.circular(20), ),
color: ColorsManager.greyColor.withOpacity(0.2), ToggleWidget(
border: Border.all(color: ColorsManager.boxDivider), value: status.switch2,
), code: 'switch_2',
padding: const EdgeInsets.all(16), deviceId: deviceId,
child: livingRoomControlWidgets( label: 'Ceiling Light',
value: status.value, code: status.code, deviceId: device.uuid!), ),
); ToggleWidget(
}, value: status.switch3,
code: 'switch_3',
deviceId: deviceId,
label: 'Spotlight',
),
],
); );
} }
} }

View File

@ -0,0 +1,80 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/living_room_switch/bloc/living_room_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class ToggleWidget extends StatelessWidget {
final bool value;
final String code;
final String deviceId;
final String label;
const ToggleWidget({
super.key,
required this.value,
required this.code,
required this.deviceId,
required this.label,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: ColorsManager.greyColor.withOpacity(0.2),
border: Border.all(color: ColorsManager.boxDivider),
),
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipOval(
child: Container(
color: ColorsManager.whiteColors,
child: SvgPicture.asset(
Assets.lightPulp,
width: 60,
height: 60,
fit: BoxFit.cover,
),
)),
SizedBox(
height: 20,
width: 35,
child: CupertinoSwitch(
value: value,
activeColor: ColorsManager.dialogBlueTitle,
onChanged: (newValue) {
context.read<LivingRoomBloc>().add(
LivingRoomControl(
deviceId: deviceId,
code: code,
value: newValue,
),
);
},
),
),
],
),
const Spacer(),
Text(
label,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
],
),
);
}
}