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; },