From d75d438106f1b987391c133f5e9c6081bb7f47c5 Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 1 Oct 2024 16:24:36 +0300 Subject: [PATCH] two touch & three touch --- .../bloc/one_touch_bloc/one_touch_bloc.dart | 116 +-- .../three_touch_bloc/three_touch_bloc.dart | 748 ++++++++++++++++++ .../three_touch_bloc/three_touch_event.dart | 147 ++++ .../three_touch_bloc/three_touch_state.dart | 90 +++ .../bloc/two_touch_bloc/two_touch_bloc.dart | 688 ++++++++++++++++ .../bloc/two_touch_bloc/two_touch_event.dart | 162 ++++ .../bloc/two_touch_bloc/two_touch_state.dart | 91 +++ .../model/group_three_touch_model.dart | 15 + .../devices/model/group_two_touch_model.dart | 13 + .../devices/model/one_touch_model.dart | 3 +- .../devices/model/three_touch_model.dart | 82 ++ .../devices/model/two_touch_model.dart | 63 ++ .../devices/view/widgets/circular_button.dart | 32 +- .../widgets/one_touch/one_touch_screen.dart | 1 - .../widgets/one_touch/one_touch_setting.dart | 97 ++- .../view/widgets/restart_status_dialog.dart | 230 ++++++ .../view/widgets/room_page_switch.dart | 15 + .../view/widgets/three_touch/gang_switch.dart | 56 ++ .../widgets/three_touch/schedule_screen.dart | 186 +++++ .../three_touch/three_touch_interface.dart | 67 ++ .../widgets/three_touch/three_touch_list.dart | 88 +++ .../three_touch/three_touch_screen.dart | 474 +++++++++++ .../three_touch/three_touch_wizard.dart | 45 ++ .../widgets/three_touch/timer_screen.dart | 321 ++++++++ .../three_touch/two_touch_setting.dart | 382 +++++++++ .../two_touch/two_schedule_screen.dart | 141 ++++ .../widgets/two_touch/two_timer_screen.dart | 303 +++++++ .../two_touch/two_touch_Interface.dart | 67 ++ .../widgets/two_touch/two_touch_list.dart | 80 ++ .../widgets/two_touch/two_touch_screen.dart | 333 ++++++++ .../widgets/two_touch/two_touch_setting.dart | 613 ++++++++++++++ .../widgets/two_touch/two_touch_wizard.dart | 47 ++ lib/utils/resource_manager/constants.dart | 117 ++- 33 files changed, 5813 insertions(+), 100 deletions(-) create mode 100644 lib/features/devices/bloc/three_touch_bloc/three_touch_bloc.dart create mode 100644 lib/features/devices/bloc/three_touch_bloc/three_touch_event.dart create mode 100644 lib/features/devices/bloc/three_touch_bloc/three_touch_state.dart create mode 100644 lib/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart create mode 100644 lib/features/devices/bloc/two_touch_bloc/two_touch_event.dart create mode 100644 lib/features/devices/bloc/two_touch_bloc/two_touch_state.dart create mode 100644 lib/features/devices/model/group_three_touch_model.dart create mode 100644 lib/features/devices/model/group_two_touch_model.dart create mode 100644 lib/features/devices/model/three_touch_model.dart create mode 100644 lib/features/devices/model/two_touch_model.dart create mode 100644 lib/features/devices/view/widgets/restart_status_dialog.dart create mode 100644 lib/features/devices/view/widgets/three_touch/gang_switch.dart create mode 100644 lib/features/devices/view/widgets/three_touch/schedule_screen.dart create mode 100644 lib/features/devices/view/widgets/three_touch/three_touch_interface.dart create mode 100644 lib/features/devices/view/widgets/three_touch/three_touch_list.dart create mode 100644 lib/features/devices/view/widgets/three_touch/three_touch_screen.dart create mode 100644 lib/features/devices/view/widgets/three_touch/three_touch_wizard.dart create mode 100644 lib/features/devices/view/widgets/three_touch/timer_screen.dart create mode 100644 lib/features/devices/view/widgets/three_touch/two_touch_setting.dart create mode 100644 lib/features/devices/view/widgets/two_touch/two_schedule_screen.dart create mode 100644 lib/features/devices/view/widgets/two_touch/two_timer_screen.dart create mode 100644 lib/features/devices/view/widgets/two_touch/two_touch_Interface.dart create mode 100644 lib/features/devices/view/widgets/two_touch/two_touch_list.dart create mode 100644 lib/features/devices/view/widgets/two_touch/two_touch_screen.dart create mode 100644 lib/features/devices/view/widgets/two_touch/two_touch_setting.dart create mode 100644 lib/features/devices/view/widgets/two_touch/two_touch_wizard.dart 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 index 9be349b..ce23db2 100644 --- a/lib/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart +++ b/lib/features/devices/bloc/one_touch_bloc/one_touch_bloc.dart @@ -508,7 +508,54 @@ class OneTouchBloc extends Bloc { String statusSelected = ''; String optionSelected = ''; - // void _changeStatus( + + Future _changeStatus( + ChangeStatusEvent event, Emitter emit) async { + 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, + ); + Future.delayed(const Duration(seconds: 1), () async { + add(const InitialEvent(groupScreen: false)); + }); + Future.delayed(const Duration(seconds: 1), () async { + emit(UpdateState(oneTouchModel: deviceStatus)); + + }); + + // add(const InitialEvent(groupScreen: false)); + } else { + print('Invalid statusSelected or optionSelected'); + } + } +} + + + +// void _changeStatus( // ChangeStatusEvent event, Emitter emit) async { // emit(LoadingNewSate(oneTouchModel: deviceStatus)); // try { @@ -581,55 +628,18 @@ class OneTouchBloc extends Bloc { // } // } - void _changeStatus( - ChangeStatusEvent event, Emitter emit) async { - emit(LoadingInitialState()); - emit(UpdateState(oneTouchModel: deviceStatus)); - emit(LoadingNewSate(oneTouchModel: deviceStatus)); - - try { - await _handleDeviceControl(event.deviceId, event.context); - add(InitialEvent(groupScreen: oneTouchGroup)); - emit(UpdateState(oneTouchModel: deviceStatus)); - } catch (error) { - print('Error controlling device: $error'); - add(InitialEvent(groupScreen: oneTouchGroup)); - } - } - - Future _handleDeviceControl( - String deviceId, BuildContext context) async { - 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_off', - 'Power Off': 'power_on', - 'Restart Memory': 'last', - }, - }; - - final selectedControl = controlMap[optionSelected]?[statusSelected]; - if (selectedControl != null) { - await DevicesAPI.controlDevice( - DeviceControlModel( - deviceId: oneTouchId, code: optionSelected, value: selectedControl), - oneTouchId, - ).then( - (value) { - Navigator.pop(context); - }, - ); - } else { - print('Invalid statusSelected or optionSelected'); - } - } -} + // Future _changeStatus( + // ChangeStatusEvent event, Emitter emit) async { + // try { + // emit(LoadingInitialState()); + // // emit(LoadingNewSate(oneTouchModel: deviceStatus)); + // await _handleDeviceControl(event.deviceId, event.context); + // Future.delayed(const Duration(seconds: 1), () async { + // add(const InitialEvent(groupScreen: false)); + // }); + // emit(UpdateState(oneTouchModel: deviceStatus)); + // } catch (error) { + // print('Error controlling device: $error'); + // add(InitialEvent(groupScreen: oneTouchGroup)); + // } + // } 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..6f11773 --- /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_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 { + 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, + ); + Future.delayed(const Duration(seconds: 1), () async { + add(const InitialEvent(groupScreen: false)); + }); + Future.delayed(const Duration(seconds: 1), () async { + emit(UpdateState(threeTouchModel: deviceStatus)); + }); + } else { + print('Invalid statusSelected or optionSelected'); + } + } +} 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..c093e99 --- /dev/null +++ b/lib/features/devices/bloc/three_touch_bloc/three_touch_state.dart @@ -0,0 +1,90 @@ +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); +} 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..a390c40 --- /dev/null +++ b/lib/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart @@ -0,0 +1,688 @@ +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 { + emit(LoadingInitialState()); + + print(optionSelected); + print(statusSelected); + 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, + ); + Future.delayed(const Duration(seconds: 1), () async { + add(const InitialEvent()); + }); + Future.delayed(const Duration(seconds: 1), () async { + emit(UpdateState(twoTouchModel: deviceStatus)); + }); + } else { + print('Invalid statusSelected or optionSelected'); + } + } +} 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/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 index da15264..34ea764 100644 --- a/lib/features/devices/model/one_touch_model.dart +++ b/lib/features/devices/model/one_touch_model.dart @@ -15,7 +15,8 @@ class OneTouchModel { required this.firstCountDown, required this.light_mode, required this.relay, - required this.relay_status_1}); + required this.relay_status_1 + }); factory OneTouchModel.fromJson(List jsonList) { late bool _switch; 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..bf807ad --- /dev/null +++ b/lib/features/devices/model/three_touch_model.dart @@ -0,0 +1,82 @@ +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 int _thirdCountDown; + 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 index ebead6a..97156c7 100644 --- a/lib/features/devices/view/widgets/circular_button.dart +++ b/lib/features/devices/view/widgets/circular_button.dart @@ -1,12 +1,10 @@ - - 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 { +class CircularButton extends StatelessWidget { const CircularButton({ super.key, required this.device, @@ -31,7 +29,6 @@ class CircularButton extends StatelessWidget { ), child: GestureDetector( onTap: onTap, - child: Stack( alignment: Alignment.center, children: [ @@ -51,11 +48,26 @@ class CircularButton extends StatelessWidget { borderRadius: BorderRadius.circular(100), ), child: Center( - child: Icon( - icons, - color: ColorsManager.primaryColorWithOpacity, - size: 25, - ), + 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, + ), ), ), ], @@ -73,4 +85,4 @@ class CircularButton extends StatelessWidget { ], ); } -} \ No newline at end of file +} 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 index b8399d7..383e7d0 100644 --- a/lib/features/devices/view/widgets/one_touch/one_touch_screen.dart +++ b/lib/features/devices/view/widgets/one_touch/one_touch_screen.dart @@ -16,7 +16,6 @@ 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/constants.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; 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 index 0eb09ba..36781bf 100644 --- a/lib/features/devices/view/widgets/one_touch/one_touch_setting.dart +++ b/lib/features/devices/view/widgets/one_touch/one_touch_setting.dart @@ -4,7 +4,7 @@ import 'package:syncrow_app/features/devices/bloc/one_touch_bloc/one_touch_bloc. 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/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'; @@ -19,23 +19,27 @@ class OneTouchSetting extends StatelessWidget { @override Widget build(BuildContext context) { - List groupOneTouchModel = []; - return DefaultScaffold( title: 'Setting', child: BlocProvider( create: (context) => OneTouchBloc(switchCode: '', oneTouchId: device?.uuid ?? '') - ..add(InitialWizardEvent()), + ..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); - bool allSwitchesOn = false; - - if (state is UpdateGroupState) { - groupOneTouchModel = state.oneTouchList; - allSwitchesOn = state.allSwitches; + // if (state is UpdateState) { + // oneTouchModel = state.oneTouchModel; + // } + if (state is UpdateState) { + oneTouchModel = state.oneTouchModel; } return state is LoadingInitialState ? const Center( @@ -54,13 +58,15 @@ class OneTouchSetting extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ InkWell( - onTap: () { + onTap: () async { oneTouchBloc.optionSelected = 'relay_status'; - showDialog( + final result = await showDialog( context: context, builder: (context) { return RestartStatusDialog( + initialSelectedLabel: + oneTouchModel!.relay.value, cancelTab: () { Navigator.of(context).pop(); }, @@ -68,6 +74,7 @@ class OneTouchSetting extends StatelessWidget { oneTouchBloc.add(ChangeStatusEvent( deviceId: device!.uuid!, context: context)); + Navigator.of(context).pop(true); }, title: 'Restart Status', label1: 'Power Off', @@ -88,6 +95,13 @@ class OneTouchSetting extends StatelessWidget { ); }, ); + if (result == true) { + // Future.delayed(const Duration(seconds: 1), + // () async { + // oneTouchBloc.add(const InitialEvent( + // groupScreen: false)); + // }); + } }, child: Container( padding: const EdgeInsets.only( @@ -105,8 +119,8 @@ class OneTouchSetting extends StatelessWidget { children: [ BodyMedium( fontSize: 13, - text: oneTouchBloc - .deviceStatus.relay.value, + text: + oneTouchModel!.relay.value, fontColor: ColorsManager.textGray, ), @@ -126,13 +140,14 @@ class OneTouchSetting extends StatelessWidget { padding: const EdgeInsets.only( bottom: 10, top: 10), child: InkWell( - onTap: () { - oneTouchBloc.optionSelected = - 'light_mode'; - showDialog( + 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(); }, @@ -141,6 +156,7 @@ class OneTouchSetting extends StatelessWidget { ChangeStatusEvent( deviceId: device!.uuid!, context: context)); + Navigator.of(context).pop(true); }, title: 'Indicator Status', label1: 'Off', @@ -161,6 +177,10 @@ class OneTouchSetting extends StatelessWidget { ); }, ); + if (result == true) { + // oneTouchBloc.add(const InitialEvent( + // groupScreen: false)); + } }, child: SizedBox( child: Row( @@ -197,13 +217,16 @@ class OneTouchSetting extends StatelessWidget { padding: const EdgeInsets.only( bottom: 10, top: 10), child: InkWell( - onTap: () { + onTap: () async { oneTouchBloc.optionSelected = 'relay_status_1'; - showDialog( + final result = await showDialog( context: context, builder: (context) { return RestartStatusDialog( + initialSelectedLabel: + oneTouchModel! + .relay_status_1.value, cancelTab: () { Navigator.of(context).pop(); }, @@ -212,32 +235,36 @@ class OneTouchSetting extends StatelessWidget { 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) { + print(selected); oneTouchBloc.statusSelected = selected; - print( - 'Selected: $selected for Label 1'); }, onTapLabel2: (selected) { + print(selected); + oneTouchBloc.statusSelected = selected; - print( - 'Selected: $selected for Label 2'); }, onTapLabel3: (selected) { + print(selected); + oneTouchBloc.statusSelected = selected; - print( - 'Selected: $selected for Label 3'); }, ); }, ); + if (result == true) { + oneTouchBloc.add(const InitialEvent( + groupScreen: false)); + } }, child: Row( mainAxisAlignment: @@ -284,13 +311,14 @@ class RestartStatusDialog extends StatefulWidget { final String label3; final String title; - // Add onTap callbacks as parameters 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, @@ -301,6 +329,7 @@ class RestartStatusDialog extends StatefulWidget { this.onTapLabel3, required this.cancelTab, required this.confirmTab, + this.initialSelectedLabel, }); @override @@ -308,7 +337,14 @@ class RestartStatusDialog extends StatefulWidget { } class _RestartStatusDialogState extends State { - String _selectedOption = ''; + late String _selectedOption; + + @override + void initState() { + super.initState(); + + _selectedOption = widget.initialSelectedLabel ?? ''; + } @override Widget build(BuildContext context) { @@ -432,15 +468,14 @@ class _RestartStatusDialogState extends State { style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400), ), CircularCheckbox( - value: _selectedOption == - label, // True if the current option is selected + value: _selectedOption == label, onChanged: (bool? value) { if (value == true) { setState(() { - _selectedOption = label; // Select only one option + _selectedOption = label; }); if (onTap != null) { - onTap(label); // Call the passed onTap callback + onTap(label); } } }, 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 639547c..851aabf 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -14,7 +14,9 @@ import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.d 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'; @@ -172,6 +174,19 @@ void showDeviceInterface(DeviceModel device, BuildContext 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_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..098eb7c --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/three_touch_screen.dart @@ -0,0 +1,474 @@ +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/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)); + }, + // control: DeviceControlModel( + // deviceId: device.uuid, + // // code: 'switch_1', + // code: device.status[0].code, + // // value: true, + // value: device.status[0].value, + // ), + ), + 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)); + }, + // control: DeviceControlModel( + // // deviceId: 'bfe10693d4fd263206ocq9', + // // code: 'switch_2', + // // value: true, + // deviceId: device.uuid, + // code: device.status[1].code, + // value: device.status[1].value, + // ), + ), + 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()); + }, + ), + // Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Card( + // elevation: 3, + // shape: RoundedRectangleBorder( + // borderRadius: + // BorderRadius.circular(100), + // ), + // child: GestureDetector( + // onTap: () { + // // DevicesCubit.getInstance().deviceControl( + // // DeviceControlModel( + // // deviceId: device.uuid, + // // code: 'switch_1', + // // value: true, + // // ), + // // device.uuid!, + // // ); + // // DevicesCubit.getInstance().deviceControl( + // // DeviceControlModel( + // // deviceId: device.uuid, + // // code: 'switch_2', + // // value: true, + // // ), + // // device.uuid!, + // // ); + // // DevicesCubit.getInstance().deviceControl( + // // DeviceControlModel( + // // deviceId: device.uuid, + // // code: 'switch_3', + // // value: true, + // // ), + // // device.uuid!, + // // ); + // 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) => + ScheduleScreen( + 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) => + // ScheduleScreen( + // 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.u_turn_left, + label: "All Off", + onTap: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, + animation2) => + ScheduleScreen( + device: device!, + ))); + }, + ), + // Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Card( + // elevation: 3, + // shape: RoundedRectangleBorder( + // borderRadius: + // BorderRadius.circular(100), + // ), + // child: GestureDetector( + // onTap: () { + // // DevicesCubit.getInstance().deviceControl( + // // DeviceControlModel( + // // deviceId: device.uuid, + // // code: 'switch_1', + // // value: false, + // // ), + // // device.uuid!, + // // ); + // // DevicesCubit.getInstance().deviceControl( + // // DeviceControlModel( + // // deviceId: device.uuid, + // // code: 'switch_2', + // // value: false, + // // ), + // // device.uuid!, + // // ); + // // DevicesCubit.getInstance().deviceControl( + // // DeviceControlModel( + // // deviceId: device.uuid, + // // code: 'switch_3', + // // value: false, + // // ), + // // device.uuid!, + // // ); + // BlocProvider.of( + // context) + // .add(AllOffEvent()); + // }, + // 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: "Off", + // style: context.bodyMedium + // .copyWith( + // color: ColorsManager + // .primaryColorWithOpacity, + // fontWeight: + // FontWeight + // .bold), + // ), + // ), + // ), + // ], + // ), + // ), + // ), + // const SizedBox(height: 10), + // BodySmall( + // text: "All Off", + // style: context.bodyMedium.copyWith( + // color: ColorsManager.textPrimaryColor, + // ), + // ), + // ], + // ), + ], + ), + ), + Expanded(child: SizedBox()) + ], + ), + ), + ], + ), + ); + }, + ), + ); + } +} 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..a100faf --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/three_touch_wizard.dart @@ -0,0 +1,45 @@ +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( + 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..d264d66 --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/timer_screen.dart @@ -0,0 +1,321 @@ +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/three_touch/two_touch_setting.dart b/lib/features/devices/view/widgets/three_touch/two_touch_setting.dart new file mode 100644 index 0000000..ce05eca --- /dev/null +++ b/lib/features/devices/view/widgets/three_touch/two_touch_setting.dart @@ -0,0 +1,382 @@ +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(const InitialEvent(groupScreen: false)), + child: BlocBuilder( + builder: (context, state) { + final twoTouchBloc = 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 UpdateState) { + deviceStatus = state.threeTouchModel; + } + 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: + deviceStatus!.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: 1), + // () 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: 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 { + twoTouchBloc.optionSelected = + 'light_mode'; + final result = await showDialog( + context: context, + builder: (context) { + return RestartStatusDialog( + initialSelectedLabel: + deviceStatus.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) { + // 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: 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: deviceStatus + .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) { + // 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: deviceStatus + .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) { + // 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_1.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + ], + ), + ), + ), + ], + ); + }, + ), + )); + } +} + 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..f6c813a --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_touch_setting.dart @@ -0,0 +1,613 @@ +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/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: 1), + // () 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: + 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) { + // 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: 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) { + // 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) { + print('selected===$selected'); + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel2: (selected) { + print('selected===$selected'); + + twoTouchBloc.statusSelected = + selected; + }, + onTapLabel3: (selected) { + print('selected===$selected'); + + twoTouchBloc.statusSelected = + selected; + }, + ); + }, + ); + if (result == true) { + // 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_1.value, + fontColor: + ColorsManager.textGray, + ), + const Icon( + Icons.keyboard_arrow_right, + color: ColorsManager.textGray, + ), + ], + ) + ], + ), + )), + ], + ), + ), + ), + ], + ); + }, + ), + )); + } +} + +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/two_touch/two_touch_wizard.dart b/lib/features/devices/view/widgets/two_touch/two_touch_wizard.dart new file mode 100644 index 0000000..287dfa0 --- /dev/null +++ b/lib/features/devices/view/widgets/two_touch/two_touch_wizard.dart @@ -0,0 +1,47 @@ +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( + 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/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index cac66a9..7b7114d 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -51,6 +51,9 @@ enum DeviceType { WH, DS, OneTouch, + TowTouch, + ThreeTouch, + Other, } @@ -78,6 +81,8 @@ Map devicesTypesMap = { "WH": DeviceType.WH, "DS": DeviceType.DS, "1GT": DeviceType.OneTouch, + "2GT": DeviceType.TowTouch, + "3GT": DeviceType.ThreeTouch, }; Map> devicesFunctionsMap = { DeviceType.AC: [ @@ -321,6 +326,110 @@ Map> devicesFunctionsMap = { "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'] + })), + ], }; enum TempModes { hot, cold, wind } @@ -662,9 +771,9 @@ extension lightStatusExtension on lightStatus { switch (this) { case lightStatus.off: return "Off"; - case lightStatus.on_off: - return "On/Off Status"; case lightStatus.switchPosition: + return "On/Off Status"; + case lightStatus.on_off: return "Restart Memory"; } } @@ -674,9 +783,9 @@ extension lightStatusExtension on lightStatus { case "none": return lightStatus.off; case "relay": - return lightStatus.on_off; - case "pos": return lightStatus.switchPosition; + case "pos": + return lightStatus.on_off; default: throw ArgumentError("Invalid access type: $value"); }