diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart index 6d40c61..ae6a942 100644 --- a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart @@ -26,7 +26,8 @@ class OneGangBloc extends Bloc { bool oneGangGroup = false; List devicesList = []; - OneGangBloc({required this.oneGangId, required this.switchCode}) : super(InitialState()) { + OneGangBloc({required this.oneGangId, required this.switchCode}) + : super(InitialState()) { on(_fetchOneGangStatus); on(_oneGangUpdated); on(_changeFirstSwitch); @@ -49,7 +50,8 @@ class OneGangBloc extends Bloc { on(_groupAllOff); } - void _fetchOneGangStatus(InitialEvent event, Emitter emit) async { + void _fetchOneGangStatus( + InitialEvent event, Emitter emit) async { emit(LoadingInitialState()); try { var response = await DevicesAPI.getDeviceStatus(oneGangId); @@ -68,18 +70,21 @@ class OneGangBloc extends Bloc { _listenToChanges() { try { - DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$oneGangId'); + DatabaseReference ref = + FirebaseDatabase.instance.ref('device-status/$oneGangId'); Stream stream = ref.onValue; stream.listen((DatabaseEvent event) async { if (_timer != null) { await Future.delayed(const Duration(seconds: 2)); } - Map usersMap = event.snapshot.value as Map; + Map usersMap = + event.snapshot.value as Map; List statusList = []; usersMap['status'].forEach((element) { - statusList.add(StatusModel(code: element['code'], value: element['value'])); + statusList + .add(StatusModel(code: element['code'], value: element['value'])); }); deviceStatus = OneGangModel.fromJson(statusList); @@ -94,7 +99,8 @@ class OneGangBloc extends Bloc { emit(UpdateState(oneGangModel: deviceStatus)); } - void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter emit) async { + void _changeFirstSwitch( + ChangeFirstSwitchStatusEvent event, Emitter emit) async { emit(LoadingNewSate(oneGangModel: deviceStatus)); try { deviceStatus.firstSwitch = !event.value; @@ -119,17 +125,20 @@ class OneGangBloc extends Bloc { } } - void _changeSliding(ChangeSlidingSegment event, Emitter emit) async { + void _changeSliding( + ChangeSlidingSegment event, Emitter emit) async { emit(ChangeSlidingSegmentState(value: event.value)); } - void _setCounterValue(SetCounterValue event, Emitter emit) async { + void _setCounterValue( + SetCounterValue event, Emitter emit) async { emit(LoadingNewSate(oneGangModel: deviceStatus)); int seconds = 0; try { seconds = event.duration.inSeconds; final response = await DevicesAPI.controlDevice( - DeviceControlModel(deviceId: oneGangId, code: event.deviceCode, value: seconds), + DeviceControlModel( + deviceId: oneGangId, code: event.deviceCode, value: seconds), oneGangId); if (response['success'] ?? false) { @@ -152,7 +161,8 @@ class OneGangBloc extends Bloc { } } - void _getCounterValue(GetCounterEvent event, Emitter emit) async { + void _getCounterValue( + GetCounterEvent event, Emitter emit) async { emit(LoadingInitialState()); try { var response = await DevicesAPI.getDeviceStatus(oneGangId); @@ -241,7 +251,8 @@ class OneGangBloc extends Bloc { deviceId: oneGangId, ); List jsonData = response; - listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); + listSchedule = + jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); emit(InitialState()); } on DioException catch (e) { final errorData = e.response!.data; @@ -252,12 +263,13 @@ class OneGangBloc extends Bloc { int? getTimeStampWithoutSeconds(DateTime? dateTime) { if (dateTime == null) return null; - DateTime dateTimeWithoutSeconds = - DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute); + DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, + dateTime.day, dateTime.hour, dateTime.minute); return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; } - Future toggleChange(ToggleScheduleEvent event, Emitter emit) async { + Future toggleChange( + ToggleScheduleEvent event, Emitter emit) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.changeSchedule( @@ -276,7 +288,8 @@ class OneGangBloc extends Bloc { } } - Future deleteSchedule(DeleteScheduleEvent event, Emitter emit) async { + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.deleteSchedule( @@ -296,8 +309,8 @@ class OneGangBloc extends Bloc { } } - - void toggleCreateSchedule(ToggleCreateScheduleEvent event, Emitter emit) { + void toggleCreateSchedule( + ToggleCreateScheduleEvent event, Emitter emit) { emit(LoadingInitialState()); createSchedule = !createSchedule; selectedDays.clear(); @@ -326,7 +339,8 @@ class OneGangBloc extends Bloc { int selectedTabIndex = 0; - void toggleSelectedIndex(ToggleSelectedEvent event, Emitter emit) { + void toggleSelectedIndex( + ToggleSelectedEvent event, Emitter emit) { emit(LoadingInitialState()); selectedTabIndex = event.index; emit(ChangeSlidingSegmentState(value: selectedTabIndex)); @@ -335,7 +349,8 @@ class OneGangBloc extends Bloc { List groupOneGangList = []; bool allSwitchesOn = true; - void _fetchOneGangWizardStatus(InitialWizardEvent event, Emitter emit) async { + void _fetchOneGangWizardStatus( + InitialWizardEvent event, Emitter emit) async { emit(LoadingInitialState()); try { devicesList = []; @@ -345,7 +360,8 @@ class OneGangBloc extends Bloc { HomeCubit.getInstance().selectedSpace?.id ?? '', '1G'); for (int i = 0; i < devicesList.length; i++) { - var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); + var response = + await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); List statusModelList = []; for (var status in response['status']) { statusModelList.add(StatusModel.fromJson(status)); @@ -366,15 +382,16 @@ class OneGangBloc extends Bloc { return true; }); } - emit(UpdateGroupState(oneGangList: groupOneGangList, allSwitches: allSwitchesOn)); + emit(UpdateGroupState( + oneGangList: groupOneGangList, allSwitches: allSwitchesOn)); } catch (e) { emit(FailedState(error: e.toString())); return; } } - void _changeFirstWizardSwitch( - ChangeFirstWizardSwitchStatusEvent event, Emitter emit) async { + void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event, + Emitter emit) async { emit(LoadingNewSate(oneGangModel: deviceStatus)); try { bool allSwitchesValue = true; @@ -387,7 +404,8 @@ class OneGangBloc extends Bloc { } }); - emit(UpdateGroupState(oneGangList: groupOneGangList, allSwitches: allSwitchesValue)); + emit(UpdateGroupState( + oneGangList: groupOneGangList, allSwitches: allSwitchesValue)); final response = await DevicesAPI.deviceBatchController( code: 'switch_1', @@ -415,7 +433,8 @@ class OneGangBloc extends Bloc { emit(UpdateGroupState(oneGangList: groupOneGangList, allSwitches: true)); // Get a list of all device IDs - List allDeviceIds = groupOneGangList.map((device) => device.deviceId).toList(); + List allDeviceIds = + groupOneGangList.map((device) => device.deviceId).toList(); // First call for switch_1 final response = await DevicesAPI.deviceBatchController( @@ -447,7 +466,8 @@ class OneGangBloc extends Bloc { emit(UpdateGroupState(oneGangList: groupOneGangList, allSwitches: false)); // Get a list of all device IDs - List allDeviceIds = groupOneGangList.map((device) => device.deviceId).toList(); + List allDeviceIds = + groupOneGangList.map((device) => device.deviceId).toList(); // First call for switch_1 final response = await DevicesAPI.deviceBatchController( diff --git a/lib/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart b/lib/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart new file mode 100644 index 0000000..14d85f3 --- /dev/null +++ b/lib/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart @@ -0,0 +1,551 @@ +import 'dart:async'; +import 'package:dio/dio.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_state.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_event.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_one_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/one_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/schedule_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class OneTouchBloc extends Bloc { + final String oneTouchId; + final String switchCode; + OneTouchModel deviceStatus = OneTouchModel( + firstSwitch: false, + firstCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off); + Timer? _timer; + + bool oneTouchGroup = false; + List devicesList = []; + + OneTouchBloc({required this.oneTouchId, required this.switchCode}) + : super(InitialState()) { + on(_fetchOneTouchStatus); + on(_oneTouchUpdated); + on(_changeFirstSwitch); + on(_changeSliding); + on(_setCounterValue); + on(_getCounterValue); + on(_onTickTimer); + on(_onClose); + on(toggleDaySelection); + on(saveSchedule); + on(getSchedule); + on(toggleChange); + on(deleteSchedule); + on(toggleSelectedIndex); + on(toggleCreateSchedule); + on(_fetchOneTouchWizardStatus); + on(_changeFirstWizardSwitch); + on(_groupAllOn); + on(_groupAllOff); + on(_changeStatus); + } + + void _fetchOneTouchStatus( + InitialEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + var response = await DevicesAPI.getDeviceStatus(oneTouchId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = OneTouchModel.fromJson(statusModelList); + emit(UpdateState(oneTouchModel: deviceStatus)); + _listenToChanges(); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + _listenToChanges() { + try { + DatabaseReference ref = + FirebaseDatabase.instance.ref('device-status/$oneTouchId'); + Stream stream = ref.onValue; + + stream.listen((DatabaseEvent event) async { + if (_timer != null) { + await Future.delayed(const Duration(seconds: 2)); + } + Map usersMap = + event.snapshot.value as Map; + List statusList = []; + + usersMap['status'].forEach((element) { + statusList + .add(StatusModel(code: element['code'], value: element['value'])); + }); + + deviceStatus = OneTouchModel.fromJson(statusList); + if (!isClosed) { + add(OneTouchUpdated()); + } + }); + } catch (_) {} + } + + _oneTouchUpdated(OneTouchUpdated event, Emitter emit) { + emit(UpdateState(oneTouchModel: deviceStatus)); + } + + void _changeFirstSwitch( + ChangeFirstSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(oneTouchModel: deviceStatus)); + try { + deviceStatus.firstSwitch = !event.value; + emit(UpdateState(oneTouchModel: deviceStatus)); + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: oneTouchGroup ? event.deviceId : oneTouchId, + code: 'switch_1', + value: !event.value), + oneTouchGroup ? event.deviceId : oneTouchId); + + if (!response['success']) { + add(InitialEvent(groupScreen: oneTouchGroup)); + } + }); + } catch (_) { + add(InitialEvent(groupScreen: oneTouchGroup)); + } + } + + void _changeSliding( + ChangeSlidingSegment event, Emitter emit) async { + emit(ChangeSlidingSegmentState(value: event.value)); + } + + void _setCounterValue( + SetCounterValue event, Emitter emit) async { + emit(LoadingNewSate(oneTouchModel: deviceStatus)); + int seconds = 0; + try { + seconds = event.duration.inSeconds; + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: oneTouchId, code: event.deviceCode, value: seconds), + oneTouchId); + + if (response['success'] ?? false) { + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown = seconds; + } + } else { + emit(const FailedState(error: 'Something went wrong')); + return; + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + if (seconds > 0) { + _onStartTimer(seconds); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + void _getCounterValue( + GetCounterEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + var response = await DevicesAPI.getDeviceStatus(oneTouchId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = OneTouchModel.fromJson(statusModelList); + + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown > 0 + ? _onStartTimer(deviceStatus.firstCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.firstCountDown)); + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + void _onClose(OnClose event, Emitter emit) { + _timer?.cancel(); + } + + void _onStartTimer(int seconds) { + _timer?.cancel(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + add(TickTimer(seconds - timer.tick)); + }); + } + + void _onTickTimer(TickTimer event, Emitter emit) { + if (event.remainingTime > 0) { + emit(TimerRunInProgress(event.remainingTime)); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + List> days = [ + {"day": "Sun", "key": "Sun"}, + {"day": "Mon", "key": "Mon"}, + {"day": "Tue", "key": "Tue"}, + {"day": "Wed", "key": "Wed"}, + {"day": "Thu", "key": "Thu"}, + {"day": "Fri", "key": "Fri"}, + {"day": "Sat", "key": "Sat"}, + ]; + + Future saveSchedule( + ScheduleSave event, + Emitter emit, + ) async { + try { + if (selectedDays.isNotEmpty) { + emit(LoadingInitialState()); + await DevicesAPI.postSchedule( + category: switchCode, + deviceId: oneTouchId, + time: getTimeStampWithoutSeconds(selectedTime).toString(), + code: switchCode, + value: toggleSchedule, + days: selectedDays); + CustomSnackBar.displaySnackBar('Save Successfully'); + add(GetScheduleEvent()); + emit(SaveSchedule()); + + add(const ToggleCreateScheduleEvent(index: 1)); + } else { + CustomSnackBar.displaySnackBar('Please select days'); + } + } catch (e) { + emit(FailedState(error: e.toString())); + } + } + + Future getSchedule( + GetScheduleEvent event, + Emitter emit, + ) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.getSchedule( + category: switchCode, + deviceId: oneTouchId, + ); + List jsonData = response; + listSchedule = + jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); + emit(InitialState()); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + int? getTimeStampWithoutSeconds(DateTime? dateTime) { + if (dateTime == null) return null; + DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, + dateTime.day, dateTime.hour, dateTime.minute); + return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; + } + + Future toggleChange( + ToggleScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.changeSchedule( + scheduleId: event.id, deviceUuid: oneTouchId, enable: event.toggle); + if (response == true) { + add(GetScheduleEvent()); + toggleSchedule = event.toggle; + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.deleteSchedule( + scheduleId: event.id, + deviceUuid: oneTouchId, + ); + if (response == true) { + add(GetScheduleEvent()); + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + void toggleCreateSchedule( + ToggleCreateScheduleEvent event, Emitter emit) { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime = DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); + } + + bool toggleSchedule = true; + List selectedDays = []; + bool createSchedule = false; + List listSchedule = []; + DateTime? selectedTime = DateTime.now(); + Future toggleDaySelection( + ToggleDaySelectionEvent event, + Emitter emit, + ) async { + emit(LoadingInitialState()); + if (selectedDays.contains(event.key)) { + selectedDays.remove(event.key); + } else { + selectedDays.add(event.key); + } + emit(ChangeTimeState()); + add(ChangeSlidingSegment(value: 1)); + } + + int selectedTabIndex = 0; + + void toggleSelectedIndex( + ToggleSelectedEvent event, Emitter emit) { + emit(LoadingInitialState()); + selectedTabIndex = event.index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + + List groupOneTouchList = []; + bool allSwitchesOn = true; + + void _fetchOneTouchWizardStatus( + InitialWizardEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + devicesList = []; + groupOneTouchList = []; + allSwitchesOn = true; + devicesList = await DevicesAPI.getDeviceByGroupName( + HomeCubit.getInstance().selectedSpace?.id ?? '', '1GT'); + + for (int i = 0; i < devicesList.length; i++) { + var response = + await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = OneTouchModel.fromJson(statusModelList); + groupOneTouchList.add(GroupOneTouchModel( + deviceId: devicesList[i].uuid ?? '', + deviceName: devicesList[i].name ?? '', + firstSwitch: deviceStatus.firstSwitch, + )); + } + + if (groupOneTouchList.isNotEmpty) { + groupOneTouchList.firstWhere((element) { + if (!element.firstSwitch) { + allSwitchesOn = false; + } + return true; + }); + } + emit(UpdateGroupState( + oneTouchList: groupOneTouchList, allSwitches: allSwitchesOn)); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event, + Emitter emit) async { + emit(LoadingNewSate(oneTouchModel: deviceStatus)); + try { + bool allSwitchesValue = true; + groupOneTouchList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.firstSwitch = !event.value; + } + if (!element.firstSwitch) { + allSwitchesValue = false; + } + }); + final response = await DevicesAPI.deviceBatchController( + code: 'switch_1', + devicesUuid: [event.deviceId], + value: !event.value, + ); + + emit(UpdateGroupState( + oneTouchList: groupOneTouchList, allSwitches: allSwitchesValue)); + if (!response['success']) { + add(InitialEvent(groupScreen: oneTouchGroup)); + } + } catch (_) { + add(InitialEvent(groupScreen: oneTouchGroup)); + } + } + + void _groupAllOn(GroupAllOnEvent event, Emitter emit) async { + emit(LoadingNewSate(oneTouchModel: deviceStatus)); + try { + // Set all switches (firstSwitch and secondSwitch) based on the event value (on/off) + for (int i = 0; i < groupOneTouchList.length; i++) { + groupOneTouchList[i].firstSwitch = true; + } + + // Emit the state with updated values + emit( + UpdateGroupState(oneTouchList: groupOneTouchList, allSwitches: true)); + + // Get a list of all device IDs + List allDeviceIds = + groupOneTouchList.map((device) => device.deviceId).toList(); + + // First call for switch_1 + final response1 = await DevicesAPI.deviceBatchController( + code: 'switch_1', // Controls first switch for all devices + devicesUuid: allDeviceIds, + value: true, // true (on) or false (off) depending on the event value + ); + + // Second call for switch_2 + final response2 = await DevicesAPI.deviceBatchController( + code: 'switch_2', // Controls second switch for all devices + devicesUuid: allDeviceIds, + value: true, // true (on) or false (off) depending on the event value + ); + + // Check if either response is unsuccessful, then reset to initial state + if (!response1['success'] || !response2['success']) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + } + } catch (_) { + // In case of an error, delay and reset the screen to initial state + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + } + } + + void _groupAllOff(GroupAllOffEvent event, Emitter emit) async { + emit(LoadingNewSate(oneTouchModel: deviceStatus)); + try { + // Set all switches (firstSwitch and secondSwitch) based on the event value (on/off) + for (int i = 0; i < groupOneTouchList.length; i++) { + groupOneTouchList[i].firstSwitch = false; + } + + // Emit the state with updated values + emit(UpdateGroupState( + oneTouchList: groupOneTouchList, allSwitches: false)); + + // Get a list of all device IDs + List allDeviceIds = + groupOneTouchList.map((device) => device.deviceId).toList(); + + // First call for switch_1 + final response1 = await DevicesAPI.deviceBatchController( + code: 'switch_1', // Controls first switch for all devices + devicesUuid: allDeviceIds, + value: true, // true (on) or false (off) depending on the event value + ); + + // Second call for switch_2 + final response2 = await DevicesAPI.deviceBatchController( + code: 'switch_2', // Controls second switch for all devices + devicesUuid: allDeviceIds, + value: true, // true (on) or false (off) depending on the event value + ); + // Check if either response is unsuccessful, then reset to initial state + if (!response1['success'] || !response2['success']) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + } + } catch (_) { + // In case of an error, delay and reset the screen to initial state + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + } + } + + String statusSelected = ''; + String optionSelected = ''; + + Future _changeStatus( + ChangeStatusEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + + final Map> controlMap = { + "relay_status": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + "light_mode": { + 'Off': 'none', + 'On/Off Status': 'relay', + 'Switch Position': 'pos', + }, + "relay_status_1": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + }; + + final selectedControl = controlMap[optionSelected]?[statusSelected]; + if (selectedControl != null) { + await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: oneTouchId, code: optionSelected, value: selectedControl), + oneTouchId, + ); + } else { + print('Invalid statusSelected or optionSelected'); + } + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } +} diff --git a/lib/features/devices/bloc/one_touch_bloc/one_touch_event.dart b/lib/features/devices/bloc/one_touch_bloc/one_touch_event.dart new file mode 100644 index 0000000..68d1fa5 --- /dev/null +++ b/lib/features/devices/bloc/one_touch_bloc/one_touch_event.dart @@ -0,0 +1,148 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +abstract class OneTouchEvent extends Equatable { + const OneTouchEvent(); + + @override + List get props => []; +} + +class LoadingEvent extends OneTouchEvent {} + +class OneTouchUpdated extends OneTouchEvent {} + +class InitialEvent extends OneTouchEvent { + final bool groupScreen; + const InitialEvent({required this.groupScreen}); + @override + List get props => [groupScreen]; +} + +class ChangeFirstSwitchStatusEvent extends OneTouchEvent { + final bool value; + final String deviceId; + const ChangeFirstSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeSecondSwitchStatusEvent extends OneTouchEvent { + final bool value; + final String deviceId; + const ChangeSecondSwitchStatusEvent( + {required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class AllOffEvent extends OneTouchEvent {} + +class AllOnEvent extends OneTouchEvent {} + +class GroupAllOnEvent extends OneTouchEvent {} + +class GroupAllOffEvent extends OneTouchEvent {} + +class ChangeSlidingSegment extends OneTouchEvent { + final int value; + const ChangeSlidingSegment({required this.value}); + @override + List get props => [value]; +} + +class GetCounterEvent extends OneTouchEvent { + final String deviceCode; + const GetCounterEvent({required this.deviceCode}); + @override + List get props => [deviceCode]; +} + +class SetCounterValue extends OneTouchEvent { + final Duration duration; + final String deviceCode; + const SetCounterValue({required this.duration, required this.deviceCode}); + @override + List get props => [duration, deviceCode]; +} + +class StartTimer extends OneTouchEvent { + final int duration; + + const StartTimer(this.duration); + + @override + List get props => [duration]; +} + +class TickTimer extends OneTouchEvent { + final int remainingTime; + + const TickTimer(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class StopTimer extends OneTouchEvent {} + +class OnClose extends OneTouchEvent {} + +class InitialWizardEvent extends OneTouchEvent {} + +//------------------- Schedule ----------=--------- +class GetScheduleEvent extends OneTouchEvent {} + +class ScheduleSave extends OneTouchEvent {} + +class ToggleScheduleEvent extends OneTouchEvent { + final String id; + final bool toggle; + const ToggleScheduleEvent({required this.toggle, required this.id}); + @override + List get props => [toggle, id]; +} + +class ToggleDaySelectionEvent extends OneTouchEvent { + final String key; + + const ToggleDaySelectionEvent({required this.key}); + @override + List get props => [key]; +} + +class DeleteScheduleEvent extends OneTouchEvent { + final String id; + const DeleteScheduleEvent({required this.id}); + @override + List get props => [id]; +} + +class ToggleSelectedEvent extends OneTouchEvent { + final int index; + const ToggleSelectedEvent({required this.index}); + @override + List get props => [index]; +} + +class ToggleCreateScheduleEvent extends OneTouchEvent { + final int index; + const ToggleCreateScheduleEvent({required this.index}); + @override + List get props => [index]; +} + +class ChangeFirstWizardSwitchStatusEvent extends OneTouchEvent { + final bool value; + final String deviceId; + const ChangeFirstWizardSwitchStatusEvent( + {required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeStatusEvent extends OneTouchEvent { + final String deviceId; + final BuildContext context; + const ChangeStatusEvent({this.deviceId = '',required this.context}); +} diff --git a/lib/features/devices/bloc/one_touch_bloc/one_touch_state.dart b/lib/features/devices/bloc/one_touch_bloc/one_touch_state.dart new file mode 100644 index 0000000..96e1888 --- /dev/null +++ b/lib/features/devices/bloc/one_touch_bloc/one_touch_state.dart @@ -0,0 +1,92 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/group_one_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/one_touch_model.dart'; + + +class OneTouchState extends Equatable { + const OneTouchState(); + + @override + List get props => []; +} + +class InitialState extends OneTouchState {} + +class LoadingInitialState extends OneTouchState {} + +class UpdateState extends OneTouchState { + final OneTouchModel oneTouchModel; + const UpdateState({required this.oneTouchModel}); + + @override + List get props => [oneTouchModel]; +} + +class LoadingNewSate extends OneTouchState { + final OneTouchModel oneTouchModel; + const LoadingNewSate({required this.oneTouchModel}); + + @override + List get props => [OneTouchModel]; +} + +class UpdateGroupState extends OneTouchState { + final List oneTouchList; + final bool allSwitches; + + const UpdateGroupState({required this.oneTouchList, required this.allSwitches}); + + @override + List get props => [oneTouchList, allSwitches]; +} + +class FailedState extends OneTouchState { + final String error; + + const FailedState({required this.error}); + + @override + List get props => [error]; +} + +class ChangeSlidingSegmentState extends OneTouchState { + final int value; + + const ChangeSlidingSegmentState({required this.value}); + + @override + List get props => [value]; +} + +class UpdateTimerState extends OneTouchState { + final int seconds; + const UpdateTimerState({required this.seconds}); + + @override + List get props => [seconds]; +} + +class TimerRunInProgress extends OneTouchState { + final int remainingTime; + + const TimerRunInProgress(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class TimerRunComplete extends OneTouchState {} + +class SaveSchedule extends OneTouchState {} + +class IsToggleState extends OneTouchState { + final bool? onOff; + const IsToggleState({this.onOff}); +} + +class ChangeTimeState extends OneTouchState {} + +class UpdateCreateScheduleState extends OneTouchState { + final bool createSchedule; + UpdateCreateScheduleState(this.createSchedule); +} diff --git a/lib/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart b/lib/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart new file mode 100644 index 0000000..da8a97e --- /dev/null +++ b/lib/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart @@ -0,0 +1,748 @@ +import 'dart:async'; +import 'package:dio/dio.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_three_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/schedule_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/three_touch_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class ThreeTouchBloc extends Bloc { + final String threeTouchId; + final String switchCode; + ThreeTouchModel deviceStatus = ThreeTouchModel( + firstSwitch: false, + secondSwitch: false, + thirdSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + thirdCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off, + relay_status_2: status.off, + relay_status_3: status.off, + ); + Timer? _timer; + + bool threeTouchGroup = false; + List devicesList = []; + List groupThreeTouchList = []; + bool allSwitchesOn = true; + + ThreeTouchBloc({required this.threeTouchId, required this.switchCode}) + : super(InitialState()) { + on(_fetchThreeTouchStatus); + on(_threeTouchUpdated); + on(_changeFirstSwitch); + on(_changeSecondSwitch); + on(_changeThirdSwitch); + on(_allOff); + on(_allOn); + on(_changeSliding); + on(_setCounterValue); + on(_getCounterValue); + on(_onTickTimer); + on(_onClose); + on(_groupAllOn); + on(_groupAllOff); + + on(toggleDaySelection); + on(saveSchedule); + on(getSchedule); + on(toggleChange); + on(deleteSchedule); + + on(toggleSelectedIndex); + on(toggleCreateSchedule); + on(_changeStatus); + } + + void _fetchThreeTouchStatus( + InitialEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + threeTouchGroup = event.groupScreen; + if (threeTouchGroup) { + devicesList = []; + groupThreeTouchList = []; + allSwitchesOn = true; + devicesList = await DevicesAPI.getDeviceByGroupName( + HomeCubit.getInstance().selectedSpace?.id ?? '', '3GT'); + + for (int i = 0; i < devicesList.length; i++) { + var response = + await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = ThreeTouchModel.fromJson(statusModelList); + + groupThreeTouchList.add(GroupThreeTouchModel( + deviceId: devicesList[i].uuid ?? '', + deviceName: devicesList[i].name ?? '', + firstSwitch: deviceStatus.firstSwitch, + secondSwitch: deviceStatus.secondSwitch, + thirdSwitch: deviceStatus.thirdSwitch)); + } + + if (groupThreeTouchList.isNotEmpty) { + groupThreeTouchList.firstWhere((element) { + if (!element.firstSwitch || + !element.secondSwitch || + !element.thirdSwitch) { + allSwitchesOn = false; + } + return true; + }); + } + emit(UpdateGroupState( + threeTouchList: groupThreeTouchList, allSwitches: allSwitchesOn)); + } else { + var response = await DevicesAPI.getDeviceStatus(threeTouchId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = ThreeTouchModel.fromJson(statusModelList); + emit(UpdateState(threeTouchModel: deviceStatus)); + _listenToChanges(); + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + _listenToChanges() { + try { + DatabaseReference ref = + FirebaseDatabase.instance.ref('device-status/$threeTouchId'); + Stream stream = ref.onValue; + + stream.listen((DatabaseEvent event) async { + if (_timer != null) { + await Future.delayed(const Duration(seconds: 2)); + } + Map usersMap = + event.snapshot.value as Map; + List statusList = []; + + usersMap['status'].forEach((element) { + statusList + .add(StatusModel(code: element['code'], value: element['value'])); + }); + + deviceStatus = ThreeTouchModel.fromJson(statusList); + if (!isClosed) { + add(ThreeTouchUpdated()); + } + }); + } catch (_) {} + } + + _threeTouchUpdated(ThreeTouchUpdated event, Emitter emit) { + emit(UpdateState(threeTouchModel: deviceStatus)); + } + + void _changeFirstSwitch( + ChangeFirstSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + try { + if (threeTouchGroup) { + bool allSwitchesValue = true; + groupThreeTouchList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.firstSwitch = !event.value; + } + if (!element.firstSwitch || + !element.secondSwitch || + !element.thirdSwitch) { + allSwitchesValue = false; + } + }); + emit(UpdateGroupState( + threeTouchList: groupThreeTouchList, + allSwitches: allSwitchesValue)); + } else { + deviceStatus.firstSwitch = !event.value; + emit(UpdateState(threeTouchModel: deviceStatus)); + } + + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchGroup ? event.deviceId : threeTouchId, + code: 'switch_1', + value: !event.value), + threeTouchGroup ? event.deviceId : threeTouchId); + + if (!response['success']) { + add(InitialEvent(groupScreen: threeTouchGroup)); + } + }); + } catch (_) { + add(InitialEvent(groupScreen: threeTouchGroup)); + } + } + + void _changeSecondSwitch(ChangeSecondSwitchStatusEvent event, + Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + try { + if (threeTouchGroup) { + bool allSwitchesValue = true; + groupThreeTouchList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.secondSwitch = !event.value; + } + if (!element.firstSwitch || + !element.secondSwitch || + !element.thirdSwitch) { + allSwitchesValue = false; + } + }); + emit(UpdateGroupState( + threeTouchList: groupThreeTouchList, + allSwitches: allSwitchesValue)); + } else { + deviceStatus.secondSwitch = !event.value; + emit(UpdateState(threeTouchModel: deviceStatus)); + } + + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchGroup ? event.deviceId : threeTouchId, + code: 'switch_2', + value: !event.value), + threeTouchGroup ? event.deviceId : threeTouchId); + + if (!response['success']) { + add(InitialEvent(groupScreen: threeTouchGroup)); + } + }); + } catch (_) { + add(InitialEvent(groupScreen: threeTouchGroup)); + } + } + + void _changeThirdSwitch( + ChangeThirdSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + try { + if (threeTouchGroup) { + bool allSwitchesValue = true; + groupThreeTouchList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.thirdSwitch = !event.value; + } + if (!element.firstSwitch || + !element.secondSwitch || + !element.thirdSwitch) { + allSwitchesValue = false; + } + }); + emit(UpdateGroupState( + threeTouchList: groupThreeTouchList, + allSwitches: allSwitchesValue)); + } else { + deviceStatus.thirdSwitch = !event.value; + emit(UpdateState(threeTouchModel: deviceStatus)); + } + + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchGroup ? event.deviceId : threeTouchId, + code: 'switch_3', + value: !event.value), + threeTouchGroup ? event.deviceId : threeTouchId); + + if (!response['success']) { + add(InitialEvent(groupScreen: threeTouchGroup)); + } + }); + } catch (_) { + add(InitialEvent(groupScreen: threeTouchGroup)); + } + } + + void _allOff(AllOffEvent event, Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + + try { + deviceStatus.firstSwitch = false; + deviceStatus.secondSwitch = false; + deviceStatus.thirdSwitch = false; + emit(UpdateState(threeTouchModel: deviceStatus)); + + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, + code: 'switch_1', + value: deviceStatus.firstSwitch), + threeTouchId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, + code: 'switch_2', + value: deviceStatus.secondSwitch), + threeTouchId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, + code: 'switch_3', + value: deviceStatus.thirdSwitch), + threeTouchId), + ]); + + if (response.every((element) => !element['success'])) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: false)); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: false)); + } + } + + void _allOn(AllOnEvent event, Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + + try { + deviceStatus.firstSwitch = true; + deviceStatus.secondSwitch = true; + deviceStatus.thirdSwitch = true; + emit(UpdateState(threeTouchModel: deviceStatus)); + + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, + code: 'switch_1', + value: deviceStatus.firstSwitch), + threeTouchId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, + code: 'switch_2', + value: deviceStatus.secondSwitch), + threeTouchId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, + code: 'switch_3', + value: deviceStatus.thirdSwitch), + threeTouchId), + ]); + + if (response.every((element) => !element['success'])) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: false)); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: false)); + } + } + + void _groupAllOn(GroupAllOnEvent event, Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + try { + for (int i = 0; i < groupThreeTouchList.length; i++) { + groupThreeTouchList[i].firstSwitch = true; + groupThreeTouchList[i].secondSwitch = true; + groupThreeTouchList[i].thirdSwitch = true; + } + emit(UpdateGroupState( + threeTouchList: groupThreeTouchList, allSwitches: true)); + + for (int i = 0; i < groupThreeTouchList.length; i++) { + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupThreeTouchList[i].deviceId, + code: 'switch_1', + value: true), + groupThreeTouchList[i].deviceId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupThreeTouchList[i].deviceId, + code: 'switch_2', + value: true), + groupThreeTouchList[i].deviceId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupThreeTouchList[i].deviceId, + code: 'switch_3', + value: true), + groupThreeTouchList[i].deviceId), + ]); + + if (response.every((element) => !element['success'])) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + break; + } + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + } + } + + void _groupAllOff( + GroupAllOffEvent event, Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + try { + for (int i = 0; i < groupThreeTouchList.length; i++) { + groupThreeTouchList[i].firstSwitch = false; + groupThreeTouchList[i].secondSwitch = false; + groupThreeTouchList[i].thirdSwitch = false; + } + emit(UpdateGroupState( + threeTouchList: groupThreeTouchList, allSwitches: false)); + + for (int i = 0; i < groupThreeTouchList.length; i++) { + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupThreeTouchList[i].deviceId, + code: 'switch_1', + value: false), + groupThreeTouchList[i].deviceId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupThreeTouchList[i].deviceId, + code: 'switch_2', + value: false), + groupThreeTouchList[i].deviceId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupThreeTouchList[i].deviceId, + code: 'switch_3', + value: false), + groupThreeTouchList[i].deviceId), + ]); + + if (response.every((element) => !element['success'])) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + break; + } + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent(groupScreen: true)); + } + } + + void _changeSliding( + ChangeSlidingSegment event, Emitter emit) async { + emit(ChangeSlidingSegmentState(value: event.value)); + } + + void _setCounterValue( + SetCounterValue event, Emitter emit) async { + emit(LoadingNewSate(threeTouchModel: deviceStatus)); + int seconds = 0; + try { + seconds = event.duration.inSeconds; + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, code: event.deviceCode, value: seconds), + threeTouchId); + + if (response['success'] ?? false) { + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown = seconds; + } else if (event.deviceCode == 'countdown_2') { + deviceStatus.secondCountDown = seconds; + } else if (event.deviceCode == 'countdown_3') { + deviceStatus.thirdCountDown = seconds; + } + } else { + emit(const FailedState(error: 'Something went wrong')); + return; + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + if (seconds > 0) { + _onStartTimer(seconds); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + void _getCounterValue( + GetCounterEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + var response = await DevicesAPI.getDeviceStatus(threeTouchId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = ThreeTouchModel.fromJson(statusModelList); + + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown > 0 + ? _onStartTimer(deviceStatus.firstCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.firstCountDown)); + } else if (event.deviceCode == 'countdown_2') { + deviceStatus.secondCountDown > 0 + ? _onStartTimer(deviceStatus.secondCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.secondCountDown)); + } else if (event.deviceCode == 'countdown_3') { + deviceStatus.thirdCountDown > 0 + ? _onStartTimer(deviceStatus.thirdCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.thirdCountDown)); + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + void _onClose(OnClose event, Emitter emit) { + _timer?.cancel(); + } + + void _onStartTimer(int seconds) { + _timer?.cancel(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + add(TickTimer(seconds - timer.tick)); + }); + } + + void _onTickTimer(TickTimer event, Emitter emit) { + if (event.remainingTime > 0) { + emit(TimerRunInProgress(event.remainingTime)); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + List> days = [ + {"day": "Sun", "key": "Sun"}, + {"day": "Mon", "key": "Mon"}, + {"day": "Tue", "key": "Tue"}, + {"day": "Wed", "key": "Wed"}, + {"day": "Thu", "key": "Thu"}, + {"day": "Fri", "key": "Fri"}, + {"day": "Sat", "key": "Sat"}, + ]; + + Future toggleDaySelection( + ToggleDaySelectionEvent event, + Emitter emit, + ) async { + emit(LoadingInitialState()); + if (selectedDays.contains(event.key)) { + selectedDays.remove(event.key); + } else { + selectedDays.add(event.key); + } + emit(ChangeTimeState()); + add(ChangeSlidingSegment(value: 1)); + } + + Future saveSchedule( + ThreeTouchSave event, + Emitter emit, + ) async { + try { + if (selectedDays.isNotEmpty) { + emit(LoadingInitialState()); + await DevicesAPI.postSchedule( + category: switchCode, + deviceId: threeTouchId, + time: getTimeStampWithoutSeconds(selectedTime).toString(), + code: switchCode, + value: toggleSchedule, + days: selectedDays); + CustomSnackBar.displaySnackBar('Save Successfully'); + add(GetScheduleEvent()); + emit(ThreeTouchSaveSchedule()); + add(const ToggleCreateScheduleEvent(index: 1)); + } else { + CustomSnackBar.displaySnackBar('Please select days'); + } + } catch (e) { + emit(FailedState(error: e.toString())); + } + } + + Future getSchedule( + GetScheduleEvent event, + Emitter emit, + ) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.getSchedule( + category: switchCode, + deviceId: threeTouchId, + ); + List jsonData = response; + listSchedule = + jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); + emit(InitialState()); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + int? getTimeStampWithoutSeconds(DateTime? dateTime) { + if (dateTime == null) return null; + DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, + dateTime.day, dateTime.hour, dateTime.minute); + return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; + } + + Future toggleChange( + ToggleScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.changeSchedule( + scheduleId: event.id, deviceUuid: threeTouchId, enable: event.toggle); + if (response == true) { + add(GetScheduleEvent()); + toggleSchedule = event.toggle; + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.deleteSchedule( + scheduleId: event.id, + deviceUuid: threeTouchId, + ); + if (response == true) { + add(GetScheduleEvent()); + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + void toggleSelectedIndex( + ToggleSelectedEvent event, Emitter emit) { + emit(LoadingInitialState()); + selectedTabIndex = event.index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + + void toggleCreateSchedule( + ToggleCreateScheduleEvent event, Emitter emit) { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime = DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); + } + + int selectedTabIndex = 0; + bool toggleSchedule = true; + List selectedDays = []; + bool createSchedule = false; + List listSchedule = []; + DateTime? selectedTime = DateTime.now(); + + String statusSelected = ''; + String optionSelected = ''; + + Future _changeStatus( + ChangeStatusEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final Map> controlMap = { + "relay_status": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + "light_mode": { + 'Off': 'none', + 'On/Off Status': 'relay', + 'Switch Position': 'pos', + }, + "relay_status_1": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + "relay_status_2": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + "relay_status_3": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + }; + + final selectedControl = controlMap[optionSelected]?[statusSelected]; + if (selectedControl != null) { + await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: threeTouchId, + code: optionSelected, + value: selectedControl), + threeTouchId, + ); + } else { + print('Invalid statusSelected or optionSelected'); + } + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } +} diff --git a/lib/features/devices/bloc/three_touch_bloc/three_touch_event.dart b/lib/features/devices/bloc/three_touch_bloc/three_touch_event.dart new file mode 100644 index 0000000..e6ef1fe --- /dev/null +++ b/lib/features/devices/bloc/three_touch_bloc/three_touch_event.dart @@ -0,0 +1,147 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +abstract class ThreeTouchEvent extends Equatable { + const ThreeTouchEvent(); + + @override + List get props => []; +} + +class LoadingEvent extends ThreeTouchEvent {} + +class ThreeTouchUpdated extends ThreeTouchEvent {} + +class InitialEvent extends ThreeTouchEvent { + final bool groupScreen; + const InitialEvent({required this.groupScreen}); + @override + List get props => [groupScreen]; +} + +class ChangeFirstSwitchStatusEvent extends ThreeTouchEvent { + final bool value; + final String deviceId; + const ChangeFirstSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeSecondSwitchStatusEvent extends ThreeTouchEvent { + final bool value; + final String deviceId; + const ChangeSecondSwitchStatusEvent( + {required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeThirdSwitchStatusEvent extends ThreeTouchEvent { + final bool value; + final String deviceId; + const ChangeThirdSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class AllOffEvent extends ThreeTouchEvent {} + +class AllOnEvent extends ThreeTouchEvent {} + +class GroupAllOnEvent extends ThreeTouchEvent {} + +class GroupAllOffEvent extends ThreeTouchEvent {} + +class ChangeSlidingSegment extends ThreeTouchEvent { + final int value; + const ChangeSlidingSegment({required this.value}); + @override + List get props => [value]; +} + +class GetCounterEvent extends ThreeTouchEvent { + final String deviceCode; + const GetCounterEvent({required this.deviceCode}); + @override + List get props => [deviceCode]; +} + +class SetCounterValue extends ThreeTouchEvent { + final Duration duration; + final String deviceCode; + const SetCounterValue({required this.duration, required this.deviceCode}); + @override + List get props => [duration, deviceCode]; +} + +class StartTimer extends ThreeTouchEvent { + final int duration; + + const StartTimer(this.duration); + + @override + List get props => [duration]; +} + +class TickTimer extends ThreeTouchEvent { + final int remainingTime; + + const TickTimer(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class StopTimer extends ThreeTouchEvent {} + +class OnClose extends ThreeTouchEvent {} + +//------------------- Schedule ----------=--------- +class GetScheduleEvent extends ThreeTouchEvent {} + +class ThreeTouchSave extends ThreeTouchEvent {} + +class ToggleScheduleEvent extends ThreeTouchEvent { + final String id; + final bool toggle; + const ToggleScheduleEvent({required this.toggle, required this.id}); + @override + List get props => [toggle, id]; +} + +class ToggleDaySelectionEvent extends ThreeTouchEvent { + final String key; + + const ToggleDaySelectionEvent({required this.key}); + @override + List get props => [key]; +} + +class DeleteScheduleEvent extends ThreeTouchEvent { + final String id; + const DeleteScheduleEvent({required this.id}); + @override + List get props => [id]; +} + +class ToggleSelectedEvent extends ThreeTouchEvent { + final int index; + const ToggleSelectedEvent({required this.index}); + @override + List get props => [index]; +} + +class ToggleCreateScheduleEvent extends ThreeTouchEvent { + final int index; + const ToggleCreateScheduleEvent({required this.index}); + @override + List get props => [index]; +} + +class InitialWizardDevises extends ThreeTouchEvent {} + +class ChangeStatusEvent extends ThreeTouchEvent { + final String deviceId; + final BuildContext context; + const ChangeStatusEvent({this.deviceId = '', required this.context}); +} diff --git a/lib/features/devices/bloc/three_touch_bloc/three_touch_state.dart b/lib/features/devices/bloc/three_touch_bloc/three_touch_state.dart new file mode 100644 index 0000000..ec675ba --- /dev/null +++ b/lib/features/devices/bloc/three_touch_bloc/three_touch_state.dart @@ -0,0 +1,96 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/group_three_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/three_touch_model.dart'; + +class ThreeTouchState extends Equatable { + const ThreeTouchState(); + + @override + List get props => []; +} + +class InitialState extends ThreeTouchState {} + +class LoadingInitialState extends ThreeTouchState {} + +class UpdateState extends ThreeTouchState { + final ThreeTouchModel threeTouchModel; + const UpdateState({required this.threeTouchModel}); + + @override + List get props => [threeTouchModel]; +} + +class LoadingNewSate extends ThreeTouchState { + final ThreeTouchModel threeTouchModel; + const LoadingNewSate({required this.threeTouchModel}); + + @override + List get props => [threeTouchModel]; +} + +class UpdateGroupState extends ThreeTouchState { + final List threeTouchList; + final bool allSwitches; + + const UpdateGroupState( + {required this.threeTouchList, required this.allSwitches}); + + @override + List get props => [threeTouchList, allSwitches]; +} + +class FailedState extends ThreeTouchState { + final String error; + + const FailedState({required this.error}); + + @override + List get props => [error]; +} + +class ChangeSlidingSegmentState extends ThreeTouchState { + final int value; + + const ChangeSlidingSegmentState({required this.value}); + + @override + List get props => [value]; +} + +class UpdateTimerState extends ThreeTouchState { + final int seconds; + const UpdateTimerState({required this.seconds}); + + @override + List get props => [seconds]; +} + +class TimerRunInProgress extends ThreeTouchState { + final int remainingTime; + + const TimerRunInProgress(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class TimerRunComplete extends ThreeTouchState {} + +class ThreeTouchSaveSchedule extends ThreeTouchState {} + +class IsToggleState extends ThreeTouchState { + final bool? onOff; + const IsToggleState({this.onOff}); +} + +class ChangeTimeState extends ThreeTouchState {} + +class UpdateCreateScheduleState extends ThreeTouchState { + final bool createSchedule; + UpdateCreateScheduleState(this.createSchedule); +} + +class ChangeStateSetting extends ThreeTouchState { + const ChangeStateSetting(); +} diff --git a/lib/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart b/lib/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart new file mode 100644 index 0000000..0f4c794 --- /dev/null +++ b/lib/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart @@ -0,0 +1,687 @@ +import 'dart:async'; +import 'package:dio/dio.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; + +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_two_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/schedule_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/two_touch_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class TwoTouchBloc extends Bloc { + final String twoTouchId; + final String switchCode; + TwoTouchModel deviceStatus = TwoTouchModel( + firstSwitch: false, + secondSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off, + relay_status_2: status.off, + ); + Timer? _timer; + + bool twoTouchGroup = false; + List devicesList = []; + List groupTwoTouchList = []; + bool allSwitchesOn = true; + bool toggleSchedule = true; + List selectedDays = []; + + bool createSchedule = false; + List listSchedule = []; + + TwoTouchBloc({required this.twoTouchId, required this.switchCode}) + : super(InitialState()) { + on(_fetchTwoTouchStatus); + on(_twoTouchUpdated); + on(_changeFirstSwitch); + on(_changeSecondSwitch); + on(_allOff); + on(_allOn); + on(_changeSliding); + on(_setCounterValue); + on(_getCounterValue); + on(_onTickTimer); + on(_onClose); + on(_groupAllOn); + on(_groupAllOff); + on(toggleDaySelection); + on(saveSchedule); + on(getSchedule); + on(toggleRepeat); + on(deleteSchedule); + on(toggleSelectedIndex); + on(toggleCreateSchedule); + on(_fetchTwoTouchWizardStatus); + on(_changeFirstWizardSwitch); + on(_changeSecondWizardSwitch); + on(_changeStatus); + } + + DateTime? selectedTime = DateTime.now(); + + int selectedTabIndex = 0; + + void toggleSelectedIndex( + ToggleSelectedEvent event, Emitter emit) { + emit(LoadingInitialState()); + selectedTabIndex = event.index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + + void toggleCreateSchedule( + ToggleCreateScheduleEvent event, Emitter emit) { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime = DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); + } + + void _fetchTwoTouchStatus( + InitialEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + var response = await DevicesAPI.getDeviceStatus(twoTouchId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = TwoTouchModel.fromJson(statusModelList); + emit(UpdateState(twoTouchModel: deviceStatus)); + _listenToChanges(); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + _listenToChanges() { + try { + DatabaseReference ref = + FirebaseDatabase.instance.ref('device-status/$twoTouchId'); + Stream stream = ref.onValue; + + stream.listen((DatabaseEvent event) async { + if (_timer != null) { + await Future.delayed(const Duration(seconds: 2)); + } + Map usersMap = + event.snapshot.value as Map; + List statusList = []; + + usersMap['status'].forEach((element) { + statusList + .add(StatusModel(code: element['code'], value: element['value'])); + }); + + deviceStatus = TwoTouchModel.fromJson(statusList); + if (!isClosed) { + add(TwoTouchUpdated()); + } + }); + } catch (_) {} + } + + _twoTouchUpdated(TwoTouchUpdated event, Emitter emit) { + emit(UpdateState(twoTouchModel: deviceStatus)); + } + + void _changeFirstSwitch( + ChangeFirstSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + try { + deviceStatus.firstSwitch = !event.value; + emit(UpdateState(twoTouchModel: deviceStatus)); + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, code: 'switch_1', value: !event.value), + twoTouchId); + + if (!response['success']) { + add(const InitialEvent()); + } + }); + } catch (_) { + add(const InitialEvent()); + } + } + + void _changeSecondSwitch( + ChangeSecondSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + try { + deviceStatus.secondSwitch = !event.value; + emit(UpdateState(twoTouchModel: deviceStatus)); + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, code: 'switch_2', value: !event.value), + twoTouchId); + + if (!response['success']) { + add(const InitialEvent()); + } + }); + } catch (_) { + add(const InitialEvent()); + } + } + + void _allOff(AllOffEvent event, Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + + try { + deviceStatus.firstSwitch = false; + deviceStatus.secondSwitch = false; + emit(UpdateState(twoTouchModel: deviceStatus)); + + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, + code: 'switch_1', + value: deviceStatus.firstSwitch), + twoTouchId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, + code: 'switch_2', + value: deviceStatus.secondSwitch), + twoTouchId), + ]); + + if (response.every((element) => !element['success'])) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent()); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent()); + } + } + + void _allOn(AllOnEvent event, Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + try { + deviceStatus.firstSwitch = true; + deviceStatus.secondSwitch = true; + emit(UpdateState(twoTouchModel: deviceStatus)); + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, + code: 'switch_1', + value: deviceStatus.firstSwitch), + twoTouchId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, + code: 'switch_2', + value: deviceStatus.secondSwitch), + twoTouchId), + ]); + if (response.every((element) => !element['success'])) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent()); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(const InitialEvent()); + } + } + + void _groupAllOn(GroupAllOnEvent event, Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + try { + for (int i = 0; i < groupTwoTouchList.length; i++) { + groupTwoTouchList[i].firstSwitch = true; + groupTwoTouchList[i].secondSwitch = true; + } + emit( + UpdateGroupState(twoTouchList: groupTwoTouchList, allSwitches: true)); + List allDeviceIds = + groupTwoTouchList.map((device) => device.deviceId).toList(); + + final response1 = await DevicesAPI.deviceBatchController( + code: 'switch_1', + devicesUuid: allDeviceIds, + value: true, + ); + + final response2 = await DevicesAPI.deviceBatchController( + code: 'switch_2', + devicesUuid: allDeviceIds, + value: true, + ); + + if (response1['failedResults'].toString() != '[]' || + response2['failedResults'].toString() != '[]') { + await Future.delayed(const Duration(milliseconds: 500)); + add(InitialWizardEvent()); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(InitialWizardEvent()); + } + } + + void _groupAllOff(GroupAllOffEvent event, Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + try { + for (int i = 0; i < groupTwoTouchList.length; i++) { + groupTwoTouchList[i].firstSwitch = false; + groupTwoTouchList[i].secondSwitch = false; + } + + emit(UpdateGroupState( + twoTouchList: groupTwoTouchList, allSwitches: false)); + + List allDeviceIds = + groupTwoTouchList.map((device) => device.deviceId).toList(); + + final response1 = await DevicesAPI.deviceBatchController( + code: 'switch_1', + devicesUuid: allDeviceIds, + value: false, + ); + + final response2 = await DevicesAPI.deviceBatchController( + code: 'switch_2', + devicesUuid: allDeviceIds, + value: false, + ); + + if (response1['failedResults'].toString() != '[]' || + response2['failedResults'].toString() != '[]') { + await Future.delayed(const Duration(milliseconds: 500)); + add(InitialWizardEvent()); + } + } catch (_) { + await Future.delayed(const Duration(milliseconds: 500)); + add(InitialWizardEvent()); + } + } + + void _changeSliding( + ChangeSlidingSegment event, Emitter emit) async { + emit(ChangeSlidingSegmentState(value: event.value)); + } + + void _setCounterValue( + SetCounterValue event, Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + int seconds = 0; + try { + seconds = event.duration.inSeconds; + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, code: event.deviceCode, value: seconds), + twoTouchId); + + if (response['success'] ?? false) { + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown = seconds; + } else if (event.deviceCode == 'countdown_2') { + deviceStatus.secondCountDown = seconds; + } + } else { + emit(const FailedState(error: 'Something went wrong')); + return; + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + if (seconds > 0) { + _onStartTimer(seconds); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + void _getCounterValue( + GetCounterEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + add(GetScheduleEvent()); + var response = await DevicesAPI.getDeviceStatus(twoTouchId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = TwoTouchModel.fromJson(statusModelList); + + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown > 0 + ? _onStartTimer(deviceStatus.firstCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.firstCountDown)); + } else if (event.deviceCode == 'countdown_2') { + deviceStatus.secondCountDown > 0 + ? _onStartTimer(deviceStatus.secondCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.secondCountDown)); + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + void _onClose(OnClose event, Emitter emit) { + _timer?.cancel(); + } + + void _onStartTimer(int seconds) { + _timer?.cancel(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + add(TickTimer(seconds - timer.tick)); + }); + } + + void _onTickTimer(TickTimer event, Emitter emit) { + if (event.remainingTime > 0) { + emit(TimerRunInProgress(event.remainingTime)); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + List> days = [ + {"day": "Sun", "key": "Sun"}, + {"day": "Mon", "key": "Mon"}, + {"day": "Tue", "key": "Tue"}, + {"day": "Wed", "key": "Wed"}, + {"day": "Thu", "key": "Thu"}, + {"day": "Fri", "key": "Fri"}, + {"day": "Sat", "key": "Sat"}, + ]; + + Future toggleDaySelection( + ToggleDaySelectionEvent event, + Emitter emit, + ) async { + emit(LoadingInitialState()); + if (selectedDays.contains(event.key)) { + selectedDays.remove(event.key); + } else { + selectedDays.add(event.key); + } + emit(ChangeTimeState()); + add(const ChangeSlidingSegment(value: 1)); + } + + Future saveSchedule( + TwoTouchSave event, + Emitter emit, + ) async { + try { + if (selectedDays.isNotEmpty) { + emit(LoadingInitialState()); + await DevicesAPI.postSchedule( + category: switchCode, + deviceId: twoTouchId, + time: getTimeStampWithoutSeconds(selectedTime).toString(), + code: switchCode, + value: toggleSchedule, + days: selectedDays); + CustomSnackBar.displaySnackBar('Save Successfully'); + add(GetScheduleEvent()); + emit(TwoTouchSaveSchedule()); + add(const ToggleCreateScheduleEvent(index: 1)); + // toggleCreateSchedule(); + } else { + CustomSnackBar.displaySnackBar('Please select days'); + } + } catch (e) { + emit(FailedState(error: e.toString())); + } + } + + Future getSchedule( + GetScheduleEvent event, + Emitter emit, + ) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.getSchedule( + category: switchCode, + deviceId: twoTouchId, + ); + List jsonData = response; + listSchedule = + jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); + emit(InitialState()); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + int? getTimeStampWithoutSeconds(DateTime? dateTime) { + if (dateTime == null) return null; + DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, + dateTime.day, dateTime.hour, dateTime.minute); + return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; + } + + Future toggleRepeat( + ToggleScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.changeSchedule( + scheduleId: event.id, deviceUuid: twoTouchId, enable: event.toggle); + if (response == true) { + add(GetScheduleEvent()); + toggleSchedule = event.toggle; + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.deleteSchedule( + scheduleId: event.id, + deviceUuid: twoTouchId, + ); + if (response == true) { + add(GetScheduleEvent()); + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + void _fetchTwoTouchWizardStatus( + InitialWizardEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + devicesList = []; + groupTwoTouchList = []; + allSwitchesOn = true; + devicesList = await DevicesAPI.getDeviceByGroupName( + HomeCubit.getInstance().selectedSpace?.id ?? '', '2GT'); + + for (int i = 0; i < devicesList.length; i++) { + var response = + await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = TwoTouchModel.fromJson(statusModelList); + + groupTwoTouchList.add(GroupTwoTouchModel( + deviceId: devicesList[i].uuid ?? '', + deviceName: devicesList[i].name ?? '', + firstSwitch: deviceStatus.firstSwitch, + secondSwitch: deviceStatus.secondSwitch, + )); + } + + if (groupTwoTouchList.isNotEmpty) { + groupTwoTouchList.firstWhere((element) { + if (!element.firstSwitch || !element.secondSwitch) { + allSwitchesOn = false; + } + return true; + }); + } + emit(UpdateGroupState( + twoTouchList: groupTwoTouchList, allSwitches: allSwitchesOn)); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event, + Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + try { + bool allSwitchesValue = true; + groupTwoTouchList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.firstSwitch = !event.value; + } + if (!element.firstSwitch || !element.secondSwitch) { + allSwitchesValue = false; + } + }); + + emit(UpdateGroupState( + twoTouchList: groupTwoTouchList, allSwitches: allSwitchesValue)); + + List allDeviceIds = + groupTwoTouchList.map((device) => device.deviceId).toList(); + final response = await DevicesAPI.deviceBatchController( + code: 'switch_1', + devicesUuid: allDeviceIds, + value: !event.value, + ); + + if (response['failedResults'].toString() != '[]') { + add(InitialWizardEvent()); + } + } catch (_) { + add(InitialWizardEvent()); + } + } + + void _changeSecondWizardSwitch(ChangeSecondWizardSwitchStatusEvent event, + Emitter emit) async { + emit(LoadingNewSate(twoTouchModel: deviceStatus)); + try { + bool allSwitchesValue = true; + groupTwoTouchList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.secondSwitch = !event.value; + } + if (!element.firstSwitch || !element.secondSwitch) { + allSwitchesValue = false; + } + }); + List allDeviceIds = + groupTwoTouchList.map((device) => device.deviceId).toList(); + + emit(UpdateGroupState( + twoTouchList: groupTwoTouchList, allSwitches: allSwitchesValue)); + + final response = await DevicesAPI.deviceBatchController( + code: 'switch_2', + devicesUuid: allDeviceIds, + value: !event.value, + ); + + if (response['failedResults'].toString() != '[]') { + add(InitialWizardEvent()); + } + } catch (_) { + add(InitialWizardEvent()); + } + } + + String statusSelected = ''; + String optionSelected = ''; + + Future _changeStatus( + ChangeStatusEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final Map> controlMap = { + "relay_status": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + "light_mode": { + 'Off': 'none', + 'On/Off Status': 'relay', + 'Switch Position': 'pos', + }, + "relay_status_1": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + "relay_status_2": { + 'Power On': 'power_on', + 'Power Off': 'power_off', + 'Restart Memory': 'last', + }, + }; + + final selectedControl = controlMap[optionSelected]?[statusSelected]; + if (selectedControl != null) { + await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoTouchId, + code: optionSelected, + value: selectedControl), + twoTouchId, + ); + } else { + print('Invalid statusSelected or optionSelected'); + } + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } +} diff --git a/lib/features/devices/bloc/two_touch_bloc/two_touch_event.dart b/lib/features/devices/bloc/two_touch_bloc/two_touch_event.dart new file mode 100644 index 0000000..f7013bf --- /dev/null +++ b/lib/features/devices/bloc/two_touch_bloc/two_touch_event.dart @@ -0,0 +1,162 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; + +abstract class TwoTouchEvent extends Equatable { + const TwoTouchEvent(); + + @override + List get props => []; +} + +class LoadingEvent extends TwoTouchEvent {} + +class TwoTouchUpdated extends TwoTouchEvent {} + +class TwoTouchSave extends TwoTouchEvent {} + +class ToggleScheduleEvent extends TwoTouchEvent { + final String id; + final bool toggle; + const ToggleScheduleEvent({required this.toggle, required this.id}); + @override + List get props => [toggle, id]; +} + +class errorMessage extends TwoTouchEvent {} + +class ToggleDaySelectionEvent extends TwoTouchEvent { + final String key; + + const ToggleDaySelectionEvent({required this.key}); + @override + List get props => [key]; +} + +class InitialEvent extends TwoTouchEvent { + const InitialEvent(); + @override + List get props => []; +} + +class ChangeFirstSwitchStatusEvent extends TwoTouchEvent { + final bool value; + final String deviceId; + const ChangeFirstSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeSecondSwitchStatusEvent extends TwoTouchEvent { + final bool value; + final String deviceId; + const ChangeSecondSwitchStatusEvent( + {required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class AllOffEvent extends TwoTouchEvent {} + +class AllOnEvent extends TwoTouchEvent {} + +class GroupAllOnEvent extends TwoTouchEvent {} + +class GroupAllOffEvent extends TwoTouchEvent {} + + +class ChangeSlidingSegment extends TwoTouchEvent { + final int value; + const ChangeSlidingSegment({required this.value}); + @override + List get props => [value]; +} + +class GetCounterEvent extends TwoTouchEvent { + final String deviceCode; + const GetCounterEvent({required this.deviceCode}); + @override + List get props => [deviceCode]; +} + +class SetCounterValue extends TwoTouchEvent { + final Duration duration; + final String deviceCode; + const SetCounterValue({required this.duration, required this.deviceCode}); + @override + List get props => [duration, deviceCode]; +} + +class StartTimer extends TwoTouchEvent { + final int duration; + + const StartTimer(this.duration); + + @override + List get props => [duration]; +} + +class TickTimer extends TwoTouchEvent { + final int remainingTime; + + const TickTimer(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class StopTimer extends TwoTouchEvent {} + +class OnClose extends TwoTouchEvent {} + +class GetScheduleEvent extends TwoTouchEvent {} + +class DeleteScheduleEvent extends TwoTouchEvent { + final String id; + const DeleteScheduleEvent({required this.id}); + @override + List get props => [id]; +} + +class TabChangedEvent extends TwoTouchEvent { + final int index; + TabChangedEvent({required this.index}); +} + +class ToggleSelectedEvent extends TwoTouchEvent { + final int index; + const ToggleSelectedEvent({required this.index}); + @override + List get props => [index]; +} + +class ToggleCreateScheduleEvent extends TwoTouchEvent { + final int index; + const ToggleCreateScheduleEvent({required this.index}); + @override + List get props => [index]; +} + +class InitialWizardEvent extends TwoTouchEvent {} + +class ChangeFirstWizardSwitchStatusEvent extends TwoTouchEvent { + final bool value; + final String deviceId; + const ChangeFirstWizardSwitchStatusEvent( + {required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeSecondWizardSwitchStatusEvent extends TwoTouchEvent { + final bool value; + final String deviceId; + const ChangeSecondWizardSwitchStatusEvent( + {required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} +class ChangeStatusEvent extends TwoTouchEvent { + final String deviceId; + final BuildContext context; + const ChangeStatusEvent({this.deviceId = '',required this.context}); +} diff --git a/lib/features/devices/bloc/two_touch_bloc/two_touch_state.dart b/lib/features/devices/bloc/two_touch_bloc/two_touch_state.dart new file mode 100644 index 0000000..db574fc --- /dev/null +++ b/lib/features/devices/bloc/two_touch_bloc/two_touch_state.dart @@ -0,0 +1,91 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/group_two_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/two_touch_model.dart'; + +class TwoTouchState extends Equatable { + const TwoTouchState(); + + @override + List get props => []; +} + +class InitialState extends TwoTouchState {} + +class TwoTouchSaveSchedule extends TwoTouchState {} + +class ChangeTimeState extends TwoTouchState {} + +class LoadingInitialState extends TwoTouchState {} + +class UpdateState extends TwoTouchState { + final TwoTouchModel twoTouchModel; + const UpdateState({required this.twoTouchModel}); + + @override + List get props => [TwoTouchModel]; +} + +class LoadingNewSate extends TwoTouchState { + final TwoTouchModel twoTouchModel; + const LoadingNewSate({required this.twoTouchModel}); + + @override + List get props => [TwoTouchModel]; +} + +class UpdateGroupState extends TwoTouchState { + final List twoTouchList; + final bool allSwitches; + + const UpdateGroupState({required this.twoTouchList, required this.allSwitches}); + + @override + List get props => [twoTouchList, allSwitches]; +} + +class FailedState extends TwoTouchState { + final String error; + + const FailedState({required this.error}); + + @override + List get props => [error]; +} + +class ChangeSlidingSegmentState extends TwoTouchState { + final int value; + + const ChangeSlidingSegmentState({required this.value}); + + @override + List get props => [value]; +} + +class UpdateTimerState extends TwoTouchState { + final int seconds; + const UpdateTimerState({required this.seconds}); + + @override + List get props => [seconds]; +} + +class TimerRunInProgress extends TwoTouchState { + final int remainingTime; + + const TimerRunInProgress(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class TimerRunComplete extends TwoTouchState {} + +class IsToggleState extends TwoTouchState { + final bool? onOff; + const IsToggleState({this.onOff}); +} + +class UpdateCreateScheduleState extends TwoTouchState { + final bool createSchedule; + UpdateCreateScheduleState(this.createSchedule); +} diff --git a/lib/features/devices/model/device_category_model.dart b/lib/features/devices/model/device_category_model.dart index e391d3a..8ef4334 100644 --- a/lib/features/devices/model/device_category_model.dart +++ b/lib/features/devices/model/device_category_model.dart @@ -67,4 +67,7 @@ Map deviceTypeIconMap = { DeviceType.WH: Assets.waterHeaterIcon, DeviceType.DS: Assets.doorSensorIcon, DeviceType.Other: Assets.assetsIconsAC, + DeviceType.OneTouch: Assets.oneGang, + DeviceType.TowTouch: Assets.twoGang, + DeviceType.ThreeTouch: Assets.assetsIconsGang, }; diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 866c51a..2374198 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -66,6 +66,12 @@ class DeviceModel { tempIcon = Assets.waterHeaterIcon; } else if (type == DeviceType.DS) { tempIcon = Assets.doorSensorIcon; + } else if (type == DeviceType.OneTouch) { + tempIcon = Assets.oneGang; + } else if (type == DeviceType.TowTouch) { + tempIcon = Assets.twoGang; + } else if (type == DeviceType.ThreeTouch) { + tempIcon = Assets.assetsIcons3GangSwitch; } else { tempIcon = Assets.assetsIconsLogo; } diff --git a/lib/features/devices/model/group_one_touch_model.dart b/lib/features/devices/model/group_one_touch_model.dart new file mode 100644 index 0000000..d6ac8bf --- /dev/null +++ b/lib/features/devices/model/group_one_touch_model.dart @@ -0,0 +1,11 @@ +class GroupOneTouchModel { + final String deviceId; + final String deviceName; + bool firstSwitch; + + GroupOneTouchModel({ + required this.deviceId, + required this.deviceName, + required this.firstSwitch, + }); +} diff --git a/lib/features/devices/model/group_three_touch_model.dart b/lib/features/devices/model/group_three_touch_model.dart new file mode 100644 index 0000000..129642c --- /dev/null +++ b/lib/features/devices/model/group_three_touch_model.dart @@ -0,0 +1,15 @@ +class GroupThreeTouchModel { + final String deviceId; + final String deviceName; + bool firstSwitch; + bool secondSwitch; + bool thirdSwitch; + + GroupThreeTouchModel({ + required this.deviceId, + required this.deviceName, + required this.firstSwitch, + required this.secondSwitch, + required this.thirdSwitch, + }); +} diff --git a/lib/features/devices/model/group_two_touch_model.dart b/lib/features/devices/model/group_two_touch_model.dart new file mode 100644 index 0000000..77a0325 --- /dev/null +++ b/lib/features/devices/model/group_two_touch_model.dart @@ -0,0 +1,13 @@ +class GroupTwoTouchModel { + final String deviceId; + final String deviceName; + bool firstSwitch; + bool secondSwitch; + + GroupTwoTouchModel({ + required this.deviceId, + required this.deviceName, + required this.firstSwitch, + required this.secondSwitch, + }); +} diff --git a/lib/features/devices/model/one_touch_model.dart b/lib/features/devices/model/one_touch_model.dart new file mode 100644 index 0000000..34ea764 --- /dev/null +++ b/lib/features/devices/model/one_touch_model.dart @@ -0,0 +1,50 @@ + + +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class OneTouchModel { + bool firstSwitch; + int firstCountDown; + status relay; + lightStatus light_mode; + status relay_status_1; + + OneTouchModel( + {required this.firstSwitch, + required this.firstCountDown, + required this.light_mode, + required this.relay, + required this.relay_status_1 + }); + + factory OneTouchModel.fromJson(List jsonList) { + late bool _switch; + late int _count; + late String _relay; + late String _light_mode; + late String relay_status_1; + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'switch_1') { + _switch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'countdown_1') { + _count = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status') { + _relay = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'light_mode') { + _light_mode = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status_1') { + relay_status_1 = jsonList[i].value ?? 0; + } + } + return OneTouchModel( + firstSwitch: _switch, + firstCountDown: _count, + light_mode: lightStatusExtension.fromString(_light_mode) , + relay: StatusExtension.fromString(_relay ) , + relay_status_1: StatusExtension.fromString(relay_status_1 ) + + ); + } +} diff --git a/lib/features/devices/model/three_touch_model.dart b/lib/features/devices/model/three_touch_model.dart new file mode 100644 index 0000000..f8c55bf --- /dev/null +++ b/lib/features/devices/model/three_touch_model.dart @@ -0,0 +1,81 @@ +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class ThreeTouchModel { + bool firstSwitch; + bool secondSwitch; + bool thirdSwitch; + int firstCountDown; + int secondCountDown; + int thirdCountDown; + status relay; + lightStatus light_mode; + status relay_status_1; + status relay_status_2; + status relay_status_3; + + ThreeTouchModel( + {required this.firstSwitch, + required this.secondSwitch, + required this.thirdSwitch, + required this.firstCountDown, + required this.secondCountDown, + required this.thirdCountDown, + required this.light_mode, + required this.relay, + required this.relay_status_1, + required this.relay_status_2, + required this.relay_status_3}); + + factory ThreeTouchModel.fromJson(List jsonList) { + late bool _firstSwitch; + late bool _secondSwitch; + late bool _thirdSwitch; + late int _firstCount; + late int _secondCount; + late int _thirdCount; + late String _relay; + late String _light_mode; + late String _relay_status_1; + late String _relay_status_2; + late String _relay_status_3; + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'switch_1') { + _firstSwitch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'switch_2') { + _secondSwitch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'switch_3') { + _thirdSwitch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'countdown_1') { + _firstCount = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'countdown_2') { + _secondCount = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'countdown_3') { + _thirdCount = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status') { + _relay = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'light_mode') { + _light_mode = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status_1') { + _relay_status_1 = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status_2') { + _relay_status_2 = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status_3') { + _relay_status_3 = jsonList[i].value ?? 0; + } + } + return ThreeTouchModel( + firstSwitch: _firstSwitch, + secondSwitch: _secondSwitch, + thirdSwitch: _thirdSwitch, + firstCountDown: _firstCount, + secondCountDown: _secondCount, + thirdCountDown: _thirdCount, + light_mode: lightStatusExtension.fromString(_light_mode), + relay: StatusExtension.fromString(_relay), + relay_status_1: StatusExtension.fromString(_relay_status_1), + relay_status_2: StatusExtension.fromString(_relay_status_2), + relay_status_3: StatusExtension.fromString(_relay_status_3)); + } +} diff --git a/lib/features/devices/model/two_touch_model.dart b/lib/features/devices/model/two_touch_model.dart new file mode 100644 index 0000000..616d61e --- /dev/null +++ b/lib/features/devices/model/two_touch_model.dart @@ -0,0 +1,63 @@ +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class TwoTouchModel { + bool firstSwitch; + bool secondSwitch; + int firstCountDown; + int secondCountDown; + status relay; + lightStatus light_mode; + status relay_status_1; + status relay_status_2; + + TwoTouchModel( + {required this.firstSwitch, + required this.secondSwitch, + required this.firstCountDown, + required this.secondCountDown, + required this.light_mode, + required this.relay, + required this.relay_status_1, + required this.relay_status_2}); + + factory TwoTouchModel.fromJson(List jsonList) { + late bool _firstSwitch; + late bool _secondSwitch; + late int _firstCount; + late int _secondCount; + late String _relay; + late String _light_mode; + late String _relay_status_1; + late String _relay_status_2; + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'switch_1') { + _firstSwitch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'switch_2') { + _secondSwitch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'countdown_1') { + _firstCount = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'countdown_2') { + _secondCount = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status') { + _relay = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'light_mode') { + _light_mode = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status_1') { + _relay_status_1 = jsonList[i].value ?? 0; + } else if (jsonList[i].code == 'relay_status_2') { + _relay_status_2 = jsonList[i].value ?? 0; + } + } + return TwoTouchModel( + firstSwitch: _firstSwitch, + secondSwitch: _secondSwitch, + firstCountDown: _firstCount, + secondCountDown: _secondCount, + light_mode: lightStatusExtension.fromString(_light_mode), + relay: StatusExtension.fromString(_relay), + relay_status_1: StatusExtension.fromString(_relay_status_1), + relay_status_2: StatusExtension.fromString(_relay_status_2)); + } +} diff --git a/lib/features/devices/view/widgets/circular_button.dart b/lib/features/devices/view/widgets/circular_button.dart new file mode 100644 index 0000000..97156c7 --- /dev/null +++ b/lib/features/devices/view/widgets/circular_button.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class CircularButton extends StatelessWidget { + const CircularButton({ + super.key, + required this.device, + required this.label, + required this.onTap, + required this.icons, + }); + + final DeviceModel? device; + final String label; + final Function()? onTap; + final IconData icons; + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + child: GestureDetector( + onTap: onTap, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(100), + ), + ), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(100), + ), + child: Center( + child: label == 'All On' + ? BodySmall( + text: "On", + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontWeight.bold), + ) + : label == 'All Off' + ? BodySmall( + text: "Off", + style: context.bodyMedium.copyWith( + color: + ColorsManager.primaryColorWithOpacity, + fontWeight: FontWeight.bold), + ) + : Icon( + icons, + color: ColorsManager.primaryColorWithOpacity, + size: 25, + ), + ), + ), + ], + ), + ), + ), + const SizedBox(height: 10), + BodySmall( + // text: "Timer", + text: label, + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + ), + ), + ], + ); + } +} diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_wizard.dart b/lib/features/devices/view/widgets/one_gang/one_gang_wizard.dart index 92013ed..e7b4fbc 100644 --- a/lib/features/devices/view/widgets/one_gang/one_gang_wizard.dart +++ b/lib/features/devices/view/widgets/one_gang/one_gang_wizard.dart @@ -19,28 +19,32 @@ class OneGangWizard extends StatelessWidget { List groupOneGangModel = []; return DefaultScaffold( + title: 'Lights', child: BlocProvider( - create: (context) => - OneGangBloc(switchCode: '', oneGangId: device?.uuid ?? '')..add(InitialWizardEvent()), - child: BlocBuilder( - builder: (context, state) { - bool allSwitchesOn = false; + create: (context) => + OneGangBloc(switchCode: '', oneGangId: device?.uuid ?? '') + ..add(InitialWizardEvent()), + child: BlocBuilder( + builder: (context, state) { + bool allSwitchesOn = false; - if (state is UpdateGroupState) { - groupOneGangModel = state.oneGangList; - allSwitchesOn = state.allSwitches; - } - return state is LoadingInitialState - ? const Center( - child: - DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), - ) - : OneGangList( - oneGangList: groupOneGangModel, - allSwitches: allSwitchesOn, - ); - }, - ), - )); + if (state is UpdateGroupState) { + groupOneGangModel = state.oneGangList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : OneGangList( + oneGangList: groupOneGangModel, + allSwitches: allSwitchesOn, + ); + }, + ), + )); } } diff --git a/lib/features/devices/view/widgets/one_touch/one_gang_list.dart b/lib/features/devices/view/widgets/one_touch/one_gang_list.dart new file mode 100644 index 0000000..9f0fc11 --- /dev/null +++ b/lib/features/devices/view/widgets/one_touch/one_gang_list.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/group_one_touch_model.dart'; + +import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; + +class OneTouchList extends StatelessWidget { + const OneTouchList( + {super.key, required this.oneTouchList, required this.allSwitches}); + + final List oneTouchList; + final bool allSwitches; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 10), + const BodySmall(text: 'All Lights'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: allSwitches, + action: () { + BlocProvider.of(context).add(GroupAllOnEvent()); + }, + secondAction: () { + BlocProvider.of(context).add(GroupAllOffEvent()); + }, + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(0), + itemCount: oneTouchList.length, + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + BodySmall(text: oneTouchList[index].deviceName), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: oneTouchList[index].firstSwitch, + action: () { + BlocProvider.of(context).add( + ChangeFirstWizardSwitchStatusEvent( + value: oneTouchList[index].firstSwitch, + deviceId: oneTouchList[index].deviceId)); + }, + ), + ], + ); + }, + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/devices/view/widgets/one_touch/one_touch_screen.dart b/lib/features/devices/view/widgets/one_touch/one_touch_screen.dart new file mode 100644 index 0000000..383e7d0 --- /dev/null +++ b/lib/features/devices/view/widgets/one_touch/one_touch_screen.dart @@ -0,0 +1,221 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_one_touch_model.dart'; +import 'package:syncrow_app/features/devices/model/one_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/circular_button.dart'; +import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_setting.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_timer_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_gang/gang_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class OneTouchScreen extends StatelessWidget { + const OneTouchScreen({super.key, this.device}); + + final DeviceModel? device; + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: device != null + ? DeviceAppbar( + deviceName: device!.name!, + deviceUuid: device!.uuid!, + ) + : AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: BodyLarge( + text: device?.name ?? 'Lights', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + ), + body: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: SafeArea( + child: Padding( + padding: EdgeInsets.only( + left: Constants.defaultPadding, + right: Constants.defaultPadding, + bottom: Constants.bottomNavBarHeight, + ), + child: BlocProvider( + create: (context) => OneTouchBloc( + switchCode: 'switch_1', oneTouchId: device?.uuid ?? '') + ..add(const InitialEvent(groupScreen: false)), + child: BlocBuilder( + builder: (context, state) { + OneTouchModel oneTouchModel = OneTouchModel( + firstSwitch: false, + firstCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off); + + List groupOneTouchModel = []; + bool allSwitchesOn = false; + + if (state is LoadingNewSate) { + oneTouchModel = state.oneTouchModel; + } else if (state is UpdateState) { + oneTouchModel = state.oneTouchModel; + } else if (state is UpdateGroupState) { + groupOneTouchModel = state.oneTouchList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + BlocProvider.of(context).add( + InitialEvent( + groupScreen: + device != null ? false : true)); + }, + child: ListView( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: [ + const Expanded(child: SizedBox.shrink()), + Expanded( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Column( + children: [ + GangSwitch( + threeGangSwitch: device!, + value: + oneTouchModel.firstSwitch, + action: () { + BlocProvider.of< + OneTouchBloc>( + context) + .add(ChangeFirstSwitchStatusEvent( + value: oneTouchModel + .firstSwitch)); + }, + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: " Entrance Light", + fontColor: ColorsManager + .textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + ], + ), + ), + Center( + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + CircularButton( + device: device, + icons: Icons.access_time, + label: 'Timer', + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, + animation1, + animation2) => + TimerScheduleScreen( + switchCode: + 'switch_1', + device: device!, + deviceCode: + 'countdown_1', + ))); + }, + ), + const SizedBox( + width: 30, + ), + CircularButton( + device: device, + icons: Icons.settings, + label: 'Setting', + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, + animation1, + animation2) => + OneTouchSetting( + device: device, + ))); + }, + ), + ], + ), + ), + const Expanded(child: SizedBox()) + ], + ), + ), + ], + ), + ); + }, + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/one_touch/one_touch_setting.dart b/lib/features/devices/view/widgets/one_touch/one_touch_setting.dart new file mode 100644 index 0000000..52be48f --- /dev/null +++ b/lib/features/devices/view/widgets/one_touch/one_touch_setting.dart @@ -0,0 +1,311 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/one_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/restart_status_dialog.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class OneTouchSetting extends StatelessWidget { + const OneTouchSetting({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Setting', + child: BlocProvider( + create: (context) => + OneTouchBloc(switchCode: '', oneTouchId: device?.uuid ?? '') + ..add(const InitialEvent(groupScreen: false)), + child: BlocBuilder( + builder: (context, state) { + final oneTouchBloc = BlocProvider.of(context); + OneTouchModel? oneTouchModel = OneTouchModel( + firstSwitch: false, + firstCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off); + + // if (state is UpdateState) { + // oneTouchModel = state.oneTouchModel; + // } + if (state is UpdateState) { + oneTouchModel = state.oneTouchModel; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : Column( + children: [ + Container( + child: DefaultContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () async { + oneTouchBloc.optionSelected = + 'relay_status'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: oneTouchBloc + .deviceStatus.relay.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + oneTouchBloc.add(ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed(const Duration(seconds: 2), + () async { + oneTouchBloc.add(const InitialEvent( + groupScreen: false)); + }); + } + }, + child: Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: oneTouchBloc + .deviceStatus.relay.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + )), + ), + const Divider( + color: ColorsManager.graysColor, + ), + Padding( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + oneTouchBloc.optionSelected = + 'light_mode'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + oneTouchModel!.light_mode.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + oneTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Indicator Status', + label1: 'Off', + label2: 'On/Off Status', + label3: 'Switch Position', + onTapLabel1: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + oneTouchBloc.add(const InitialEvent( + groupScreen: false)); + }); + } + }, + child: SizedBox( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Indicator Status', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: oneTouchModel + .light_mode.value, + fontColor: ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + )), + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + oneTouchBloc.optionSelected = + 'relay_status_1'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + oneTouchModel! + .relay_status_1.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + oneTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status 1', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + oneTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + oneTouchBloc.add(const InitialEvent( + groupScreen: false)); + }); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status 1', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: oneTouchModel + .relay_status_1.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + ], + ), + ), + ), + ], + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/one_touch/one_touch_timer_screen.dart b/lib/features/devices/view/widgets/one_touch/one_touch_timer_screen.dart new file mode 100644 index 0000000..c9525a6 --- /dev/null +++ b/lib/features/devices/view/widgets/one_touch/one_touch_timer_screen.dart @@ -0,0 +1,296 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/shared_widgets/create_schedule.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/schedule_list.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class TimerScheduleScreen extends StatelessWidget { + final DeviceModel device; + final String deviceCode; + final String switchCode; + const TimerScheduleScreen( + {required this.device, + required this.deviceCode, + required this.switchCode, + super.key}); + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: BlocProvider( + create: (context) => + OneTouchBloc(switchCode: switchCode, oneTouchId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), + child: BlocBuilder( + builder: (context, state) { + final oneTouchBloc = BlocProvider.of(context); + Duration duration = Duration.zero; + int countNum = 0; + if (state is UpdateTimerState) { + countNum = state.seconds; + } else if (state is TimerRunInProgress) { + countNum = state.remainingTime; + } else if (state is TimerRunComplete) { + countNum = 0; + } else if (state is LoadingNewSate) { + countNum = 0; + } + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + oneTouchBloc.add(OnClose()); + Navigator.pop(context); + } + }, + child: DefaultTabController( + length: 2, + child: DefaultScaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: const BodyLarge( + text: 'Schedule', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + actions: [ + oneTouchBloc.createSchedule == true + ? TextButton( + onPressed: () { + oneTouchBloc.add(ScheduleSave()); + }, + child: const Text('Save')) + : oneTouchBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + // oneTouchBloc.toggleCreateSchedule(); + oneTouchBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), + ], + ), + child: state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(30)), + ), + ), + child: TabBar( + onTap: (value) { + if (value == 0) { + if (oneTouchBloc.createSchedule == + true) { + // oneTouchBloc.toggleCreateSchedule(); + oneTouchBloc.add( + const ToggleCreateScheduleEvent( + index: 0)); + } + oneTouchBloc.add( + const ToggleSelectedEvent( + index: 0)); + } else { + oneTouchBloc.add( + const ToggleSelectedEvent( + index: 1)); + } + }, + indicatorColor: + Colors.white, // Customize the indicator + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20)), + ), + ), + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + physics: + const NeverScrollableScrollPhysics(), // Disable swiping + + children: [ + Center( + child: Container( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + countNum > 0 + ? BodyLarge( + text: _formatDuration( + countNum), + fontColor: ColorsManager + .slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: + CupertinoTimerPickerMode + .hm, + onTimerDurationChanged: + (Duration + newDuration) { + duration = newDuration; + }, + ), + GestureDetector( + onTap: () { + if (state + is LoadingNewSate) { + return; + } + if (countNum > 0) { + oneTouchBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: Duration + .zero)); + } else if (duration != + Duration.zero) { + oneTouchBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: + duration)); + } + }, + child: SvgPicture.asset( + countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + ), + SizedBox( + child: oneTouchBloc.createSchedule == + true + ? CreateSchedule( + onToggleChanged: (bool isOn) { + oneTouchBloc.toggleSchedule = + isOn; + }, + onDateTimeChanged: + (DateTime dateTime) { + oneTouchBloc.selectedTime = + dateTime; + }, + days: oneTouchBloc.days, + selectDays: (List + selectedDays) { + oneTouchBloc.selectedDays = + selectedDays; + }, + ) + : Padding( + padding: const EdgeInsets.only( + top: 10), + child: ScheduleListView( + listSchedule: oneTouchBloc + .listSchedule, // Pass the schedule list here + onDismissed: (scheduleId) { + oneTouchBloc.listSchedule + .removeWhere((schedule) => + schedule + .scheduleId == + scheduleId); + oneTouchBloc.add( + DeleteScheduleEvent( + id: scheduleId)); + }, + onToggleSchedule: + (scheduleId, isEnabled) { + oneTouchBloc.add( + ToggleScheduleEvent( + id: scheduleId, + toggle: isEnabled, + )); + }, + ), + ), + ), + ], + ), + ), + ], + ), + ))); + }, + ), + ), + ); + } + + String _formatDuration(int seconds) { + final hours = (seconds ~/ 3600).toString().padLeft(2, '0'); + final minutes = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0'); + final secs = (seconds % 60).toString().padLeft(2, '0'); + return '$hours:$minutes:$secs'; + } +} diff --git a/lib/features/devices/view/widgets/one_touch/one_touch_wizard.dart b/lib/features/devices/view/widgets/one_touch/one_touch_wizard.dart new file mode 100644 index 0000000..f314b14 --- /dev/null +++ b/lib/features/devices/view/widgets/one_touch/one_touch_wizard.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_state.dart'; + +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_one_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_gang_list.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; + +class OneTouchWizard extends StatelessWidget { + const OneTouchWizard({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + List groupOneTouchModel = []; + + return DefaultScaffold( + title: 'Lights', + child: BlocProvider( + create: (context) => + OneTouchBloc(switchCode: '', oneTouchId: device?.uuid ?? '') + ..add(InitialWizardEvent()), + child: BlocBuilder( + builder: (context, state) { + bool allSwitchesOn = false; + + if (state is UpdateGroupState) { + groupOneTouchModel = state.oneTouchList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : OneTouchList( + oneTouchList: groupOneTouchModel, + allSwitches: allSwitchesOn, + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/restart_status_dialog.dart b/lib/features/devices/view/widgets/restart_status_dialog.dart new file mode 100644 index 0000000..35fc843 --- /dev/null +++ b/lib/features/devices/view/widgets/restart_status_dialog.dart @@ -0,0 +1,230 @@ + +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class RestartStatusDialog extends StatefulWidget { + final String label1; + final String label2; + final String label3; + final String title; + + final Function(String)? onTapLabel1; + final Function(String)? onTapLabel2; + final Function(String)? onTapLabel3; + final Function()? cancelTab; + final Function()? confirmTab; + + final String? initialSelectedLabel; + + RestartStatusDialog({ + required this.label1, + required this.label2, + required this.label3, + required this.title, + this.onTapLabel1, + this.onTapLabel2, + this.onTapLabel3, + required this.cancelTab, + required this.confirmTab, + this.initialSelectedLabel, + }); + + @override + _RestartStatusDialogState createState() => _RestartStatusDialogState(); +} + +class _RestartStatusDialogState extends State { + late String _selectedOption; + + @override + void initState() { + super.initState(); + + _selectedOption = widget.initialSelectedLabel ?? ''; + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + height: 10, + ), + BodyLarge( + text: widget.title, + fontWeight: FontWeight.w700, + fontColor: ColorsManager.primaryColor, + fontSize: 16, + ), + const Padding( + padding: EdgeInsets.only(left: 15, right: 15), + child: Divider( + color: ColorsManager.textGray, + ), + ), + Padding( + padding: const EdgeInsets.only(left: 15, right: 15), + child: Column( + children: [ + _buildCheckboxOption( + label: widget.label1, + onTap: widget.onTapLabel1, + ), + _buildCheckboxOption( + label: widget.label2, + onTap: widget.onTapLabel2, + ), + _buildCheckboxOption( + label: widget.label3, + onTap: widget.onTapLabel3, + ), + ], + ), + ), + Row( + children: [ + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: SizedBox( + child: InkWell( + onTap: widget.cancelTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Cancel', + style: TextStyle( + color: ColorsManager.textGray, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + ), + ), + ), + ), + ), + Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: ColorsManager.textGray, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.textGray, + width: 1.0, + ), + )), + child: InkWell( + onTap: widget.confirmTab, + child: const Padding( + padding: EdgeInsets.all(15), + child: Center( + child: Text( + 'Confirm', + style: TextStyle( + color: ColorsManager.primaryColor, + fontSize: 14, + fontWeight: FontWeight.w400), + ), + ), + )), + )) + ], + ) + ], + ), + ); + } + + Widget _buildCheckboxOption( + {required String label, Function(String)? onTap}) { + return Padding( + padding: const EdgeInsets.only(bottom: 10, top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BodyMedium( + text: label, + style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400), + ), + CircularCheckbox( + value: _selectedOption == label, + onChanged: (bool? value) { + if (value == true) { + setState(() { + _selectedOption = label; + }); + if (onTap != null) { + onTap(label); + } + } + }, + ), + ], + ), + ); + } +} + +class CircularCheckbox extends StatefulWidget { + final bool value; + final ValueChanged onChanged; + + CircularCheckbox({required this.value, required this.onChanged}); + + @override + _CircularCheckboxState createState() => _CircularCheckboxState(); +} + +class _CircularCheckboxState extends State { + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + widget.onChanged(!widget.value); + }, + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: widget.value + ? ColorsManager.primaryColorWithOpacity.withOpacity(0.01) + : Colors.grey, + width: 2.0, + ), + color: widget.value + ? ColorsManager.primaryColorWithOpacity + : Colors.transparent, + ), + width: 24.0, + height: 24.0, + child: widget.value + ? const Icon( + Icons.check, + color: Colors.white, + size: 16.0, + ) + : null, + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index 93260dd..851aabf 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -13,7 +13,10 @@ import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_senso import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_Interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/wall_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_interface.dart'; @@ -164,6 +167,26 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { PageRouteBuilder( pageBuilder: (context, animation1, animation2) => DoorSensorScreen(device: device))); + + case DeviceType.OneTouch: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + OneTouchScreen(device: device))); + + case DeviceType.TowTouch: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + TwoTouchInterface(touchSwitch: device))); + case DeviceType.ThreeTouch: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + ThreeTouchInterface(touchSwitch: device))); break; default: } diff --git a/lib/features/devices/view/widgets/three_gang/three_gang_wizard.dart b/lib/features/devices/view/widgets/three_gang/three_gang_wizard.dart index 392ad20..a673734 100644 --- a/lib/features/devices/view/widgets/three_gang/three_gang_wizard.dart +++ b/lib/features/devices/view/widgets/three_gang/three_gang_wizard.dart @@ -17,29 +17,33 @@ class ThreeGangWizard extends StatelessWidget { @override Widget build(BuildContext context) { return DefaultScaffold( + title: 'Lights', child: BlocProvider( - create: (context) => ThreeGangBloc(switchCode: '', threeGangId: device?.uuid ?? '') - ..add(InitialEvent(groupScreen: device != null ? false : true)), - child: BlocBuilder( - builder: (context, state) { - List groupThreeGangModel = []; - bool allSwitchesOn = false; + create: (context) => + ThreeGangBloc(switchCode: '', threeGangId: device?.uuid ?? '') + ..add(InitialEvent(groupScreen: device != null ? false : true)), + child: BlocBuilder( + builder: (context, state) { + List groupThreeGangModel = []; + bool allSwitchesOn = false; - if (state is UpdateGroupState) { - groupThreeGangModel = state.threeGangList; - allSwitchesOn = state.allSwitches; - } - return state is LoadingInitialState - ? const Center( - child: - DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), - ) - : ThreeGangList( - threeGangList: groupThreeGangModel, - allSwitches: allSwitchesOn, - ); - }, - ), - )); + if (state is UpdateGroupState) { + groupThreeGangModel = state.threeGangList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : ThreeGangList( + threeGangList: groupThreeGangModel, + allSwitches: allSwitchesOn, + ); + }, + ), + )); } } diff --git a/lib/features/devices/view/widgets/three_touch/gang_switch.dart b/lib/features/devices/view/widgets/three_touch/gang_switch.dart new file mode 100644 index 0000000..543d006 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/gang_switch.dart @@ -0,0 +1,56 @@ +// import 'package:flutter/material.dart'; +// // import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:flutter_svg/svg.dart'; +// // import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/two_gang_bloc.dart'; +// // import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/two_gang_state.dart'; +// import 'package:syncrow_app/features/devices/model/device_model.dart'; +// import 'package:syncrow_app/generated/assets.dart'; +// import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +// class GangSwitch extends StatelessWidget { +// const GangSwitch( +// {super.key, required this.threeGangSwitch, required this.value, required this.action}); + +// final DeviceModel threeGangSwitch; +// final bool value; +// final Function action; + +// @override +// Widget build(BuildContext context) { +// return InkWell( +// overlayColor: WidgetStateProperty.all(Colors.transparent), +// onTap: () { +// action(); +// // var tempControl = DeviceControlModel( +// // deviceId: control.deviceId, code: control.code!, value: !control.value!); +// // DevicesCubit.getInstance().deviceControl( +// // tempControl, +// // control.deviceId!, +// // ); +// }, +// child: Stack( +// alignment: value ? Alignment.topCenter : Alignment.bottomCenter, +// children: [ +// Container( +// decoration: BoxDecoration( +// borderRadius: const BorderRadius.all(Radius.circular(100.0)), +// color: value ? ColorsManager.primaryColorWithOpacity : ColorsManager.switchOffColor, +// ), +// width: 60, +// height: 115, +// ), +// Padding( +// padding: const EdgeInsets.all(5.0), +// child: SizedBox.square( +// dimension: 60, +// child: SvgPicture.asset( +// value ? Assets.assetsIconsLightSwitchOn : Assets.assetsIconsLightSwitchOff, +// fit: BoxFit.fill, +// ), +// ), +// ), +// ], +// ), +// ); +// } +// } diff --git a/lib/features/devices/view/widgets/three_touch/schedule_screen.dart b/lib/features/devices/view/widgets/three_touch/schedule_screen.dart new file mode 100644 index 0000000..0eec597 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/schedule_screen.dart @@ -0,0 +1,186 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_touch/timer_screen.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class ScheduleScreen extends StatelessWidget { + final DeviceModel device; + const ScheduleScreen({required this.device, super.key}); + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: const BodyLarge( + text: 'Schedule', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + ), + body: SafeArea( + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 48, + ), + DefaultContainer( + width: MediaQuery.sizeOf(context).width, + child: Column( + children: [ + InkWell( + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: + (context, animation1, animation2) => + TimerScheduleScreen( + switchCode: "switch_1", + device: device, + deviceCode: 'countdown_1', + ))); + }, + child: Container( + padding: const EdgeInsets.only( + left: 25, right: 15, top: 20, bottom: 20), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + BodySmall( + text: "Bedside Light", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + fontSize: 15, + fontWeight: FontWeight.w400, + ), + ), + const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 18, + ) + ], + ), + ), + ), + const Divider( + color: ColorsManager.dividerColor, + ), + InkWell( + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: + (context, animation1, animation2) => + TimerScheduleScreen( + switchCode: "switch_2", + device: device, + deviceCode: 'countdown_2', + ))); + }, + child: Container( + padding: const EdgeInsets.only( + left: 25, right: 15, top: 20, bottom: 20), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + BodySmall( + text: "Ceiling Light", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + fontSize: 15, + fontWeight: FontWeight.w400, + ), + ), + const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 18, + ) + ], + ), + ), + ), + const Divider( + color: ColorsManager.dividerColor, + ), + InkWell( + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: + (context, animation1, animation2) => + TimerScheduleScreen( + switchCode: "switch_3", + device: device, + deviceCode: 'countdown_3', + ))); + }, + child: Container( + padding: const EdgeInsets.only( + left: 25, right: 15, top: 20, bottom: 20), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + BodySmall( + text: "Spotlight", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + fontSize: 15, + fontWeight: FontWeight.w400, + ), + ), + const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 18, + ) + ], + ), + ), + ), + ], + )), + ], + ), + ), + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/three_touch/three_touch_interface.dart b/lib/features/devices/view/widgets/three_touch/three_touch_interface.dart new file mode 100644 index 0000000..21bfac4 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/three_touch_interface.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_screen.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class ThreeTouchInterface extends StatelessWidget { + const ThreeTouchInterface({super.key, this.touchSwitch}); + + final DeviceModel? touchSwitch; + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: touchSwitch != null + ? DeviceAppbar( + deviceName: touchSwitch!.name!, + deviceUuid: touchSwitch!.uuid!, + ) + : AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: BodyLarge( + text: touchSwitch?.name ?? 'Lights', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + ), + body: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: SafeArea( + child: Padding( + padding: EdgeInsets.only( + left: Constants.defaultPadding, + right: Constants.defaultPadding, + bottom: Constants.bottomNavBarHeight, + ), + child: ThreeTouchScreen(device: touchSwitch), + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/three_touch/three_touch_list.dart b/lib/features/devices/view/widgets/three_touch/three_touch_list.dart new file mode 100644 index 0000000..aed2946 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/three_touch_list.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/group_three_touch_model.dart'; +import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; + +class ThreeTouchList extends StatelessWidget { + const ThreeTouchList({super.key, required this.threeTouchList, required this.allSwitches}); + + final List threeTouchList; + final bool allSwitches; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 10), + const BodySmall(text: 'All Lights'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: allSwitches, + action: () { + BlocProvider.of(context).add(GroupAllOnEvent()); + }, + secondAction: () { + BlocProvider.of(context).add(GroupAllOffEvent()); + }, + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(0), + itemCount: threeTouchList.length, + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + BodySmall(text: '${threeTouchList[index].deviceName} beside light'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: threeTouchList[index].firstSwitch, + action: () { + BlocProvider.of(context).add(ChangeFirstSwitchStatusEvent( + value: threeTouchList[index].firstSwitch, + deviceId: threeTouchList[index].deviceId)); + }, + ), + const SizedBox(height: 10), + BodySmall(text: '${threeTouchList[index].deviceName} ceiling light'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: threeTouchList[index].secondSwitch, + action: () { + BlocProvider.of(context).add(ChangeSecondSwitchStatusEvent( + value: threeTouchList[index].secondSwitch, + deviceId: threeTouchList[index].deviceId)); + }, + ), + const SizedBox(height: 10), + BodySmall(text: '${threeTouchList[index].deviceName} spotlight'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: threeTouchList[index].thirdSwitch, + action: () { + BlocProvider.of(context).add(ChangeThirdSwitchStatusEvent( + value: threeTouchList[index].thirdSwitch, + deviceId: threeTouchList[index].deviceId)); + }, + ), + ], + ); + }, + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/devices/view/widgets/three_touch/three_touch_screen.dart b/lib/features/devices/view/widgets/three_touch/three_touch_screen.dart new file mode 100644 index 0000000..cce4c53 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/three_touch_screen.dart @@ -0,0 +1,228 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_state.dart'; + +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/three_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/circular_button.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_gang/gang_switch.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_touch/schedule_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_setting.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class ThreeTouchScreen extends StatelessWidget { + const ThreeTouchScreen({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => + ThreeTouchBloc(switchCode: '', threeTouchId: device?.uuid ?? '') + ..add(InitialEvent(groupScreen: device != null ? false : true)), + child: BlocBuilder( + builder: (context, state) { + ThreeTouchModel threeTouchModel = ThreeTouchModel( + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off, + relay_status_2: status.off, + relay_status_3: status.off, + firstSwitch: false, + secondSwitch: false, + thirdSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + thirdCountDown: 0); + + if (state is LoadingNewSate) { + threeTouchModel = state.threeTouchModel; + } else if (state is UpdateState) { + threeTouchModel = state.threeTouchModel; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + BlocProvider.of(context).add(InitialEvent( + groupScreen: device != null ? false : true)); + }, + child: ListView( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Expanded(child: SizedBox.shrink()), + Expanded( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + children: [ + GangSwitch( + threeGangSwitch: device!, + value: threeTouchModel.firstSwitch, + action: () { + BlocProvider.of(context) + .add(ChangeFirstSwitchStatusEvent( + value: threeTouchModel + .firstSwitch)); + }, + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: "Bedside Light", + fontColor: + ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + Column( + children: [ + GangSwitch( + threeGangSwitch: device!, + value: threeTouchModel.secondSwitch, + action: () { + BlocProvider.of( context) .add( + ChangeSecondSwitchStatusEvent( + value: threeTouchModel + .secondSwitch)); + }, + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: "Ceiling Light", + fontColor: + ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + Column( + children: [ + GangSwitch( + threeGangSwitch: device!, + value: threeTouchModel.thirdSwitch, + action: () { + BlocProvider.of( + context) + .add(ChangeThirdSwitchStatusEvent( + value: threeTouchModel + .thirdSwitch)); + }, + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: "Spotlight", + fontColor: + ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + ], + ), + ), + Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CircularButton( + device: device, + icons: Icons.u_turn_left, + label: "All On", + onTap: () { + BlocProvider.of(context) + .add(AllOnEvent()); + }, + ), + const SizedBox( + width: 20, + ), + CircularButton( + device: device, + icons: Icons.access_time, + label: "Timer", + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, + animation2) => + ScheduleScreen( + device: device!, + ))); + }, + ), + const SizedBox( + width: 20, + ), + CircularButton( + device: device, + icons: Icons.access_time, + label: "Setting", + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, + animation2) => + ThreeTouchSetting( + device: device, + ))); + }, + ), + const SizedBox( + width: 20, + ), + CircularButton( + device: device, + icons: Icons.u_turn_left, + label: "All Off", + onTap: () { + BlocProvider.of(context) + .add(AllOffEvent()); + }, + ), + ], + ), + ), + Expanded(child: SizedBox()) + ], + ), + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/three_touch/three_touch_setting.dart b/lib/features/devices/view/widgets/three_touch/three_touch_setting.dart new file mode 100644 index 0000000..21ef6b3 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/three_touch_setting.dart @@ -0,0 +1,501 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/three_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/restart_status_dialog.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class ThreeTouchSetting extends StatelessWidget { + const ThreeTouchSetting({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Setting', + child: BlocProvider( + create: (context) => + ThreeTouchBloc(switchCode: '', threeTouchId: device?.uuid ?? '') + ..add(InitialEvent(groupScreen: false)), + child: BlocBuilder( + builder: (context, state) { + final threeTouchBloc = BlocProvider.of(context); + ThreeTouchModel deviceStatus = ThreeTouchModel( + firstSwitch: false, + secondSwitch: false, + thirdSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + thirdCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off, + relay_status_2: status.off, + relay_status_3: status.off, + ); + if (state is LoadingNewSate) { + deviceStatus = state.threeTouchModel; + } else if (state is UpdateState) { + deviceStatus = state.threeTouchModel; + } + // if (state is ChangeStateSetting) { + // // Navigator.of(context).pop(true); + // threeTouchBloc + // .add(InitialSettingDevises(id: device?.uuid ?? '')); + // } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : Column( + children: [ + Container( + child: DefaultContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () async { + threeTouchBloc.optionSelected = + 'relay_status'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: threeTouchBloc + .deviceStatus.relay.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + threeTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed(const Duration(seconds: 2), + () async { + threeTouchBloc.add( + InitialEvent(groupScreen: false)); + }); + } + }, + child: Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: threeTouchBloc + .deviceStatus.relay.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + )), + ), + const Divider( + color: ColorsManager.graysColor, + ), + Padding( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + threeTouchBloc.optionSelected = + 'light_mode'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: threeTouchBloc + .deviceStatus.light_mode.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + threeTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Indicator Status', + label1: 'Off', + label2: 'On/Off Status', + label3: 'Switch Position', + onTapLabel1: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + threeTouchBloc.add( + InitialEvent(groupScreen: false)); + }); + } + }, + child: SizedBox( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Indicator Status', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: threeTouchBloc.deviceStatus + .light_mode.value, + fontColor: ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + )), + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + threeTouchBloc.optionSelected = + 'relay_status_1'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + threeTouchBloc.deviceStatus + .relay_status_1.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + threeTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status 1', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + threeTouchBloc.add( + const InitialEvent( + groupScreen: false)); + }); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status 1', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: threeTouchBloc + .deviceStatus + .relay_status_1 + .value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + const Divider( + color: ColorsManager.graysColor, + ), + Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + threeTouchBloc.optionSelected = + 'relay_status_2'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + threeTouchBloc.deviceStatus + .relay_status_2.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + threeTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status 2', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + threeTouchBloc.add( + const InitialEvent( + groupScreen: false)); + threeTouchBloc.add( + const InitialEvent( + groupScreen: false)); + }); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status 2', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: threeTouchBloc + .deviceStatus + .relay_status_2 + .value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + const Divider( + color: ColorsManager.graysColor, + ), + Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + threeTouchBloc.optionSelected = + 'relay_status_3'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + threeTouchBloc.deviceStatus + .relay_status_3.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + threeTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status 3', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + threeTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + threeTouchBloc.add( + const InitialEvent( + groupScreen: false)); + threeTouchBloc.add( + const InitialEvent( + groupScreen: false)); + }); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status 3', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: threeTouchBloc + .deviceStatus + .relay_status_3 + .value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + ], + ), + ), + ), + ], + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/three_touch/three_touch_wizard.dart b/lib/features/devices/view/widgets/three_touch/three_touch_wizard.dart new file mode 100644 index 0000000..9df741f --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/three_touch_wizard.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_three_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_list.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; + +class ThreeTouchWizard extends StatelessWidget { + const ThreeTouchWizard({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Lights', + child: BlocProvider( + create: (context) => + ThreeTouchBloc(switchCode: '', threeTouchId: device?.uuid ?? '') + ..add(InitialEvent(groupScreen: device != null ? false : true)), + child: BlocBuilder( + builder: (context, state) { + List groupThreeTouchModel = []; + bool allSwitchesOn = false; + + if (state is UpdateGroupState) { + groupThreeTouchModel = state.threeTouchList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : + ThreeTouchList( + threeTouchList: groupThreeTouchModel, + allSwitches: allSwitchesOn, + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/three_touch/timer_screen.dart b/lib/features/devices/view/widgets/three_touch/timer_screen.dart new file mode 100644 index 0000000..37fcab7 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/timer_screen.dart @@ -0,0 +1,320 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/three_touch_bloc/three_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/shared_widgets/create_schedule.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/schedule_list.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class TimerScheduleScreen extends StatelessWidget { + final DeviceModel device; + final String deviceCode; + final String switchCode; + const TimerScheduleScreen( + {required this.device, + required this.deviceCode, + required this.switchCode, + super.key}); + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: BlocProvider( + create: (context) => ThreeTouchBloc( + switchCode: switchCode, threeTouchId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), + child: BlocBuilder( + builder: (context, state) { + final threeTouchBloc = BlocProvider.of(context); + Duration duration = Duration.zero; + int countNum = 0; + if (state is UpdateTimerState) { + countNum = state.seconds; + } else if (state is TimerRunInProgress) { + countNum = state.remainingTime; + } else if (state is TimerRunComplete) { + countNum = 0; + } else if (state is LoadingNewSate) { + countNum = 0; + } + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + threeTouchBloc.add(OnClose()); + Navigator.pop(context); + } + }, + child: DefaultTabController( + length: 2, + child: DefaultScaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: const BodyLarge( + text: 'Schedule', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + actions: [ + threeTouchBloc.createSchedule == true + ? TextButton( + onPressed: () { + threeTouchBloc.add(ThreeTouchSave()); + }, + child: const Text('Save')) + : threeTouchBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + // oneTouchBloc.toggleCreateSchedule(); + threeTouchBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), + ], + ), + child: state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(30)), + ), + ), + child: TabBar( + // onTap: (value) { + // if (value == 0) { + // if (threeTouchBloc.createSchedule == + // true) { + // threeTouchBloc.toggleCreateSchedule(); + // } + // threeTouchBloc.toggleSelectedIndex(0); + // } else { + // threeTouchBloc.toggleSelectedIndex(1); + // } + // }, + + onTap: (value) { + if (value == 0) { + if (threeTouchBloc.createSchedule == + true) { + // oneTouchBloc.toggleCreateSchedule(); + threeTouchBloc.add( + const ToggleCreateScheduleEvent( + index: 0)); + } + threeTouchBloc.add( + const ToggleSelectedEvent( + index: 0)); + } else { + threeTouchBloc.add( + const ToggleSelectedEvent( + index: 1)); + } + }, + indicatorColor: Colors.white, + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20)), + ), + ), + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + physics: const NeverScrollableScrollPhysics(), // Disable swiping + + children: [ + Center( + child: Container( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + countNum > 0 + ? BodyLarge( + text: _formatDuration( + countNum), + fontColor: ColorsManager + .slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: + CupertinoTimerPickerMode + .hm, + onTimerDurationChanged: + (Duration + newDuration) { + duration = newDuration; + }, + ), + GestureDetector( + onTap: () { + if (state + is LoadingNewSate) { + return; + } + if (countNum > 0) { + threeTouchBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: Duration + .zero)); + } else if (duration != + Duration.zero) { + threeTouchBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: + duration)); + } + }, + child: SvgPicture.asset( + countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + ), + SizedBox( + child: threeTouchBloc.createSchedule == + true + ? CreateSchedule( + onToggleChanged: (bool isOn) { + threeTouchBloc.toggleSchedule = + isOn; + }, + onDateTimeChanged: + (DateTime dateTime) { + threeTouchBloc.selectedTime = + dateTime; + }, + days: threeTouchBloc.days, + selectDays: (List + selectedDays) { + threeTouchBloc.selectedDays = + selectedDays; + }, + ) + : Padding( + padding: const EdgeInsets.only( + top: 10), + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + child: ScheduleListView( + listSchedule: threeTouchBloc + .listSchedule, // Pass the schedule list here + onDismissed: + (scheduleId) { + threeTouchBloc + .listSchedule + .removeWhere( + (schedule) => + schedule + .scheduleId == + scheduleId); + threeTouchBloc.add( + DeleteScheduleEvent( + id: scheduleId)); + }, + onToggleSchedule: + (scheduleId, + isEnabled) { + threeTouchBloc.add( + ToggleScheduleEvent( + id: scheduleId, + toggle: isEnabled, + )); + }, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ))); + }, + ), + ), + ); + } + + String _formatDuration(int seconds) { + final hours = (seconds ~/ 3600).toString().padLeft(2, '0'); + final minutes = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0'); + final secs = (seconds % 60).toString().padLeft(2, '0'); + return '$hours:$minutes:$secs'; + } +} diff --git a/lib/features/devices/view/widgets/two_gang/two_gang_wizard.dart b/lib/features/devices/view/widgets/two_gang/two_gang_wizard.dart index 04055c5..7a46dd2 100644 --- a/lib/features/devices/view/widgets/two_gang/two_gang_wizard.dart +++ b/lib/features/devices/view/widgets/two_gang/two_gang_wizard.dart @@ -19,28 +19,32 @@ class TwoGangWizard extends StatelessWidget { List groupTwoGangModel = []; return DefaultScaffold( + title: 'Lights', child: BlocProvider( - create: (context) => - TwoGangBloc(switchCode: '', twoGangId: device?.uuid ?? '')..add(InitialWizardEvent()), - child: BlocBuilder( - builder: (context, state) { - bool allSwitchesOn = false; + create: (context) => + TwoGangBloc(switchCode: '', twoGangId: device?.uuid ?? '') + ..add(InitialWizardEvent()), + child: BlocBuilder( + builder: (context, state) { + bool allSwitchesOn = false; - if (state is UpdateGroupState) { - groupTwoGangModel = state.twoGangList; - allSwitchesOn = state.allSwitches; - } - return state is LoadingInitialState - ? const Center( - child: - DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), - ) - : TwoGangList( - twoGangList: groupTwoGangModel, - allSwitches: allSwitchesOn, - ); - }, - ), - )); + if (state is UpdateGroupState) { + groupTwoGangModel = state.twoGangList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : TwoGangList( + twoGangList: groupTwoGangModel, + allSwitches: allSwitchesOn, + ); + }, + ), + )); } } diff --git a/lib/features/devices/view/widgets/two_touch/two_schedule_screen.dart b/lib/features/devices/view/widgets/two_touch/two_schedule_screen.dart new file mode 100644 index 0000000..1d60056 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_schedule_screen.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_Touch/two_timer_screen.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class TwoTouchScheduleScreen extends StatelessWidget { + final DeviceModel device; + const TwoTouchScheduleScreen({required this.device, super.key}); + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: const BodyLarge( + text: 'Schedule', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + ), + body: SafeArea( + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 48, + ), + DefaultContainer( + width: MediaQuery.sizeOf(context).width, + child: Column( + children: [ + InkWell( + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( + device: device, + deviceCode: 'countdown_1', + switchCode:'switch_1' , + ))); + }, + child: Container( + padding: + const EdgeInsets.only(left: 25, right: 15, top: 20, bottom: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BodySmall( + text: "Cove Light", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + fontSize: 15, + fontWeight: FontWeight.w400, + ), + ), + const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 18, + ) + ], + ), + ), + ), + const Divider( + color: ColorsManager.dividerColor, + ), + InkWell( + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( + device: device, + deviceCode: 'countdown_2', + switchCode:'switch_2' , + ))); + }, + child: Container( + padding: + const EdgeInsets.only(left: 25, right: 15, top: 20, bottom: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BodySmall( + text: "Chandelier", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + fontSize: 15, + fontWeight: FontWeight.w400, + ), + ), + const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 18, + ) + ], + ), + ), + ), + + ], + )), + ], + ), + ), + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/two_touch/two_timer_screen.dart b/lib/features/devices/view/widgets/two_touch/two_timer_screen.dart new file mode 100644 index 0000000..2be5863 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_timer_screen.dart @@ -0,0 +1,303 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/shared_widgets/create_schedule.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/schedule_list.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class TimerScheduleScreen extends StatelessWidget { + final DeviceModel device; + final String deviceCode; + final String switchCode; + const TimerScheduleScreen( + {required this.device, + required this.deviceCode, + required this.switchCode, + super.key}); + + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: BlocProvider( + create: (context) => + TwoTouchBloc(switchCode: switchCode, twoTouchId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), + child: BlocBuilder( + builder: (context, state) { + final twoTouchBloc = BlocProvider.of(context); + Duration duration = Duration.zero; + int countNum = 0; + if (state is UpdateTimerState) { + countNum = state.seconds; + } else if (state is TimerRunInProgress) { + countNum = state.remainingTime; + } else if (state is TimerRunComplete) { + countNum = 0; + } else if (state is LoadingNewSate) { + countNum = 0; + } + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + twoTouchBloc.add(OnClose()); + Navigator.pop(context); + } + }, + child: DefaultTabController( + length: 2, + child: DefaultScaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: const BodyLarge( + text: 'Schedule', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + actions: [ + twoTouchBloc.createSchedule == true + ? TextButton( + onPressed: () { + twoTouchBloc.add(TwoTouchSave()); + }, + child: const Text('Save')) + : twoTouchBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + // oneTouchBloc.toggleCreateSchedule(); + twoTouchBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), + ], + ), + child: state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(30)), + ), + ), + child: TabBar( + onTap: (value) { + if (value == 0) { + if (twoTouchBloc.createSchedule == + true) { + // oneTouchBloc.toggleCreateSchedule(); + twoTouchBloc.add( + const ToggleCreateScheduleEvent( + index: 0)); + } + twoTouchBloc.add( + const ToggleSelectedEvent( + index: 0)); + } else { + twoTouchBloc.add( + const ToggleSelectedEvent( + index: 1)); + } + }, + indicatorColor: + Colors.white, // Customize the indicator + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20)), + ), + ), + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + physics: const NeverScrollableScrollPhysics(), // Disable swiping + + children: [ + Center( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + countNum > 0 + ? BodyLarge( + text: _formatDuration( + countNum), + fontColor: ColorsManager + .slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: + CupertinoTimerPickerMode + .hm, + onTimerDurationChanged: + (Duration + newDuration) { + duration = newDuration; + }, + ), + GestureDetector( + onTap: () { + if (state + is LoadingNewSate) { + return; + } + if (countNum > 0) { + twoTouchBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: Duration + .zero)); + } else if (duration != + Duration.zero) { + twoTouchBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: + duration)); + } + }, + child: SvgPicture.asset( + countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + SizedBox( + child: twoTouchBloc.createSchedule == + true + ? CreateSchedule( + onToggleChanged: (bool isOn) { + twoTouchBloc.toggleSchedule = + isOn; + }, + onDateTimeChanged: + (DateTime dateTime) { + twoTouchBloc.selectedTime = + dateTime; + }, + days: twoTouchBloc.days, + selectDays: (List + selectedDays) { + twoTouchBloc.selectedDays = + selectedDays; + }, + ) + : Padding( + padding: const EdgeInsets.only( + top: 10), + child: Column( + children: [ + Expanded( + child: ScheduleListView( + listSchedule: twoTouchBloc + .listSchedule, // Pass the schedule list here + onDismissed: + (scheduleId) { + twoTouchBloc + .listSchedule + .removeWhere( + (schedule) => + schedule + .scheduleId == + scheduleId); + twoTouchBloc.add( + DeleteScheduleEvent( + id: scheduleId)); + }, + onToggleSchedule: + (scheduleId, + isEnabled) { + twoTouchBloc.add( + ToggleScheduleEvent( + id: scheduleId, + toggle: isEnabled, + )); + }, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ))); + }, + ), + ), + ); + } + + String _formatDuration(int seconds) { + final hours = (seconds ~/ 3600).toString().padLeft(2, '0'); + final minutes = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0'); + final secs = (seconds % 60).toString().padLeft(2, '0'); + return '$hours:$minutes:$secs'; + } +} diff --git a/lib/features/devices/view/widgets/two_touch/two_touch_Interface.dart b/lib/features/devices/view/widgets/two_touch/two_touch_Interface.dart new file mode 100644 index 0000000..649b6b1 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_touch_Interface.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_screen.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class TwoTouchInterface extends StatelessWidget { + const TwoTouchInterface({super.key, this.touchSwitch}); + + final DeviceModel? touchSwitch; + @override + Widget build(BuildContext context) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: touchSwitch != null + ? DeviceAppbar( + deviceName: touchSwitch!.name!, + deviceUuid: touchSwitch!.uuid!, + ) + : AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: BodyLarge( + text: touchSwitch?.name ?? 'Lights', + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + ), + body: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: SafeArea( + child: Padding( + padding: EdgeInsets.only( + left: Constants.defaultPadding, + right: Constants.defaultPadding, + bottom: Constants.bottomNavBarHeight, + ), + child: TwoTouchScreen(device: touchSwitch), + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/two_touch/two_touch_list.dart b/lib/features/devices/view/widgets/two_touch/two_touch_list.dart new file mode 100644 index 0000000..ebc5c14 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_touch_list.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/group_two_touch_model.dart'; +import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; + +class TwoTouchList extends StatelessWidget { + const TwoTouchList( + {super.key, required this.twoTouchList, required this.allSwitches}); + + final List twoTouchList; + final bool allSwitches; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 10), + const BodySmall(text: 'All Lights'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: allSwitches, + action: () { + BlocProvider.of(context).add(GroupAllOnEvent()); + }, + secondAction: () { + BlocProvider.of(context).add(GroupAllOffEvent()); + }, + ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.all(0), + itemCount: twoTouchList.length, + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + BodySmall(text: twoTouchList[index].deviceName), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: twoTouchList[index].firstSwitch, + action: () { + BlocProvider.of(context).add( + ChangeFirstWizardSwitchStatusEvent( + value: twoTouchList[index].firstSwitch, + deviceId: twoTouchList[index].deviceId)); + }, + ), + const SizedBox(height: 10), + BodySmall(text: twoTouchList[index].deviceName), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: twoTouchList[index].secondSwitch, + action: () { + BlocProvider.of(context).add( + ChangeSecondWizardSwitchStatusEvent( + value: twoTouchList[index].secondSwitch, + deviceId: twoTouchList[index].deviceId)); + }, + ), + ], + ); + }, + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/devices/view/widgets/two_touch/two_touch_screen.dart b/lib/features/devices/view/widgets/two_touch/two_touch_screen.dart new file mode 100644 index 0000000..0b12089 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_touch_screen.dart @@ -0,0 +1,333 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/two_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/circular_button.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_gang/gang_switch.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_setting.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_schedule_screen.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class TwoTouchScreen extends StatelessWidget { + const TwoTouchScreen({super.key, this.device, this.switchCode}); + final DeviceModel? device; + final String? switchCode; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => TwoTouchBloc( + switchCode: switchCode ?? '', twoTouchId: device?.uuid ?? '') + ..add(const InitialEvent()), + child: BlocBuilder( + builder: (context, state) { + TwoTouchModel twoTouchModel = TwoTouchModel( + firstSwitch: false, + secondSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off, + relay_status_2: status.off, + ); + + if (state is LoadingNewSate) { + twoTouchModel = state.twoTouchModel; + } else if (state is UpdateState) { + twoTouchModel = state.twoTouchModel; + } + + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + BlocProvider.of(context) + .add(const InitialEvent()); + }, + child: ListView( + children: [ + SizedBox( + height: MediaQuery.of(context).size.height, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Expanded(child: SizedBox.shrink()), + Expanded( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + children: [ + GangSwitch( + threeGangSwitch: device!, + value: twoTouchModel.firstSwitch, + action: () { + BlocProvider.of(context) + .add(ChangeFirstSwitchStatusEvent( + value: twoTouchModel + .firstSwitch)); + }, + ), + const SizedBox(height: 20), + const SizedBox( + width: 77, + child: BodySmall( + text: "Cove Light", + fontColor: + ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + Column( + children: [ + GangSwitch( + threeGangSwitch: device!, + value: twoTouchModel.secondSwitch, + action: () { + BlocProvider.of(context) + .add( + ChangeSecondSwitchStatusEvent( + value: twoTouchModel + .secondSwitch)); + }, + ), + const SizedBox(height: 20), + const SizedBox( + width: 77, + child: BodySmall( + text: "Chandelier", + fontColor: + ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + ], + ), + ), + Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CircularButton( + device: device, + icons: Icons.u_turn_left, + label: "All On", + onTap: () { + BlocProvider.of(context) + .add(AllOnEvent()); + }, + ), + + // Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + + // Card( + // elevation: 3, + // shape: RoundedRectangleBorder( + // borderRadius: + // BorderRadius.circular(100), + // ), + // child: GestureDetector( + // onTap: () { + // BlocProvider.of( + // context) + // .add(AllOnEvent()); + // }, + // child: Stack( + // alignment: Alignment.center, + // children: [ + // Container( + // width: 60, + // height: 60, + // decoration: BoxDecoration( + // color: Colors.grey[300], + // borderRadius: + // BorderRadius.circular( + // 100), + // ), + // ), + // Container( + // width: 40, + // height: 40, + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: + // BorderRadius.circular( + // 100), + // ), + // child: Center( + // child: BodySmall( + // text: "On", + // style: context.bodyMedium + // .copyWith( + // color: ColorsManager + // .primaryColorWithOpacity, + // fontWeight: + // FontWeight + // .bold), + // ), + // ), + // ), + // ], + // ), + // ), + // ), + // const SizedBox(height: 10), + // BodySmall( + // text: "All On", + // style: context.bodyMedium.copyWith( + // color: ColorsManager.textPrimaryColor, + // ), + // ), + // ], + // ), + const SizedBox( + width: 20, + ), + CircularButton( + device: device, + icons: Icons.access_time, + label: "Timer", + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, + animation2) => + TwoTouchScheduleScreen( + device: device!, + ))); + }, + ), + // Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Card( + // elevation: 3, + // shape: RoundedRectangleBorder( + // borderRadius: + // BorderRadius.circular(100), + // ), + // child: GestureDetector( + // onTap: () { + // Navigator.push( + // context, + // PageRouteBuilder( + // pageBuilder: (context, + // animation1, + // animation2) => + // TwoTouchScheduleScreen( + // device: device!, + // ))); + // }, + // child: Stack( + // alignment: Alignment.center, + // children: [ + // Container( + // width: 60, + // height: 60, + // decoration: BoxDecoration( + // color: Colors.grey[300], + // borderRadius: + // BorderRadius.circular( + // 100), + // ), + // ), + // Container( + // width: 40, + // height: 40, + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: + // BorderRadius.circular( + // 100), + // ), + // child: Center( + // child: Icon( + // Icons.access_time, + // color: ColorsManager + // .primaryColorWithOpacity, + // size: 25, + // ), + // ), + // ), + // ], + // ), + // ), + // ), + // const SizedBox(height: 10), + // BodySmall( + // text: "Timer", + // style: context.bodyMedium.copyWith( + // color: ColorsManager.textPrimaryColor, + // ), + // ), + // ], + // ), + const SizedBox( + width: 20, + ), + CircularButton( + device: device, + icons: Icons.access_time, + label: "Setting", + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, + animation2) => + TwoTouchSetting( + device: device, + ))); + }, + ), + const SizedBox( + width: 20, + ), + CircularButton( + device: device, + icons: Icons.access_time, + label: "All Off", + onTap: () { + BlocProvider.of(context) + .add(AllOffEvent()); + }, + ), + + ], + ), + ), + Expanded(child: SizedBox()) + ], + ), + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/two_touch/two_touch_setting.dart b/lib/features/devices/view/widgets/two_touch/two_touch_setting.dart new file mode 100644 index 0000000..2e03119 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_touch_setting.dart @@ -0,0 +1,394 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/two_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/restart_status_dialog.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class TwoTouchSetting extends StatelessWidget { + const TwoTouchSetting({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Setting', + child: BlocProvider( + create: (context) => + TwoTouchBloc(switchCode: '', twoTouchId: device?.uuid ?? '') + ..add(const InitialEvent()), + child: BlocBuilder( + builder: (context, state) { + final twoTouchBloc = BlocProvider.of(context); + TwoTouchModel? twoTouchModel = TwoTouchModel( + relay_status_2: status.off, + secondCountDown: 0, + secondSwitch: false, + firstSwitch: false, + firstCountDown: 0, + light_mode: lightStatus.off, + relay: status.off, + relay_status_1: status.off); + + // if (state is UpdateState) { + // TwoTouchModel = state.TwoTouchModel; + // } + if (state is UpdateState) { + twoTouchModel = state.twoTouchModel; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : Column( + children: [ + Container( + child: DefaultContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () async { + twoTouchBloc.optionSelected = + 'relay_status'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + twoTouchModel!.relay.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + twoTouchBloc.add(ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed(const Duration(seconds: 2), + () async { + twoTouchBloc.add(const InitialEvent()); + }); + } + }, + child: Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: twoTouchModel.relay.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + )), + ), + const Divider( + color: ColorsManager.graysColor, + ), + Padding( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + twoTouchBloc.optionSelected = + 'light_mode'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + twoTouchModel!.light_mode.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + twoTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Indicator Status', + label1: 'Off', + label2: 'On/Off Status', + label3: 'Switch Position', + onTapLabel1: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + twoTouchBloc + .add(const InitialEvent()); + }); + } + }, + child: SizedBox( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Indicator Status', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: twoTouchBloc.deviceStatus + .light_mode.value, + fontColor: ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + )), + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + twoTouchBloc.optionSelected = + 'relay_status_1'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + twoTouchModel! + .relay_status_1.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + twoTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status 1', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + twoTouchBloc + .add(const InitialEvent()); + }); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status 1', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: twoTouchBloc.deviceStatus + .relay_status_1.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + const Divider( + color: ColorsManager.graysColor, + ), + Container( + padding: const EdgeInsets.only( + bottom: 10, top: 10), + child: InkWell( + onTap: () async { + twoTouchBloc.optionSelected = + 'relay_status_2'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + twoTouchModel! + .relay_status_2.value, + cancelTab: () { + Navigator.of(context).pop(); + }, + confirmTab: () { + twoTouchBloc.add( + ChangeStatusEvent( + deviceId: device!.uuid!, + context: context)); + Navigator.of(context).pop(true); + }, + title: 'Restart Status 2', + label1: 'Power Off', + label2: 'Power On', + label3: 'Restart Memory', + onTapLabel1: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + twoTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + Future.delayed( + const Duration(seconds: 2), + () async { + twoTouchBloc + .add(const InitialEvent()); + }); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const BodyLarge( + fontSize: 15, + text: 'Restart Status 2', + fontWeight: FontWeight.w400, + ), + Row( + children: [ + BodyMedium( + fontSize: 13, + text: twoTouchBloc.deviceStatus + .relay_status_2.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + ], + ), + ), + ), + ], + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/two_touch/two_touch_wizard.dart b/lib/features/devices/view/widgets/two_touch/two_touch_wizard.dart new file mode 100644 index 0000000..6afa758 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_touch_wizard.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_state.dart'; + +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/group_two_touch_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_list.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; + +class TwoTouchWizard extends StatelessWidget { + const TwoTouchWizard({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + List groupTwoTouchModel = []; + + return DefaultScaffold( + title: 'Lights', + child: BlocProvider( + create: (context) => + TwoTouchBloc(switchCode: '', twoTouchId: device?.uuid ?? '') + ..add(InitialWizardEvent()), + child: BlocBuilder( + builder: (context, state) { + bool allSwitchesOn = false; + + if (state is UpdateGroupState) { + groupTwoTouchModel = state.twoTouchList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : TwoTouchList( + twoTouchList: groupTwoTouchModel, + allSwitches: allSwitchesOn, + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/wizard_page.dart b/lib/features/devices/view/widgets/wizard_page.dart index a23f505..d617bad 100644 --- a/lib/features/devices/view/widgets/wizard_page.dart +++ b/lib/features/devices/view/widgets/wizard_page.dart @@ -3,8 +3,11 @@ import 'package:flutter_svg/svg.dart'; import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_wizard.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_touch/one_touch_wizard.dart'; import 'package:syncrow_app/features/devices/view/widgets/three_gang/three_gang_wizard.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_touch/three_touch_wizard.dart'; import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_wizard.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_wizard.dart'; import 'package:syncrow_app/features/devices/view/widgets/water_heater/wh_wizard.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; @@ -64,6 +67,30 @@ class WizardPage extends StatelessWidget { pageBuilder: (context, animation1, animation2) => const WHWizard())); } + + if (groupsList[index].name == '1GT') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + const OneTouchWizard())); + } + + if (groupsList[index].name == '2GT') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + const TwoTouchWizard())); + } + + if (groupsList[index].name == '3GT') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + const ThreeTouchWizard())); + } }, child: DefaultContainer( padding: const EdgeInsets.all(15), diff --git a/lib/features/shared_widgets/devices_default_switch.dart b/lib/features/shared_widgets/devices_default_switch.dart index 54c88bb..f31fb50 100644 --- a/lib/features/shared_widgets/devices_default_switch.dart +++ b/lib/features/shared_widgets/devices_default_switch.dart @@ -4,7 +4,10 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class DevicesDefaultSwitch extends StatelessWidget { const DevicesDefaultSwitch( - {super.key, required this.switchValue, required this.action, this.secondAction}); + {super.key, + required this.switchValue, + required this.action, + this.secondAction}); final bool switchValue; final Function action; @@ -22,7 +25,9 @@ class DevicesDefaultSwitch extends StatelessWidget { child: Container( height: 60, decoration: BoxDecoration( - color: switchValue ? ColorsManager.primaryColor : Colors.white, + color: switchValue + ? ColorsManager.switchButton.withOpacity(0.6) + : Colors.white, borderRadius: const BorderRadius.only( topLeft: Radius.circular(15), bottomLeft: Radius.circular(15), @@ -30,9 +35,10 @@ class DevicesDefaultSwitch extends StatelessWidget { ), child: Center( child: BodyMedium( + fontSize: 14, text: "ON", fontColor: switchValue ? Colors.white : null, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w700, ), ), ), @@ -50,7 +56,9 @@ class DevicesDefaultSwitch extends StatelessWidget { child: Container( height: 60, decoration: BoxDecoration( - color: switchValue ? Colors.white : ColorsManager.primaryColor, + color: switchValue + ? Colors.white + : ColorsManager.switchButton.withOpacity(0.6), borderRadius: const BorderRadius.only( topRight: Radius.circular(15), bottomRight: Radius.circular(15), @@ -58,9 +66,10 @@ class DevicesDefaultSwitch extends StatelessWidget { ), child: Center( child: BodyMedium( + fontSize: 14, text: "OFF", fontColor: switchValue ? null : Colors.white, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w700, ), ), ), diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 2b18884..c7305db 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -33,10 +33,11 @@ class DevicesAPI { static Future> controlDevice( DeviceControlModel controlModel, String deviceId) async { try { + print(controlModel.toJson()); final response = await _httpService.post( path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', deviceId), body: controlModel.toJson(), - showServerMessage: false, + showServerMessage: true, expectedResponseModel: (json) { return json; }, @@ -72,7 +73,8 @@ class DevicesAPI { static Future> getDeviceStatus(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId), + path: ApiEndpoints.deviceFunctionsStatus + .replaceAll('{deviceUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -82,7 +84,9 @@ class DevicesAPI { } static Future> renamePass( - {required String name, required String doorLockUuid, required String passwordId}) async { + {required String name, + required String doorLockUuid, + required String passwordId}) async { final response = await _httpService.put( path: ApiEndpoints.renamePassword .replaceAll('{doorLockUuid}', doorLockUuid) @@ -107,7 +111,8 @@ class DevicesAPI { return response; } - static Future> getDeviceByGroupName(String unitId, String groupName) async { + static Future> getDeviceByGroupName( + String unitId, String groupName) async { final response = await _httpService.get( path: ApiEndpoints.devicesByGroupName .replaceAll('{unitUuid}', unitId) @@ -146,7 +151,8 @@ class DevicesAPI { return response; } - static Future> getDevicesByGatewayId(String gatewayId) async { + static Future> getDevicesByGatewayId( + String gatewayId) async { final response = await _httpService.get( path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId), showServerMessage: false, @@ -168,7 +174,8 @@ class DevicesAPI { String deviceId, ) async { final response = await _httpService.get( - path: ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.getTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -179,7 +186,8 @@ class DevicesAPI { static Future getOneTimePasswords(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.getOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.getOneTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -190,7 +198,8 @@ class DevicesAPI { static Future getTimeLimitPasswords(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.getMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.getMultipleTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -215,10 +224,12 @@ class DevicesAPI { "invalidTime": invalidTime, }; if (scheduleList != null) { - body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); + body["scheduleList"] = + scheduleList.map((schedule) => schedule.toJson()).toList(); } final response = await _httpService.post( - path: ApiEndpoints.addTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.addTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), body: body, showServerMessage: false, expectedResponseModel: (json) => json, @@ -229,7 +240,8 @@ class DevicesAPI { static Future generateOneTimePassword({deviceId}) async { try { final response = await _httpService.post( - path: ApiEndpoints.addOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.addOneTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -241,10 +253,12 @@ class DevicesAPI { } } - static Future generateMultiTimePassword({deviceId, effectiveTime, invalidTime}) async { + static Future generateMultiTimePassword( + {deviceId, effectiveTime, invalidTime}) async { try { final response = await _httpService.post( - path: ApiEndpoints.addMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.addMultipleTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: true, body: {"effectiveTime": effectiveTime, "invalidTime": invalidTime}, expectedResponseModel: (json) { diff --git a/lib/utils/resource_manager/color_manager.dart b/lib/utils/resource_manager/color_manager.dart index 7cf3131..78e9a50 100644 --- a/lib/utils/resource_manager/color_manager.dart +++ b/lib/utils/resource_manager/color_manager.dart @@ -5,7 +5,8 @@ abstract class ColorsManager { static const Color switchOffColor = Color(0x7F8D99AE); static const Color primaryColor = Color(0xFF0030CB); static const Color secondaryTextColor = Color(0xFF848484); - static Color primaryColorWithOpacity = const Color(0xFF023DFE).withOpacity(0.6); + static Color primaryColorWithOpacity = + const Color(0xFF023DFE).withOpacity(0.6); static const Color onPrimaryColor = Colors.white; static const Color secondaryColor = Color(0xFF023DFE); static const Color onSecondaryColor = Color(0xFF023DFE); @@ -27,4 +28,5 @@ abstract class ColorsManager { static const Color red = Colors.red; static const Color graysColor = Color(0xffEBEBEB); static const Color textGray = Color(0xffD5D5D5); + static const Color switchButton = Color(0xff023DFE); } diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 2ce280f..7b7114d 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -50,6 +50,10 @@ enum DeviceType { WallSensor, WH, DS, + OneTouch, + TowTouch, + ThreeTouch, + Other, } @@ -76,6 +80,9 @@ Map devicesTypesMap = { "CUR": DeviceType.Curtain, "WH": DeviceType.WH, "DS": DeviceType.DS, + "1GT": DeviceType.OneTouch, + "2GT": DeviceType.TowTouch, + "3GT": DeviceType.ThreeTouch, }; Map> devicesFunctionsMap = { DeviceType.AC: [ @@ -284,13 +291,144 @@ Map> devicesFunctionsMap = { FunctionModel( code: 'doorcontact_state', type: functionTypesMap['Raw'], - values: ValueModel.fromJson({}) - ), + values: ValueModel.fromJson({})), FunctionModel( code: 'battery_percentage', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({}) - ), + values: ValueModel.fromJson({})), + ], + DeviceType.OneTouch: [ + FunctionModel( + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'countdown_1', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + FunctionModel( + code: 'relay_status', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + FunctionModel( + code: 'light_mode', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['none', 'relay', 'pos'] + })), + FunctionModel( + code: 'relay_status_1', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + ], + DeviceType.TowTouch: [ + FunctionModel( + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'countdown_1', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + FunctionModel( + code: 'countdown_2', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + FunctionModel( + code: 'relay_status', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + FunctionModel( + code: 'light_mode', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['none', 'relay', 'pos'] + })), + FunctionModel( + code: 'relay_status_1', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + FunctionModel( + code: 'relay_status_2', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + ], + + DeviceType.ThreeTouch: [ + FunctionModel( + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'switch_3', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), + FunctionModel( + code: 'countdown_1', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + FunctionModel( + code: 'countdown_2', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + FunctionModel( + code: 'countdown_3', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + FunctionModel( + code: 'relay_status', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + FunctionModel( + code: 'light_mode', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['none', 'relay', 'pos'] + })), + FunctionModel( + code: 'relay_status_1', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + FunctionModel( + code: 'relay_status_2', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), + FunctionModel( + code: 'relay_status_3', + type: functionTypesMap['Enum'], + values: ValueModel.fromJson({ + "range": ['power_off', 'power_on', 'last'] + })), ], }; @@ -589,3 +727,67 @@ List> members = [ 'role': MemberRole.FamilyMember, }, ]; + +enum status { + on, + off, + restart, +} + +extension StatusExtension on status { + String get value { + switch (this) { + case status.off: + return "Power Off"; + case status.on: + return "Power On"; + case status.restart: + return "Restart Memory"; + } + } + + static status fromString(String value) { + switch (value) { + case "power_off": + return status.off; + case "power_on": + return status.on; + case "last": + return status.restart; + default: + throw ArgumentError("Invalid access type: $value"); + } + } +} + +enum lightStatus { + off, + on_off, + switchPosition, +} + +extension lightStatusExtension on lightStatus { + String get value { + switch (this) { + case lightStatus.off: + return "Off"; + case lightStatus.switchPosition: + return "On/Off Status"; + case lightStatus.on_off: + return "Restart Memory"; + } + } + + static lightStatus fromString(String value) { + switch (value) { + case "none": + return lightStatus.off; + case "relay": + return lightStatus.switchPosition; + case "pos": + return lightStatus.on_off; + default: + throw ArgumentError("Invalid access type: $value"); + } + } +}