From b5842194ffc451ac885a1fe33c9f211ae7986c92 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 12 Sep 2024 16:59:03 +0300 Subject: [PATCH 1/9] Implement Two-Gang & One-Gang --- assets/icons/1gang.svg | 5 + .../bloc/one_gang_bloc/one_gang_bloc.dart | 179 ++++++++ .../bloc/one_gang_bloc/one_gang_event.dart | 88 ++++ .../bloc/one_gang_bloc/one_gang_state.dart | 79 ++++ .../bloc/three_gang_bloc/three_gang_bloc.dart | 1 - .../bloc/two_gang_bloc/two_gang_bloc.dart | 419 ++++++++++++++++++ .../bloc/two_gang_bloc/two_gang_event.dart | 88 ++++ .../bloc/two_gang_bloc/two_gang_state.dart | 78 ++++ .../devices/model/groupTwoGangModel.dart | 13 + .../devices/model/one_gang_model.dart | 30 ++ .../devices/model/two_gang_model.dart | 40 ++ .../widgets/one_gang/one_gang_Interface.dart | 68 +++ .../widgets/one_gang/one_gang_screen.dart | 168 +++++++ .../one_gang/one_gang_timer_screen.dart | 184 ++++++++ .../view/widgets/room_page_switch.dart | 17 +- .../view/widgets/three_gang/gang_switch.dart | 4 +- .../widgets/two_gang/two_gang_Interface.dart | 67 +++ .../view/widgets/two_gang/two_gang_list.dart | 81 ++++ .../widgets/two_gang/two_gang_screen.dart | 340 ++++++++++++++ .../widgets/two_gang/two_schedule_screen.dart | 139 ++++++ .../widgets/two_gang/two_timer_screen.dart | 187 ++++++++ lib/generated/assets.dart | 6 + lib/utils/resource_manager/constants.dart | 31 ++ 23 files changed, 2308 insertions(+), 4 deletions(-) create mode 100644 assets/icons/1gang.svg create mode 100644 lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart create mode 100644 lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart create mode 100644 lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart create mode 100644 lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart create mode 100644 lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart create mode 100644 lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart create mode 100644 lib/features/devices/model/groupTwoGangModel.dart create mode 100644 lib/features/devices/model/one_gang_model.dart create mode 100644 lib/features/devices/model/two_gang_model.dart create mode 100644 lib/features/devices/view/widgets/one_gang/one_gang_Interface.dart create mode 100644 lib/features/devices/view/widgets/one_gang/one_gang_screen.dart create mode 100644 lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart create mode 100644 lib/features/devices/view/widgets/two_gang/two_gang_Interface.dart create mode 100644 lib/features/devices/view/widgets/two_gang/two_gang_list.dart create mode 100644 lib/features/devices/view/widgets/two_gang/two_gang_screen.dart create mode 100644 lib/features/devices/view/widgets/two_gang/two_schedule_screen.dart create mode 100644 lib/features/devices/view/widgets/two_gang/two_timer_screen.dart diff --git a/assets/icons/1gang.svg b/assets/icons/1gang.svg new file mode 100644 index 0000000..23dee22 --- /dev/null +++ b/assets/icons/1gang.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart new file mode 100644 index 0000000..b92717d --- /dev/null +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart @@ -0,0 +1,179 @@ +import 'dart:async'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_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/one_gang_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'one_gang_event.dart'; + +class OneGangBloc extends Bloc { + final String oneGangId; + OneGangModel deviceStatus = OneGangModel( + firstSwitch: false, + firstCountDown: 0, + ); + Timer? _timer; + + bool oneGangGroup = false; + List devicesList = []; + bool allSwitchesOn = true; + + OneGangBloc({required this.oneGangId}) : super(InitialState()) { + on(_fetchTwoGangStatus); + on(_oneGangUpdated); + on(_changeFirstSwitch); + on(_changeSliding); + on(_setCounterValue); + on(_getCounterValue); + on(_onTickTimer); + on(_onClose); + } + + void _fetchTwoGangStatus(InitialEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + var response = await DevicesAPI.getDeviceStatus(oneGangId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = OneGangModel.fromJson(statusModelList); + emit(UpdateState(oneGangModel: deviceStatus)); + _listenToChanges(); + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + _listenToChanges() { + try { + DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$oneGangId'); + Stream stream = ref.onValue; + + stream.listen((DatabaseEvent event) async { + if (_timer != null) { + await Future.delayed(const Duration(seconds: 2)); + } + Map usersMap = event.snapshot.value as Map; + List statusList = []; + + usersMap['status'].forEach((element) { + statusList.add(StatusModel(code: element['code'], value: element['value'])); + }); + + deviceStatus = OneGangModel.fromJson(statusList); + if (!isClosed) { + add(OneGangUpdated()); + } + }); + } catch (_) {} + } + + _oneGangUpdated(OneGangUpdated event, Emitter emit) { + emit(UpdateState(oneGangModel: deviceStatus)); + } + + void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(oneGangModel: deviceStatus)); + try { + deviceStatus.firstSwitch = !event.value; + emit(UpdateState(oneGangModel: deviceStatus)); + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: oneGangGroup ? event.deviceId : oneGangId, + code: 'switch_1', + value: !event.value), + oneGangGroup ? event.deviceId : oneGangId); + + if (!response['success']) { + add(InitialEvent(groupScreen: oneGangGroup)); + } + }); + } catch (_) { + add(InitialEvent(groupScreen: oneGangGroup)); + } + } + + + void _changeSliding(ChangeSlidingSegment event, Emitter emit) async { + emit(ChangeSlidingSegmentState(value: event.value)); + } + + void _setCounterValue(SetCounterValue event, Emitter emit) async { + emit(LoadingNewSate(oneGangModel: deviceStatus)); + int seconds = 0; + try { + seconds = event.duration.inSeconds; + final response = await DevicesAPI.controlDevice( + DeviceControlModel(deviceId: oneGangId, code: event.deviceCode, value: seconds), + oneGangId); + + if (response['success'] ?? false) { + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown = seconds; + } + } else { + emit(const FailedState(error: 'Something went wrong')); + return; + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + if (seconds > 0) { + _onStartTimer(seconds); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } + + void _getCounterValue(GetCounterEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + var response = await DevicesAPI.getDeviceStatus(oneGangId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = OneGangModel.fromJson(statusModelList); + + if (event.deviceCode == 'countdown_1') { + deviceStatus.firstCountDown > 0 + ? _onStartTimer(deviceStatus.firstCountDown) + : emit(UpdateTimerState(seconds: deviceStatus.firstCountDown)); + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + void _onClose(OnClose event, Emitter emit) { + _timer?.cancel(); + } + + void _onStartTimer(int seconds) { + _timer?.cancel(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + add(TickTimer(seconds - timer.tick)); + }); + } + + void _onTickTimer(TickTimer event, Emitter emit) { + if (event.remainingTime > 0) { + emit(TimerRunInProgress(event.remainingTime)); + } else { + _timer?.cancel(); + emit(TimerRunComplete()); + } + } +} diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart new file mode 100644 index 0000000..e245b39 --- /dev/null +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart @@ -0,0 +1,88 @@ +import 'package:equatable/equatable.dart'; + +abstract class OneGangEvent extends Equatable { + const OneGangEvent(); + + @override + List get props => []; +} + +class LoadingEvent extends OneGangEvent {} + +class OneGangUpdated extends OneGangEvent {} + +class InitialEvent extends OneGangEvent { + final bool groupScreen; + const InitialEvent({required this.groupScreen}); + @override + List get props => [groupScreen]; +} + +class ChangeFirstSwitchStatusEvent extends OneGangEvent { + final bool value; + final String deviceId; + const ChangeFirstSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeSecondSwitchStatusEvent extends OneGangEvent { + final bool value; + final String deviceId; + const ChangeSecondSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + + +class AllOffEvent extends OneGangEvent {} + +class AllOnEvent extends OneGangEvent {} + +class GroupAllOnEvent extends OneGangEvent {} + +class GroupAllOffEvent extends OneGangEvent {} + +class ChangeSlidingSegment extends OneGangEvent { + final int value; + const ChangeSlidingSegment({required this.value}); + @override + List get props => [value]; +} + +class GetCounterEvent extends OneGangEvent { + final String deviceCode; + const GetCounterEvent({required this.deviceCode}); + @override + List get props => [deviceCode]; +} + +class SetCounterValue extends OneGangEvent { + final Duration duration; + final String deviceCode; + const SetCounterValue({required this.duration, required this.deviceCode}); + @override + List get props => [duration, deviceCode]; +} + +class StartTimer extends OneGangEvent { + final int duration; + + const StartTimer(this.duration); + + @override + List get props => [duration]; +} + +class TickTimer extends OneGangEvent { + final int remainingTime; + + const TickTimer(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class StopTimer extends OneGangEvent {} + +class OnClose extends OneGangEvent {} diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart new file mode 100644 index 0000000..54ab9e7 --- /dev/null +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart @@ -0,0 +1,79 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart'; +import 'package:syncrow_app/features/devices/model/one_gang_model.dart'; +import 'package:syncrow_app/features/devices/model/two_gang_model.dart'; + + +class OneGangState extends Equatable { + const OneGangState(); + + @override + List get props => []; +} + +class InitialState extends OneGangState {} + +class LoadingInitialState extends OneGangState {} + +class UpdateState extends OneGangState { + final OneGangModel oneGangModel; + const UpdateState({required this.oneGangModel}); + + @override + List get props => [TwoGangModel]; +} + +class LoadingNewSate extends OneGangState { + final OneGangModel oneGangModel; + const LoadingNewSate({required this.oneGangModel}); + + @override + List get props => [OneGangModel]; +} + +class UpdateGroupState extends OneGangState { + final List twoGangList; + final bool allSwitches; + + const UpdateGroupState({required this.twoGangList, required this.allSwitches}); + + @override + List get props => [twoGangList, allSwitches]; +} + +class FailedState extends OneGangState { + final String error; + + const FailedState({required this.error}); + + @override + List get props => [error]; +} + +class ChangeSlidingSegmentState extends OneGangState { + final int value; + + const ChangeSlidingSegmentState({required this.value}); + + @override + List get props => [value]; +} + +class UpdateTimerState extends OneGangState { + final int seconds; + const UpdateTimerState({required this.seconds}); + + @override + List get props => [seconds]; +} + +class TimerRunInProgress extends OneGangState { + final int remainingTime; + + const TimerRunInProgress(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class TimerRunComplete extends OneGangState {} diff --git a/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart b/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart index 53bb034..c796892 100644 --- a/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart +++ b/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart @@ -190,7 +190,6 @@ class ThreeGangBloc extends Bloc { if (_timer != null) { _timer!.cancel(); } - _timer = Timer(const Duration(milliseconds: 500), () async { final response = await DevicesAPI.controlDevice( DeviceControlModel( diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart new file mode 100644 index 0000000..8e32ef7 --- /dev/null +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart @@ -0,0 +1,419 @@ +import 'dart:async'; +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_gang_bloc/two_gang_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_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/groupTwoGangModel.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/two_gang_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; + +class TwoGangBloc extends Bloc { + final String twoGangId; + TwoGangModel deviceStatus = TwoGangModel( + firstSwitch: false, + secondSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + ); + Timer? _timer; + // Timer? _firstSwitchTimer; + // Timer? _secondSwitchTimer; + // Timer? _thirdSwitchTimer; + + bool twoGangGroup = false; + List devicesList = []; + List groupTwoGangList = []; + bool allSwitchesOn = true; + + TwoGangBloc({required this.twoGangId}) : super(InitialState()) { + on(_fetchTwoGangStatus); + on(_twoGangUpdated); + on(_changeFirstSwitch); + on(_changeSecondSwitch); + on(_allOff); + on(_allOn); + on(_changeSliding); + on(_setCounterValue); + on(_getCounterValue); + on(_onTickTimer); + on(_onClose); + on(_groupAllOn); + on(_groupAllOff); + } + + void _fetchTwoGangStatus(InitialEvent event, Emitter emit) async { + emit(LoadingInitialState()); + try { + twoGangGroup = event.groupScreen; + if (twoGangGroup) { + devicesList = []; + groupTwoGangList = []; + allSwitchesOn = true; + devicesList = await DevicesAPI.getDeviceByGroupName( + HomeCubit.getInstance().selectedSpace?.id ?? '', '2G'); + + 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 = TwoGangModel.fromJson(statusModelList); + + groupTwoGangList.add(GroupTwoGangModel( + deviceId: devicesList[i].uuid ?? '', + deviceName: devicesList[i].name ?? '', + firstSwitch: deviceStatus.firstSwitch, + secondSwitch: deviceStatus.secondSwitch, + )); + } + + if (groupTwoGangList.isNotEmpty) { + groupTwoGangList.firstWhere((element) { + if (!element.firstSwitch || !element.secondSwitch ) { + allSwitchesOn = false; + } + return true; + }); + } + emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesOn)); + } else { + var response = await DevicesAPI.getDeviceStatus(twoGangId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = TwoGangModel.fromJson(statusModelList); + emit(UpdateState(twoGangModel: deviceStatus)); + _listenToChanges(); + } + } catch (e) { + emit(FailedState(error: e.toString())); + return; + } + } + + _listenToChanges() { + try { + DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$twoGangId'); + 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 = TwoGangModel.fromJson(statusList); + if (!isClosed) { + add(TwoGangUpdated()); + } + }); + } catch (_) {} + } + + _twoGangUpdated(TwoGangUpdated event, Emitter emit) { + emit(UpdateState(twoGangModel: deviceStatus)); + } + + void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(twoGangModel: deviceStatus)); + try { + if (twoGangGroup) { + bool allSwitchesValue = true; + groupTwoGangList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.firstSwitch = !event.value; + } + if (!element.firstSwitch || !element.secondSwitch ) { + allSwitchesValue = false; + } + }); + emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesValue)); + } else { + deviceStatus.firstSwitch = !event.value; + emit(UpdateState(twoGangModel: deviceStatus)); + } + + if (_timer != null) { + _timer!.cancel(); + } + + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoGangGroup ? event.deviceId : twoGangId, + code: 'switch_1', + value: !event.value), + twoGangGroup ? event.deviceId : twoGangId); + + if (!response['success']) { + add(InitialEvent(groupScreen: twoGangGroup)); + } + }); + } catch (_) { + add(InitialEvent(groupScreen: twoGangGroup)); + } + } + + void _changeSecondSwitch( + ChangeSecondSwitchStatusEvent event, Emitter emit) async { + emit(LoadingNewSate(twoGangModel: deviceStatus)); + try { + if (twoGangGroup) { + bool allSwitchesValue = true; + groupTwoGangList.forEach((element) { + if (element.deviceId == event.deviceId) { + element.secondSwitch = !event.value; + } + if (!element.firstSwitch || !element.secondSwitch ) { + allSwitchesValue = false; + } + }); + emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesValue)); + } else { + deviceStatus.secondSwitch = !event.value; + emit(UpdateState(twoGangModel: deviceStatus)); + } + + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(const Duration(milliseconds: 500), () async { + final response = await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoGangGroup ? event.deviceId : twoGangId, + code: 'switch_2', + value: !event.value), + twoGangGroup ? event.deviceId : twoGangId); + + if (!response['success']) { + add(InitialEvent(groupScreen: twoGangGroup)); + } + }); + } catch (_) { + add(InitialEvent(groupScreen: twoGangGroup)); + } + } + + + void _allOff(AllOffEvent event, Emitter emit) async { + emit(LoadingNewSate(twoGangModel: deviceStatus)); + + try { + deviceStatus.firstSwitch = false; + deviceStatus.secondSwitch = false; + emit(UpdateState(twoGangModel: deviceStatus)); + + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoGangId, code: 'switch_1', value: deviceStatus.firstSwitch), + twoGangId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoGangId, code: 'switch_2', value: deviceStatus.secondSwitch), + twoGangId), + + ]); + + 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(twoGangModel: deviceStatus)); + + try { + deviceStatus.firstSwitch = true; + deviceStatus.secondSwitch = true; + emit(UpdateState(twoGangModel: deviceStatus)); + + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoGangId, code: 'switch_1', value: deviceStatus.firstSwitch), + twoGangId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: twoGangId, code: 'switch_2', value: deviceStatus.secondSwitch), + twoGangId), + + ]); + + 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(twoGangModel: deviceStatus)); + try { + for (int i = 0; i < groupTwoGangList.length; i++) { + groupTwoGangList[i].firstSwitch = true; + groupTwoGangList[i].secondSwitch = true; + + } + emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: true)); + + for (int i = 0; i < groupTwoGangList.length; i++) { + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: true), + groupTwoGangList[i].deviceId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: true), + groupTwoGangList[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(twoGangModel: deviceStatus)); + try { + for (int i = 0; i < groupTwoGangList.length; i++) { + groupTwoGangList[i].firstSwitch = false; + groupTwoGangList[i].secondSwitch = false; + } + emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: false)); + + for (int i = 0; i < groupTwoGangList.length; i++) { + final response = await Future.wait([ + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: false), + groupTwoGangList[i].deviceId), + DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: false), + groupTwoGangList[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(twoGangModel: deviceStatus)); + int seconds = 0; + try { + seconds = event.duration.inSeconds; + final response = await DevicesAPI.controlDevice( + DeviceControlModel(deviceId: twoGangId, code: event.deviceCode, value: seconds), + twoGangId); + + 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 { + var response = await DevicesAPI.getDeviceStatus(twoGangId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = TwoGangModel.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(); + // _firstSwitchTimer?.cancel(); + // _secondSwitchTimer?.cancel(); + // _thirdSwitchTimer?.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()); + } + } +} diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart new file mode 100644 index 0000000..6be514c --- /dev/null +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart @@ -0,0 +1,88 @@ +import 'package:equatable/equatable.dart'; + +abstract class TwoGangEvent extends Equatable { + const TwoGangEvent(); + + @override + List get props => []; +} + +class LoadingEvent extends TwoGangEvent {} + +class TwoGangUpdated extends TwoGangEvent {} + +class InitialEvent extends TwoGangEvent { + final bool groupScreen; + const InitialEvent({required this.groupScreen}); + @override + List get props => [groupScreen]; +} + +class ChangeFirstSwitchStatusEvent extends TwoGangEvent { + final bool value; + final String deviceId; + const ChangeFirstSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + +class ChangeSecondSwitchStatusEvent extends TwoGangEvent { + final bool value; + final String deviceId; + const ChangeSecondSwitchStatusEvent({required this.value, this.deviceId = ''}); + @override + List get props => [value, deviceId]; +} + + +class AllOffEvent extends TwoGangEvent {} + +class AllOnEvent extends TwoGangEvent {} + +class GroupAllOnEvent extends TwoGangEvent {} + +class GroupAllOffEvent extends TwoGangEvent {} + +class ChangeSlidingSegment extends TwoGangEvent { + final int value; + const ChangeSlidingSegment({required this.value}); + @override + List get props => [value]; +} + +class GetCounterEvent extends TwoGangEvent { + final String deviceCode; + const GetCounterEvent({required this.deviceCode}); + @override + List get props => [deviceCode]; +} + +class SetCounterValue extends TwoGangEvent { + final Duration duration; + final String deviceCode; + const SetCounterValue({required this.duration, required this.deviceCode}); + @override + List get props => [duration, deviceCode]; +} + +class StartTimer extends TwoGangEvent { + final int duration; + + const StartTimer(this.duration); + + @override + List get props => [duration]; +} + +class TickTimer extends TwoGangEvent { + final int remainingTime; + + const TickTimer(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class StopTimer extends TwoGangEvent {} + +class OnClose extends TwoGangEvent {} diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart new file mode 100644 index 0000000..c967a36 --- /dev/null +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart @@ -0,0 +1,78 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart'; +import 'package:syncrow_app/features/devices/model/two_gang_model.dart'; + + +class TwoGangState extends Equatable { + const TwoGangState(); + + @override + List get props => []; +} + +class InitialState extends TwoGangState {} + +class LoadingInitialState extends TwoGangState {} + +class UpdateState extends TwoGangState { + final TwoGangModel twoGangModel; + const UpdateState({required this.twoGangModel}); + + @override + List get props => [TwoGangModel]; +} + +class LoadingNewSate extends TwoGangState { + final TwoGangModel twoGangModel; + const LoadingNewSate({required this.twoGangModel}); + + @override + List get props => [TwoGangModel]; +} + +class UpdateGroupState extends TwoGangState { + final List twoGangList; + final bool allSwitches; + + const UpdateGroupState({required this.twoGangList, required this.allSwitches}); + + @override + List get props => [twoGangList, allSwitches]; +} + +class FailedState extends TwoGangState { + final String error; + + const FailedState({required this.error}); + + @override + List get props => [error]; +} + +class ChangeSlidingSegmentState extends TwoGangState { + final int value; + + const ChangeSlidingSegmentState({required this.value}); + + @override + List get props => [value]; +} + +class UpdateTimerState extends TwoGangState { + final int seconds; + const UpdateTimerState({required this.seconds}); + + @override + List get props => [seconds]; +} + +class TimerRunInProgress extends TwoGangState { + final int remainingTime; + + const TimerRunInProgress(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class TimerRunComplete extends TwoGangState {} diff --git a/lib/features/devices/model/groupTwoGangModel.dart b/lib/features/devices/model/groupTwoGangModel.dart new file mode 100644 index 0000000..f385b4e --- /dev/null +++ b/lib/features/devices/model/groupTwoGangModel.dart @@ -0,0 +1,13 @@ +class GroupTwoGangModel { + final String deviceId; + final String deviceName; + bool firstSwitch; + bool secondSwitch; + + GroupTwoGangModel({ + required this.deviceId, + required this.deviceName, + required this.firstSwitch, + required this.secondSwitch, + }); +} diff --git a/lib/features/devices/model/one_gang_model.dart b/lib/features/devices/model/one_gang_model.dart new file mode 100644 index 0000000..4e73231 --- /dev/null +++ b/lib/features/devices/model/one_gang_model.dart @@ -0,0 +1,30 @@ + +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class OneGangModel { + bool firstSwitch; + int firstCountDown; + + OneGangModel( + {required this.firstSwitch, + required this.firstCountDown, + }); + + factory OneGangModel.fromJson(List jsonList) { + late bool _switch; + late int _count; + + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'switch_1') { + _switch = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'countdown_1') { + _count = jsonList[i].value ?? 0; + } + } + return OneGangModel( + firstSwitch: _switch, + firstCountDown: _count, + ); + } +} diff --git a/lib/features/devices/model/two_gang_model.dart b/lib/features/devices/model/two_gang_model.dart new file mode 100644 index 0000000..00f88d1 --- /dev/null +++ b/lib/features/devices/model/two_gang_model.dart @@ -0,0 +1,40 @@ +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class TwoGangModel { + bool firstSwitch; + bool secondSwitch; + int firstCountDown; + int secondCountDown; + + TwoGangModel( + {required this.firstSwitch, + required this.secondSwitch, + required this.firstCountDown, + required this.secondCountDown, + }); + + factory TwoGangModel.fromJson(List jsonList) { + late bool _firstSwitch; + late bool _secondSwitch; + late int _firstCount; + late int _secondCount; + + 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; + } + } + return TwoGangModel( + firstSwitch: _firstSwitch, + secondSwitch: _secondSwitch, + firstCountDown: _firstCount, + secondCountDown: _secondCount, + ); + } +} diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_Interface.dart b/lib/features/devices/view/widgets/one_gang/one_gang_Interface.dart new file mode 100644 index 0000000..636a730 --- /dev/null +++ b/lib/features/devices/view/widgets/one_gang/one_gang_Interface.dart @@ -0,0 +1,68 @@ +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/one_gang/one_gang_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_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 OneGangInterface extends StatelessWidget { + const OneGangInterface({super.key, this.gangSwitch}); + + final DeviceModel? gangSwitch; + @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: gangSwitch != null + ? DeviceAppbar( + deviceName: gangSwitch!.name!, + deviceUuid: gangSwitch!.uuid!, + ) + : AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: BodyLarge( + text: gangSwitch?.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: OneGangScreen(device: gangSwitch), + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_screen.dart b/lib/features/devices/view/widgets/one_gang/one_gang_screen.dart new file mode 100644 index 0000000..566946c --- /dev/null +++ b/lib/features/devices/view/widgets/one_gang/one_gang_screen.dart @@ -0,0 +1,168 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/one_gang_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_gang/gang_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import '../../../bloc/one_gang_bloc/one_gang_event.dart'; + +class OneGangScreen extends StatelessWidget { + const OneGangScreen({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => OneGangBloc(oneGangId : device?.uuid ?? '') + ..add(const InitialEvent(groupScreen:false)), + child: BlocBuilder( + builder: (context, state) { + OneGangModel oneGangModel = OneGangModel( + firstSwitch: false, + firstCountDown: 0, + ); + + if (state is LoadingNewSate) { + oneGangModel = state.oneGangModel; + } else if (state is UpdateState) { + oneGangModel = state.oneGangModel; + } + 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: oneGangModel.firstSwitch, + action: () { + BlocProvider.of(context).add( + ChangeFirstSwitchStatusEvent( + value: oneGangModel.firstSwitch)); + }, + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text:" Entrance Light", + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + ], + ), + ), + Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + width: 20, + ), + 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) => OneGangTimerScreen( + device: device, + deviceCode: 'countdown_1', + ))); + }, + 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, + ), + ), + ], + ), + + ], + ), + ), + Expanded(child: SizedBox()) + ], + ), + ), + ], + ), + ); + }, + ), + ); + + } +} diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart new file mode 100644 index 0000000..51a5756 --- /dev/null +++ b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart @@ -0,0 +1,184 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_event.dart'; +import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.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 OneGangTimerScreen extends StatelessWidget { + final DeviceModel? device; + final String? deviceCode; + const OneGangTimerScreen({super.key,this.device,this.deviceCode}); + + @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: BlocProvider( + create: (context) => OneGangBloc(oneGangId: device!.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode!)), + child: BlocBuilder( + builder: (context, state) { + Duration duration = Duration.zero; + int countNum = 0; + int tabNum = 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; + } + + if (state is ChangeSlidingSegmentState) { + tabNum = state.value; + } + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + BlocProvider.of(context).add(OnClose()); + Navigator.pop(context); + } + }, + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + padding: const EdgeInsets.all(24), + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: state is LoadingInitialState || state is LoadingNewSate + ? const Center( + child: DefaultContainer( + width: 50, height: 50, child: CircularProgressIndicator()), + ) + : Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.sizeOf(context).width, + decoration: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + ), + child: CupertinoSlidingSegmentedControl( + thumbColor: ColorsManager.slidingBlueColor, + onValueChanged: (value) { + BlocProvider.of(context) + .add(ChangeSlidingSegment(value: value ?? 0)); + }, + groupValue: + state is ChangeSlidingSegmentState ? state.value : 0, + backgroundColor: Colors.white, + children: { + 0: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + 1: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + }, + ), + ), + if (tabNum == 0) + countNum > 0 + ? BodyLarge( + text: _formatDuration(countNum), + fontColor: ColorsManager.slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: CupertinoTimerPickerMode.hm, + onTimerDurationChanged: (Duration newDuration) { + duration = newDuration; + }, + ), + if (tabNum == 0) + GestureDetector( + onTap: () { + if (state is LoadingNewSate) { + return; + } + if (countNum > 0) { + BlocProvider.of(context).add( + SetCounterValue( + deviceCode: deviceCode!, + duration: Duration.zero)); + } else if (duration != Duration.zero) { + BlocProvider.of(context).add( + SetCounterValue( + deviceCode: deviceCode!, duration: duration)); + } + }, + child: SvgPicture.asset( + countNum > 0 ? Assets.pauseIcon : Assets.playIcon)) + ], + ))); + }, + ), + ), + ), + ), + ); + } + 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/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index 980de66..cfb5445 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -11,6 +11,10 @@ import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_screen.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_screen.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'; @@ -126,13 +130,24 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { break; case DeviceType.LightBulb: navigateToInterface(LightInterface(light: device), context); + case DeviceType.OneGang: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + OneGangInterface(gangSwitch: device))); + case DeviceType.TwoGang: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + TwoGangInterface(gangSwitch: device))); case DeviceType.ThreeGang: Navigator.push( context, PageRouteBuilder( pageBuilder: (context, animation1, animation2) => ThreeGangInterface(gangSwitch: device))); - // navigateToInterface(ThreeGangInterface(gangSwitch: device), context); break; default: } diff --git a/lib/features/devices/view/widgets/three_gang/gang_switch.dart b/lib/features/devices/view/widgets/three_gang/gang_switch.dart index c0bd8f9..8b5dc55 100644 --- a/lib/features/devices/view/widgets/three_gang/gang_switch.dart +++ b/lib/features/devices/view/widgets/three_gang/gang_switch.dart @@ -1,8 +1,8 @@ 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/three_gang_bloc.dart'; -// import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_state.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'; diff --git a/lib/features/devices/view/widgets/two_gang/two_gang_Interface.dart b/lib/features/devices/view/widgets/two_gang/two_gang_Interface.dart new file mode 100644 index 0000000..029934f --- /dev/null +++ b/lib/features/devices/view/widgets/two_gang/two_gang_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_gang/two_gang_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 TwoGangInterface extends StatelessWidget { + const TwoGangInterface({super.key, this.gangSwitch}); + + final DeviceModel? gangSwitch; + @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: gangSwitch != null + ? DeviceAppbar( + deviceName: gangSwitch!.name!, + deviceUuid: gangSwitch!.uuid!, + ) + : AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: BodyLarge( + text: gangSwitch?.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: TwoGangScreen(device: gangSwitch), + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/two_gang/two_gang_list.dart b/lib/features/devices/view/widgets/two_gang/two_gang_list.dart new file mode 100644 index 0000000..065dd62 --- /dev/null +++ b/lib/features/devices/view/widgets/two_gang/two_gang_list.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_event.dart'; +import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_state.dart'; +import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart'; +import 'package:syncrow_app/features/devices/model/group_three_gang_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 TwoGangList extends StatelessWidget { + const TwoGangList({super.key, required this.twoGangList, required this.allSwitches}); + + final List twoGangList; + 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: twoGangList.length, + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + BodySmall(text: '${twoGangList[index].deviceName} beside light'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: twoGangList[index].firstSwitch, + action: () { + BlocProvider.of(context).add(ChangeFirstSwitchStatusEvent( + value: twoGangList[index].firstSwitch, + deviceId: twoGangList[index].deviceId)); + }, + ), + const SizedBox(height: 10), + BodySmall(text: '${twoGangList[index].deviceName} ceiling light'), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: twoGangList[index].secondSwitch, + action: () { + BlocProvider.of(context).add(ChangeSecondSwitchStatusEvent( + value: twoGangList[index].secondSwitch, + deviceId: twoGangList[index].deviceId)); + }, + ), + const SizedBox(height: 10), + BodySmall(text: '${twoGangList[index].deviceName} spotlight'), + const SizedBox(height: 5), + ], + ); + }, + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/devices/view/widgets/two_gang/two_gang_screen.dart b/lib/features/devices/view/widgets/two_gang/two_gang_screen.dart new file mode 100644 index 0000000..4bb158d --- /dev/null +++ b/lib/features/devices/view/widgets/two_gang/two_gang_screen.dart @@ -0,0 +1,340 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart'; +import 'package:syncrow_app/features/devices/model/two_gang_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/three_gang/gang_switch.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_list.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_gang/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/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class TwoGangScreen extends StatelessWidget { + const TwoGangScreen({super.key, this.device}); + + final DeviceModel? device; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => TwoGangBloc(twoGangId: device?.uuid ?? '') + ..add(InitialEvent(groupScreen: device != null ? false : true)), + child: BlocBuilder( + builder: (context, state) { + TwoGangModel twoGangModel = TwoGangModel( + firstSwitch: false, + secondSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + ); + + List groupTwoGangModel = []; + bool allSwitchesOn = false; + + if (state is LoadingNewSate) { + twoGangModel = state.twoGangModel; + } else if (state is UpdateState) { + twoGangModel = state.twoGangModel; + } else if (state is UpdateGroupState) { + groupTwoGangModel = state.twoGangList; + allSwitchesOn = state.allSwitches; + } + return state is LoadingInitialState + ? const Center( + child: + DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), + ) + : device == null + ? TwoGangList( + twoGangList: groupTwoGangModel, + allSwitches: allSwitchesOn, + ) + : 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: twoGangModel.firstSwitch, + action: () { + BlocProvider.of(context).add( + ChangeFirstSwitchStatusEvent( + value: twoGangModel.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: twoGangModel.secondSwitch, + action: () { + BlocProvider.of(context).add( + ChangeSecondSwitchStatusEvent( + value: twoGangModel.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: [ + 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, + ), + 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) => + TwoGangScheduleScreen( + 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, + ), + 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/two_gang/two_schedule_screen.dart b/lib/features/devices/view/widgets/two_gang/two_schedule_screen.dart new file mode 100644 index 0000000..f4df531 --- /dev/null +++ b/lib/features/devices/view/widgets/two_gang/two_schedule_screen.dart @@ -0,0 +1,139 @@ +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_gang/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 TwoGangScheduleScreen extends StatelessWidget { + final DeviceModel device; + const TwoGangScheduleScreen({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) => TwoTimerScreen( + 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) => TwoTimerScreen( + 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, + ) + ], + ), + ), + ), + + ], + )), + ], + ), + ), + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart new file mode 100644 index 0000000..720e80e --- /dev/null +++ b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart @@ -0,0 +1,187 @@ +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_gang_bloc/two_gang_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.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'; + +import '../../../bloc/two_gang_bloc/two_gang_event.dart'; + + +class TwoTimerScreen extends StatelessWidget { + final DeviceModel device; + final String deviceCode; + const TwoTimerScreen({required this.device, required this.deviceCode, 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: BlocProvider( + create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)), + child: BlocBuilder( + builder: (context, state) { + Duration duration = Duration.zero; + int countNum = 0; + int tabNum = 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; + } + + if (state is ChangeSlidingSegmentState) { + tabNum = state.value; + } + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + BlocProvider.of(context).add(OnClose()); + Navigator.pop(context); + } + }, + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + padding: const EdgeInsets.all(24), + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: state is LoadingInitialState || state is LoadingNewSate + ? const Center( + child: DefaultContainer( + width: 50, height: 50, child: CircularProgressIndicator()), + ) + : Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.sizeOf(context).width, + decoration: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + ), + child: CupertinoSlidingSegmentedControl( + thumbColor: ColorsManager.slidingBlueColor, + onValueChanged: (value) { + BlocProvider.of(context) + .add(ChangeSlidingSegment(value: value ?? 0)); + }, + groupValue: + state is ChangeSlidingSegmentState ? state.value : 0, + backgroundColor: Colors.white, + children: { + 0: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + 1: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + }, + ), + ), + if (tabNum == 0) + countNum > 0 + ? BodyLarge( + text: _formatDuration(countNum), + fontColor: ColorsManager.slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: CupertinoTimerPickerMode.hm, + onTimerDurationChanged: (Duration newDuration) { + duration = newDuration; + }, + ), + if (tabNum == 0) + GestureDetector( + onTap: () { + if (state is LoadingNewSate) { + return; + } + if (countNum > 0) { + BlocProvider.of(context).add( + SetCounterValue( + deviceCode: deviceCode, + duration: Duration.zero)); + } else if (duration != Duration.zero) { + BlocProvider.of(context).add( + SetCounterValue( + deviceCode: deviceCode, duration: duration)); + } + }, + child: SvgPicture.asset( + countNum > 0 ? Assets.pauseIcon : Assets.playIcon)) + ], + ))); + }, + ), + ), + ), + ), + ); + } + + 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/generated/assets.dart b/lib/generated/assets.dart index caae2da..e592f1e 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1029,4 +1029,10 @@ class Assets { static const String assetsPresenceState = "assets/icons/functions_icons/automation_functions/presence_state.svg"; + + + //assets/icons/functions_icons/automation_functions/presence_state.svg + + static const String oneGang = + "assets/icons/1gang.svg"; } diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 774f726..a417454 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -43,6 +43,8 @@ enum DeviceType { DoorLock, Curtain, Blind, + OneGang, + TwoGang, ThreeGang, Gateway, CeilingSensor, @@ -68,6 +70,8 @@ Map devicesTypesMap = { "DL": DeviceType.DoorLock, "WPS": DeviceType.WallSensor, "3G": DeviceType.ThreeGang, + "2G": DeviceType.TwoGang, + "1G": DeviceType.OneGang, "CUR": DeviceType.Curtain, }; Map> devicesFunctionsMap = { @@ -155,6 +159,33 @@ Map> devicesFunctionsMap = { FunctionModel( code: 'indicator', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), ], + DeviceType.OneGang:[ + FunctionModel( + code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + FunctionModel( + code: 'countdown_1', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + ], + DeviceType.TwoGang:[ + 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}) + ), + ], + DeviceType.ThreeGang: [ FunctionModel( code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), From 46662b5bac34f29eef6bce101ce7254a3a5d22d0 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 15 Sep 2024 10:15:37 +0300 Subject: [PATCH 2/9] Implement Two-Gang & One-Gang --- assets/icons/1gang.svg | 6 +- assets/icons/2gang.svg | 7 +++ .../bloc/two_gang_bloc/two_gang_bloc.dart | 56 +++++-------------- lib/features/devices/model/device_model.dart | 4 ++ .../view/widgets/devices_view_body.dart | 3 +- .../devices/view/widgets/rooms_slider.dart | 1 + lib/generated/assets.dart | 6 +- lib/main.dart | 2 +- 8 files changed, 34 insertions(+), 51 deletions(-) create mode 100644 assets/icons/2gang.svg diff --git a/assets/icons/1gang.svg b/assets/icons/1gang.svg index 23dee22..647dc35 100644 --- a/assets/icons/1gang.svg +++ b/assets/icons/1gang.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/assets/icons/2gang.svg b/assets/icons/2gang.svg new file mode 100644 index 0000000..6cfe191 --- /dev/null +++ b/assets/icons/2gang.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart index 8e32ef7..b36d286 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart @@ -69,7 +69,8 @@ class TwoGangBloc extends Bloc { deviceName: devicesList[i].name ?? '', firstSwitch: deviceStatus.firstSwitch, secondSwitch: deviceStatus.secondSwitch, - )); + ) + ); } if (groupTwoGangList.isNotEmpty) { @@ -128,22 +129,8 @@ class TwoGangBloc extends Bloc { void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter emit) async { emit(LoadingNewSate(twoGangModel: deviceStatus)); try { - if (twoGangGroup) { - bool allSwitchesValue = true; - groupTwoGangList.forEach((element) { - if (element.deviceId == event.deviceId) { - element.firstSwitch = !event.value; - } - if (!element.firstSwitch || !element.secondSwitch ) { - allSwitchesValue = false; - } - }); - emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesValue)); - } else { - deviceStatus.firstSwitch = !event.value; - emit(UpdateState(twoGangModel: deviceStatus)); - } - + deviceStatus.firstSwitch = !event.value; + emit(UpdateState(twoGangModel: deviceStatus)); if (_timer != null) { _timer!.cancel(); } @@ -169,22 +156,8 @@ class TwoGangBloc extends Bloc { ChangeSecondSwitchStatusEvent event, Emitter emit) async { emit(LoadingNewSate(twoGangModel: deviceStatus)); try { - if (twoGangGroup) { - bool allSwitchesValue = true; - groupTwoGangList.forEach((element) { - if (element.deviceId == event.deviceId) { - element.secondSwitch = !event.value; - } - if (!element.firstSwitch || !element.secondSwitch ) { - allSwitchesValue = false; - } - }); - emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesValue)); - } else { - deviceStatus.secondSwitch = !event.value; - emit(UpdateState(twoGangModel: deviceStatus)); - } - + deviceStatus.secondSwitch = !event.value; + emit(UpdateState(twoGangModel: deviceStatus)); if (_timer != null) { _timer!.cancel(); } @@ -238,24 +211,23 @@ class TwoGangBloc extends Bloc { void _allOn(AllOnEvent event, Emitter emit) async { emit(LoadingNewSate(twoGangModel: deviceStatus)); - try { deviceStatus.firstSwitch = true; deviceStatus.secondSwitch = true; emit(UpdateState(twoGangModel: deviceStatus)); - final response = await Future.wait([ DevicesAPI.controlDevice( - DeviceControlModel( - deviceId: twoGangId, code: 'switch_1', value: deviceStatus.firstSwitch), + DeviceControlModel( + deviceId: twoGangId, code: 'switch_1', + value: deviceStatus.firstSwitch), twoGangId), DevicesAPI.controlDevice( - DeviceControlModel( - deviceId: twoGangId, code: 'switch_2', value: deviceStatus.secondSwitch), - twoGangId), - + DeviceControlModel( + deviceId: twoGangId, + code: 'switch_2', + value: deviceStatus.secondSwitch), + twoGangId), ]); - if (response.every((element) => !element['success'])) { await Future.delayed(const Duration(milliseconds: 500)); add(const InitialEvent(groupScreen: false)); diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 82fac11..c5ba2ae 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -58,6 +58,10 @@ class DeviceModel { tempIcon = Assets.assetsIcons3GangSwitch; } else if (type == DeviceType.Gateway) { tempIcon = Assets.assetsIconsGateway; + } else if (type == DeviceType.OneGang) { + tempIcon = Assets.oneGang; + } else if (type == DeviceType.TwoGang) { + tempIcon = Assets.twoGang; } else { tempIcon = Assets.assetsIconsLogo; } diff --git a/lib/features/devices/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 9ee89a6..53fa8a3 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -79,8 +79,7 @@ class DevicesViewBody extends StatelessWidget { ), if (HomeCubit.getInstance().selectedSpace != null) if (HomeCubit.getInstance().selectedSpace!.rooms != null) - ...HomeCubit.getInstance().selectedSpace!.rooms!.map( - (room) { + ...HomeCubit.getInstance().selectedSpace!.rooms!.map((room) { return RoomPage( room: room, ); diff --git a/lib/features/devices/view/widgets/rooms_slider.dart b/lib/features/devices/view/widgets/rooms_slider.dart index 02d85f0..a53efb3 100644 --- a/lib/features/devices/view/widgets/rooms_slider.dart +++ b/lib/features/devices/view/widgets/rooms_slider.dart @@ -22,6 +22,7 @@ class RoomsSlider extends StatelessWidget { onPageChanged: (index) { HomeCubit.getInstance().roomSliderPageChanged(index); }, + children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 15), diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index e592f1e..80d9bf8 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1030,9 +1030,9 @@ class Assets { static const String assetsPresenceState = "assets/icons/functions_icons/automation_functions/presence_state.svg"; - //assets/icons/functions_icons/automation_functions/presence_state.svg - static const String oneGang = - "assets/icons/1gang.svg"; + static const String oneGang = "assets/icons/1gang.svg"; + + static const String twoGang = "assets/icons/2gang.svg"; } diff --git a/lib/main.dart b/lib/main.dart index 05f1875..1738eac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,7 +14,7 @@ void main() { //to catch all the errors in the app and send them to firebase runZonedGuarded(() async { //to load the environment variables - const environment = String.fromEnvironment('FLAVOR', defaultValue: 'production'); + const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); await dotenv.load(fileName: '.env.$environment'); // //this is to make the app work with the self-signed certificate From ad47e34fa20e5d3979465c32c0b367dfc1895fda Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 15 Sep 2024 11:05:06 +0300 Subject: [PATCH 3/9] Implement Two-Gang & One-Gang --- lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart | 1 - .../devices/view/widgets/two_gang/two_timer_screen.dart | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart index b36d286..09829c7 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart @@ -178,7 +178,6 @@ class TwoGangBloc extends Bloc { } } - void _allOff(AllOffEvent event, Emitter emit) async { emit(LoadingNewSate(twoGangModel: deviceStatus)); diff --git a/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart index 720e80e..72c5f80 100644 --- a/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart +++ b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart @@ -13,8 +13,7 @@ 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'; - -import '../../../bloc/two_gang_bloc/two_gang_event.dart'; +import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart'; class TwoTimerScreen extends StatelessWidget { @@ -167,7 +166,8 @@ class TwoTimerScreen extends StatelessWidget { } }, child: SvgPicture.asset( - countNum > 0 ? Assets.pauseIcon : Assets.playIcon)) + countNum > 0 ? Assets.pauseIcon : Assets.playIcon) + ) ], ))); }, From ddaf36797dfae041690ca7823324465f1768929a Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 15 Sep 2024 16:48:16 +0300 Subject: [PATCH 4/9] two gang schedule --- assets/icons/emptySchedule.svg | 15 + .../bloc/two_gang_bloc/two_gang_bloc.dart | 159 ++++++-- .../bloc/two_gang_bloc/two_gang_event.dart | 15 + .../bloc/two_gang_bloc/two_gang_state.dart | 9 + .../devices/model/schedule_model.dart | 74 ++++ .../widgets/two_gang/two_timer_screen.dart | 380 +++++++++++------- .../shared_widgets/create_schedule.dart | 140 +++++++ .../shared_widgets/empty_schedule.dart | 51 +++ lib/generated/assets.dart | 2 + lib/services/api/devices_api.dart | 29 ++ 10 files changed, 694 insertions(+), 180 deletions(-) create mode 100644 assets/icons/emptySchedule.svg create mode 100644 lib/features/devices/model/schedule_model.dart create mode 100644 lib/features/shared_widgets/create_schedule.dart create mode 100644 lib/features/shared_widgets/empty_schedule.dart diff --git a/assets/icons/emptySchedule.svg b/assets/icons/emptySchedule.svg new file mode 100644 index 0000000..da72622 --- /dev/null +++ b/assets/icons/emptySchedule.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart index 09829c7..6a1b3f8 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart @@ -7,6 +7,7 @@ import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.d 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/groupTwoGangModel.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_gang_model.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; @@ -14,11 +15,11 @@ import 'package:syncrow_app/services/api/devices_api.dart'; class TwoGangBloc extends Bloc { final String twoGangId; TwoGangModel deviceStatus = TwoGangModel( - firstSwitch: false, - secondSwitch: false, - firstCountDown: 0, - secondCountDown: 0, - ); + firstSwitch: false, + secondSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + ); Timer? _timer; // Timer? _firstSwitchTimer; // Timer? _secondSwitchTimer; @@ -28,6 +29,20 @@ class TwoGangBloc extends Bloc { List devicesList = []; List groupTwoGangList = []; bool allSwitchesOn = true; + List selectedDays = []; + + bool createSchedule = false; + List listSchedule = [ + ScheduleModel( + id: '1', + category: 'category', + enable: true, + function: FunctionModel(code: 'code', value: true), + time: 'time', + timerId: 'timerId', + timezoneId: 'timezoneId', + days: ['S']) + ]; TwoGangBloc({required this.twoGangId}) : super(InitialState()) { on(_fetchTwoGangStatus); @@ -43,9 +58,20 @@ class TwoGangBloc extends Bloc { on(_onClose); on(_groupAllOn); on(_groupAllOff); + on(toggleDaySelection); } - void _fetchTwoGangStatus(InitialEvent event, Emitter emit) async { + DateTime? selectedTime; + + void toggleCreateSchedule() { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + emit(UpdateCreateScheduleState(createSchedule)); + emit(ChangeSlidingSegmentState(value: 1)); + } + + void _fetchTwoGangStatus( + InitialEvent event, Emitter emit) async { emit(LoadingInitialState()); try { twoGangGroup = event.groupScreen; @@ -57,7 +83,8 @@ class TwoGangBloc extends Bloc { HomeCubit.getInstance().selectedSpace?.id ?? '', '2G'); for (int i = 0; i < devicesList.length; i++) { - var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); + var response = + await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? ''); List statusModelList = []; for (var status in response['status']) { statusModelList.add(StatusModel.fromJson(status)); @@ -65,23 +92,23 @@ class TwoGangBloc extends Bloc { deviceStatus = TwoGangModel.fromJson(statusModelList); groupTwoGangList.add(GroupTwoGangModel( - deviceId: devicesList[i].uuid ?? '', - deviceName: devicesList[i].name ?? '', - firstSwitch: deviceStatus.firstSwitch, - secondSwitch: deviceStatus.secondSwitch, - ) - ); + deviceId: devicesList[i].uuid ?? '', + deviceName: devicesList[i].name ?? '', + firstSwitch: deviceStatus.firstSwitch, + secondSwitch: deviceStatus.secondSwitch, + )); } if (groupTwoGangList.isNotEmpty) { groupTwoGangList.firstWhere((element) { - if (!element.firstSwitch || !element.secondSwitch ) { + if (!element.firstSwitch || !element.secondSwitch) { allSwitchesOn = false; } return true; }); } - emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesOn)); + emit(UpdateGroupState( + twoGangList: groupTwoGangList, allSwitches: allSwitchesOn)); } else { var response = await DevicesAPI.getDeviceStatus(twoGangId); List statusModelList = []; @@ -100,18 +127,21 @@ class TwoGangBloc extends Bloc { _listenToChanges() { try { - DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$twoGangId'); + DatabaseReference ref = + FirebaseDatabase.instance.ref('device-status/$twoGangId'); Stream stream = ref.onValue; stream.listen((DatabaseEvent event) async { if (_timer != null) { await Future.delayed(const Duration(seconds: 2)); } - Map usersMap = event.snapshot.value as Map; + Map usersMap = + event.snapshot.value as Map; List statusList = []; usersMap['status'].forEach((element) { - statusList.add(StatusModel(code: element['code'], value: element['value'])); + statusList + .add(StatusModel(code: element['code'], value: element['value'])); }); deviceStatus = TwoGangModel.fromJson(statusList); @@ -126,7 +156,8 @@ class TwoGangBloc extends Bloc { emit(UpdateState(twoGangModel: deviceStatus)); } - void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter emit) async { + void _changeFirstSwitch( + ChangeFirstSwitchStatusEvent event, Emitter emit) async { emit(LoadingNewSate(twoGangModel: deviceStatus)); try { deviceStatus.firstSwitch = !event.value; @@ -189,13 +220,16 @@ class TwoGangBloc extends Bloc { final response = await Future.wait([ DevicesAPI.controlDevice( DeviceControlModel( - deviceId: twoGangId, code: 'switch_1', value: deviceStatus.firstSwitch), + deviceId: twoGangId, + code: 'switch_1', + value: deviceStatus.firstSwitch), twoGangId), DevicesAPI.controlDevice( DeviceControlModel( - deviceId: twoGangId, code: 'switch_2', value: deviceStatus.secondSwitch), + deviceId: twoGangId, + code: 'switch_2', + value: deviceStatus.secondSwitch), twoGangId), - ]); if (response.every((element) => !element['success'])) { @@ -216,16 +250,17 @@ class TwoGangBloc extends Bloc { emit(UpdateState(twoGangModel: deviceStatus)); final response = await Future.wait([ DevicesAPI.controlDevice( - DeviceControlModel( - deviceId: twoGangId, code: 'switch_1', - value: deviceStatus.firstSwitch), + DeviceControlModel( + deviceId: twoGangId, + code: 'switch_1', + value: deviceStatus.firstSwitch), twoGangId), DevicesAPI.controlDevice( - DeviceControlModel( - deviceId: twoGangId, - code: 'switch_2', - value: deviceStatus.secondSwitch), - twoGangId), + DeviceControlModel( + deviceId: twoGangId, + code: 'switch_2', + value: deviceStatus.secondSwitch), + twoGangId), ]); if (response.every((element) => !element['success'])) { await Future.delayed(const Duration(milliseconds: 500)); @@ -243,7 +278,6 @@ class TwoGangBloc extends Bloc { for (int i = 0; i < groupTwoGangList.length; i++) { groupTwoGangList[i].firstSwitch = true; groupTwoGangList[i].secondSwitch = true; - } emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: true)); @@ -251,11 +285,15 @@ class TwoGangBloc extends Bloc { final response = await Future.wait([ DevicesAPI.controlDevice( DeviceControlModel( - deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: true), + deviceId: groupTwoGangList[i].deviceId, + code: 'switch_1', + value: true), groupTwoGangList[i].deviceId), DevicesAPI.controlDevice( DeviceControlModel( - deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: true), + deviceId: groupTwoGangList[i].deviceId, + code: 'switch_2', + value: true), groupTwoGangList[i].deviceId), ]); @@ -284,13 +322,16 @@ class TwoGangBloc extends Bloc { final response = await Future.wait([ DevicesAPI.controlDevice( DeviceControlModel( - deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: false), + deviceId: groupTwoGangList[i].deviceId, + code: 'switch_1', + value: false), groupTwoGangList[i].deviceId), DevicesAPI.controlDevice( DeviceControlModel( - deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: false), + deviceId: groupTwoGangList[i].deviceId, + code: 'switch_2', + value: false), groupTwoGangList[i].deviceId), - ]); if (response.every((element) => !element['success'])) { @@ -305,17 +346,20 @@ class TwoGangBloc extends Bloc { } } - void _changeSliding(ChangeSlidingSegment event, Emitter emit) async { + void _changeSliding( + ChangeSlidingSegment event, Emitter emit) async { emit(ChangeSlidingSegmentState(value: event.value)); } - void _setCounterValue(SetCounterValue event, Emitter emit) async { + void _setCounterValue( + SetCounterValue event, Emitter emit) async { emit(LoadingNewSate(twoGangModel: deviceStatus)); int seconds = 0; try { seconds = event.duration.inSeconds; final response = await DevicesAPI.controlDevice( - DeviceControlModel(deviceId: twoGangId, code: event.deviceCode, value: seconds), + DeviceControlModel( + deviceId: twoGangId, code: event.deviceCode, value: seconds), twoGangId); if (response['success'] ?? false) { @@ -340,7 +384,8 @@ class TwoGangBloc extends Bloc { } } - void _getCounterValue(GetCounterEvent event, Emitter emit) async { + void _getCounterValue( + GetCounterEvent event, Emitter emit) async { emit(LoadingInitialState()); try { var response = await DevicesAPI.getDeviceStatus(twoGangId); @@ -387,4 +432,38 @@ class TwoGangBloc extends Bloc { 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)); + } + + saveSchedule() async { + final response = await DevicesAPI.postSchedule( + category: 'switch_1', + deviceId: twoGangId, + time: 'selectedTime', + code: 'switch_1', + value: true, + days: selectedDays); + } } diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart index 6be514c..9076a03 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart @@ -10,6 +10,14 @@ abstract class TwoGangEvent extends Equatable { class LoadingEvent extends TwoGangEvent {} class TwoGangUpdated extends TwoGangEvent {} +class ToggleDaySelectionEvent extends TwoGangEvent { + + final String key; + + const ToggleDaySelectionEvent({required this.key}); + @override + List get props => [key]; +} class InitialEvent extends TwoGangEvent { final bool groupScreen; @@ -43,6 +51,13 @@ class GroupAllOnEvent extends TwoGangEvent {} class GroupAllOffEvent extends TwoGangEvent {} + +// two_gang_event.dart +class ToggleCreateScheduleEvent extends TwoGangEvent {} + + + + class ChangeSlidingSegment extends TwoGangEvent { final int value; const ChangeSlidingSegment({required this.value}); diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart index c967a36..43d7c69 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart @@ -11,6 +11,7 @@ class TwoGangState extends Equatable { } class InitialState extends TwoGangState {} +class ChangeTimeState extends TwoGangState {} class LoadingInitialState extends TwoGangState {} @@ -76,3 +77,11 @@ class TimerRunInProgress extends TwoGangState { } class TimerRunComplete extends TwoGangState {} + + + +// two_gang_state.dart +class UpdateCreateScheduleState extends TwoGangState { + final bool createSchedule; + UpdateCreateScheduleState(this.createSchedule); +} diff --git a/lib/features/devices/model/schedule_model.dart b/lib/features/devices/model/schedule_model.dart new file mode 100644 index 0000000..220fd8c --- /dev/null +++ b/lib/features/devices/model/schedule_model.dart @@ -0,0 +1,74 @@ +class FunctionModel { + final String code; + final bool value; + + FunctionModel({ + required this.code, + required this.value, + }); + + // Convert a JSON object to a FunctionModel instance + factory FunctionModel.fromJson(Map json) { + return FunctionModel( + code: json['code'], + value: json['value'], + ); + } + + // Convert a FunctionModel instance to a JSON object + Map toJson() { + return { + 'code': code, + 'value': value, + }; + } +} + +class ScheduleModel { + final String id; + final String category; + final bool enable; + final FunctionModel function; + final String time; + final String timerId; + final String timezoneId; + final List days; + + ScheduleModel({ + required this.id, + required this.category, + required this.enable, + required this.function, + required this.time, + required this.timerId, + required this.timezoneId, + required this.days, + }); + + // Convert a JSON object to a Schedule instance + factory ScheduleModel.fromJson(Map json) { + return ScheduleModel( + id: json['id'], + category: json['category'], + enable: json['enable'], + function: FunctionModel.fromJson(json['function']), + time: json['time'], + timerId: json['timerId'], + timezoneId: json['timezoneId'], + days: List.from(json['days']), + ); + } + + // Convert a Schedule instance to a JSON object + Map toJson() { + return { + 'category': category, + 'enable': enable, + 'function': function.toJson(), + 'time': time, + 'timerId': timerId, + 'timezoneId': timezoneId, + 'days': days, + }; + } +} \ No newline at end of file diff --git a/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart index 72c5f80..0f61c40 100644 --- a/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart +++ b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart @@ -6,8 +6,11 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_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_container.dart'; +import 'package:syncrow_app/features/shared_widgets/empty_schedule.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/features/shared_widgets/text_widgets/body_small.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/context_extension.dart'; @@ -15,11 +18,11 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart'; - class TwoTimerScreen extends StatelessWidget { final DeviceModel device; final String deviceCode; - const TwoTimerScreen({required this.device, required this.deviceCode, super.key}); + const TwoTimerScreen( + {required this.device, required this.deviceCode, super.key}); @override Widget build(BuildContext context) { @@ -28,151 +31,248 @@ class TwoTimerScreen extends StatelessWidget { 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: BlocProvider( - create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '') - ..add(GetCounterEvent(deviceCode: deviceCode)), - child: BlocBuilder( - builder: (context, state) { - Duration duration = Duration.zero; - int countNum = 0; - int tabNum = 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; - } + child: BlocProvider( + create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)), + child: BlocBuilder( + builder: (context, state) { + final twoGangBloc = BlocProvider.of(context); - if (state is ChangeSlidingSegmentState) { - tabNum = state.value; - } - return PopScope( - canPop: false, - onPopInvoked: (didPop) { - if (!didPop) { - BlocProvider.of(context).add(OnClose()); - Navigator.pop(context); - } - }, - child: Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - padding: const EdgeInsets.all(24), - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.assetsImagesBackground, - ), - fit: BoxFit.cover, - opacity: 0.4, + Duration duration = Duration.zero; + int countNum = 0; + int tabNum = 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; + } else if (state is ChangeSlidingSegmentState) { + tabNum = state.value; + } + return 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, + ), + actions: [ + if (tabNum == 1) + twoGangBloc.createSchedule == true + ? TextButton(onPressed: () {}, child: const Text('Save')) + : IconButton( + onPressed: () { + twoGangBloc.toggleCreateSchedule(); + }, + icon: const Icon(Icons.add), ), - ), - child: state is LoadingInitialState || state is LoadingNewSate - ? const Center( - child: DefaultContainer( - width: 50, height: 50, child: CircularProgressIndicator()), - ) - : Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: MediaQuery.sizeOf(context).width, - decoration: const ShapeDecoration( - color: ColorsManager.slidingBlueColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), + ], + ), + body: SafeArea( + child: PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + BlocProvider.of(context).add(OnClose()); + Navigator.pop(context); + } + }, + child: Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + padding: const EdgeInsets.all(24), + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage(Assets.assetsImagesBackground), + fit: BoxFit.cover, + opacity: 0.4, + ), + ), + child: state is LoadingInitialState || + state is LoadingNewSate + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ) + : Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: MediaQuery.sizeOf(context).width, + decoration: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(20)), + ), + ), + child: CupertinoSlidingSegmentedControl( + thumbColor: ColorsManager.slidingBlueColor, + onValueChanged: (value) { + BlocProvider.of(context).add( + ChangeSlidingSegment( + value: value ?? 0)); + }, + groupValue: state is ChangeSlidingSegmentState + ? state.value + : 0, + backgroundColor: Colors.white, + children: { + 0: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), ), ), - child: CupertinoSlidingSegmentedControl( - thumbColor: ColorsManager.slidingBlueColor, - onValueChanged: (value) { - BlocProvider.of(context) - .add(ChangeSlidingSegment(value: value ?? 0)); - }, - groupValue: - state is ChangeSlidingSegmentState ? state.value : 0, - backgroundColor: Colors.white, - children: { - 0: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: BodySmall( - text: 'Countdown', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), + 1: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, ), - 1: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Text( - 'Schedule', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - }, + ), ), - ), - if (tabNum == 0) - countNum > 0 - ? BodyLarge( - text: _formatDuration(countNum), - fontColor: ColorsManager.slidingBlueColor, - fontSize: 40, - ) - : CupertinoTimerPicker( - mode: CupertinoTimerPickerMode.hm, - onTimerDurationChanged: (Duration newDuration) { - duration = newDuration; - }, - ), - if (tabNum == 0) - GestureDetector( - onTap: () { - if (state is LoadingNewSate) { - return; - } - if (countNum > 0) { - BlocProvider.of(context).add( - SetCounterValue( - deviceCode: deviceCode, - duration: Duration.zero)); - } else if (duration != Duration.zero) { - BlocProvider.of(context).add( - SetCounterValue( - deviceCode: deviceCode, duration: duration)); - } + }, + ), + ), + if (tabNum == 0) + countNum > 0 + ? BodyLarge( + text: _formatDuration(countNum), + fontColor: + ColorsManager.slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: CupertinoTimerPickerMode.hm, + onTimerDurationChanged: + (Duration newDuration) { + duration = newDuration; }, - child: SvgPicture.asset( - countNum > 0 ? Assets.pauseIcon : Assets.playIcon) - ) - ], - ))); - }, - ), - ), + ), + if (tabNum == 0) + GestureDetector( + onTap: () { + if (state is LoadingNewSate) { + return; + } + if (countNum > 0) { + BlocProvider.of(context) + .add(SetCounterValue( + deviceCode: deviceCode, + duration: Duration.zero)); + } else if (duration != Duration.zero) { + BlocProvider.of(context) + .add(SetCounterValue( + deviceCode: deviceCode, + duration: duration)); + } + }, + child: SvgPicture.asset(countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + if (tabNum == 1) + SizedBox( + height: + MediaQuery.of(context).size.height / 2, + child: twoGangBloc.createSchedule == true + ? CreateSchedule( + onToggleChanged: (bool isOn) { + print( + 'Toggle is now: ${isOn ? "ON" : "OFF"}'); + }, + onDateTimeChanged: + (DateTime dateTime) { + print('Selected Time: $dateTime'); + }, + days: twoGangBloc.days, + selectDays: + (List selectedDays) { + twoGangBloc.selectedDays = + selectedDays; + print( + 'Selected Days: ${twoGangBloc.selectedDays}'); + }, + ) + : Center( + child: twoGangBloc.listSchedule.isNotEmpty? + Container( + height: MediaQuery.of(context).size.height / 2, + child: ListView.builder( + itemCount: twoGangBloc.listSchedule.length, + itemBuilder: (context, index) { + return + + Dismissible( + key: Key( twoGangBloc.listSchedule[index].id), + child: InkWell( + onTap: () {}, + child: + DefaultContainer( + padding: EdgeInsets.all(20), + height: MediaQuery.of(context).size.height / 8, + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(twoGangBloc.listSchedule[index].time), + Text(twoGangBloc.listSchedule[index].days.toString()), + Text(twoGangBloc.listSchedule[index].enable.toString()) + ], + ), + ), + Expanded(child:ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: '', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: true, + onChanged: (value) { + // smartDoorBloc.add(ToggleRepeatEvent()); + }, + applyTheme: true, + )), + ),) + ], + )))); + },), + ): + EmptySchedule()), + ) + ], + ), + ), + ), + ), + ); + }, ), ), ); diff --git a/lib/features/shared_widgets/create_schedule.dart b/lib/features/shared_widgets/create_schedule.dart new file mode 100644 index 0000000..9148114 --- /dev/null +++ b/lib/features/shared_widgets/create_schedule.dart @@ -0,0 +1,140 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class CreateSchedule extends StatefulWidget { + final List> days; + final void Function(List selectedDays)? selectDays; // Callback for selected days + final Function(DateTime) onDateTimeChanged; + final void Function(bool isOn)? onToggleChanged; // Callback for toggle state + + const CreateSchedule({ + Key? key, + required this.days, + required this.selectDays, + required this.onDateTimeChanged, + this.onToggleChanged, // Optional callback + }) : super(key: key); + + @override + _CreateScheduleState createState() => _CreateScheduleState(); +} + +class _CreateScheduleState extends State { + List selectedDays = []; // List of selected days + bool isOn = false; // Boolean state for the ON/OFF toggle + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + child: Column( + children: [ + const Divider( + color: ColorsManager.greyColor, + ), + SizedBox( + height: 110, + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.time, + initialDateTime: DateTime.now(), + onDateTimeChanged: widget.onDateTimeChanged, + ), + ), + const Divider( + color: ColorsManager.greyColor, + ), + const SizedBox(height: 20), + SizedBox( + height: MediaQuery.of(context).size.height * 0.08, + child: ListView( + scrollDirection: Axis.horizontal, + children: widget.days.map((day) { + bool isSelected = selectedDays.contains(day['day']); // Check if day is selected + return Padding( + padding: const EdgeInsets.all(8.0), + child: InkWell( + onTap: () { + setState(() { + if (isSelected) { + selectedDays.remove(day['day']); // Deselect day + } else { + selectedDays.add(day['day']!); // Select day + } + if (widget.selectDays != null) { + widget.selectDays!(selectedDays); + } + }); + }, + child: Container( + width: 50, + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 8), + decoration: BoxDecoration( + border: Border.all( + color: isSelected + ? Colors.black + : ColorsManager.grayColor, + ), + color: Colors.transparent, + borderRadius: BorderRadius.circular(30), + ), + child: Center( + child: Text( + day['day']!, + style: TextStyle( + fontSize: 13, + color: isSelected + ? Colors.black + : ColorsManager.grayColor, + ), + ), + ), + ), + ), + ); + }).toList(), + ), + ), + const SizedBox(height: 20), + InkWell( + onTap: () { + setState(() { + isOn = !isOn; // Toggle the state + if (widget.onToggleChanged != null) { + widget.onToggleChanged!(isOn); // Pass the new toggle state + } + }); + }, + child: DefaultContainer( + height: 60, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + isOn ? 'ON' : 'OFF', // Change text based on state + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: isOn ? Colors.green : Colors.red, // Change color based on state + ), + ), + CircleAvatar( + backgroundColor: isOn + ? ColorsManager.secondaryColor.withOpacity(0.6) + : Colors.red.withOpacity(0.6), // Change background color based on state + child: Icon( + Icons.power_settings_new, + color: isOn ? Colors.green : Colors.red, // Change icon color + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/shared_widgets/empty_schedule.dart b/lib/features/shared_widgets/empty_schedule.dart new file mode 100644 index 0000000..fa56b38 --- /dev/null +++ b/lib/features/shared_widgets/empty_schedule.dart @@ -0,0 +1,51 @@ + + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; + +class EmptySchedule extends StatelessWidget { + const EmptySchedule({super.key}); + @override + Widget build(BuildContext context) { + return Center(child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Center( + child: SvgPicture.asset( + height: 100, + width: 100, + Assets.emptySchedule), + ), + Column( + children: [ + Center( + child: Text( + 'Please add', + style: context.displaySmall + .copyWith( + color: const Color( + 0XFFD5D5D5), + ), + ), + ), + Center( + child: Text( + 'a new schedule', + style: context.displaySmall + .copyWith( + color: const Color( + 0XFFD5D5D5), + ), + ), + ), + ], + ), + ], + ),); + } +} diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 80d9bf8..509443d 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1035,4 +1035,6 @@ class Assets { static const String oneGang = "assets/icons/1gang.svg"; static const String twoGang = "assets/icons/2gang.svg"; + + static const String emptySchedule = "assets/icons/emptySchedule.svg"; } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index f347cdd..2e32b4b 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -270,4 +270,33 @@ class DevicesAPI { ); return response; } + + static Future> postSchedule({ + required String category, + required String deviceId, + required String time, + required String code, + required bool value, + required List days, + + }) async { + final response = await _httpService.post( + path: ApiEndpoints.deleteTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + showServerMessage: false, + body: + { + "category": category, + "time": time, + "function":{ + "code": code, + "value":value, + }, + "days": days + }, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } } From 42b5ff105ff73e247e826b01c1e8546f9cf016eb Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 18 Sep 2024 12:59:28 +0300 Subject: [PATCH 5/9] schedule for one gang and two gang and three gange --- .../bloc/one_gang_bloc/one_gang_bloc.dart | 152 ++++- .../bloc/one_gang_bloc/one_gang_event.dart | 28 + .../bloc/one_gang_bloc/one_gang_state.dart | 10 + .../bloc/three_gang_bloc/three_gang_bloc.dart | 156 ++++- .../three_gang_bloc/three_gang_event.dart | 26 + .../three_gang_bloc/three_gang_state.dart | 12 + .../bloc/two_gang_bloc/two_gang_bloc.dart | 145 ++++- .../bloc/two_gang_bloc/two_gang_event.dart | 27 + .../bloc/two_gang_bloc/two_gang_state.dart | 5 + .../devices/model/schedule_model.dart | 119 ++-- .../widgets/one_gang/one_gang_screen.dart | 9 +- .../one_gang/one_gang_timer_screen.dart | 495 +++++++++++---- .../widgets/three_gang/schedule_screen.dart | 17 +- .../widgets/three_gang/three_gang_screen.dart | 4 +- .../view/widgets/three_gang/timer_screen.dart | 592 ++++++++++++++---- .../widgets/two_gang/two_gang_screen.dart | 13 +- .../widgets/two_gang/two_schedule_screen.dart | 10 +- .../widgets/two_gang/two_timer_screen.dart | 414 ++++++------ .../shared_widgets/create_schedule.dart | 9 +- .../shared_widgets/empty_schedule.dart | 68 +- .../shared_widgets/schedule_list.dart | 120 ++++ lib/main.dart | 2 +- lib/services/api/api_links_endpoints.dart | 8 + lib/services/api/devices_api.dart | 62 +- 24 files changed, 1872 insertions(+), 631 deletions(-) create mode 100644 lib/features/shared_widgets/schedule_list.dart diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart index b92717d..6dce09c 100644 --- a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart @@ -1,16 +1,20 @@ 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/devices/bloc/one_gang_bloc/one_gang_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/one_gang_model.dart'; +import 'package:syncrow_app/features/devices/model/schedule_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'one_gang_event.dart'; class OneGangBloc extends Bloc { final String oneGangId; + final String switchCode; OneGangModel deviceStatus = OneGangModel( firstSwitch: false, firstCountDown: 0, @@ -21,7 +25,7 @@ class OneGangBloc extends Bloc { List devicesList = []; bool allSwitchesOn = true; - OneGangBloc({required this.oneGangId}) : super(InitialState()) { + OneGangBloc({required this.oneGangId,required this.switchCode}) : super(InitialState()) { on(_fetchTwoGangStatus); on(_oneGangUpdated); on(_changeFirstSwitch); @@ -30,6 +34,15 @@ class OneGangBloc extends Bloc { on(_getCounterValue); on(_onTickTimer); on(_onClose); + + + + + on(toggleDaySelection); + on(saveSchedule); + on(getSchedule); + on(toggleChange); + on(deleteSchedule); } void _fetchTwoGangStatus(InitialEvent event, Emitter emit) async { @@ -176,4 +189,141 @@ class OneGangBloc extends Bloc { emit(TimerRunComplete()); } } + + List> days = [ + {"day": "Sun", "key": "Sun"}, + {"day": "Mon", "key": "Mon"}, + {"day": "Tue", "key": "Tue"}, + {"day": "Wed", "key": "Wed"}, + {"day": "Thu", "key": "Thu"}, + {"day": "Fri", "key": "Fri"}, + {"day": "Sat", "key": "Sat"}, + ]; + + Future saveSchedule(ThreeGangSave event, Emitter emit,) async { + try { + if(selectedDays.isNotEmpty) { + emit(LoadingInitialState()); + final response = await DevicesAPI.postSchedule( + category: switchCode, + deviceId: oneGangId, + time: getTimeStampWithoutSeconds(selectedTime).toString(), + code: switchCode, + value: toggleSchedule, + days: selectedDays); + CustomSnackBar.displaySnackBar('Save Successfully'); + add(GetScheduleEvent()); + emit(ThreeGangSaveSchedule()); + 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: oneGangId , + ); + 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) { + print(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: oneGangId, 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']; + // debugPrint('errorMessage=${errorMessage}'); + emit(FailedState(error: errorMessage.toString())); + } + } + + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.deleteSchedule( + scheduleId: event.id, + deviceUuid: oneGangId, ); + if (response == true) { + add(GetScheduleEvent()); + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + void toggleCreateSchedule() { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime=DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); + emit(ChangeSlidingSegmentState(value: 1)); + } + + bool toggleSchedule = true; + List selectedDays = []; + bool createSchedule = false; + List listSchedule = []; + DateTime? selectedTime=DateTime.now(); + Future toggleDaySelection( + ToggleDaySelectionEvent event, + Emitter emit, + ) async { + emit(LoadingInitialState()); + if (selectedDays.contains(event.key)) { + selectedDays.remove(event.key); + } else { + selectedDays.add(event.key); + } + emit(ChangeTimeState()); + add(ChangeSlidingSegment(value: 1)); + } + + int selectedTabIndex = 0; + + void toggleSelectedIndex(index) { + emit(LoadingInitialState()); + selectedTabIndex = index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + } diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart index e245b39..af6dee2 100644 --- a/lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_event.dart @@ -86,3 +86,31 @@ class TickTimer extends OneGangEvent { class StopTimer extends OneGangEvent {} class OnClose extends OneGangEvent {} + + + + +//------------------- Schedule ----------=--------- +class GetScheduleEvent extends OneGangEvent {} +class ThreeGangSave extends OneGangEvent {} +class ToggleScheduleEvent extends OneGangEvent { + final String id; + final bool toggle; + const ToggleScheduleEvent({required this.toggle,required this.id}); + @override + List get props => [toggle,id]; +} +class ToggleDaySelectionEvent extends OneGangEvent { + + final String key; + + const ToggleDaySelectionEvent({required this.key}); + @override + List get props => [key]; +} +class DeleteScheduleEvent extends OneGangEvent { + final String id; + const DeleteScheduleEvent({required this.id}); + @override + List get props => [id]; +} diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart index 54ab9e7..17f375a 100644 --- a/lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_state.dart @@ -77,3 +77,13 @@ class TimerRunInProgress extends OneGangState { } class TimerRunComplete extends OneGangState {} +class ThreeGangSaveSchedule extends OneGangState {} +class IsToggleState extends OneGangState { + final bool? onOff; + const IsToggleState({this.onOff}); +} +class ChangeTimeState extends OneGangState {} +class UpdateCreateScheduleState extends OneGangState { + final bool createSchedule; + UpdateCreateScheduleState(this.createSchedule); +} \ No newline at end of file diff --git a/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart b/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart index c796892..38cfab4 100644 --- a/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart +++ b/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart @@ -1,4 +1,5 @@ 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'; @@ -7,12 +8,16 @@ import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_sta 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_gang_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_gang_model.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; + class ThreeGangBloc extends Bloc { final String threeGangId; + final String switchCode; ThreeGangModel deviceStatus = ThreeGangModel( firstSwitch: false, secondSwitch: false, @@ -30,7 +35,7 @@ class ThreeGangBloc extends Bloc { List groupThreeGangList = []; bool allSwitchesOn = true; - ThreeGangBloc({required this.threeGangId}) : super(InitialState()) { + ThreeGangBloc({required this.threeGangId,required this.switchCode}) : super(InitialState()) { on(_fetchThreeGangStatus); on(_threeGangUpdated); on(_changeFirstSwitch); @@ -45,6 +50,14 @@ class ThreeGangBloc extends Bloc { on(_onClose); on(_groupAllOn); on(_groupAllOff); + + + + on(toggleDaySelection); + on(saveSchedule); + on(getSchedule); + on(toggleChange); + on(deleteSchedule); } void _fetchThreeGangStatus(InitialEvent event, Emitter emit) async { @@ -479,4 +492,145 @@ class ThreeGangBloc extends Bloc { 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(ThreeGangSave event, Emitter emit,) async { + try { + if(selectedDays.isNotEmpty) { + emit(LoadingInitialState()); + final response = await DevicesAPI.postSchedule( + category: switchCode, + deviceId: threeGangId, + time: getTimeStampWithoutSeconds(selectedTime).toString(), + code: switchCode, + value: toggleSchedule, + days: selectedDays); + CustomSnackBar.displaySnackBar('Save Successfully'); + add(GetScheduleEvent()); + emit(ThreeGangSaveSchedule()); + 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: threeGangId , + ); + 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) { + print(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: threeGangId, 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']; + // debugPrint('errorMessage=${errorMessage}'); + emit(FailedState(error: errorMessage.toString())); + } + } + + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.deleteSchedule( + scheduleId: event.id, + deviceUuid: threeGangId, ); + if (response == true) { + add(GetScheduleEvent()); + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + emit(FailedState(error: errorMessage.toString())); + } + } + + void toggleCreateSchedule() { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime=DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); + emit(ChangeSlidingSegmentState(value: 1)); + } + + int selectedTabIndex = 0; + + void toggleSelectedIndex(index) { + emit(LoadingInitialState()); + selectedTabIndex = index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + + bool toggleSchedule = true; + List selectedDays = []; + bool createSchedule = false; + List listSchedule = []; + DateTime? selectedTime=DateTime.now(); + } diff --git a/lib/features/devices/bloc/three_gang_bloc/three_gang_event.dart b/lib/features/devices/bloc/three_gang_bloc/three_gang_event.dart index 5cd3cfb..89ad267 100644 --- a/lib/features/devices/bloc/three_gang_bloc/three_gang_event.dart +++ b/lib/features/devices/bloc/three_gang_bloc/three_gang_event.dart @@ -93,3 +93,29 @@ class TickTimer extends ThreeGangEvent { class StopTimer extends ThreeGangEvent {} class OnClose extends ThreeGangEvent {} + + +//------------------- Schedule ----------=--------- +class GetScheduleEvent extends ThreeGangEvent {} +class ThreeGangSave extends ThreeGangEvent {} +class ToggleScheduleEvent extends ThreeGangEvent { + final String id; + final bool toggle; + const ToggleScheduleEvent({required this.toggle,required this.id}); + @override + List get props => [toggle,id]; +} +class ToggleDaySelectionEvent extends ThreeGangEvent { + + final String key; + + const ToggleDaySelectionEvent({required this.key}); + @override + List get props => [key]; +} +class DeleteScheduleEvent extends ThreeGangEvent { + final String id; + const DeleteScheduleEvent({required this.id}); + @override + List get props => [id]; +} diff --git a/lib/features/devices/bloc/three_gang_bloc/three_gang_state.dart b/lib/features/devices/bloc/three_gang_bloc/three_gang_state.dart index 6760dea..e2c555e 100644 --- a/lib/features/devices/bloc/three_gang_bloc/three_gang_state.dart +++ b/lib/features/devices/bloc/three_gang_bloc/three_gang_state.dart @@ -75,3 +75,15 @@ class TimerRunInProgress extends ThreeGangState { } class TimerRunComplete extends ThreeGangState {} + + +class ThreeGangSaveSchedule extends ThreeGangState {} +class IsToggleState extends ThreeGangState { + final bool? onOff; + const IsToggleState({this.onOff}); +} +class ChangeTimeState extends ThreeGangState {} +class UpdateCreateScheduleState extends ThreeGangState { + final bool createSchedule; + UpdateCreateScheduleState(this.createSchedule); +} diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart index 6a1b3f8..0772d45 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart @@ -1,4 +1,5 @@ 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'; @@ -11,9 +12,11 @@ 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_gang_model.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; class TwoGangBloc extends Bloc { final String twoGangId; + final String switchCode; TwoGangModel deviceStatus = TwoGangModel( firstSwitch: false, secondSwitch: false, @@ -21,30 +24,18 @@ class TwoGangBloc extends Bloc { secondCountDown: 0, ); Timer? _timer; - // Timer? _firstSwitchTimer; - // Timer? _secondSwitchTimer; - // Timer? _thirdSwitchTimer; bool twoGangGroup = false; List devicesList = []; List groupTwoGangList = []; bool allSwitchesOn = true; + bool toggleSchedule = true; List selectedDays = []; bool createSchedule = false; - List listSchedule = [ - ScheduleModel( - id: '1', - category: 'category', - enable: true, - function: FunctionModel(code: 'code', value: true), - time: 'time', - timerId: 'timerId', - timezoneId: 'timezoneId', - days: ['S']) - ]; + List listSchedule = []; - TwoGangBloc({required this.twoGangId}) : super(InitialState()) { + TwoGangBloc({required this.twoGangId,required this.switchCode}) : super(InitialState()) { on(_fetchTwoGangStatus); on(_twoGangUpdated); on(_changeFirstSwitch); @@ -59,19 +50,32 @@ class TwoGangBloc extends Bloc { on(_groupAllOn); on(_groupAllOff); on(toggleDaySelection); + on(saveSchedule); + on(getSchedule); + on(toggleRepeat); + on(deleteSchedule); + } - DateTime? selectedTime; + DateTime? selectedTime = DateTime.now(); void toggleCreateSchedule() { emit(LoadingInitialState()); createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime=DateTime.now(); emit(UpdateCreateScheduleState(createSchedule)); emit(ChangeSlidingSegmentState(value: 1)); } + int selectedTabIndex = 0; - void _fetchTwoGangStatus( - InitialEvent event, Emitter emit) async { + void toggleSelectedIndex(index) { + emit(LoadingInitialState()); + selectedTabIndex = index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + + void _fetchTwoGangStatus(InitialEvent event, Emitter emit) async { emit(LoadingInitialState()); try { twoGangGroup = event.groupScreen; @@ -359,13 +363,16 @@ class TwoGangBloc extends Bloc { seconds = event.duration.inSeconds; final response = await DevicesAPI.controlDevice( DeviceControlModel( - deviceId: twoGangId, code: event.deviceCode, value: seconds), + deviceId: twoGangId, + code: event.deviceCode, + value: seconds), twoGangId); if (response['success'] ?? false) { if (event.deviceCode == 'countdown_1') { deviceStatus.firstCountDown = seconds; - } else if (event.deviceCode == 'countdown_2') { + } + else if (event.deviceCode == 'countdown_2') { deviceStatus.secondCountDown = seconds; } } else { @@ -388,6 +395,7 @@ class TwoGangBloc extends Bloc { GetCounterEvent event, Emitter emit) async { emit(LoadingInitialState()); try { + add(GetScheduleEvent()); var response = await DevicesAPI.getDeviceStatus(twoGangId); List statusModelList = []; for (var status in response['status']) { @@ -457,13 +465,96 @@ class TwoGangBloc extends Bloc { add(ChangeSlidingSegment(value: 1)); } - saveSchedule() async { - final response = await DevicesAPI.postSchedule( - category: 'switch_1', + Future saveSchedule(TwoGangSave event, Emitter emit,) async { + try { + if(selectedDays.isNotEmpty) { + emit(LoadingInitialState()); + final response = await DevicesAPI.postSchedule( + category: switchCode, + deviceId: twoGangId, + time: getTimeStampWithoutSeconds(selectedTime).toString(), + code: switchCode, + value: toggleSchedule, + days: selectedDays); + CustomSnackBar.displaySnackBar('Save Successfully'); + add(GetScheduleEvent()); + emit(TwoGangSaveSchedule()); + 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: twoGangId, - time: 'selectedTime', - code: 'switch_1', - value: true, - days: selectedDays); + ); + 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) { + print(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: twoGangId, + 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']; + // debugPrint('errorMessage=${errorMessage}'); + emit(FailedState(error: errorMessage.toString())); + } + } + + Future deleteSchedule( + DeleteScheduleEvent event, Emitter emit) async { + try { + emit(LoadingInitialState()); + final response = await DevicesAPI.deleteSchedule( + scheduleId: event.id, + deviceUuid: twoGangId, ); + if (response == true) { + add(GetScheduleEvent()); + // toggleSchedule = event.id; + return toggleSchedule; + } + emit(IsToggleState(onOff: toggleSchedule)); + add(const ChangeSlidingSegment(value: 1)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + print('errorMessage=${errorMessage}'); + emit(FailedState(error: errorMessage.toString())); + } } } diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart index 9076a03..9fd4737 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_event.dart @@ -10,6 +10,15 @@ abstract class TwoGangEvent extends Equatable { class LoadingEvent extends TwoGangEvent {} class TwoGangUpdated extends TwoGangEvent {} +class TwoGangSave extends TwoGangEvent {} +class ToggleScheduleEvent extends TwoGangEvent { + final String id; + final bool toggle; + const ToggleScheduleEvent({required this.toggle,required this.id}); + @override + List get props => [toggle,id]; +} +class errorMessage extends TwoGangEvent {} class ToggleDaySelectionEvent extends TwoGangEvent { final String key; @@ -101,3 +110,21 @@ class TickTimer extends TwoGangEvent { class StopTimer extends TwoGangEvent {} class OnClose extends TwoGangEvent {} + + +class GetScheduleEvent extends TwoGangEvent {} +class DeleteScheduleEvent extends TwoGangEvent { + final String id; + const DeleteScheduleEvent({required this.id}); + @override + List get props => [id]; +} +class TabChangedEvent extends TwoGangEvent { + final int index; + TabChangedEvent({required this.index}); +} + + + + + diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart index 43d7c69..72c8537 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_state.dart @@ -11,6 +11,7 @@ class TwoGangState extends Equatable { } class InitialState extends TwoGangState {} +class TwoGangSaveSchedule extends TwoGangState {} class ChangeTimeState extends TwoGangState {} class LoadingInitialState extends TwoGangState {} @@ -77,6 +78,10 @@ class TimerRunInProgress extends TwoGangState { } class TimerRunComplete extends TwoGangState {} +class IsToggleState extends TwoGangState { + final bool? onOff; + const IsToggleState({this.onOff}); +} diff --git a/lib/features/devices/model/schedule_model.dart b/lib/features/devices/model/schedule_model.dart index 220fd8c..0261046 100644 --- a/lib/features/devices/model/schedule_model.dart +++ b/lib/features/devices/model/schedule_model.dart @@ -1,21 +1,69 @@ -class FunctionModel { - final String code; - final bool value; +import 'package:intl/intl.dart'; - FunctionModel({ +class ScheduleModel { + String category; + bool enable; + ScheduleFunctionData function; + String time; + String scheduleId; + String timezoneId; + List days; + + ScheduleModel({ + required this.category, + required this.enable, + required this.function, + required this.time, + required this.scheduleId, + required this.timezoneId, + required this.days, + }); + + // Factory method to create an instance from JSON + factory ScheduleModel.fromJson(Map json) { + return ScheduleModel( + category: json['category'], + enable: json['enable'], + function: ScheduleFunctionData.fromJson(json['function']), + time:getTimeIn12HourFormat( json['time']), + scheduleId: json['scheduleId'], + timezoneId: json['timezoneId'], + days: List.from(json['days']), + ); + } + + // Method to convert an instance into JSON + Map toJson() { + return { + 'category': category, + 'enable': enable, + 'function': function.toJson(), + 'time': time, + 'scheduleId': scheduleId, + 'timezoneId': timezoneId, + 'days': days, + }; + } +} + +class ScheduleFunctionData { + String code; + bool value; + + ScheduleFunctionData({ required this.code, required this.value, }); - // Convert a JSON object to a FunctionModel instance - factory FunctionModel.fromJson(Map json) { - return FunctionModel( + // Factory method to create an instance from JSON + factory ScheduleFunctionData.fromJson(Map json) { + return ScheduleFunctionData( code: json['code'], value: json['value'], ); } - // Convert a FunctionModel instance to a JSON object + // Method to convert an instance into JSON Map toJson() { return { 'code': code, @@ -24,51 +72,12 @@ class FunctionModel { } } -class ScheduleModel { - final String id; - final String category; - final bool enable; - final FunctionModel function; - final String time; - final String timerId; - final String timezoneId; - final List days; +String getTimeIn12HourFormat(time) { + // Parse the time string (in HH:mm format) + final DateFormat inputFormat = DateFormat("HH:mm"); + final DateFormat outputFormat = DateFormat("h:mm a"); // 12-hour format with AM/PM - ScheduleModel({ - required this.id, - required this.category, - required this.enable, - required this.function, - required this.time, - required this.timerId, - required this.timezoneId, - required this.days, - }); - - // Convert a JSON object to a Schedule instance - factory ScheduleModel.fromJson(Map json) { - return ScheduleModel( - id: json['id'], - category: json['category'], - enable: json['enable'], - function: FunctionModel.fromJson(json['function']), - time: json['time'], - timerId: json['timerId'], - timezoneId: json['timezoneId'], - days: List.from(json['days']), - ); - } - - // Convert a Schedule instance to a JSON object - Map toJson() { - return { - 'category': category, - 'enable': enable, - 'function': function.toJson(), - 'time': time, - 'timerId': timerId, - 'timezoneId': timezoneId, - 'days': days, - }; - } -} \ No newline at end of file + // Convert the time string + DateTime parsedTime = inputFormat.parse(time); + return outputFormat.format(parsedTime); +} diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_screen.dart b/lib/features/devices/view/widgets/one_gang/one_gang_screen.dart index 566946c..21aee6d 100644 --- a/lib/features/devices/view/widgets/one_gang/one_gang_screen.dart +++ b/lib/features/devices/view/widgets/one_gang/one_gang_screen.dart @@ -20,7 +20,9 @@ class OneGangScreen extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => OneGangBloc(oneGangId : device?.uuid ?? '') + create: (context) => OneGangBloc( + switchCode: 'switch_1', + oneGangId : device?.uuid ?? '') ..add(const InitialEvent(groupScreen:false)), child: BlocBuilder( builder: (context, state) { @@ -104,8 +106,9 @@ class OneGangScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => OneGangTimerScreen( - device: device, + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( + switchCode:'switch_1' , + device: device!, deviceCode: 'countdown_1', ))); }, diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart index 51a5756..d65f7b1 100644 --- a/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart +++ b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart @@ -7,7 +7,10 @@ import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_bloc.da import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_event.dart'; import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_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_container.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'; @@ -15,10 +18,186 @@ 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 OneGangTimerScreen extends StatelessWidget { - final DeviceModel? device; - final String? deviceCode; - const OneGangTimerScreen({super.key,this.device,this.deviceCode}); +// class OneGangTimerScreen extends StatelessWidget { +// final DeviceModel? device; +// final String? deviceCode; +// const OneGangTimerScreen({super.key,this.device,this.deviceCode}); +// +// @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: BlocProvider( +// create: (context) => OneGangBloc( +// switchCode: 'switch_1', +// oneGangId: device!.uuid ?? '').. +// ..add(GetCounterEvent(deviceCode: deviceCode!)), +// child: BlocBuilder( +// builder: (context, state) { +// Duration duration = Duration.zero; +// int countNum = 0; +// int tabNum = 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; +// } +// +// if (state is ChangeSlidingSegmentState) { +// tabNum = state.value; +// } +// return PopScope( +// canPop: false, +// onPopInvoked: (didPop) { +// if (!didPop) { +// BlocProvider.of(context).add(OnClose()); +// Navigator.pop(context); +// } +// }, +// child: Container( +// width: MediaQuery.sizeOf(context).width, +// height: MediaQuery.sizeOf(context).height, +// padding: const EdgeInsets.all(24), +// decoration: const BoxDecoration( +// image: DecorationImage( +// image: AssetImage( +// Assets.assetsImagesBackground, +// ), +// fit: BoxFit.cover, +// opacity: 0.4, +// ), +// ), +// child: state is LoadingInitialState || state is LoadingNewSate +// ? const Center( +// child: DefaultContainer( +// width: 50, height: 50, child: CircularProgressIndicator()), +// ) +// : Column( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// Container( +// width: MediaQuery.sizeOf(context).width, +// decoration: const ShapeDecoration( +// color: ColorsManager.slidingBlueColor, +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.all(Radius.circular(20)), +// ), +// ), +// child: CupertinoSlidingSegmentedControl( +// thumbColor: ColorsManager.slidingBlueColor, +// onValueChanged: (value) { +// BlocProvider.of(context) +// .add(ChangeSlidingSegment(value: value ?? 0)); +// }, +// groupValue: +// state is ChangeSlidingSegmentState ? state.value : 0, +// backgroundColor: Colors.white, +// children: { +// 0: Container( +// padding: const EdgeInsets.symmetric(vertical: 10), +// child: BodySmall( +// text: 'Countdown', +// style: context.bodySmall.copyWith( +// color: ColorsManager.blackColor, +// fontSize: 12, +// fontWeight: FontWeight.w400, +// ), +// ), +// ), +// 1: Container( +// padding: const EdgeInsets.symmetric(vertical: 10), +// child: Text( +// 'Schedule', +// style: context.bodySmall.copyWith( +// color: ColorsManager.blackColor, +// fontSize: 12, +// fontWeight: FontWeight.w400, +// ), +// ), +// ), +// }, +// ), +// ), +// if (tabNum == 0) +// countNum > 0 +// ? BodyLarge( +// text: _formatDuration(countNum), +// fontColor: ColorsManager.slidingBlueColor, +// fontSize: 40, +// ) +// : CupertinoTimerPicker( +// mode: CupertinoTimerPickerMode.hm, +// onTimerDurationChanged: (Duration newDuration) { +// duration = newDuration; +// }, +// ), +// if (tabNum == 0) +// GestureDetector( +// onTap: () { +// if (state is LoadingNewSate) { +// return; +// } +// if (countNum > 0) { +// BlocProvider.of(context).add( +// SetCounterValue( +// deviceCode: deviceCode!, +// duration: Duration.zero)); +// } else if (duration != Duration.zero) { +// BlocProvider.of(context).add( +// SetCounterValue( +// deviceCode: deviceCode!, duration: duration)); +// } +// }, +// child: SvgPicture.asset( +// countNum > 0 ? Assets.pauseIcon : Assets.playIcon)) +// ], +// ))); +// }, +// ), +// ), +// ), +// ), +// ); +// } +// 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'; +// } +// } + + +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) { @@ -27,90 +206,102 @@ class OneGangTimerScreen extends StatelessWidget { 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: BlocProvider( - create: (context) => OneGangBloc(oneGangId: device!.uuid ?? '') - ..add(GetCounterEvent(deviceCode: deviceCode!)), - child: BlocBuilder( - builder: (context, state) { - Duration duration = Duration.zero; - int countNum = 0; - int tabNum = 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; - } - - if (state is ChangeSlidingSegmentState) { - tabNum = state.value; - } - return PopScope( - canPop: false, - onPopInvoked: (didPop) { - if (!didPop) { - BlocProvider.of(context).add(OnClose()); - Navigator.pop(context); - } - }, - child: Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - padding: const EdgeInsets.all(24), - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.assetsImagesBackground, - ), - fit: BoxFit.cover, - opacity: 0.4, - ), + child: BlocProvider( + create: (context) => + OneGangBloc(switchCode: switchCode, oneGangId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), + child: BlocBuilder( + builder: (context, state) { + final oneGangBloc = 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) { + oneGangBloc.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, ), - child: state is LoadingInitialState || state is LoadingNewSate - ? const Center( - child: DefaultContainer( - width: 50, height: 50, child: CircularProgressIndicator()), - ) - : Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: MediaQuery.sizeOf(context).width, - decoration: const ShapeDecoration( + actions: [ + oneGangBloc.createSchedule == true ? + TextButton( + onPressed: () { + oneGangBloc.add(ThreeGangSave()); + }, + child: const Text('Save') + ) : + oneGangBloc.selectedTabIndex==1? IconButton( + onPressed: () { + oneGangBloc.toggleCreateSchedule(); + }, + icon: const Icon(Icons.add), + ):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(oneGangBloc.createSchedule == true){ + oneGangBloc.toggleCreateSchedule(); + } + oneGangBloc.toggleSelectedIndex(0); + + }else{ + oneGangBloc.toggleSelectedIndex(1); + } + }, + indicatorColor: Colors.white, // Customize the indicator + dividerHeight: 0, + // indicatorWeight: MediaQuery.of(context).size.width, + // indicatorSize: , + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( color: ColorsManager.slidingBlueColor, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), + borderRadius: + BorderRadius.all(Radius.circular(20)), ), ), - child: CupertinoSlidingSegmentedControl( - thumbColor: ColorsManager.slidingBlueColor, - onValueChanged: (value) { - BlocProvider.of(context) - .add(ChangeSlidingSegment(value: value ?? 0)); - }, - groupValue: - state is ChangeSlidingSegmentState ? state.value : 0, - backgroundColor: Colors.white, - children: { - 0: Container( - padding: const EdgeInsets.symmetric(vertical: 10), + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), child: BodySmall( text: 'Countdown', style: context.bodySmall.copyWith( @@ -120,7 +311,9 @@ class OneGangTimerScreen extends StatelessWidget { ), ), ), - 1: Container( + ), + Tab( + child: Container( padding: const EdgeInsets.symmetric(vertical: 10), child: Text( 'Schedule', @@ -131,50 +324,106 @@ class OneGangTimerScreen extends StatelessWidget { ), ), ), - }, - ), + ), + ], ), - if (tabNum == 0) - countNum > 0 - ? BodyLarge( - text: _formatDuration(countNum), - fontColor: ColorsManager.slidingBlueColor, - fontSize: 40, - ) - : CupertinoTimerPicker( - mode: CupertinoTimerPickerMode.hm, - onTimerDurationChanged: (Duration newDuration) { - duration = newDuration; - }, - ), - if (tabNum == 0) - GestureDetector( - onTap: () { - if (state is LoadingNewSate) { - return; - } - if (countNum > 0) { - BlocProvider.of(context).add( - SetCounterValue( - deviceCode: deviceCode!, - duration: Duration.zero)); - } else if (duration != Duration.zero) { - BlocProvider.of(context).add( - SetCounterValue( - deviceCode: deviceCode!, duration: duration)); - } - }, - child: SvgPicture.asset( - countNum > 0 ? Assets.pauseIcon : Assets.playIcon)) - ], - ))); - }, - ), - ), + + ), + Expanded( + child: TabBarView( + 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) { + oneGangBloc.add(SetCounterValue( + deviceCode: deviceCode, + duration: Duration.zero)); + } else if (duration != Duration.zero) { + oneGangBloc.add(SetCounterValue( + deviceCode: deviceCode, + duration: duration)); + } + }, + child: SvgPicture.asset(countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + ), + Column( + mainAxisAlignment:oneGangBloc.listSchedule.isNotEmpty? + MainAxisAlignment.start:MainAxisAlignment.center, + children: [ + SizedBox( + child: oneGangBloc.createSchedule == true ? + CreateSchedule( + onToggleChanged: (bool isOn) { + oneGangBloc.toggleSchedule = isOn; + }, + onDateTimeChanged: (DateTime dateTime) { + oneGangBloc.selectedTime=dateTime; + }, + days: oneGangBloc.days, + selectDays: (List selectedDays) { + oneGangBloc.selectedDays = selectedDays; + }, + ) + : + Padding( + padding: const EdgeInsets.only(top: 10), + child: ScheduleListView( + listSchedule: oneGangBloc.listSchedule, // Pass the schedule list here + onDismissed: (scheduleId) { + oneGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId); + oneGangBloc.add(DeleteScheduleEvent(id: scheduleId)); + }, + onToggleSchedule: (scheduleId, isEnabled) { + oneGangBloc.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'); diff --git a/lib/features/devices/view/widgets/three_gang/schedule_screen.dart b/lib/features/devices/view/widgets/three_gang/schedule_screen.dart index 1c527e3..8e1388a 100644 --- a/lib/features/devices/view/widgets/three_gang/schedule_screen.dart +++ b/lib/features/devices/view/widgets/three_gang/schedule_screen.dart @@ -63,7 +63,8 @@ class ScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TimerScreen( + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( + switchCode :"switch_1", device: device, deviceCode: 'countdown_1', ))); @@ -99,10 +100,11 @@ class ScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TimerScreen( - device: device, - deviceCode: 'countdown_2', - ))); + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( + switchCode :"switch_2", + device: device, + deviceCode: 'countdown_2', + ))); }, child: Container( padding: @@ -135,8 +137,9 @@ class ScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TimerScreen( - device: device, + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( + switchCode :"switch_3", + device: device, deviceCode: 'countdown_3', ))); }, diff --git a/lib/features/devices/view/widgets/three_gang/three_gang_screen.dart b/lib/features/devices/view/widgets/three_gang/three_gang_screen.dart index 3b42646..b9a8f0c 100644 --- a/lib/features/devices/view/widgets/three_gang/three_gang_screen.dart +++ b/lib/features/devices/view/widgets/three_gang/three_gang_screen.dart @@ -22,7 +22,9 @@ class ThreeGangScreen extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => ThreeGangBloc(threeGangId: device?.uuid ?? '') + create: (context) => ThreeGangBloc( + switchCode: '', + threeGangId: device?.uuid ?? '') ..add(InitialEvent(groupScreen: device != null ? false : true)), child: BlocBuilder( builder: (context, state) { diff --git a/lib/features/devices/view/widgets/three_gang/timer_screen.dart b/lib/features/devices/view/widgets/three_gang/timer_screen.dart index d5e1c8b..8d8a0a3 100644 --- a/lib/features/devices/view/widgets/three_gang/timer_screen.dart +++ b/lib/features/devices/view/widgets/three_gang/timer_screen.dart @@ -7,7 +7,10 @@ import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_blo import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_event.dart'; import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_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_container.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'; @@ -15,10 +18,254 @@ 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 TimerScreen extends StatelessWidget { +// class TimerScreen extends StatelessWidget { +// final DeviceModel device; +// final String deviceCode; +// final String switchCode; +// const TimerScreen( +// {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) => ThreeGangBloc( +// switchCode: switchCode, threeGangId: device.uuid ?? '')..add(GetScheduleEvent()) +// ..add(GetCounterEvent(deviceCode: deviceCode)), +// child: BlocBuilder( +// builder: (context, state) { +// final threeGangBloc = BlocProvider.of(context); +// +// Duration duration = Duration.zero; +// int countNum = 0; +// int tabNum = 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; +// } +// +// if (state is ChangeSlidingSegmentState) { +// tabNum = state.value; +// } +// return 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, +// ), +// actions: [ +// if (tabNum == 1) +// threeGangBloc.createSchedule == true +// ? TextButton(onPressed: () { +// threeGangBloc.add(ThreeGangSave() ); +// }, child: const Text('Save')) +// : IconButton( +// onPressed: () { +// threeGangBloc.toggleCreateSchedule(); +// }, +// icon: const Icon(Icons.add), +// ), +// ], +// ), +// body: SafeArea( +// child:PopScope( +// canPop: false, +// onPopInvoked: (didPop) { +// if (!didPop) { +// BlocProvider.of(context).add(OnClose()); +// Navigator.pop(context); +// } +// }, +// child: Container( +// width: MediaQuery.sizeOf(context).width, +// padding: const EdgeInsets.all(24), +// decoration: const BoxDecoration( +// image: DecorationImage( +// image: AssetImage( +// Assets.assetsImagesBackground, +// ), +// fit: BoxFit.cover, +// opacity: 0.4, +// ), +// ), +// child: state is LoadingInitialState || +// state is LoadingNewSate +// ? const Center( +// child: DefaultContainer( +// width: 50, +// height: 50, +// child: CircularProgressIndicator()), +// ) +// : Column( +// children: [ +// Container( +// width: MediaQuery.sizeOf(context).width, +// decoration: const ShapeDecoration( +// color: ColorsManager.slidingBlueColor, +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.all( +// Radius.circular(20)), +// ), +// ), +// child: +// CupertinoSlidingSegmentedControl( +// thumbColor: +// ColorsManager.slidingBlueColor, +// onValueChanged: (value) { +// BlocProvider.of(context) +// .add(ChangeSlidingSegment( +// value: value ?? 0)); +// }, +// groupValue: +// state is ChangeSlidingSegmentState +// ? state.value +// : 0, +// backgroundColor: Colors.white, +// children: { +// 0: Container( +// padding: const EdgeInsets.symmetric( +// vertical: 10), +// child: BodySmall( +// text: 'Countdown', +// style: context.bodySmall.copyWith( +// color: ColorsManager.blackColor, +// fontSize: 12, +// fontWeight: FontWeight.w400, +// ), +// ), +// ), +// 1: Container( +// padding: const EdgeInsets.symmetric( +// vertical: 10), +// child: Text( +// 'Schedule', +// style: context.bodySmall.copyWith( +// color: ColorsManager.blackColor, +// fontSize: 12, +// fontWeight: FontWeight.w400, +// ), +// ), +// ), +// }, +// ), +// ), +// if (tabNum == 0) +// countNum > 0 +// ? BodyLarge( +// text: _formatDuration(countNum), +// fontColor: +// ColorsManager.slidingBlueColor, +// fontSize: 40, +// ) +// : CupertinoTimerPicker( +// mode: CupertinoTimerPickerMode.hm, +// onTimerDurationChanged: +// (Duration newDuration) { +// duration = newDuration; +// }, +// ), +// if (tabNum == 0) +// GestureDetector( +// onTap: () { +// if (state is LoadingNewSate) { +// return; +// } +// if (countNum > 0) { +// BlocProvider.of( +// context) +// .add(SetCounterValue( +// deviceCode: deviceCode, +// duration: Duration.zero)); +// } else if (duration != +// Duration.zero) { +// BlocProvider.of( +// context) +// .add(SetCounterValue( +// deviceCode: deviceCode, +// duration: duration)); +// } +// }, +// child: SvgPicture.asset(countNum > 0 +// ? Assets.pauseIcon +// : Assets.playIcon)), +// if (tabNum == 1) +// SizedBox( +// child: threeGangBloc.createSchedule == true +// ? CreateSchedule( +// onToggleChanged: (bool isOn) { +// threeGangBloc.toggleSchedule = isOn; +// }, +// onDateTimeChanged: (DateTime dateTime) { +// threeGangBloc.selectedTime = dateTime; +// }, +// days: threeGangBloc.days, +// selectDays: (ListselectedDays) { +// threeGangBloc.selectedDays = selectedDays; +// }, +// ) : SizedBox( +// height: MediaQuery.of(context).size.height/1.9, +// child: ScheduleListView( +// listSchedule: threeGangBloc.listSchedule, // Pass the schedule list here +// onDismissed: (scheduleId) { +// threeGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId); +// threeGangBloc.add(DeleteScheduleEvent(id: scheduleId)); +// }, +// onToggleSchedule: (scheduleId, isEnabled) { +// threeGangBloc.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'; +// } +// } + + +class TimerScheduleScreen extends StatelessWidget { final DeviceModel device; final String deviceCode; - const TimerScreen({required this.device, required this.deviceCode, super.key}); + final String switchCode; + const TimerScheduleScreen( + {required this.device, + required this.deviceCode, + required this.switchCode, + super.key}); @override Widget build(BuildContext context) { @@ -27,150 +274,219 @@ class TimerScreen extends StatelessWidget { 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: BlocProvider( - create: (context) => ThreeGangBloc(threeGangId: device.uuid ?? '') - ..add(GetCounterEvent(deviceCode: deviceCode)), - child: BlocBuilder( - builder: (context, state) { - Duration duration = Duration.zero; - int countNum = 0; - int tabNum = 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; - } - - if (state is ChangeSlidingSegmentState) { - tabNum = state.value; - } - return PopScope( - canPop: false, - onPopInvoked: (didPop) { - if (!didPop) { - BlocProvider.of(context).add(OnClose()); - Navigator.pop(context); - } - }, - child: Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - padding: const EdgeInsets.all(24), - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.assetsImagesBackground, - ), - fit: BoxFit.cover, - opacity: 0.4, - ), + child: BlocProvider( + create: (context) => + ThreeGangBloc(switchCode: switchCode, threeGangId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), + child: BlocBuilder( + builder: (context, state) { + final threeGangBloc = 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) { + threeGangBloc.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, ), - child: state is LoadingInitialState || state is LoadingNewSate - ? const Center( - child: DefaultContainer( - width: 50, height: 50, child: CircularProgressIndicator()), - ) - : Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: MediaQuery.sizeOf(context).width, - decoration: const ShapeDecoration( - color: ColorsManager.slidingBlueColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), + actions: [ + threeGangBloc.createSchedule == true ? + TextButton( + onPressed: () { + threeGangBloc.add(ThreeGangSave()); + }, + child: const Text('Save') + ) : + threeGangBloc.selectedTabIndex==1? IconButton( + onPressed: () { + threeGangBloc.toggleCreateSchedule(); + }, + icon: const Icon(Icons.add), + ):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(threeGangBloc.createSchedule == true){ + threeGangBloc.toggleCreateSchedule(); + } + threeGangBloc.toggleSelectedIndex(0); + + }else{ + threeGangBloc.toggleSelectedIndex(1); + } + }, + indicatorColor: Colors.white, // Customize the indicator + dividerHeight: 0, + // indicatorWeight: MediaQuery.of(context).size.width, + // indicatorSize: , + 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, ), ), - child: CupertinoSlidingSegmentedControl( - thumbColor: ColorsManager.slidingBlueColor, - onValueChanged: (value) { - BlocProvider.of(context) - .add(ChangeSlidingSegment(value: value ?? 0)); - }, - groupValue: - state is ChangeSlidingSegmentState ? state.value : 0, - backgroundColor: Colors.white, - children: { - 0: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: BodySmall( - text: 'Countdown', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - 1: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Text( - 'Schedule', - 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, + ), ), ), - if (tabNum == 0) - countNum > 0 - ? BodyLarge( - text: _formatDuration(countNum), - fontColor: ColorsManager.slidingBlueColor, - fontSize: 40, - ) - : CupertinoTimerPicker( - mode: CupertinoTimerPickerMode.hm, - onTimerDurationChanged: (Duration newDuration) { - duration = newDuration; - }, - ), - if (tabNum == 0) - GestureDetector( - onTap: () { - if (state is LoadingNewSate) { - return; - } - if (countNum > 0) { - BlocProvider.of(context).add( - SetCounterValue( + ), + ], + ), + + ), + Expanded( + child: TabBarView( + 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) { + threeGangBloc.add(SetCounterValue( deviceCode: deviceCode, duration: Duration.zero)); - } else if (duration != Duration.zero) { - BlocProvider.of(context).add( - SetCounterValue( - deviceCode: deviceCode, duration: duration)); - } + } else if (duration != Duration.zero) { + threeGangBloc.add(SetCounterValue( + deviceCode: deviceCode, + duration: duration)); + } + }, + child: SvgPicture.asset(countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + ), + Column( + mainAxisAlignment:threeGangBloc.listSchedule.isNotEmpty? + MainAxisAlignment.start:MainAxisAlignment.center, + children: [ + SizedBox( + child: threeGangBloc.createSchedule == true ? + CreateSchedule( + onToggleChanged: (bool isOn) { + threeGangBloc.toggleSchedule = isOn; }, - child: SvgPicture.asset( - countNum > 0 ? Assets.pauseIcon : Assets.playIcon)) - ], - ))); - }, - ), - ), + onDateTimeChanged: (DateTime dateTime) { + threeGangBloc.selectedTime=dateTime; + }, + days: threeGangBloc.days, + selectDays: (List selectedDays) { + threeGangBloc.selectedDays = selectedDays; + }, + ) + : + Padding( + padding: const EdgeInsets.only(top: 10), + child: ScheduleListView( + listSchedule: threeGangBloc.listSchedule, // Pass the schedule list here + onDismissed: (scheduleId) { + threeGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId); + threeGangBloc.add(DeleteScheduleEvent(id: scheduleId)); + }, + onToggleSchedule: (scheduleId, isEnabled) { + threeGangBloc.add(ToggleScheduleEvent( + id: scheduleId, + toggle: isEnabled, + )); + }, + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + )) + ); + }, ), ), ); diff --git a/lib/features/devices/view/widgets/two_gang/two_gang_screen.dart b/lib/features/devices/view/widgets/two_gang/two_gang_screen.dart index 4bb158d..d332386 100644 --- a/lib/features/devices/view/widgets/two_gang/two_gang_screen.dart +++ b/lib/features/devices/view/widgets/two_gang/two_gang_screen.dart @@ -15,14 +15,16 @@ import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class TwoGangScreen extends StatelessWidget { - const TwoGangScreen({super.key, this.device}); - + const TwoGangScreen({super.key, this.device, this.switchCode}); final DeviceModel? device; + final String? switchCode; @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => TwoGangBloc(twoGangId: device?.uuid ?? '') + create: (context) => TwoGangBloc( + switchCode: switchCode??'', + twoGangId: device?.uuid ?? '') ..add(InitialEvent(groupScreen: device != null ? false : true)), child: BlocBuilder( builder: (context, state) { @@ -35,7 +37,6 @@ class TwoGangScreen extends StatelessWidget { List groupTwoGangModel = []; bool allSwitchesOn = false; - if (state is LoadingNewSate) { twoGangModel = state.twoGangModel; } else if (state is UpdateState) { @@ -46,9 +47,7 @@ class TwoGangScreen extends StatelessWidget { } return state is LoadingInitialState ? const Center( - child: - DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), - ) + child: DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),) : device == null ? TwoGangList( twoGangList: groupTwoGangModel, diff --git a/lib/features/devices/view/widgets/two_gang/two_schedule_screen.dart b/lib/features/devices/view/widgets/two_gang/two_schedule_screen.dart index f4df531..020272d 100644 --- a/lib/features/devices/view/widgets/two_gang/two_schedule_screen.dart +++ b/lib/features/devices/view/widgets/two_gang/two_schedule_screen.dart @@ -63,9 +63,10 @@ class TwoGangScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TwoTimerScreen( + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( device: device, deviceCode: 'countdown_1', + switchCode:'switch_1' , ))); }, child: Container( @@ -75,7 +76,7 @@ class TwoGangScheduleScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ BodySmall( - text: "Bedside Light", + text: "Cove Light", style: context.bodyMedium.copyWith( color: ColorsManager.textPrimaryColor, fontSize: 15, @@ -99,9 +100,10 @@ class TwoGangScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TwoTimerScreen( + pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( device: device, deviceCode: 'countdown_2', + switchCode:'switch_2' , ))); }, child: Container( @@ -111,7 +113,7 @@ class TwoGangScheduleScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ BodySmall( - text: "Ceiling Light", + text: "Chandelier", style: context.bodyMedium.copyWith( color: ColorsManager.textPrimaryColor, fontSize: 15, diff --git a/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart index 0f61c40..a86ae8c 100644 --- a/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart +++ b/lib/features/devices/view/widgets/two_gang/two_timer_screen.dart @@ -7,10 +7,9 @@ import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_bloc.da import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_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_container.dart'; -import 'package:syncrow_app/features/shared_widgets/empty_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_medium.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'; @@ -18,11 +17,15 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart'; -class TwoTimerScreen extends StatelessWidget { +class TimerScheduleScreen extends StatelessWidget { final DeviceModel device; final String deviceCode; - const TwoTimerScreen( - {required this.device, required this.deviceCode, super.key}); + final String switchCode; + const TimerScheduleScreen( + {required this.device, + required this.deviceCode, + required this.switchCode, + super.key}); @override Widget build(BuildContext context) { @@ -32,15 +35,15 @@ class TwoTimerScreen extends StatelessWidget { statusBarIconBrightness: Brightness.light, ), child: BlocProvider( - create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '') - ..add(GetCounterEvent(deviceCode: deviceCode)), + create: (context) => + TwoGangBloc(switchCode: switchCode, twoGangId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), child: BlocBuilder( builder: (context, state) { final twoGangBloc = BlocProvider.of(context); - Duration duration = Duration.zero; int countNum = 0; - int tabNum = 0; if (state is UpdateTimerState) { countNum = state.seconds; } else if (state is TimerRunInProgress) { @@ -49,228 +52,197 @@ class TwoTimerScreen extends StatelessWidget { countNum = 0; } else if (state is LoadingNewSate) { countNum = 0; - } else if (state is ChangeSlidingSegmentState) { - tabNum = state.value; } - return 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, - ), - actions: [ - if (tabNum == 1) - twoGangBloc.createSchedule == true - ? TextButton(onPressed: () {}, child: const Text('Save')) - : IconButton( + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (!didPop) { + twoGangBloc.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: [ + twoGangBloc.createSchedule == true ? + TextButton( + onPressed: () { + twoGangBloc.add(TwoGangSave()); + }, + child: const Text('Save') + ) : + twoGangBloc.selectedTabIndex==1? IconButton( onPressed: () { twoGangBloc.toggleCreateSchedule(); }, icon: const Icon(Icons.add), - ), - ], - ), - body: SafeArea( - child: PopScope( - canPop: false, - onPopInvoked: (didPop) { - if (!didPop) { - BlocProvider.of(context).add(OnClose()); - Navigator.pop(context); - } - }, - child: Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - padding: const EdgeInsets.all(24), - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage(Assets.assetsImagesBackground), - fit: BoxFit.cover, - opacity: 0.4, + ):SizedBox(), + ], ), - ), - child: state is LoadingInitialState || - state is LoadingNewSate - ? const Center( - child: DefaultContainer( - width: 50, - height: 50, - child: CircularProgressIndicator(), + 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)), + ), ), - ) - : Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: MediaQuery.sizeOf(context).width, - decoration: const ShapeDecoration( - color: ColorsManager.slidingBlueColor, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(20)), - ), - ), - child: CupertinoSlidingSegmentedControl( - thumbColor: ColorsManager.slidingBlueColor, - onValueChanged: (value) { - BlocProvider.of(context).add( - ChangeSlidingSegment( - value: value ?? 0)); - }, - groupValue: state is ChangeSlidingSegmentState - ? state.value - : 0, - backgroundColor: Colors.white, - children: { - 0: Container( - padding: const EdgeInsets.symmetric( - vertical: 10), - child: BodySmall( - text: 'Countdown', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - 1: Container( - padding: const EdgeInsets.symmetric( - vertical: 10), - child: Text( - 'Schedule', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - }, + child: TabBar( + onTap: (value) { + print(value); + if(value==0){ + if(twoGangBloc.createSchedule == true){ + twoGangBloc.toggleCreateSchedule(); + } + twoGangBloc.toggleSelectedIndex(0); + + }else{ + twoGangBloc.toggleSelectedIndex(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)), ), ), - if (tabNum == 0) - countNum > 0 - ? BodyLarge( - text: _formatDuration(countNum), - fontColor: - ColorsManager.slidingBlueColor, - fontSize: 40, - ) - : CupertinoTimerPicker( - mode: CupertinoTimerPickerMode.hm, - onTimerDurationChanged: - (Duration newDuration) { - duration = newDuration; - }, + 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, ), - if (tabNum == 0) - GestureDetector( - onTap: () { - if (state is LoadingNewSate) { - return; - } - if (countNum > 0) { - BlocProvider.of(context) - .add(SetCounterValue( - deviceCode: deviceCode, - duration: Duration.zero)); - } else if (duration != Duration.zero) { - BlocProvider.of(context) - .add(SetCounterValue( - deviceCode: deviceCode, - duration: duration)); - } - }, - child: SvgPicture.asset(countNum > 0 - ? Assets.pauseIcon - : Assets.playIcon)), - if (tabNum == 1) - SizedBox( - height: - MediaQuery.of(context).size.height / 2, - child: twoGangBloc.createSchedule == true - ? CreateSchedule( - onToggleChanged: (bool isOn) { - print( - 'Toggle is now: ${isOn ? "ON" : "OFF"}'); - }, - onDateTimeChanged: - (DateTime dateTime) { - print('Selected Time: $dateTime'); - }, - days: twoGangBloc.days, - selectDays: - (List selectedDays) { - twoGangBloc.selectedDays = - selectedDays; - print( - 'Selected Days: ${twoGangBloc.selectedDays}'); - }, - ) - : Center( - child: twoGangBloc.listSchedule.isNotEmpty? - Container( - height: MediaQuery.of(context).size.height / 2, - child: ListView.builder( - itemCount: twoGangBloc.listSchedule.length, - itemBuilder: (context, index) { - return - - Dismissible( - key: Key( twoGangBloc.listSchedule[index].id), - child: InkWell( - onTap: () {}, - child: - DefaultContainer( - padding: EdgeInsets.all(20), - height: MediaQuery.of(context).size.height / 8, - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(twoGangBloc.listSchedule[index].time), - Text(twoGangBloc.listSchedule[index].days.toString()), - Text(twoGangBloc.listSchedule[index].enable.toString()) - ], - ), - ), - Expanded(child:ListTile( - contentPadding: EdgeInsets.zero, - leading: const BodyMedium( - text: '', - fontWeight: FontWeight.normal, - ), - trailing: Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: true, - onChanged: (value) { - // smartDoorBloc.add(ToggleRepeatEvent()); - }, - applyTheme: true, - )), - ),) - ], - )))); - },), - ): - EmptySchedule()), - ) - ], + ), + ), + ), + 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( + 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) { + twoGangBloc.add(SetCounterValue( + deviceCode: deviceCode, + duration: Duration.zero)); + } else if (duration != Duration.zero) { + twoGangBloc.add(SetCounterValue( + deviceCode: deviceCode, + duration: duration)); + } + }, + child: SvgPicture.asset(countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + ), + Column( + mainAxisAlignment:twoGangBloc.listSchedule.isNotEmpty? + MainAxisAlignment.start:MainAxisAlignment.center, + children: [ + SizedBox( + child: twoGangBloc.createSchedule == true ? + CreateSchedule( + onToggleChanged: (bool isOn) { + twoGangBloc.toggleSchedule = isOn; + }, + onDateTimeChanged: (DateTime dateTime) { + twoGangBloc.selectedTime=dateTime; + }, + days: twoGangBloc.days, + selectDays: (List selectedDays) { + twoGangBloc.selectedDays = selectedDays; + }, + ) + : + Padding( + padding: const EdgeInsets.only(top: 10), + child: ScheduleListView( + listSchedule: twoGangBloc.listSchedule, // Pass the schedule list here + onDismissed: (scheduleId) { + twoGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId); + twoGangBloc.add(DeleteScheduleEvent(id: scheduleId)); + }, + onToggleSchedule: (scheduleId, isEnabled) { + twoGangBloc.add(ToggleScheduleEvent( + id: scheduleId, + toggle: isEnabled, + )); + }, + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + )) ); }, ), diff --git a/lib/features/shared_widgets/create_schedule.dart b/lib/features/shared_widgets/create_schedule.dart index 9148114..c8b298a 100644 --- a/lib/features/shared_widgets/create_schedule.dart +++ b/lib/features/shared_widgets/create_schedule.dart @@ -23,7 +23,7 @@ class CreateSchedule extends StatefulWidget { class _CreateScheduleState extends State { List selectedDays = []; // List of selected days - bool isOn = false; // Boolean state for the ON/OFF toggle + bool isOn = true; // Boolean state for the ON/OFF toggle @override Widget build(BuildContext context) { @@ -114,19 +114,18 @@ class _CreateScheduleState extends State { children: [ Text( isOn ? 'ON' : 'OFF', // Change text based on state - style: TextStyle( + style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: isOn ? Colors.green : Colors.red, // Change color based on state ), ), CircleAvatar( backgroundColor: isOn ? ColorsManager.secondaryColor.withOpacity(0.6) : Colors.red.withOpacity(0.6), // Change background color based on state - child: Icon( + child: const Icon( Icons.power_settings_new, - color: isOn ? Colors.green : Colors.red, // Change icon color + color: ColorsManager.onPrimaryColor, // Change icon color ), ), ], diff --git a/lib/features/shared_widgets/empty_schedule.dart b/lib/features/shared_widgets/empty_schedule.dart index fa56b38..42a311a 100644 --- a/lib/features/shared_widgets/empty_schedule.dart +++ b/lib/features/shared_widgets/empty_schedule.dart @@ -9,43 +9,43 @@ class EmptySchedule extends StatelessWidget { const EmptySchedule({super.key}); @override Widget build(BuildContext context) { - return Center(child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Center( - child: SvgPicture.asset( - height: 100, - width: 100, - Assets.emptySchedule), - ), - Column( - children: [ - Center( - child: Text( - 'Please add', - style: context.displaySmall - .copyWith( - color: const Color( - 0XFFD5D5D5), + return SizedBox( + height: 180, + width: 180, + child: Center(child: Column( + children: [ + Center( + child: SvgPicture.asset( + height: 100, + width: 100, + Assets.emptySchedule), + ), + Column( + children: [ + Center( + child: Text( + 'Please add', + style: context.displaySmall + .copyWith( + color: const Color( + 0XFFD5D5D5), + ), ), ), - ), - Center( - child: Text( - 'a new schedule', - style: context.displaySmall - .copyWith( - color: const Color( - 0XFFD5D5D5), + Center( + child: Text( + 'a new schedule', + style: context.displaySmall + .copyWith( + color: const Color( + 0XFFD5D5D5), + ), ), ), - ), - ], - ), - ], - ),); + ], + ), + ], + ),), + ); } } diff --git a/lib/features/shared_widgets/schedule_list.dart b/lib/features/shared_widgets/schedule_list.dart new file mode 100644 index 0000000..f697ca8 --- /dev/null +++ b/lib/features/shared_widgets/schedule_list.dart @@ -0,0 +1,120 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/model/schedule_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_button.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_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'empty_schedule.dart'; // for SVG icons + + +class ScheduleListView extends StatelessWidget { + final List listSchedule; + final Function(String) onDismissed; + final Function(String, bool) onToggleSchedule; + const ScheduleListView({ + Key? key, + required this.listSchedule, + required this.onDismissed, + required this.onToggleSchedule, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: listSchedule.isNotEmpty + ? SizedBox( + child: ListView.builder( + shrinkWrap: true, + itemCount: listSchedule.length, + itemBuilder: (context, index) { + return Dismissible( + key: Key(listSchedule[index].scheduleId), + background: Container( + padding: const EdgeInsets.only(right: 10), + alignment: AlignmentDirectional.centerEnd, + decoration: const ShapeDecoration( + color: Color(0xFFFF0000), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 10, right: 10), + child: SvgPicture.asset( + Assets.assetsDeleteIcon, + width: 20, + height: 22, + ), + ), + ), + direction: DismissDirection.endToStart, + onDismissed: (direction) { + + onDismissed(listSchedule[index].scheduleId); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Schedule removed')), + ); + }, + child: InkWell( + onTap: () { + + }, + child: DefaultContainer( + padding: const EdgeInsets.all(20), + height: MediaQuery.of(context).size.height / 6.4, + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BodyLarge( + text: listSchedule[index].time, + fontWeight: FontWeight.w500, + fontColor: Colors.black, + fontSize: 22, + ), + Text(listSchedule[index].days.join(' ')), + Text('Function ${listSchedule[index].enable ? "ON" : "OFF"}'), + ], + ), + ), + Expanded( + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: '', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: listSchedule[index].enable, + onChanged: (value) { + onToggleSchedule( + listSchedule[index].scheduleId, + value, + ); + }, + applyTheme: true, + ), + ), + ), + ), + ], + ), + ), + ), + ); + }, + ), + ) + : const EmptySchedule() + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 1738eac..05f1875 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,7 +14,7 @@ void main() { //to catch all the errors in the app and send them to firebase runZonedGuarded(() async { //to load the environment variables - const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); + const environment = String.fromEnvironment('FLAVOR', defaultValue: 'production'); await dotenv.load(fileName: '.env.$environment'); // //this is to make the app work with the self-signed certificate diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index f8867fc..1c3e20f 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -175,4 +175,12 @@ abstract class ApiEndpoints { //multiple-time offline static const String deleteTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}/{passwordId}'; + + + static const String saveSchedule = '/schedule/{deviceUuid}'; + static const String getSchedule = '/schedule/{deviceUuid}?category={category}'; + static const String changeSchedule = '/schedule/enable/{deviceUuid}'; + static const String deleteSchedule = '/schedule/{deviceUuid}/{scheduleId}'; + + } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 2e32b4b..cde2b82 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -1,11 +1,11 @@ import 'dart:async'; +import 'dart:convert'; import 'package:syncrow_app/features/devices/model/device_category_model.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/function_model.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/http_service.dart'; - import '../../features/devices/model/create_temporary_password_model.dart'; class DevicesAPI { @@ -280,11 +280,20 @@ class DevicesAPI { required List days, }) async { + print( { + "category": category, + "time": time, + "function":{ + "code": code, + "value":value, + }, + "days": days + }); final response = await _httpService.post( - path: ApiEndpoints.deleteTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.saveSchedule.replaceAll('{deviceUuid}', deviceId), showServerMessage: false, body: - { + jsonEncode({ "category": category, "time": time, "function":{ @@ -292,7 +301,54 @@ class DevicesAPI { "value":value, }, "days": days + },), + expectedResponseModel: (json) { + return json; }, + ); + return response; + } + + static Future getSchedule({ + required String category, + required String deviceId, + }) async { + final response = await _httpService.get( + path: ApiEndpoints.getSchedule.replaceAll('{deviceUuid}', deviceId).replaceAll('{category}', category), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } + + static Future changeSchedule({ + required String deviceUuid, + required String scheduleId, + required bool enable, + }) async { + final response = await _httpService.put( + path: ApiEndpoints.changeSchedule.replaceAll('{deviceUuid}', deviceUuid), + body: { + "scheduleId": scheduleId, + "enable": enable + }, + expectedResponseModel: (json) { + print('object===-=-=-$json'); + return json['success']; + }, + ); + return response; + } + + static Future deleteSchedule({ + required String deviceUuid, + required String scheduleId, + }) async { + print( ApiEndpoints.deleteSchedule.replaceAll('{deviceUuid}', deviceUuid).replaceAll('{scheduleId}', scheduleId)); + final response = await _httpService.delete( + path: ApiEndpoints.deleteSchedule.replaceAll('{deviceUuid}', deviceUuid).replaceAll('{scheduleId}', scheduleId), expectedResponseModel: (json) { return json; }, From d9cd24e7f0f3a4c083f9a54dbd833f9b482eef15 Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 18 Sep 2024 13:00:46 +0300 Subject: [PATCH 6/9] schedule for one gang and two gang and three gange --- .../one_gang/one_gang_timer_screen.dart | 169 ------------- .../view/widgets/three_gang/timer_screen.dart | 237 ------------------ 2 files changed, 406 deletions(-) diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart index d65f7b1..c21ccdc 100644 --- a/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart +++ b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart @@ -18,175 +18,6 @@ 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 OneGangTimerScreen extends StatelessWidget { -// final DeviceModel? device; -// final String? deviceCode; -// const OneGangTimerScreen({super.key,this.device,this.deviceCode}); -// -// @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: BlocProvider( -// create: (context) => OneGangBloc( -// switchCode: 'switch_1', -// oneGangId: device!.uuid ?? '').. -// ..add(GetCounterEvent(deviceCode: deviceCode!)), -// child: BlocBuilder( -// builder: (context, state) { -// Duration duration = Duration.zero; -// int countNum = 0; -// int tabNum = 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; -// } -// -// if (state is ChangeSlidingSegmentState) { -// tabNum = state.value; -// } -// return PopScope( -// canPop: false, -// onPopInvoked: (didPop) { -// if (!didPop) { -// BlocProvider.of(context).add(OnClose()); -// Navigator.pop(context); -// } -// }, -// child: Container( -// width: MediaQuery.sizeOf(context).width, -// height: MediaQuery.sizeOf(context).height, -// padding: const EdgeInsets.all(24), -// decoration: const BoxDecoration( -// image: DecorationImage( -// image: AssetImage( -// Assets.assetsImagesBackground, -// ), -// fit: BoxFit.cover, -// opacity: 0.4, -// ), -// ), -// child: state is LoadingInitialState || state is LoadingNewSate -// ? const Center( -// child: DefaultContainer( -// width: 50, height: 50, child: CircularProgressIndicator()), -// ) -// : Column( -// mainAxisAlignment: MainAxisAlignment.spaceBetween, -// children: [ -// Container( -// width: MediaQuery.sizeOf(context).width, -// decoration: const ShapeDecoration( -// color: ColorsManager.slidingBlueColor, -// shape: RoundedRectangleBorder( -// borderRadius: BorderRadius.all(Radius.circular(20)), -// ), -// ), -// child: CupertinoSlidingSegmentedControl( -// thumbColor: ColorsManager.slidingBlueColor, -// onValueChanged: (value) { -// BlocProvider.of(context) -// .add(ChangeSlidingSegment(value: value ?? 0)); -// }, -// groupValue: -// state is ChangeSlidingSegmentState ? state.value : 0, -// backgroundColor: Colors.white, -// children: { -// 0: Container( -// padding: const EdgeInsets.symmetric(vertical: 10), -// child: BodySmall( -// text: 'Countdown', -// style: context.bodySmall.copyWith( -// color: ColorsManager.blackColor, -// fontSize: 12, -// fontWeight: FontWeight.w400, -// ), -// ), -// ), -// 1: Container( -// padding: const EdgeInsets.symmetric(vertical: 10), -// child: Text( -// 'Schedule', -// style: context.bodySmall.copyWith( -// color: ColorsManager.blackColor, -// fontSize: 12, -// fontWeight: FontWeight.w400, -// ), -// ), -// ), -// }, -// ), -// ), -// if (tabNum == 0) -// countNum > 0 -// ? BodyLarge( -// text: _formatDuration(countNum), -// fontColor: ColorsManager.slidingBlueColor, -// fontSize: 40, -// ) -// : CupertinoTimerPicker( -// mode: CupertinoTimerPickerMode.hm, -// onTimerDurationChanged: (Duration newDuration) { -// duration = newDuration; -// }, -// ), -// if (tabNum == 0) -// GestureDetector( -// onTap: () { -// if (state is LoadingNewSate) { -// return; -// } -// if (countNum > 0) { -// BlocProvider.of(context).add( -// SetCounterValue( -// deviceCode: deviceCode!, -// duration: Duration.zero)); -// } else if (duration != Duration.zero) { -// BlocProvider.of(context).add( -// SetCounterValue( -// deviceCode: deviceCode!, duration: duration)); -// } -// }, -// child: SvgPicture.asset( -// countNum > 0 ? Assets.pauseIcon : Assets.playIcon)) -// ], -// ))); -// }, -// ), -// ), -// ), -// ), -// ); -// } -// 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'; -// } -// } class TimerScheduleScreen extends StatelessWidget { diff --git a/lib/features/devices/view/widgets/three_gang/timer_screen.dart b/lib/features/devices/view/widgets/three_gang/timer_screen.dart index 8d8a0a3..840b91d 100644 --- a/lib/features/devices/view/widgets/three_gang/timer_screen.dart +++ b/lib/features/devices/view/widgets/three_gang/timer_screen.dart @@ -18,243 +18,6 @@ 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 TimerScreen extends StatelessWidget { -// final DeviceModel device; -// final String deviceCode; -// final String switchCode; -// const TimerScreen( -// {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) => ThreeGangBloc( -// switchCode: switchCode, threeGangId: device.uuid ?? '')..add(GetScheduleEvent()) -// ..add(GetCounterEvent(deviceCode: deviceCode)), -// child: BlocBuilder( -// builder: (context, state) { -// final threeGangBloc = BlocProvider.of(context); -// -// Duration duration = Duration.zero; -// int countNum = 0; -// int tabNum = 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; -// } -// -// if (state is ChangeSlidingSegmentState) { -// tabNum = state.value; -// } -// return 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, -// ), -// actions: [ -// if (tabNum == 1) -// threeGangBloc.createSchedule == true -// ? TextButton(onPressed: () { -// threeGangBloc.add(ThreeGangSave() ); -// }, child: const Text('Save')) -// : IconButton( -// onPressed: () { -// threeGangBloc.toggleCreateSchedule(); -// }, -// icon: const Icon(Icons.add), -// ), -// ], -// ), -// body: SafeArea( -// child:PopScope( -// canPop: false, -// onPopInvoked: (didPop) { -// if (!didPop) { -// BlocProvider.of(context).add(OnClose()); -// Navigator.pop(context); -// } -// }, -// child: Container( -// width: MediaQuery.sizeOf(context).width, -// padding: const EdgeInsets.all(24), -// decoration: const BoxDecoration( -// image: DecorationImage( -// image: AssetImage( -// Assets.assetsImagesBackground, -// ), -// fit: BoxFit.cover, -// opacity: 0.4, -// ), -// ), -// child: state is LoadingInitialState || -// state is LoadingNewSate -// ? const Center( -// child: DefaultContainer( -// width: 50, -// height: 50, -// child: CircularProgressIndicator()), -// ) -// : Column( -// children: [ -// Container( -// width: MediaQuery.sizeOf(context).width, -// decoration: const ShapeDecoration( -// color: ColorsManager.slidingBlueColor, -// shape: RoundedRectangleBorder( -// borderRadius: BorderRadius.all( -// Radius.circular(20)), -// ), -// ), -// child: -// CupertinoSlidingSegmentedControl( -// thumbColor: -// ColorsManager.slidingBlueColor, -// onValueChanged: (value) { -// BlocProvider.of(context) -// .add(ChangeSlidingSegment( -// value: value ?? 0)); -// }, -// groupValue: -// state is ChangeSlidingSegmentState -// ? state.value -// : 0, -// backgroundColor: Colors.white, -// children: { -// 0: Container( -// padding: const EdgeInsets.symmetric( -// vertical: 10), -// child: BodySmall( -// text: 'Countdown', -// style: context.bodySmall.copyWith( -// color: ColorsManager.blackColor, -// fontSize: 12, -// fontWeight: FontWeight.w400, -// ), -// ), -// ), -// 1: Container( -// padding: const EdgeInsets.symmetric( -// vertical: 10), -// child: Text( -// 'Schedule', -// style: context.bodySmall.copyWith( -// color: ColorsManager.blackColor, -// fontSize: 12, -// fontWeight: FontWeight.w400, -// ), -// ), -// ), -// }, -// ), -// ), -// if (tabNum == 0) -// countNum > 0 -// ? BodyLarge( -// text: _formatDuration(countNum), -// fontColor: -// ColorsManager.slidingBlueColor, -// fontSize: 40, -// ) -// : CupertinoTimerPicker( -// mode: CupertinoTimerPickerMode.hm, -// onTimerDurationChanged: -// (Duration newDuration) { -// duration = newDuration; -// }, -// ), -// if (tabNum == 0) -// GestureDetector( -// onTap: () { -// if (state is LoadingNewSate) { -// return; -// } -// if (countNum > 0) { -// BlocProvider.of( -// context) -// .add(SetCounterValue( -// deviceCode: deviceCode, -// duration: Duration.zero)); -// } else if (duration != -// Duration.zero) { -// BlocProvider.of( -// context) -// .add(SetCounterValue( -// deviceCode: deviceCode, -// duration: duration)); -// } -// }, -// child: SvgPicture.asset(countNum > 0 -// ? Assets.pauseIcon -// : Assets.playIcon)), -// if (tabNum == 1) -// SizedBox( -// child: threeGangBloc.createSchedule == true -// ? CreateSchedule( -// onToggleChanged: (bool isOn) { -// threeGangBloc.toggleSchedule = isOn; -// }, -// onDateTimeChanged: (DateTime dateTime) { -// threeGangBloc.selectedTime = dateTime; -// }, -// days: threeGangBloc.days, -// selectDays: (ListselectedDays) { -// threeGangBloc.selectedDays = selectedDays; -// }, -// ) : SizedBox( -// height: MediaQuery.of(context).size.height/1.9, -// child: ScheduleListView( -// listSchedule: threeGangBloc.listSchedule, // Pass the schedule list here -// onDismissed: (scheduleId) { -// threeGangBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId); -// threeGangBloc.add(DeleteScheduleEvent(id: scheduleId)); -// }, -// onToggleSchedule: (scheduleId, isEnabled) { -// threeGangBloc.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'; -// } -// } class TimerScheduleScreen extends StatelessWidget { From 740ca4e4ba46742fde0014edcb9666c79a0a61ba Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 18 Sep 2024 13:01:15 +0300 Subject: [PATCH 7/9] schedule for one gang and two gang and three gange --- lib/services/api/devices_api.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index cde2b82..ca12999 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -335,7 +335,6 @@ class DevicesAPI { "enable": enable }, expectedResponseModel: (json) { - print('object===-=-=-$json'); return json['success']; }, ); @@ -346,7 +345,6 @@ class DevicesAPI { required String deviceUuid, required String scheduleId, }) async { - print( ApiEndpoints.deleteSchedule.replaceAll('{deviceUuid}', deviceUuid).replaceAll('{scheduleId}', scheduleId)); final response = await _httpService.delete( path: ApiEndpoints.deleteSchedule.replaceAll('{deviceUuid}', deviceUuid).replaceAll('{scheduleId}', scheduleId), expectedResponseModel: (json) { From b7f59902cd91c409d78325b1dc14b9819cb46f76 Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 18 Sep 2024 13:19:43 +0300 Subject: [PATCH 8/9] schedule for one gang and two gang and three gange --- lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart | 2 -- .../devices/bloc/three_gang_bloc/three_gang_bloc.dart | 5 ----- lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart | 7 ------- .../view/widgets/one_gang/one_gang_timer_screen.dart | 6 ------ .../devices/view/widgets/three_gang/timer_screen.dart | 2 -- lib/features/shared_widgets/schedule_list.dart | 2 +- 6 files changed, 1 insertion(+), 23 deletions(-) diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart index 6dce09c..f4bb8ea 100644 --- a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart @@ -242,7 +242,6 @@ class OneGangBloc extends Bloc { } int? getTimeStampWithoutSeconds(DateTime? dateTime) { - print(dateTime); if (dateTime == null) return null; DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute); @@ -265,7 +264,6 @@ class OneGangBloc extends Bloc { } on DioException catch (e) { final errorData = e.response!.data; String errorMessage = errorData['message']; - // debugPrint('errorMessage=${errorMessage}'); emit(FailedState(error: errorMessage.toString())); } } diff --git a/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart b/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart index 38cfab4..a707013 100644 --- a/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart +++ b/lib/features/devices/bloc/three_gang_bloc/three_gang_bloc.dart @@ -472,9 +472,6 @@ class ThreeGangBloc extends Bloc { void _onClose(OnClose event, Emitter emit) { _timer?.cancel(); - // _firstSwitchTimer?.cancel(); - // _secondSwitchTimer?.cancel(); - // _thirdSwitchTimer?.cancel(); } void _onStartTimer(int seconds) { @@ -562,7 +559,6 @@ class ThreeGangBloc extends Bloc { } int? getTimeStampWithoutSeconds(DateTime? dateTime) { - print(dateTime); if (dateTime == null) return null; DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute); @@ -585,7 +581,6 @@ class ThreeGangBloc extends Bloc { } on DioException catch (e) { final errorData = e.response!.data; String errorMessage = errorData['message']; - // debugPrint('errorMessage=${errorMessage}'); emit(FailedState(error: errorMessage.toString())); } } diff --git a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart index 0772d45..b2f1e45 100644 --- a/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart +++ b/lib/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart @@ -420,9 +420,6 @@ class TwoGangBloc extends Bloc { void _onClose(OnClose event, Emitter emit) { _timer?.cancel(); - // _firstSwitchTimer?.cancel(); - // _secondSwitchTimer?.cancel(); - // _thirdSwitchTimer?.cancel(); } void _onStartTimer(int seconds) { @@ -506,7 +503,6 @@ class TwoGangBloc extends Bloc { } int? getTimeStampWithoutSeconds(DateTime? dateTime) { - print(dateTime); if (dateTime == null) return null; DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute); @@ -531,7 +527,6 @@ class TwoGangBloc extends Bloc { } on DioException catch (e) { final errorData = e.response!.data; String errorMessage = errorData['message']; - // debugPrint('errorMessage=${errorMessage}'); emit(FailedState(error: errorMessage.toString())); } } @@ -545,7 +540,6 @@ class TwoGangBloc extends Bloc { deviceUuid: twoGangId, ); if (response == true) { add(GetScheduleEvent()); - // toggleSchedule = event.id; return toggleSchedule; } emit(IsToggleState(onOff: toggleSchedule)); @@ -553,7 +547,6 @@ class TwoGangBloc extends Bloc { } on DioException catch (e) { final errorData = e.response!.data; String errorMessage = errorData['message']; - print('errorMessage=${errorMessage}'); emit(FailedState(error: errorMessage.toString())); } } diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart index c21ccdc..605d5d5 100644 --- a/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart +++ b/lib/features/devices/view/widgets/one_gang/one_gang_timer_screen.dart @@ -8,7 +8,6 @@ import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_event.d import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_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_container.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'; @@ -19,7 +18,6 @@ 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; @@ -111,15 +109,12 @@ class TimerScheduleScreen extends StatelessWidget { oneGangBloc.toggleCreateSchedule(); } oneGangBloc.toggleSelectedIndex(0); - }else{ oneGangBloc.toggleSelectedIndex(1); } }, indicatorColor: Colors.white, // Customize the indicator dividerHeight: 0, - // indicatorWeight: MediaQuery.of(context).size.width, - // indicatorSize: , indicatorSize: TabBarIndicatorSize.tab, indicator: const ShapeDecoration( color: ColorsManager.slidingBlueColor, @@ -158,7 +153,6 @@ class TimerScheduleScreen extends StatelessWidget { ), ], ), - ), Expanded( child: TabBarView( diff --git a/lib/features/devices/view/widgets/three_gang/timer_screen.dart b/lib/features/devices/view/widgets/three_gang/timer_screen.dart index 840b91d..d15d4b1 100644 --- a/lib/features/devices/view/widgets/three_gang/timer_screen.dart +++ b/lib/features/devices/view/widgets/three_gang/timer_screen.dart @@ -118,8 +118,6 @@ class TimerScheduleScreen extends StatelessWidget { }, indicatorColor: Colors.white, // Customize the indicator dividerHeight: 0, - // indicatorWeight: MediaQuery.of(context).size.width, - // indicatorSize: , indicatorSize: TabBarIndicatorSize.tab, indicator: const ShapeDecoration( color: ColorsManager.slidingBlueColor, diff --git a/lib/features/shared_widgets/schedule_list.dart b/lib/features/shared_widgets/schedule_list.dart index f697ca8..0b48f86 100644 --- a/lib/features/shared_widgets/schedule_list.dart +++ b/lib/features/shared_widgets/schedule_list.dart @@ -80,7 +80,7 @@ class ScheduleListView extends StatelessWidget { fontSize: 22, ), Text(listSchedule[index].days.join(' ')), - Text('Function ${listSchedule[index].enable ? "ON" : "OFF"}'), + Text('Function ${listSchedule[index].function.value ? "ON" : "OFF"}'), ], ), ), From 65bb388daf0475cbed75f4c595d1be0ad3cbf807 Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 18 Sep 2024 14:04:18 +0300 Subject: [PATCH 9/9] schedule for one gang and two gang and three gange --- lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart index f4bb8ea..987b873 100644 --- a/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart +++ b/lib/features/devices/bloc/one_gang_bloc/one_gang_bloc.dart @@ -23,10 +23,9 @@ class OneGangBloc extends Bloc { bool oneGangGroup = false; List devicesList = []; - bool allSwitchesOn = true; OneGangBloc({required this.oneGangId,required this.switchCode}) : super(InitialState()) { - on(_fetchTwoGangStatus); + on(_fetchOneGangStatus); on(_oneGangUpdated); on(_changeFirstSwitch); on(_changeSliding); @@ -45,7 +44,7 @@ class OneGangBloc extends Bloc { on(deleteSchedule); } - void _fetchTwoGangStatus(InitialEvent event, Emitter emit) async { + void _fetchOneGangStatus(InitialEvent event, Emitter emit) async { emit(LoadingInitialState()); try { var response = await DevicesAPI.getDeviceStatus(oneGangId);