diff --git a/assets/icons/closed_door.svg b/assets/icons/closed_door.svg new file mode 100644 index 0000000..3c155c2 --- /dev/null +++ b/assets/icons/closed_door.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/door_notification_setting_icon.svg b/assets/icons/door_notification_setting_icon.svg new file mode 100644 index 0000000..75e9aa8 --- /dev/null +++ b/assets/icons/door_notification_setting_icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/door_records_icon.svg b/assets/icons/door_records_icon.svg new file mode 100644 index 0000000..df459ff --- /dev/null +++ b/assets/icons/door_records_icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/door_sensor_icon.svg b/assets/icons/door_sensor_icon.svg new file mode 100644 index 0000000..5357720 --- /dev/null +++ b/assets/icons/door_sensor_icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/opened_door.svg b/assets/icons/opened_door.svg new file mode 100644 index 0000000..40aa398 --- /dev/null +++ b/assets/icons/opened_door.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index 2952e70..276bfd8 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -8,7 +8,6 @@ import 'package:syncrow_app/features/auth/model/user_model.dart'; import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/services/api/authentication_api.dart'; -import 'package:syncrow_app/services/api/profile_api.dart'; import 'package:syncrow_app/utils/helpers/shared_preferences_helper.dart'; import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; diff --git a/lib/features/devices/bloc/door_sensor_bloc/door_sensor_bloc.dart b/lib/features/devices/bloc/door_sensor_bloc/door_sensor_bloc.dart new file mode 100644 index 0000000..213dbeb --- /dev/null +++ b/lib/features/devices/bloc/door_sensor_bloc/door_sensor_bloc.dart @@ -0,0 +1,179 @@ +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/door_sensor_bloc/door_sensor_event.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/devices/model/door_sensor_model.dart'; +import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; + +class DoorSensorBloc extends Bloc { + final String DSId; + DoorSensorBloc({ + required this.DSId, + }) : super(const DoorSensorState()) { + on(_fetchStatus); + on(fetchLogsForLastMonth); + on(_toggleLowBattery); + on(_toggleClosingReminder); + on(_toggleDoorAlarm); + } + Timer? _timer; + bool lowBattery = false; + bool closingReminder = false; + bool doorAlarm = false; + DoorSensorModel deviceStatus = + DoorSensorModel(doorContactState: false, batteryPercentage: 0); + + void _fetchStatus( + DoorSensorInitial event, Emitter emit) async { + emit(DoorSensorLoadingState()); + try { + var response = await DevicesAPI.getDeviceStatus(DSId); + List statusModelList = []; + for (var status in response['status']) { + statusModelList.add(StatusModel.fromJson(status)); + } + deviceStatus = DoorSensorModel.fromJson( + statusModelList, + ); + emit(UpdateState(doorSensor: deviceStatus)); + Future.delayed(const Duration(milliseconds: 500)); + _listenToChanges(); + } catch (e) { + emit(DoorSensorFailedState(errorMessage: e.toString())); + return; + } + } + + // Toggle functions for each switch + void _toggleLowBattery( + ToggleLowBatteryEvent event, Emitter emit) async { + emit(LoadingNewSate(doorSensor: deviceStatus)); + try { + lowBattery = event.isLowBatteryEnabled; + emit(UpdateState(doorSensor: deviceStatus)); + + // API call to update the state, if necessary + await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: DSId, + code: 'low_battery', + value: lowBattery, + ), + DSId, + ); + } catch (e) { + emit(DoorSensorFailedState(errorMessage: e.toString())); + } + } + + void _toggleClosingReminder( + ToggleClosingReminderEvent event, Emitter emit) async { + emit(LoadingNewSate(doorSensor: deviceStatus)); + try { + closingReminder = event.isClosingReminderEnabled; + emit(UpdateState(doorSensor: deviceStatus)); + + // API call to update the state, if necessary + await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: DSId, + code: 'closing_reminder', + value: closingReminder, + ), + DSId, + ); + } catch (e) { + emit(DoorSensorFailedState(errorMessage: e.toString())); + } + } + + void _toggleDoorAlarm( + ToggleDoorAlarmEvent event, Emitter emit) async { + emit(LoadingNewSate(doorSensor: deviceStatus)); + try { + doorAlarm = event.isDoorAlarmEnabled; + emit(UpdateState(doorSensor: deviceStatus)); + + // API call to update the state, if necessary + await DevicesAPI.controlDevice( + DeviceControlModel( + deviceId: DSId, + code: 'door_alarm', + value: doorAlarm, + ), + DSId, + ); + } catch (e) { + emit(DoorSensorFailedState(errorMessage: e.toString())); + } + } + + DeviceReport recordGroups = + DeviceReport(startTime: '0', endTime: '0', data: []); + + Future fetchLogsForLastMonth( + ReportLogsInitial event, Emitter emit) async { + // Get the current date and time + DateTime now = DateTime.now(); + + // Calculate the date one month ago + DateTime lastMonth = DateTime(now.year, now.month - 1, now.day); + + // Convert the date to milliseconds since epoch (Unix timestamp in milliseconds) + int startTime = lastMonth.millisecondsSinceEpoch; + int endTime = now.millisecondsSinceEpoch; + try { + emit(DoorSensorLoadingState()); + var response = await DevicesAPI.getReportLogs( + startTime: + startTime.toString(), // Convert to String if the API expects it + endTime: endTime.toString(), // Convert to String if the API expects it + deviceUuid: DSId, + code: 'doorcontact_state', + ); + print('response======${response}'); + recordGroups = response; + // Process response here + emit(UpdateState(doorSensor: deviceStatus)); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = errorData['message']; + // Handle error + print('Error fetching logs: ${errorMessage}'); + } + } + + _listenToChanges() { + try { + DatabaseReference ref = + FirebaseDatabase.instance.ref('device-status/$DSId'); + 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: true)); + }); + + deviceStatus = DoorSensorModel.fromJson(statusList); + if (!isClosed) { + add( + DoorSensorSwitch(switchD: deviceStatus.doorContactState), + ); + } + }); + } catch (_) {} + } +} diff --git a/lib/features/devices/bloc/door_sensor_bloc/door_sensor_event.dart b/lib/features/devices/bloc/door_sensor_bloc/door_sensor_event.dart new file mode 100644 index 0000000..8a99623 --- /dev/null +++ b/lib/features/devices/bloc/door_sensor_bloc/door_sensor_event.dart @@ -0,0 +1,102 @@ +import 'package:equatable/equatable.dart'; + +abstract class DoorSensorEvent extends Equatable { + const DoorSensorEvent(); + + @override + List get props => []; +} + +class DoorSensorLoading extends DoorSensorEvent {} + +class DoorSensorSwitch extends DoorSensorEvent { + final bool switchD; + final String deviceId; + final String productId; + const DoorSensorSwitch({required this.switchD, this.deviceId = '', this.productId = ''}); + + @override + List get props => [switchD, deviceId, productId]; +} + +class DoorSensorUpdated extends DoorSensorEvent {} + +class DoorSensorInitial extends DoorSensorEvent { + const DoorSensorInitial(); +} + +class ReportLogsInitial extends DoorSensorEvent { + const ReportLogsInitial(); +} + + +class DoorSensorChangeStatus extends DoorSensorEvent {} + + +class GetCounterEvent extends DoorSensorEvent { + final String deviceCode; + const GetCounterEvent({required this.deviceCode}); + @override + List get props => [deviceCode]; +} + + + +class ToggleLowBatteryEvent extends DoorSensorEvent { + final bool isLowBatteryEnabled; + + const ToggleLowBatteryEvent(this.isLowBatteryEnabled); + + @override + List get props => [isLowBatteryEnabled]; +} + +class ToggleClosingReminderEvent extends DoorSensorEvent { + final bool isClosingReminderEnabled; + + const ToggleClosingReminderEvent(this.isClosingReminderEnabled); + + @override + List get props => [isClosingReminderEnabled]; +} + +class ToggleDoorAlarmEvent extends DoorSensorEvent { + final bool isDoorAlarmEnabled; + + const ToggleDoorAlarmEvent(this.isDoorAlarmEnabled); + + @override + List get props => [isDoorAlarmEnabled]; +} + + + +class SetCounterValue extends DoorSensorEvent { + final Duration duration; + final String deviceCode; + const SetCounterValue({required this.duration, required this.deviceCode}); + @override + List get props => [duration, deviceCode]; +} + +class StartTimer extends DoorSensorEvent { + final int duration; + + const StartTimer(this.duration); + + @override + List get props => [duration]; +} + +class TickTimer extends DoorSensorEvent { + final int remainingTime; + + const TickTimer(this.remainingTime); + + @override + List get props => [remainingTime]; +} + +class StopTimer extends DoorSensorEvent {} + +class OnClose extends DoorSensorEvent {} diff --git a/lib/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart b/lib/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart new file mode 100644 index 0000000..b95e63d --- /dev/null +++ b/lib/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart @@ -0,0 +1,128 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/devices/model/door_sensor_model.dart'; + + class DoorSensorState extends Equatable { + const DoorSensorState(); + + @override + List get props => []; +} + +class DoorSensorInitialState extends DoorSensorState {} + +class DoorSensorLoadingState extends DoorSensorState {} + +class DoorSensorFailedState extends DoorSensorState { + final String errorMessage; + + const DoorSensorFailedState({required this.errorMessage}); + + @override + List get props => [errorMessage]; +} + +class UpdateState extends DoorSensorState { + final DoorSensorModel doorSensor; + const UpdateState({required this.doorSensor}); + + @override + List get props => [doorSensor]; +} + + +class LoadingNewSate extends DoorSensorState { + final DoorSensorModel doorSensor; + const LoadingNewSate({required this.doorSensor}); + + @override + List get props => [doorSensor]; +} + +// class WHModifyingState extends DoorSensorState { +// final WHModel whStatusModel; +// const WHModifyingState({required this.whStatusModel}); +// +// @override +// List get props => [whStatusModel]; +// } +// +// class GetWHStatusState extends DoorSensorState { +// final WHModel whStatusModel; +// const GetWHStatusState({required this.whStatusModel}); +// +// @override +// List get props => [whStatusModel]; +// } +// +// +// class WHFailedState extends WaterHeaterState { +// final String errorMessage; +// +// const WHFailedState({required this.errorMessage}); +// +// @override +// List get props => [errorMessage]; +// } + + +// class LoadingNewSate extends WaterHeaterState { +// final WHModel whModel; +// const LoadingNewSate({required this.whModel}); +// +// @override +// List get props => [whModel]; +// } +// class TimerRunComplete extends WaterHeaterState {} +// class UpdateTimerState extends WaterHeaterState { +// final int seconds; +// const UpdateTimerState({required this.seconds}); +// +// @override +// List get props => [seconds]; +// } +// +// class TimerRunInProgress extends WaterHeaterState { +// final int remainingTime; +// +// const TimerRunInProgress(this.remainingTime); +// +// @override +// List get props => [remainingTime]; +// } +// +// class SaveSchedule extends WaterHeaterState {} +// class ChangeTimeState extends WaterHeaterState {} +// class UpdateCreateScheduleState extends WaterHeaterState { +// final bool createSchedule; +// UpdateCreateScheduleState(this.createSchedule); +// } +// class IsToggleState extends WaterHeaterState { +// final bool? onOff; +// const IsToggleState({this.onOff}); +// } +// +// +// class UpdateState extends WaterHeaterState { +// final WHModel whModel; +// const UpdateState({required this.whModel}); +// +// @override +// List get props => [WHModel]; +// } +// +// class ChangeSwitchStatusEvent extends WaterHeaterState { +// final bool value; +// final String deviceId; +// const ChangeSwitchStatusEvent({required this.value, this.deviceId = ''}); +// @override +// List get props => [value, deviceId]; +// } +// +// class ChangeSlidingSegmentState extends WaterHeaterState { +// final int value; +// +// const ChangeSlidingSegmentState({required this.value}); +// +// @override +// List get props => [value]; +// } \ No newline at end of file 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 987b873..db30079 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 @@ -24,7 +24,7 @@ class OneGangBloc extends Bloc { bool oneGangGroup = false; List devicesList = []; - OneGangBloc({required this.oneGangId,required this.switchCode}) : super(InitialState()) { + OneGangBloc({required this.oneGangId, required this.switchCode}) : super(InitialState()) { on(_fetchOneGangStatus); on(_oneGangUpdated); on(_changeFirstSwitch); @@ -34,14 +34,13 @@ class OneGangBloc extends Bloc { on(_onTickTimer); on(_onClose); - - - on(toggleDaySelection); - on(saveSchedule); + on(saveSchedule); on(getSchedule); on(toggleChange); on(deleteSchedule); + on(toggleSelectedIndex); + on(toggleCreateSchedule); } void _fetchOneGangStatus(InitialEvent event, Emitter emit) async { @@ -114,7 +113,6 @@ class OneGangBloc extends Bloc { } } - void _changeSliding(ChangeSlidingSegment event, Emitter emit) async { emit(ChangeSlidingSegmentState(value: event.value)); } @@ -199,11 +197,14 @@ class OneGangBloc extends Bloc { {"day": "Sat", "key": "Sat"}, ]; - Future saveSchedule(ThreeGangSave event, Emitter emit,) async { + Future saveSchedule( + ScheduleSave event, + Emitter emit, + ) async { try { - if(selectedDays.isNotEmpty) { + if (selectedDays.isNotEmpty) { emit(LoadingInitialState()); - final response = await DevicesAPI.postSchedule( + await DevicesAPI.postSchedule( category: switchCode, deviceId: oneGangId, time: getTimeStampWithoutSeconds(selectedTime).toString(), @@ -212,9 +213,10 @@ class OneGangBloc extends Bloc { days: selectedDays); CustomSnackBar.displaySnackBar('Save Successfully'); add(GetScheduleEvent()); - emit(ThreeGangSaveSchedule()); - toggleCreateSchedule(); - }else{ + emit(SaveSchedule()); + + add(const ToggleCreateScheduleEvent(index:1 )); + } else { CustomSnackBar.displaySnackBar('Please select days'); } } catch (e) { @@ -222,13 +224,15 @@ class OneGangBloc extends Bloc { } } - - Future getSchedule(GetScheduleEvent event, Emitter emit,) async { + Future getSchedule( + GetScheduleEvent event, + Emitter emit, + ) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.getSchedule( category: switchCode, - deviceId: oneGangId , + deviceId: oneGangId, ); List jsonData = response; listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); @@ -242,13 +246,12 @@ class OneGangBloc extends Bloc { int? getTimeStampWithoutSeconds(DateTime? dateTime) { if (dateTime == null) return null; - DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, - dateTime.day, dateTime.hour, dateTime.minute); + DateTime dateTimeWithoutSeconds = + DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute); return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; } - Future toggleChange( - ToggleScheduleEvent event, Emitter emit) async { + Future toggleChange(ToggleScheduleEvent event, Emitter emit) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.changeSchedule( @@ -267,13 +270,13 @@ class OneGangBloc extends Bloc { } } - Future deleteSchedule( - DeleteScheduleEvent event, Emitter emit) async { + Future deleteSchedule(DeleteScheduleEvent event, Emitter emit) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.deleteSchedule( scheduleId: event.id, - deviceUuid: oneGangId, ); + deviceUuid: oneGangId, + ); if (response == true) { add(GetScheduleEvent()); return toggleSchedule; @@ -287,24 +290,33 @@ class OneGangBloc extends Bloc { } } - void toggleCreateSchedule() { - emit(LoadingInitialState()); - createSchedule = !createSchedule; - selectedDays.clear(); - selectedTime=DateTime.now(); - emit(UpdateCreateScheduleState(createSchedule)); - emit(ChangeSlidingSegmentState(value: 1)); - } + // void toggleCreateSchedule() { + // emit(LoadingInitialState()); + // createSchedule = !createSchedule; + // selectedDays.clear(); + // selectedTime = DateTime.now(); + // emit(UpdateCreateScheduleState(createSchedule)); + // emit(ChangeSlidingSegmentState(value: 1)); + // } + + + void toggleCreateSchedule(ToggleCreateScheduleEvent event, Emitter emit) { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime = DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); +} bool toggleSchedule = true; List selectedDays = []; bool createSchedule = false; List listSchedule = []; - DateTime? selectedTime=DateTime.now(); + DateTime? selectedTime = DateTime.now(); Future toggleDaySelection( - ToggleDaySelectionEvent event, - Emitter emit, - ) async { + ToggleDaySelectionEvent event, + Emitter emit, + ) async { emit(LoadingInitialState()); if (selectedDays.contains(event.key)) { selectedDays.remove(event.key); @@ -317,10 +329,9 @@ class OneGangBloc extends Bloc { int selectedTabIndex = 0; - void toggleSelectedIndex(index) { + void toggleSelectedIndex( ToggleSelectedEvent event, Emitter emit) { emit(LoadingInitialState()); - selectedTabIndex = index; + selectedTabIndex =event.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 af6dee2..a59f653 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 @@ -92,7 +92,7 @@ class OnClose extends OneGangEvent {} //------------------- Schedule ----------=--------- class GetScheduleEvent extends OneGangEvent {} -class ThreeGangSave extends OneGangEvent {} +class ScheduleSave extends OneGangEvent {} class ToggleScheduleEvent extends OneGangEvent { final String id; final bool toggle; @@ -114,3 +114,19 @@ class DeleteScheduleEvent extends OneGangEvent { @override List get props => [id]; } + + +class ToggleSelectedEvent extends OneGangEvent { + final int index; + const ToggleSelectedEvent({required this.index}); + @override + List get props => [index]; +} + + +class ToggleCreateScheduleEvent extends OneGangEvent { + final int index; + const ToggleCreateScheduleEvent({required this.index}); + @override + List get props => [index]; +} 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 17f375a..693851d 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 @@ -1,9 +1,8 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart'; +import 'package:syncrow_app/features/devices/model/group_one_gang_model.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(); @@ -32,13 +31,13 @@ class LoadingNewSate extends OneGangState { } class UpdateGroupState extends OneGangState { - final List twoGangList; + final List oneGangList; final bool allSwitches; - const UpdateGroupState({required this.twoGangList, required this.allSwitches}); + const UpdateGroupState({required this.oneGangList, required this.allSwitches}); @override - List get props => [twoGangList, allSwitches]; + List get props => [oneGangList, allSwitches]; } class FailedState extends OneGangState { @@ -77,13 +76,17 @@ class TimerRunInProgress extends OneGangState { } class TimerRunComplete extends OneGangState {} -class ThreeGangSaveSchedule extends OneGangState {} + +class SaveSchedule 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 a707013..1985acd 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 @@ -14,7 +14,6 @@ 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; @@ -35,7 +34,7 @@ class ThreeGangBloc extends Bloc { List groupThreeGangList = []; bool allSwitchesOn = true; - ThreeGangBloc({required this.threeGangId,required this.switchCode}) : super(InitialState()) { + ThreeGangBloc({required this.threeGangId, required this.switchCode}) : super(InitialState()) { on(_fetchThreeGangStatus); on(_threeGangUpdated); on(_changeFirstSwitch); @@ -51,13 +50,14 @@ class ThreeGangBloc extends Bloc { on(_groupAllOn); on(_groupAllOff); - - on(toggleDaySelection); on(saveSchedule); on(getSchedule); on(toggleChange); on(deleteSchedule); + + on(toggleSelectedIndex); + on(toggleCreateSchedule); } void _fetchThreeGangStatus(InitialEvent event, Emitter emit) async { @@ -490,9 +490,6 @@ class ThreeGangBloc extends Bloc { } } - - - List> days = [ {"day": "Sun", "key": "Sun"}, {"day": "Mon", "key": "Mon"}, @@ -504,9 +501,9 @@ class ThreeGangBloc extends Bloc { ]; Future toggleDaySelection( - ToggleDaySelectionEvent event, - Emitter emit, - ) async { + ToggleDaySelectionEvent event, + Emitter emit, + ) async { emit(LoadingInitialState()); if (selectedDays.contains(event.key)) { selectedDays.remove(event.key); @@ -517,11 +514,14 @@ class ThreeGangBloc extends Bloc { add(ChangeSlidingSegment(value: 1)); } - Future saveSchedule(ThreeGangSave event, Emitter emit,) async { + Future saveSchedule( + ThreeGangSave event, + Emitter emit, + ) async { try { - if(selectedDays.isNotEmpty) { + if (selectedDays.isNotEmpty) { emit(LoadingInitialState()); - final response = await DevicesAPI.postSchedule( + await DevicesAPI.postSchedule( category: switchCode, deviceId: threeGangId, time: getTimeStampWithoutSeconds(selectedTime).toString(), @@ -531,8 +531,8 @@ class ThreeGangBloc extends Bloc { CustomSnackBar.displaySnackBar('Save Successfully'); add(GetScheduleEvent()); emit(ThreeGangSaveSchedule()); - toggleCreateSchedule(); - }else{ + add(const ToggleCreateScheduleEvent(index:1 )); + } else { CustomSnackBar.displaySnackBar('Please select days'); } } catch (e) { @@ -540,13 +540,15 @@ class ThreeGangBloc extends Bloc { } } - - Future getSchedule(GetScheduleEvent event, Emitter emit,) async { + Future getSchedule( + GetScheduleEvent event, + Emitter emit, + ) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.getSchedule( category: switchCode, - deviceId: threeGangId , + deviceId: threeGangId, ); List jsonData = response; listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); @@ -560,13 +562,12 @@ class ThreeGangBloc extends Bloc { int? getTimeStampWithoutSeconds(DateTime? dateTime) { if (dateTime == null) return null; - DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month, - dateTime.day, dateTime.hour, dateTime.minute); + DateTime dateTimeWithoutSeconds = + DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute); return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000; } - Future toggleChange( - ToggleScheduleEvent event, Emitter emit) async { + Future toggleChange(ToggleScheduleEvent event, Emitter emit) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.changeSchedule( @@ -585,13 +586,13 @@ class ThreeGangBloc extends Bloc { } } - Future deleteSchedule( - DeleteScheduleEvent event, Emitter emit) async { + Future deleteSchedule(DeleteScheduleEvent event, Emitter emit) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.deleteSchedule( - scheduleId: event.id, - deviceUuid: threeGangId, ); + scheduleId: event.id, + deviceUuid: threeGangId, + ); if (response == true) { add(GetScheduleEvent()); return toggleSchedule; @@ -605,27 +606,43 @@ class ThreeGangBloc extends Bloc { } } - void toggleCreateSchedule() { - emit(LoadingInitialState()); - createSchedule = !createSchedule; - selectedDays.clear(); - selectedTime=DateTime.now(); - emit(UpdateCreateScheduleState(createSchedule)); - emit(ChangeSlidingSegmentState(value: 1)); - } + // void toggleCreateSchedule() { + // emit(LoadingInitialState()); + // createSchedule = !createSchedule; + // selectedDays.clear(); + // selectedTime = DateTime.now(); + // emit(UpdateCreateScheduleState(createSchedule)); + // emit(ChangeSlidingSegmentState(value: 1)); + // } - int selectedTabIndex = 0; - void toggleSelectedIndex(index) { + + // void toggleSelectedIndex(index) { + // emit(LoadingInitialState()); + // selectedTabIndex = index; + // emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + // } + + + void toggleSelectedIndex( ToggleSelectedEvent event, Emitter emit) { emit(LoadingInitialState()); - selectedTabIndex = index; + selectedTabIndex =event.index; emit(ChangeSlidingSegmentState(value: selectedTabIndex)); } + void toggleCreateSchedule(ToggleCreateScheduleEvent event, Emitter emit) { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime = DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); +} + + + int selectedTabIndex = 0; bool toggleSchedule = true; List selectedDays = []; bool createSchedule = false; List listSchedule = []; - DateTime? selectedTime=DateTime.now(); - + 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 89ad267..12e727b 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 @@ -119,3 +119,20 @@ class DeleteScheduleEvent extends ThreeGangEvent { @override List get props => [id]; } + + + +class ToggleSelectedEvent extends ThreeGangEvent { + final int index; + const ToggleSelectedEvent({required this.index}); + @override + List get props => [index]; +} + + +class ToggleCreateScheduleEvent extends ThreeGangEvent { + final int index; + const ToggleCreateScheduleEvent({required this.index}); + @override + List get props => [index]; +} \ No newline at end of file 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 b2f1e45..0891db2 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,7 +7,7 @@ import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.d 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/group_two_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/two_gang_model.dart'; @@ -35,7 +35,8 @@ class TwoGangBloc extends Bloc { bool createSchedule = false; List listSchedule = []; - TwoGangBloc({required this.twoGangId,required this.switchCode}) : super(InitialState()) { + TwoGangBloc({required this.twoGangId, required this.switchCode}) + : super(InitialState()) { on(_fetchTwoGangStatus); on(_twoGangUpdated); on(_changeFirstSwitch); @@ -54,28 +55,47 @@ class TwoGangBloc extends Bloc { on(getSchedule); on(toggleRepeat); on(deleteSchedule); - + on(toggleSelectedIndex); + on(toggleCreateSchedule); } DateTime? selectedTime = DateTime.now(); - void toggleCreateSchedule() { - emit(LoadingInitialState()); - createSchedule = !createSchedule; - selectedDays.clear(); - selectedTime=DateTime.now(); - emit(UpdateCreateScheduleState(createSchedule)); - emit(ChangeSlidingSegmentState(value: 1)); - } + // void toggleCreateSchedule() { + // emit(LoadingInitialState()); + // createSchedule = !createSchedule; + // selectedDays.clear(); + // selectedTime = DateTime.now(); + // emit(UpdateCreateScheduleState(createSchedule)); + // emit(ChangeSlidingSegmentState(value: 1)); + // } + int selectedTabIndex = 0; - void toggleSelectedIndex(index) { + // void toggleSelectedIndex(index) { + // emit(LoadingInitialState()); + // selectedTabIndex = index; + // emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + // } + + void toggleSelectedIndex( + ToggleSelectedEvent event, Emitter emit) { emit(LoadingInitialState()); - selectedTabIndex = index; + selectedTabIndex = event.index; emit(ChangeSlidingSegmentState(value: selectedTabIndex)); } - void _fetchTwoGangStatus(InitialEvent event, Emitter emit) async { + void toggleCreateSchedule( + ToggleCreateScheduleEvent event, Emitter emit) { + emit(LoadingInitialState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime = DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); + } + + void _fetchTwoGangStatus( + InitialEvent event, Emitter emit) async { emit(LoadingInitialState()); try { twoGangGroup = event.groupScreen; @@ -363,16 +383,13 @@ 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 { @@ -462,9 +479,12 @@ class TwoGangBloc extends Bloc { add(ChangeSlidingSegment(value: 1)); } - Future saveSchedule(TwoGangSave event, Emitter emit,) async { + Future saveSchedule( + TwoGangSave event, + Emitter emit, + ) async { try { - if(selectedDays.isNotEmpty) { + if (selectedDays.isNotEmpty) { emit(LoadingInitialState()); final response = await DevicesAPI.postSchedule( category: switchCode, @@ -476,8 +496,9 @@ class TwoGangBloc extends Bloc { CustomSnackBar.displaySnackBar('Save Successfully'); add(GetScheduleEvent()); emit(TwoGangSaveSchedule()); - toggleCreateSchedule(); - }else{ + add(const ToggleCreateScheduleEvent(index: 1)); + // toggleCreateSchedule(); + } else { CustomSnackBar.displaySnackBar('Please select days'); } } catch (e) { @@ -485,7 +506,10 @@ class TwoGangBloc extends Bloc { } } - Future getSchedule(GetScheduleEvent event, Emitter emit,) async { + Future getSchedule( + GetScheduleEvent event, + Emitter emit, + ) async { try { emit(LoadingInitialState()); final response = await DevicesAPI.getSchedule( @@ -493,7 +517,8 @@ class TwoGangBloc extends Bloc { deviceId: twoGangId, ); List jsonData = response; - listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); + listSchedule = + jsonData.map((item) => ScheduleModel.fromJson(item)).toList(); emit(InitialState()); } on DioException catch (e) { final errorData = e.response!.data; @@ -514,9 +539,7 @@ class TwoGangBloc extends Bloc { try { emit(LoadingInitialState()); final response = await DevicesAPI.changeSchedule( - scheduleId: event.id, - deviceUuid: twoGangId, - enable: event.toggle); + scheduleId: event.id, deviceUuid: twoGangId, enable: event.toggle); if (response == true) { add(GetScheduleEvent()); toggleSchedule = event.toggle; @@ -537,7 +560,8 @@ class TwoGangBloc extends Bloc { emit(LoadingInitialState()); final response = await DevicesAPI.deleteSchedule( scheduleId: event.id, - deviceUuid: twoGangId, ); + deviceUuid: twoGangId, + ); if (response == true) { add(GetScheduleEvent()); return toggleSchedule; 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 9fd4737..db8bd96 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 @@ -62,7 +62,7 @@ class GroupAllOffEvent extends TwoGangEvent {} // two_gang_event.dart -class ToggleCreateScheduleEvent extends TwoGangEvent {} +// class ToggleCreateScheduleEvent extends TwoGangEvent {} @@ -125,6 +125,20 @@ class TabChangedEvent extends TwoGangEvent { } +class ToggleSelectedEvent extends TwoGangEvent { + final int index; + const ToggleSelectedEvent({required this.index}); + @override + List get props => [index]; +} + + +class ToggleCreateScheduleEvent extends TwoGangEvent { + final int index; + const ToggleCreateScheduleEvent({required this.index}); + @override + List get props => [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 72c8537..3435e4d 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 @@ -1,8 +1,7 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart'; +import 'package:syncrow_app/features/devices/model/group_two_gang_model.dart'; import 'package:syncrow_app/features/devices/model/two_gang_model.dart'; - class TwoGangState extends Equatable { const TwoGangState(); @@ -11,7 +10,9 @@ class TwoGangState extends Equatable { } class InitialState extends TwoGangState {} + class TwoGangSaveSchedule extends TwoGangState {} + class ChangeTimeState extends TwoGangState {} class LoadingInitialState extends TwoGangState {} @@ -78,13 +79,12 @@ class TimerRunInProgress extends TwoGangState { } class TimerRunComplete extends TwoGangState {} + class IsToggleState extends TwoGangState { final bool? onOff; const IsToggleState({this.onOff}); } - - // two_gang_state.dart class UpdateCreateScheduleState extends TwoGangState { final bool createSchedule; diff --git a/lib/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart b/lib/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart index 3a397c0..78e25ec 100644 --- a/lib/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart +++ b/lib/features/devices/bloc/water_heater_bloc/water_heater_bloc.dart @@ -48,6 +48,9 @@ class WaterHeaterBloc extends Bloc { on(_onTickTimer); on(_onClose); on(showTime); + + on(toggleSelectedIndex); + on(toggleCreateSchedule); } void _fetchWaterHeaterStatus(WaterHeaterInitial event, Emitter emit) async { @@ -201,7 +204,7 @@ class WaterHeaterBloc extends Bloc { CustomSnackBar.displaySnackBar('Save Successfully'); add(GetScheduleEvent()); emit(SaveSchedule()); - toggleCreateSchedule(); + add(const ToggleCreateScheduleEvent(index:1 )); }else{ CustomSnackBar.displaySnackBar('Please select days'); } @@ -272,22 +275,43 @@ class WaterHeaterBloc extends Bloc { } } - void toggleCreateSchedule() { - emit(WHLoadingState()); - createSchedule = !createSchedule; - selectedDays.clear(); - selectedTime=DateTime.now(); - emit(UpdateCreateScheduleState(createSchedule)); - } + // void toggleCreateSchedule() { + // emit(WHLoadingState()); + // createSchedule = !createSchedule; + // selectedDays.clear(); + // selectedTime=DateTime.now(); + // emit(UpdateCreateScheduleState(createSchedule)); + // } void toggleCreateCirculate() { - emit(WHLoadingState()); + emit(WHLoadingState()); createCirculate = !createCirculate; selectedDays.clear(); selectedTime=DateTime.now(); emit(UpdateCreateScheduleState(createCirculate)); } + + // void toggleSelectedIndex(index) { + // emit(WHLoadingState()); + // selectedTabIndex = index; + // emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + // } + void toggleSelectedIndex( ToggleSelectedEvent event, Emitter emit) { + emit(WHLoadingState()); + selectedTabIndex =event.index; + emit(ChangeSlidingSegmentState(value: selectedTabIndex)); + } + + + void toggleCreateSchedule(ToggleCreateScheduleEvent event, Emitter emit) { + emit(WHLoadingState()); + createSchedule = !createSchedule; + selectedDays.clear(); + selectedTime = DateTime.now(); + emit(UpdateCreateScheduleState(createSchedule)); +} + Future toggleDaySelection( ToggleDaySelectionEvent event, Emitter emit, @@ -303,11 +327,6 @@ class WaterHeaterBloc extends Bloc { int selectedTabIndex = 0; - void toggleSelectedIndex(index) { - emit(WHLoadingState()); - selectedTabIndex = index; - emit(ChangeSlidingSegmentState(value: selectedTabIndex)); - } showTime(SelectTimeEvent event, Emitter emit) async { final TimeOfDay? timePicked = await showTimePicker( diff --git a/lib/features/devices/bloc/water_heater_bloc/water_heater_event.dart b/lib/features/devices/bloc/water_heater_bloc/water_heater_event.dart index a0f8ef9..d39efa9 100644 --- a/lib/features/devices/bloc/water_heater_bloc/water_heater_event.dart +++ b/lib/features/devices/bloc/water_heater_bloc/water_heater_event.dart @@ -98,4 +98,28 @@ class SelectTimeEvent extends WaterHeaterEvent { const SelectTimeEvent({required this.context, required this.isEffective}); @override List get props => [context, isEffective]; -} \ No newline at end of file +} + + +class ToggleSelectedEvent extends WaterHeaterEvent { + final int index; + const ToggleSelectedEvent({required this.index}); + @override + List get props => [index]; +} + + +class ToggleCreateScheduleEvent extends WaterHeaterEvent { + final int index; + const ToggleCreateScheduleEvent({required this.index}); + @override + List get props => [index]; +} + + +class ToggleCreateCirculateEvent extends WaterHeaterEvent { + final int index; + const ToggleCreateCirculateEvent({required this.index}); + @override + List get props => [index]; +} diff --git a/lib/features/devices/model/device_category_model.dart b/lib/features/devices/model/device_category_model.dart index 99ed743..e391d3a 100644 --- a/lib/features/devices/model/device_category_model.dart +++ b/lib/features/devices/model/device_category_model.dart @@ -42,7 +42,9 @@ class DevicesCategoryModel { : name = json['groupName'], // id = json['groupId'], type = devicesTypesMap[json['groupName']] ?? DeviceType.Other, - icon = deviceTypeIconMap[devicesTypesMap[json['groupName']] ?? DeviceType.Other] ?? '', + icon = deviceTypeIconMap[ + devicesTypesMap[json['groupName']] ?? DeviceType.Other] ?? + '', devices = [], isSelected = false; @@ -60,5 +62,9 @@ Map deviceTypeIconMap = { DeviceType.CeilingSensor: Assets.assetsIconsSensors, DeviceType.WallSensor: Assets.assetsIconsSensors, DeviceType.ThreeGang: Assets.assetsIconsGang, + DeviceType.OneGang: Assets.oneGang, + DeviceType.TwoGang: Assets.twoGang, + DeviceType.WH: Assets.waterHeaterIcon, + DeviceType.DS: Assets.doorSensorIcon, DeviceType.Other: Assets.assetsIconsAC, }; diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 2e961f5..866c51a 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -62,11 +62,14 @@ class DeviceModel { tempIcon = Assets.oneGang; } else if (type == DeviceType.TwoGang) { tempIcon = Assets.twoGang; - }else if (type == DeviceType.WH) { + } else if (type == DeviceType.WH) { tempIcon = Assets.waterHeaterIcon; + } else if (type == DeviceType.DS) { + tempIcon = Assets.doorSensorIcon; } else { tempIcon = Assets.assetsIconsLogo; } + return DeviceModel( icon: tempIcon, activeTime: json['activeTime'], diff --git a/lib/features/devices/model/device_report_model.dart b/lib/features/devices/model/device_report_model.dart new file mode 100644 index 0000000..c640d81 --- /dev/null +++ b/lib/features/devices/model/device_report_model.dart @@ -0,0 +1,51 @@ +class DeviceReport { + final dynamic deviceUuid; + final dynamic startTime; + final dynamic endTime; + final List? data; + + DeviceReport({ + this.deviceUuid, + this.startTime, + this.endTime, + this.data, + }); + + DeviceReport.fromJson(Map json) + : deviceUuid = json['deviceUuid'] as String?, + startTime = json['startTime'] as String?, + endTime = json['endTime'] as String?, + data = (json['data'] as List?) + ?.map((e) => DeviceEvent.fromJson(e as Map)) + .toList(); + + Map toJson() => { + 'deviceUuid': deviceUuid, + 'startTime': startTime, + 'endTime': endTime, + 'data': data?.map((e) => e.toJson()).toList(), + }; +} + +class DeviceEvent { + final String? code; + final int? eventTime; + final String? value; + + DeviceEvent({ + this.code, + this.eventTime, + this.value, + }); + + DeviceEvent.fromJson(Map json) + : code = json['code'] as String?, + eventTime = json['eventTime'] as int?, + value = json['value'] as String?; + + Map toJson() => { + 'code': code, + 'eventTime': eventTime, + 'value': value, + }; +} diff --git a/lib/features/devices/model/door_sensor_model.dart b/lib/features/devices/model/door_sensor_model.dart new file mode 100644 index 0000000..7ef8b1d --- /dev/null +++ b/lib/features/devices/model/door_sensor_model.dart @@ -0,0 +1,33 @@ + + + + +import 'package:syncrow_app/features/devices/model/status_model.dart'; + +class DoorSensorModel { + bool doorContactState; + int batteryPercentage; + + DoorSensorModel( + {required this.doorContactState, + required this.batteryPercentage, + }); + + factory DoorSensorModel.fromJson(List jsonList) { + late bool _doorContactState; + late int _batteryPercentage; + + + for (int i = 0; i < jsonList.length; i++) { + if (jsonList[i].code == 'doorcontact_state') { + _doorContactState = jsonList[i].value ?? false; + } else if (jsonList[i].code == 'battery_percentage') { + _batteryPercentage = jsonList[i].value ?? 0; + } + } + return DoorSensorModel( + doorContactState: _doorContactState, + batteryPercentage: _batteryPercentage, + ); + } +} diff --git a/lib/features/devices/model/group_one_gang_model.dart b/lib/features/devices/model/group_one_gang_model.dart new file mode 100644 index 0000000..419f127 --- /dev/null +++ b/lib/features/devices/model/group_one_gang_model.dart @@ -0,0 +1,11 @@ +class GroupOneGangModel { + final String deviceId; + final String deviceName; + bool firstSwitch; + + GroupOneGangModel({ + required this.deviceId, + required this.deviceName, + required this.firstSwitch, + }); +} diff --git a/lib/features/devices/model/groupTwoGangModel.dart b/lib/features/devices/model/group_two_gang_model.dart similarity index 100% rename from lib/features/devices/model/groupTwoGangModel.dart rename to lib/features/devices/model/group_two_gang_model.dart diff --git a/lib/features/devices/model/report_model.dart b/lib/features/devices/model/report_model.dart new file mode 100644 index 0000000..7b47e47 --- /dev/null +++ b/lib/features/devices/model/report_model.dart @@ -0,0 +1,23 @@ +// Record model +class Record { + final String status; + final String time; + final bool isOpen; + + Record({ + required this.status, + required this.time, + required this.isOpen, + }); +} + +// RecordGroup model to group records by date +class RecordGroup { + final DateTime date; + final List records; + + RecordGroup({ + required this.date, + required this.records, + }); +} diff --git a/lib/features/devices/view/widgets/curtains/curtain_list.dart b/lib/features/devices/view/widgets/curtains/curtain_list.dart index bf3900d..4c66149 100644 --- a/lib/features/devices/view/widgets/curtains/curtain_list.dart +++ b/lib/features/devices/view/widgets/curtains/curtain_list.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/device_model.dart'; -import 'package:syncrow_app/features/devices/view/widgets/universal_switch.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 CurtainList extends StatelessWidget { diff --git a/lib/features/devices/view/widgets/curtains/curtain_view.dart b/lib/features/devices/view/widgets/curtains/curtain_view.dart index 153bce8..9f89aa2 100644 --- a/lib/features/devices/view/widgets/curtains/curtain_view.dart +++ b/lib/features/devices/view/widgets/curtains/curtain_view.dart @@ -20,16 +20,16 @@ class CurtainView extends StatelessWidget { child: BlocBuilder( builder: (context, state) { double curtainWidth = 270; - double blindHeight = 310; + // double blindHeight = 310; if (state is CurtainsOpening) { curtainWidth = state.curtainWidth; - blindHeight = state.blindHeight; + // blindHeight = state.blindHeight; } else if (state is CurtainsClosing) { curtainWidth = state.curtainWidth; - blindHeight = state.blindHeight; + // blindHeight = state.blindHeight; } else if (state is CurtainsPaused) { curtainWidth = state.curtainWidth; - blindHeight = state.blindHeight; + // blindHeight = state.blindHeight; } return DefaultScaffold( title: curtain.name, @@ -58,7 +58,8 @@ class CurtainView extends StatelessWidget { width: curtainWidth, child: Stack( children: List.generate( - 10, (index) { + 10, + (index) { double spacing = curtainWidth / 9; double leftMostPosition = index * spacing; return AnimatedPositioned( diff --git a/lib/features/devices/view/widgets/device_appbar.dart b/lib/features/devices/view/widgets/device_appbar.dart index 7de34d7..9640429 100644 --- a/lib/features/devices/view/widgets/device_appbar.dart +++ b/lib/features/devices/view/widgets/device_appbar.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; import 'package:syncrow_app/features/devices/view/widgets/popup_menu_widget.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; @@ -11,30 +10,33 @@ class DeviceAppbar extends StatelessWidget implements PreferredSizeWidget { final String deviceUuid; final double appBarHeight = 56.0; final void Function()? onPressed; - const DeviceAppbar({super.key, required this.deviceName, required this.deviceUuid,this.onPressed}); + const DeviceAppbar( + {super.key, required this.deviceName, required this.deviceUuid, this.onPressed}); @override Widget build(BuildContext context) { - return AppBar( + return AppBar( backgroundColor: Colors.transparent, centerTitle: true, title: BodyLarge( - text: deviceName ?? "", + text: deviceName, fontColor: ColorsManager.primaryColor, fontWeight: FontsManager.bold, ), actions: [ - IconButton(onPressed: () { - showPopupMenu(context: context, items: [ - PopupMenuItem( - onTap: () async { - HomeCubit.getInstance().updateDevice(deviceUuid); - }, - value: 'Update', - child: const Text('Update'), - ) - ]); - }, icon: Icon(Icons.edit)) + IconButton( + onPressed: () { + showPopupMenu(context: context, items: [ + PopupMenuItem( + onTap: () async { + HomeCubit.getInstance().updateDevice(deviceUuid); + }, + value: 'Update', + child: const Text('Update'), + ) + ]); + }, + icon: Icon(Icons.edit)) ], ); } diff --git a/lib/features/devices/view/widgets/door_sensor/door_notification_settings.dart b/lib/features/devices/view/widgets/door_sensor/door_notification_settings.dart new file mode 100644 index 0000000..6bb6d6f --- /dev/null +++ b/lib/features/devices/view/widgets/door_sensor/door_notification_settings.dart @@ -0,0 +1,114 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_event.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class NotificationSettingsPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Notification Settings', + child: BlocProvider( + create: (context) => DoorSensorBloc(DSId: '')..add(const DoorSensorInitial()), + child: BlocBuilder( + builder: (context, state) { + final doorSensorBloc = BlocProvider.of(context); + + return state is DoorSensorLoadingState + ? const Center( + child: DefaultContainer( + width: 50, height: 50, child: CircularProgressIndicator()), + ) + : Column( + children: [ + DefaultContainer( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 50, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyLarge( + text: 'Low Battery', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: doorSensorBloc.lowBattery, + onChanged: (value) { + context + .read() + .add(ToggleLowBatteryEvent(value)); + }, + applyTheme: true, + )), + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + SizedBox( + height: 50, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyLarge( + text: 'Closing Reminder', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: doorSensorBloc.closingReminder, + onChanged: (value) { + context + .read() + .add(ToggleClosingReminderEvent(value)); + }, + applyTheme: true, + )), + ), + ), + const Divider( + color: ColorsManager.graysColor, + ), + SizedBox( + height: 50, + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyLarge( + text: 'Door Alarm', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: doorSensorBloc.closingReminder, + onChanged: (value) { + context + .read() + .add(ToggleDoorAlarmEvent(value)); + }, + applyTheme: true, + )), + ), + ), + ], + ), + ), + ], + ); + }, + ), + )); + } +} diff --git a/lib/features/devices/view/widgets/door_sensor/door_records_screen.dart b/lib/features/devices/view/widgets/door_sensor/door_records_screen.dart new file mode 100644 index 0000000..556a983 --- /dev/null +++ b/lib/features/devices/view/widgets/door_sensor/door_records_screen.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_event.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart'; +import 'package:syncrow_app/features/devices/model/device_report_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class DoorRecordsScreen extends StatelessWidget { + final String DSId; + const DoorRecordsScreen({super.key, required this.DSId}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Records', + child: BlocProvider( + create: (context) => + DoorSensorBloc(DSId: DSId)..add(const ReportLogsInitial()), + child: BlocBuilder( + builder: (context, state) { + final doorSensorBloc = BlocProvider.of(context); + + if (state is DoorSensorLoadingState) { + return const Center( + child: DefaultContainer( + width: 50, height: 50, child: CircularProgressIndicator()), + ); + } else if (state is UpdateState) { + // Group records by formatted date + final Map> groupedRecords = {}; + + // Iterate over the data list in DeviceReport + for (var record in doorSensorBloc.recordGroups.data!) { + final DateTime eventDateTime = + DateTime.fromMillisecondsSinceEpoch(record.eventTime!); + final String formattedDate = + DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime); + + // Group by formatted date + if (groupedRecords.containsKey(formattedDate)) { + groupedRecords[formattedDate]!.add(record); + } else { + groupedRecords[formattedDate] = [record]; + } + } + + // Build the ListView with grouped data + return ListView.builder( + itemCount: groupedRecords.length, + itemBuilder: (context, index) { + final String date = groupedRecords.keys.elementAt(index); + final List recordsForDate = + groupedRecords[date]!; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Date header + Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + date, + style: const TextStyle( + color: ColorsManager.grayColor, + fontSize: 13, + fontWeight: FontWeight.w700, + ), + ), + ), + // List of records for the specific date + DefaultContainer( + child: Column( + children: [ + ...recordsForDate.asMap().entries.map((entry) { + final int idx = entry.key; + final DeviceEvent record = entry.value; + final DateTime eventDateTime = + DateTime.fromMillisecondsSinceEpoch( + record.eventTime!); + final String formattedTime = + DateFormat('HH:mm:ss').format(eventDateTime); + + return Column( + children: [ + Container( + child: ListTile( + leading: Icon( + record.value == 'true' + ? Icons.radio_button_checked + : Icons.radio_button_unchecked, + color: record.value == 'true' + ? Colors.blue + : Colors.grey, + ), + title: Text( + record.value == 'true' + ? "Opened" + : "Closed", + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + subtitle: Text('$formattedTime'), + ), + ), + // Only add Divider if it's not the last item + if (idx != recordsForDate.length - 1) + const Divider( + color: ColorsManager.graysColor, + ), + ], + ); + }).toList(), + ], + ), + ), + ], + ); + }, + ); + } + return const Center(child: Text('No data available.')); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/door_sensor/door_sensor_screen.dart b/lib/features/devices/view/widgets/door_sensor/door_sensor_screen.dart new file mode 100644 index 0000000..1ae3961 --- /dev/null +++ b/lib/features/devices/view/widgets/door_sensor/door_sensor_screen.dart @@ -0,0 +1,189 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_event.dart'; +import 'package:syncrow_app/features/devices/bloc/door_sensor_bloc/door_sensor_state.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/door_sensor_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_notification_settings.dart'; +import 'package:syncrow_app/features/devices/view/widgets/door_sensor/door_records_screen.dart'; +import 'package:syncrow_app/features/shared_widgets/battery_bar.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +class DoorSensorScreen extends StatelessWidget { + final DeviceModel? device; + + const DoorSensorScreen({super.key, this.device}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Door Sensor', + child: BlocProvider( + create: (context) => + DoorSensorBloc(DSId: device?.uuid ?? '')..add(const DoorSensorInitial()), + child: BlocBuilder( + builder: (context, state) { + final doorSensorBloc = BlocProvider.of(context); + DoorSensorModel model = DoorSensorModel(batteryPercentage: 0, doorContactState: false); + if (state is LoadingNewSate) { + model = state.doorSensor; + } else if (state is UpdateState) { + model = state.doorSensor; + } + return state is DoorSensorLoadingState + ? const Center( + child: + DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), + ) + : RefreshIndicator( + onRefresh: () async { + doorSensorBloc.add(const DoorSensorInitial()); + }, + child: ListView( + children: [ + SizedBox( + height: MediaQuery.sizeOf(context).height * 0.8, + child: Column( + children: [ + BatteryBar( + batteryPercentage: model.batteryPercentage, + ), + Expanded( + flex: 4, + child: InkWell( + overlayColor: WidgetStateProperty.all(Colors.transparent), + onTap: () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + // color: Colors.white.withOpacity(0.1), + borderRadius: BorderRadius.circular(890), + boxShadow: [ + BoxShadow( + color: Colors.white.withOpacity(0.1), + blurRadius: 24, + offset: const Offset(-5, -5), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.black.withOpacity(0.11), + blurRadius: 25, + offset: const Offset(5, 5), + blurStyle: BlurStyle.outer, + ), + BoxShadow( + color: Colors.black.withOpacity(0.13), + blurRadius: 30, + offset: const Offset(5, 5), + blurStyle: BlurStyle.inner, + ), + ], + ), + child: SvgPicture.asset( + model.doorContactState + ? Assets.doorOpen + : Assets.doorClose, + fit: BoxFit.fill, + ), + ), + ], + ), + ), + ), + Flexible( + child: Row( + children: [ + Expanded( + child: DefaultContainer( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + DoorRecordsScreen(DSId: device!.uuid!)), + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: + const BoxConstraints(maxHeight: 46, maxWidth: 50), + child: SvgPicture.asset(Assets.doorRecordsIcon), + ), + const SizedBox( + height: 15, + ), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Records', + // doorLockButtons.keys.elementAt(index), + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + const SizedBox( + width: 10, + ), + Expanded( + child: DefaultContainer( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => NotificationSettingsPage()), + ); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ConstrainedBox( + constraints: + const BoxConstraints(maxHeight: 46, maxWidth: 50), + child: + SvgPicture.asset(Assets.doorNotificationSetting), + ), + const SizedBox( + height: 15, + ), + const Flexible( + child: FittedBox( + child: BodySmall( + text: 'Notification Settings', + // doorLockButtons.keys.elementAt(index), + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ) + ], + ), + ), + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/gateway/gateway_view.dart b/lib/features/devices/view/widgets/gateway/gateway_view.dart index 62ce1ed..0fa9831 100644 --- a/lib/features/devices/view/widgets/gateway/gateway_view.dart +++ b/lib/features/devices/view/widgets/gateway/gateway_view.dart @@ -8,12 +8,10 @@ import 'package:syncrow_app/features/devices/bloc/gateway_bloc/gateway_state.dar 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/room_page_switch.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 '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 GateWayView extends StatelessWidget { final DeviceModel gatewayObj; @@ -38,9 +36,10 @@ class GateWayView extends StatelessWidget { backgroundColor: ColorsManager.backgroundColor, extendBodyBehindAppBar: true, extendBody: true, - appBar:DeviceAppbar( - deviceName: 'Gateway', - deviceUuid: gatewayObj.uuid!,), + appBar: DeviceAppbar( + deviceName: 'Gateway', + deviceUuid: gatewayObj.uuid!, + ), body: Container( width: MediaQuery.sizeOf(context).width, height: MediaQuery.sizeOf(context).height, @@ -55,11 +54,11 @@ class GateWayView extends StatelessWidget { ), ), child: RefreshIndicator( - onRefresh:()async { - BlocProvider.of(context).add(GatewayInitial(gatewayId: gatewayObj.uuid ?? '')); - }, - child: ListView( - + onRefresh: () async { + BlocProvider.of(context) + .add(GatewayInitial(gatewayId: gatewayObj.uuid ?? '')); + }, + child: ListView( children: [ Container( height: MediaQuery.of(context).size.height, @@ -120,7 +119,8 @@ class GateWayView extends StatelessWidget { ) : Expanded( child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, @@ -141,8 +141,7 @@ class GateWayView extends StatelessWidget { ], ), ), - ) - )); + ))); })); } } diff --git a/lib/features/devices/view/widgets/lights/lights_view_list.dart b/lib/features/devices/view/widgets/lights/lights_view_list.dart index 98e9757..576a103 100644 --- a/lib/features/devices/view/widgets/lights/lights_view_list.dart +++ b/lib/features/devices/view/widgets/lights/lights_view_list.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/lights_list.dart'; import 'package:syncrow_app/features/devices/view/widgets/universal_switch.dart'; diff --git a/lib/features/devices/view/widgets/offline_one_time_password_page.dart b/lib/features/devices/view/widgets/offline_one_time_password_page.dart index 6c560cd..baadcc8 100644 --- a/lib/features/devices/view/widgets/offline_one_time_password_page.dart +++ b/lib/features/devices/view/widgets/offline_one_time_password_page.dart @@ -1,5 +1,3 @@ - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -7,7 +5,6 @@ import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_blo import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/repeat_widget.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/default_scaffold.dart'; import 'package:syncrow_app/features/shared_widgets/door_lock_button.dart'; @@ -29,14 +26,12 @@ class OfflineOneTimePasswordPage extends StatelessWidget { create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!), child: BlocConsumer(listener: (context, state) { if (state is FailedState) { - CustomSnackBar.displaySnackBar( - state.errorMessage - ); + CustomSnackBar.displaySnackBar(state.errorMessage); } - if (state is IsRepeatState){ + if (state is IsRepeatState) { isRepeat = state.repeat; } - if (state is GeneratePasswordOneTimestate ){ + if (state is GeneratePasswordOneTimestate) { generated = state.generated; } }, builder: (context, state) { @@ -54,170 +49,173 @@ class OfflineOneTimePasswordPage extends StatelessWidget { onPressed: () { Navigator.of(context).pop(true); }, - icon: const Icon(Icons.arrow_back) - ), + icon: const Icon(Icons.arrow_back)), ), child: state is LoadingInitialState ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const BodyMedium( - text: 'Save the password immediately. The password is not displayed in the app.', - fontWeight: FontWeight.normal, - fontColor: ColorsManager.grayColor, - ), - const SizedBox( - height: 20, - ), - const BodyMedium( - text: '7-Digit Password', - fontWeight: FontWeight.normal, - fontColor: ColorsManager.grayColor, - ), - DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children:smartDoorBloc.passwordController.text.isEmpty? - List.generate(10, (index) { - return const Padding( - padding: EdgeInsets.symmetric(horizontal: 4.0,vertical: 15), - child: Icon( - Icons.circle, - size: 20.0, - color: Colors.black, - ), - ); - }) :[ - Expanded( - child: Row( - children: [ - - Expanded( - child: BodyLarge( - style: const TextStyle( - color: ColorsManager.primaryColor, - fontWeight: FontWeight.bold, - letterSpacing: 8.0 , - fontSize: 25, - wordSpacing: 2), - textAlign: TextAlign.center, - text: smartDoorBloc.passwordController.text, - fontSize: 23, - ),), - - IconButton( - onPressed: () async { - await Clipboard.setData(ClipboardData( - text: smartDoorBloc.passwordController.text)); - }, - icon: const Icon(Icons.copy) - ), - ], - ), - ), - ], - )), - const SizedBox( - width: 10, - ), - - ], + const BodyMedium( + text: + 'Save the password immediately. The password is not displayed in the app.', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, ), - if(smartDoorBloc.passwordController.text.isNotEmpty) - Column( + const SizedBox( + height: 20, + ), + const BodyMedium( + text: '7-Digit Password', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, + ), + DefaultContainer( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), + child: Column( children: [ - const Divider( - color: ColorsManager.graysColor, - ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, children: [ - Expanded( - child: Container( - padding: const EdgeInsets.all(10.0), - child: const BodyMedium( - text: 'Password Name', - fontWeight: FontWeight.normal, - ), - ), + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: smartDoorBloc.passwordController.text.isEmpty + ? List.generate(10, (index) { + return const Padding( + padding: EdgeInsets.symmetric( + horizontal: 4.0, vertical: 15), + child: Icon( + Icons.circle, + size: 20.0, + color: Colors.black, + ), + ); + }) + : [ + Expanded( + child: Row( + children: [ + Expanded( + child: BodyLarge( + style: const TextStyle( + color: ColorsManager.primaryColor, + fontWeight: FontWeight.bold, + letterSpacing: 8.0, + fontSize: 25, + wordSpacing: 2), + textAlign: TextAlign.center, + text: smartDoorBloc.passwordController.text, + fontSize: 23, + ), + ), + IconButton( + onPressed: () async { + await Clipboard.setData(ClipboardData( + text: smartDoorBloc + .passwordController.text)); + }, + icon: const Icon(Icons.copy)), + ], + ), + ), + ], + )), + const SizedBox( + width: 10, ), - SizedBox( - width: MediaQuery.of(context).size.width / 2.6, - child: TextFormField( - controller: BlocProvider.of(context).passwordNameController, - decoration: const InputDecoration( - hintText: 'Enter The Name', - hintStyle: TextStyle( - fontSize: 14, color: ColorsManager.textGray)), - )), ], ), - + if (smartDoorBloc.passwordController.text.isNotEmpty) + Column( + children: [ + const Divider( + color: ColorsManager.graysColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.all(10.0), + child: const BodyMedium( + text: 'Password Name', + fontWeight: FontWeight.normal, + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width / 2.6, + child: TextFormField( + controller: BlocProvider.of(context) + .passwordNameController, + decoration: const InputDecoration( + hintText: 'Enter The Name', + hintStyle: TextStyle( + fontSize: 14, color: ColorsManager.textGray)), + )), + ], + ), + ], + ), ], ), + ), + const SizedBox( + height: 20, + ), + const BodyMedium( + textAlign: TextAlign.center, + text: + 'Save the password immediately. The password is not displayed in the app.', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, + ), + + // NameTimeWidget(type:type!), + const SizedBox( + height: 20, + ), + Center( + child: SizedBox( + width: MediaQuery.of(context).size.width / 1.5, + child: DoorLockButton( + isDone: generated, + isLoading: smartDoorBloc.isSavingPassword, + borderRadius: 30, + backgroundColor: ColorsManager.primaryColor, + onPressed: () async { + if (generated == false) { + smartDoorBloc + .add(GenerateAndSavePasswordOneTimeEvent(context: context)); + } else { + if (smartDoorBloc.passwordNameController.text.isNotEmpty) { + smartDoorBloc.add(RenamePasswordEvent()); + } + Navigator.of(context).pop(true); + } + }, + child: const BodyMedium( + text: 'Obtain Password', + fontWeight: FontWeight.bold, + fontColor: Colors.white, + ), + ), + ), + ), + const SizedBox( + height: 20, + ), + isRepeat ? const RepeatWidget() : const SizedBox(), + const SizedBox( + height: 40, + ) ], ), ), - const SizedBox( - height: 20, - ), - const BodyMedium( - textAlign: TextAlign.center, - text: 'Save the password immediately. The password is not displayed in the app.', - fontWeight: FontWeight.normal, - fontColor: ColorsManager.grayColor, - ), - - // NameTimeWidget(type:type!), - const SizedBox( - height: 20, - ), - Center( - child: SizedBox( - width: MediaQuery.of(context).size.width/1.5, - child: DoorLockButton( - isDone: generated, - isLoading: smartDoorBloc.isSavingPassword , - borderRadius: 30, - backgroundColor:ColorsManager.primaryColor , - onPressed: () async { - if(generated==false){ - smartDoorBloc.add(GenerateAndSavePasswordOneTimeEvent(context: context)); - }else{ - if(smartDoorBloc.passwordNameController.text.isNotEmpty){ - smartDoorBloc.add(RenamePasswordEvent()); - } - Navigator.of(context).pop(true); - } - }, - child: const BodyMedium( - text: 'Obtain Password', - fontWeight: FontWeight.bold, - fontColor: Colors.white, - ),), - ), - ), - const SizedBox( - height: 20, - ), - isRepeat? const RepeatWidget():const SizedBox(), - const SizedBox( - height: 40, - ) - ], - ), - ), ); })); } 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 index 636a730..28f2728 100644 --- a/lib/features/devices/view/widgets/one_gang/one_gang_Interface.dart +++ b/lib/features/devices/view/widgets/one_gang/one_gang_Interface.dart @@ -3,7 +3,6 @@ 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'; diff --git a/lib/features/devices/view/widgets/one_gang/one_gang_list.dart b/lib/features/devices/view/widgets/one_gang/one_gang_list.dart new file mode 100644 index 0000000..ba03339 --- /dev/null +++ b/lib/features/devices/view/widgets/one_gang/one_gang_list.dart @@ -0,0 +1,66 @@ +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_event.dart'; +import 'package:syncrow_app/features/devices/bloc/one_gang_bloc/one_gang_state.dart'; +import 'package:syncrow_app/features/devices/model/group_one_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 OneGangList extends StatelessWidget { + const OneGangList({super.key, required this.oneGangList, required this.allSwitches}); + + final List oneGangList; + 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: oneGangList.length, + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10), + BodySmall(text: oneGangList[index].deviceName), + const SizedBox(height: 5), + DevicesDefaultSwitch( + switchValue: oneGangList[index].firstSwitch, + action: () { + BlocProvider.of(context).add(ChangeFirstSwitchStatusEvent( + value: oneGangList[index].firstSwitch, + deviceId: oneGangList[index].deviceId)); + }, + ), + ], + ); + }, + ), + ], + ), + ); + }, + ); + } +} 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 21aee6d..2bc3353 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 @@ -3,7 +3,9 @@ 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/group_one_gang_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_list.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'; @@ -20,10 +22,9 @@ class OneGangScreen extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => OneGangBloc( - switchCode: 'switch_1', - oneGangId : device?.uuid ?? '') - ..add(const InitialEvent(groupScreen:false)), + create: (context) => + OneGangBloc(switchCode: 'switch_1', oneGangId: device?.uuid ?? '') + ..add(const InitialEvent(groupScreen: false)), child: BlocBuilder( builder: (context, state) { OneGangModel oneGangModel = OneGangModel( @@ -31,141 +32,166 @@ class OneGangScreen extends StatelessWidget { firstCountDown: 0, ); + List groupOneGangModel = []; + bool allSwitchesOn = false; + if (state is LoadingNewSate) { oneGangModel = state.oneGangModel; } else if (state is UpdateState) { oneGangModel = state.oneGangModel; + } else if (state is UpdateGroupState) { + groupOneGangModel = state.oneGangList; + allSwitchesOn = state.allSwitches; } - return state is LoadingInitialState ? - const Center( - child: - DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), - ): - RefreshIndicator( - onRefresh: () async { - BlocProvider.of(context) - .add(InitialEvent(groupScreen: device != null ? false : true)); - }, - child: ListView( - children: [ - SizedBox( - height: MediaQuery.of(context).size.height, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Expanded(child: SizedBox.shrink()), - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Column( + return state is LoadingInitialState + ? const Center( + child: DefaultContainer( + width: 50, + height: 50, + child: CircularProgressIndicator()), + ) + : device == null + ? OneGangList( + oneGangList: groupOneGangModel, + 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: [ - 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) => TimerScheduleScreen( - switchCode:'switch_1' , - 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), + 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)); + }, ), - ), - 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: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: " Entrance Light", + fontColor: ColorsManager + .textPrimaryColor, + textAlign: TextAlign.center, ), ), - ), - ], - ), + ], + ), + ], ), ), - const SizedBox(height: 10), - BodySmall( - text: "Timer", - style: context.bodyMedium.copyWith( - color: ColorsManager.textPrimaryColor, + 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: () { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, + animation1, + animation2) => + TimerScheduleScreen( + switchCode: + 'switch_1', + 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()) ], ), - - ], - ), + ), + ], ), - 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 index 605d5d5..2d766c1 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 @@ -17,16 +17,15 @@ import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; - class TimerScheduleScreen extends StatelessWidget { final DeviceModel device; final String deviceCode; final String switchCode; const TimerScheduleScreen( {required this.device, - required this.deviceCode, - required this.switchCode, - super.key}); + required this.deviceCode, + required this.switchCode, + super.key}); @override Widget build(BuildContext context) { @@ -37,9 +36,9 @@ class TimerScheduleScreen extends StatelessWidget { ), child: BlocProvider( create: (context) => - OneGangBloc(switchCode: switchCode, oneGangId: device.uuid ?? '') - ..add(GetCounterEvent(deviceCode: deviceCode)) - ..add(GetScheduleEvent()), + OneGangBloc(switchCode: switchCode, oneGangId: device.uuid ?? '') + ..add(GetCounterEvent(deviceCode: deviceCode)) + ..add(GetScheduleEvent()), child: BlocBuilder( builder: (context, state) { final oneGangBloc = BlocProvider.of(context); @@ -64,7 +63,7 @@ class TimerScheduleScreen extends StatelessWidget { }, child: DefaultTabController( length: 2, - child: DefaultScaffold( + child: DefaultScaffold( appBar: AppBar( backgroundColor: Colors.transparent, centerTitle: true, @@ -74,175 +73,213 @@ class TimerScheduleScreen extends StatelessWidget { fontWeight: FontsManager.bold, ), 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(), + oneGangBloc.createSchedule == true + ? TextButton( + onPressed: () { + oneGangBloc.add(ScheduleSave()); + }, + child: const Text('Save')) + : oneGangBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + // oneGangBloc.toggleCreateSchedule(); + oneGangBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), ], ), - child: - state is LoadingInitialState? - const Center(child: CircularProgressIndicator()): - Column( - children: [ - Container( - width: MediaQuery.of(context).size.width, - decoration: const ShapeDecoration( - color: ColorsManager.onPrimaryColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(30)), - ), - ), - child: TabBar( - onTap: (value) { - if(value==0){ - if(oneGangBloc.createSchedule == true){ - oneGangBloc.toggleCreateSchedule(); - } - oneGangBloc.toggleSelectedIndex(0); - }else{ - oneGangBloc.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)), - ), - ), - tabs: [ - Tab( - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 10), - child: BodySmall( - text: 'Countdown', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - Tab( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Text( - 'Schedule', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ], - ), - ), - Expanded( - child: TabBarView( + child: state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Column( 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)), - ], + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(30)), ), ), - ), - 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, - )); - }, - ), + child: TabBar( + onTap: (value) { + if (value == 0) { + if (oneGangBloc.createSchedule == + true) { + // oneGangBloc.toggleCreateSchedule(); + oneGangBloc.add( + const ToggleCreateScheduleEvent( + index: 0)); + } + oneGangBloc.add( + const ToggleSelectedEvent( + index: 0)); + } else { + oneGangBloc.add( + const ToggleSelectedEvent( + index: 1)); + } + }, + indicatorColor: + Colors.white, // Customize the indicator + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20)), ), ), - ], + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + physics: const NeverScrollableScrollPhysics(), // Disable swiping + + children: [ + Center( + child: Container( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + countNum > 0 + ? BodyLarge( + text: _formatDuration( + countNum), + fontColor: ColorsManager + .slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: + CupertinoTimerPickerMode + .hm, + onTimerDurationChanged: + (Duration + newDuration) { + duration = newDuration; + }, + ), + GestureDetector( + onTap: () { + if (state + is LoadingNewSate) { + return; + } + if (countNum > 0) { + 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)), + ], + ), + ), + ), + 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, + )); + }, + ), + ), + ), + ], + ), ), ], ), - ), - ], - ), - )) - ); + ))); }, ), ), diff --git a/lib/features/devices/view/widgets/room_page.dart b/lib/features/devices/view/widgets/room_page.dart index 68b2ac1..ce2028a 100644 --- a/lib/features/devices/view/widgets/room_page.dart +++ b/lib/features/devices/view/widgets/room_page.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/model/room_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/room_page_switch.dart'; diff --git a/lib/features/devices/view/widgets/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index acd7669..93260dd 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -9,10 +9,10 @@ import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; 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/door_sensor/door_sensor_screen.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/wall_sensor/wall_sensor_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart'; @@ -31,7 +31,6 @@ class RoomPageSwitch extends StatelessWidget { }); final DeviceModel device; - @override Widget build(BuildContext context) { return GestureDetector( @@ -87,7 +86,8 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => ACsView(deviceModel: device))); + pageBuilder: (context, animation1, animation2) => + ACsView(deviceModel: device))); // navigateToInterface(ACsView(deviceModel: device), context); break; case DeviceType.WallSensor: @@ -110,8 +110,9 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => - CurtainView(curtain: device,))); + pageBuilder: (context, animation1, animation2) => CurtainView( + curtain: device, + ))); break; case DeviceType.Blind: break; @@ -119,14 +120,16 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => DoorInterface(doorLock: device))); + pageBuilder: (context, animation1, animation2) => + DoorInterface(doorLock: device))); // navigateToInterface(DoorInterface(doorlock: device), context); break; case DeviceType.Gateway: Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => GateWayView(gatewayObj: device))); + pageBuilder: (context, animation1, animation2) => + GateWayView(gatewayObj: device))); break; case DeviceType.LightBulb: navigateToInterface(LightInterface(light: device), context); @@ -149,13 +152,18 @@ void showDeviceInterface(DeviceModel device, BuildContext context) { pageBuilder: (context, animation1, animation2) => ThreeGangInterface(gangSwitch: device))); - - case DeviceType.WH: + case DeviceType.WH: Navigator.push( context, PageRouteBuilder( pageBuilder: (context, animation1, animation2) => WaterHeaterPage(device: device))); + case DeviceType.DS: + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => + DoorSensorScreen(device: device))); break; default: } diff --git a/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart index 7d2b807..efeeda3 100644 --- a/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart @@ -23,7 +23,7 @@ class CreateTemporaryPassword extends StatelessWidget { @override Widget build(BuildContext context) { bool isRepeat = false; - bool generated = false; + // bool generated = false; return BlocProvider( create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!), child: BlocConsumer(listener: (context, state) { @@ -35,11 +35,11 @@ class CreateTemporaryPassword extends StatelessWidget { ), ); } - if (state is IsRepeatState){ + if (state is IsRepeatState) { isRepeat = state.repeat; } - if (state is GeneratePasswordOneTimestate ){ - generated = state.generated; + if (state is GeneratePasswordOneTimestate) { + // generated = state.generated; } }, builder: (context, state) { final smartDoorBloc = BlocProvider.of(context); @@ -56,141 +56,142 @@ class CreateTemporaryPassword extends StatelessWidget { onPressed: () { Navigator.of(context).pop(true); }, - icon: const Icon(Icons.arrow_back) - ), - actions: - type == 'Online Password'?[ - TextButton( - onPressed: () { - smartDoorBloc.add(SavePasswordEvent(context: context)); - }, - child: const Text('Save') - ) - ]:null, + icon: const Icon(Icons.arrow_back)), + actions: type == 'Online Password' + ? [ + TextButton( + onPressed: () { + smartDoorBloc.add(SavePasswordEvent(context: context)); + }, + child: const Text('Save')) + ] + : null, ), child: state is LoadingInitialState ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const BodyMedium( - text: 'Save the password immediately. The password is not displayed in the app.', - fontWeight: FontWeight.normal, - fontColor: ColorsManager.grayColor, - ), - const SizedBox( - height: 20, - ), - const BodyMedium( - text: '7-Digit Password', - fontWeight: FontWeight.normal, - fontColor: ColorsManager.grayColor, - ), - DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: type == 'Online Password'?0:25), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - flex: 2, - child: PinCodeTextField( - onCompleted: (value) { - if (value.split('').every((char) => char == '1')) { - smartDoorBloc.passwordController.clear(); - CustomSnackBar.displaySnackBar('All characters cannot be 1.'); - } - }, - autoDisposeControllers: false, - keyboardType: TextInputType.phone, - length: 7, - // enabled:type == 'Online Password'? true:false, - obscureText: false, - animationType: AnimationType.fade, - pinTheme: PinTheme( - shape: PinCodeFieldShape.underline, - fieldHeight: 45, - fieldWidth: 20, - activeFillColor: Colors.white, - disabledColor: Colors.grey, - activeColor: Colors.grey, - errorBorderColor: Colors.grey, - inactiveColor: Colors.grey, - inactiveFillColor: Colors.grey, - selectedColor: Colors.grey), - animationDuration: const Duration(milliseconds: 300), - backgroundColor: Colors.white, - enableActiveFill: false, - controller: smartDoorBloc.passwordController, - appContext: context, - )), - const SizedBox( - width: 10, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const BodyMedium( + text: + 'Save the password immediately. The password is not displayed in the app.', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, + ), + const SizedBox( + height: 20, + ), + const BodyMedium( + text: '7-Digit Password', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, + ), + DefaultContainer( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: type == 'Online Password' ? 0 : 25), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + flex: 2, + child: PinCodeTextField( + onCompleted: (value) { + if (value.split('').every((char) => char == '1')) { + smartDoorBloc.passwordController.clear(); + CustomSnackBar.displaySnackBar( + 'All characters cannot be 1.'); + } + }, + autoDisposeControllers: false, + keyboardType: TextInputType.phone, + length: 7, + // enabled:type == 'Online Password'? true:false, + obscureText: false, + animationType: AnimationType.fade, + pinTheme: PinTheme( + shape: PinCodeFieldShape.underline, + fieldHeight: 45, + fieldWidth: 20, + activeFillColor: Colors.white, + disabledColor: Colors.grey, + activeColor: Colors.grey, + errorBorderColor: Colors.grey, + inactiveColor: Colors.grey, + inactiveFillColor: Colors.grey, + selectedColor: Colors.grey), + animationDuration: const Duration(milliseconds: 300), + backgroundColor: Colors.white, + enableActiveFill: false, + controller: smartDoorBloc.passwordController, + appContext: context, + )), + const SizedBox( + width: 10, + ), + if (type == 'Online Password') + Flexible( + child: InkWell( + onTap: () { + smartDoorBloc.add(GeneratePasswordEvent()); + }, + child: const BodyMedium( + text: 'Generate Randomly', + fontWeight: FontWeight.bold, + fontColor: ColorsManager.primaryColor, + )), + ) + ], + ), ), - if(type == 'Online Password') - Flexible( - child: InkWell( - onTap: () { - smartDoorBloc.add(GeneratePasswordEvent()); + ), + if (smartDoorBloc.passwordController.text.isNotEmpty) + TextButton( + onPressed: () async { + await Clipboard.setData( + ClipboardData(text: smartDoorBloc.passwordController.text)); + }, + child: const Text('Copy')), + const SizedBox( + height: 20, + ), + NameTimeWidget(), + const SizedBox( + height: 20, + ), + DefaultContainer( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: 'Repeat', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: smartDoorBloc.repeat, + onChanged: (value) { + smartDoorBloc.add(ToggleRepeatEvent()); }, - child: const BodyMedium( - text: 'Generate Randomly', - fontWeight: FontWeight.bold, - fontColor: ColorsManager.primaryColor, - )), - ) - ], - ), + applyTheme: true, + )), + ), + ), + const SizedBox( + height: 20, + ), + isRepeat ? const RepeatWidget() : const SizedBox(), + const SizedBox( + height: 40, + ) + ], ), ), - if(smartDoorBloc.passwordController.text.isNotEmpty) - TextButton( - onPressed: () async { - await Clipboard.setData(ClipboardData( - text: smartDoorBloc.passwordController.text)); - }, - child: const Text('Copy') - ), - const SizedBox( - height: 20, - ), - NameTimeWidget(), - const SizedBox( - height: 20, - ), - DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - child: ListTile( - contentPadding: EdgeInsets.zero, - leading: const BodyMedium( - text: 'Repeat', - fontWeight: FontWeight.normal, - ), - trailing: Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: smartDoorBloc.repeat, - onChanged: (value) { - smartDoorBloc.add(ToggleRepeatEvent()); - }, - applyTheme: true, - )), - ), - ) , - const SizedBox( - height: 20, - ), - isRepeat? const RepeatWidget():const SizedBox(), - const SizedBox( - height: 40, - ) - ], - ), - ), ); })); } diff --git a/lib/features/devices/view/widgets/smart_door/door_dialog.dart b/lib/features/devices/view/widgets/smart_door/door_dialog.dart index b1a672d..db95566 100644 --- a/lib/features/devices/view/widgets/smart_door/door_dialog.dart +++ b/lib/features/devices/view/widgets/smart_door/door_dialog.dart @@ -31,16 +31,18 @@ class DoorDialogState extends State { @override Widget build(BuildContext context) { - final effectiveTime = widget.temporaryPassword?.effectiveTime ??int.parse( widget.offline?.gmtStart); - final invalidTime = widget.temporaryPassword?.invalidTime ?? int.parse(widget.offline?.gmtExpired); + final effectiveTime = + widget.temporaryPassword?.effectiveTime ?? int.parse(widget.offline?.gmtStart); + final invalidTime = + widget.temporaryPassword?.invalidTime ?? int.parse(widget.offline?.gmtExpired); final DateTime effectiveDateTime = - DateTime.fromMillisecondsSinceEpoch(effectiveTime! * 1000, isUtc: false); + DateTime.fromMillisecondsSinceEpoch(effectiveTime * 1000, isUtc: false); String formattedDateEffectiveTime = DateFormat('yyyy-MM-dd').format(effectiveDateTime); String formattedTimeEffectiveTime = DateFormat('hh:mm a').format(effectiveDateTime); final DateTime expiredDateTime = - DateTime.fromMillisecondsSinceEpoch(invalidTime! * 1000, isUtc: false); + DateTime.fromMillisecondsSinceEpoch(invalidTime * 1000, isUtc: false); String formattedDateExpiredDateTime = DateFormat('yyyy-MM-dd').format(expiredDateTime); String formattedTimeExpiredDateTime = DateFormat('hh:mm a').format(expiredDateTime); return Dialog( @@ -116,60 +118,59 @@ class DoorDialogState extends State { width: double.infinity, color: ColorsManager.greyColor, ), - widget.temporaryPassword?.effectiveTime!=null? - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InkWell( - onTap: () { - Navigator.pop(context); - }, - child: Center( - child: BodyMedium( - text: 'Cancel', - style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), - ), - ), - ), - Container( - height: 50, - width: 1, - color: ColorsManager.greyColor, - ), - InkWell( - onTap: () { - Navigator.pop(context, 'delete'); - }, - child: Center( - child: BodyMedium( - text: 'Delete Password', - style: context.bodyMedium.copyWith(color: ColorsManager.primaryColorWithOpacity), - ), - ), - ), - ], - ): - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Container( - height: 50, - child: InkWell( - onTap: () { - Navigator.pop(context); - }, - child: Center( - child: BodyMedium( - text: 'Cancel', - style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), + widget.temporaryPassword?.effectiveTime != null + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), + ), + ), ), - ), - ), - - ), - - ], - ) + Container( + height: 50, + width: 1, + color: ColorsManager.greyColor, + ), + InkWell( + onTap: () { + Navigator.pop(context, 'delete'); + }, + child: Center( + child: BodyMedium( + text: 'Delete Password', + style: context.bodyMedium + .copyWith(color: ColorsManager.primaryColorWithOpacity), + ), + ), + ), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + height: 50, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), + ), + ), + ), + ), + ], + ) ], ), ), diff --git a/lib/features/devices/view/widgets/smart_door/door_grid.dart b/lib/features/devices/view/widgets/smart_door/door_grid.dart index fc5c290..a37be12 100644 --- a/lib/features/devices/view/widgets/smart_door/door_grid.dart +++ b/lib/features/devices/view/widgets/smart_door/door_grid.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/members_management_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/smart_linkage_view.dart'; @@ -10,9 +9,8 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart import 'package:syncrow_app/generated/assets.dart'; class DoorLockGrid extends StatelessWidget { - String uuid; - DoorLockGrid({ - super.key,required this.uuid}); + final String uuid; + const DoorLockGrid({super.key, required this.uuid}); @override Widget build(BuildContext context) { @@ -67,24 +65,24 @@ class DoorLockGrid extends StatelessWidget { } List> doorLockButtons({val}) => [ - { - 'title': 'Unlocking Records', - 'image': Assets.assetsIconsDoorlockAssetsUnlockingRecords, - 'page': const UnlockingRecordsView(), - }, - { - 'title': 'Members Management', - 'image': Assets.assetsIconsDoorlockAssetsMembersManagement, - 'page': const MembersManagementView(), - }, - { - 'title': 'Temporary Password', - 'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword, - 'page': TemporaryPasswordPage(deviceId:val) , - }, - { - 'title': 'Smart Linkage', - 'image': Assets.assetsIconsDoorlockAssetsSmartLinkage, - 'page': const SmartLinkgeView() - }, -]; + { + 'title': 'Unlocking Records', + 'image': Assets.assetsIconsDoorlockAssetsUnlockingRecords, + 'page': const UnlockingRecordsView(), + }, + { + 'title': 'Members Management', + 'image': Assets.assetsIconsDoorlockAssetsMembersManagement, + 'page': const MembersManagementView(), + }, + { + 'title': 'Temporary Password', + 'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword, + 'page': TemporaryPasswordPage(deviceId: val), + }, + { + 'title': 'Smart Linkage', + 'image': Assets.assetsIconsDoorlockAssetsSmartLinkage, + 'page': const SmartLinkgeView() + }, + ]; diff --git a/lib/features/devices/view/widgets/smart_door/onetime_password_page.dart b/lib/features/devices/view/widgets/smart_door/onetime_password_page.dart index ba54fc5..a5f1d62 100644 --- a/lib/features/devices/view/widgets/smart_door/onetime_password_page.dart +++ b/lib/features/devices/view/widgets/smart_door/onetime_password_page.dart @@ -14,98 +14,103 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class OnetimePasswordPage extends StatelessWidget { final String? deviceId; - const OnetimePasswordPage({super.key, this.deviceId,}); + const OnetimePasswordPage({ + super.key, + this.deviceId, + }); @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialOneTimePassword( )), - child: BlocConsumer( - listener: (context, state) { - if (state is FailedState) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(state.errorMessage), - backgroundColor: Colors.red, - ), - ); - } - }, - builder: (context, state) { - final smartDoorBloc = BlocProvider.of(context); - return DefaultScaffold( - title: 'Passwords', - actions: [ - IconButton( - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => OfflineOneTimePasswordPage(deviceId: deviceId, ) - )).then((result) { - if(result!=null){ - smartDoorBloc.add(InitialOneTimePassword()); - smartDoorBloc.add(InitialOneTimePassword()); - } - }); - }, - icon: const Icon(Icons.add) - ) - ], - child: Builder( - builder: (context) { - return state is LoadingInitialState - ? const Center(child: CircularProgressIndicator()) - : Center( - child: smartDoorBloc.oneTimePasswords!.isNotEmpty - ? ListView.builder( - itemCount: smartDoorBloc.oneTimePasswords!.length, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.all(5.0), - child: DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), - child: ListTile( - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset(Assets.timeLimitedPasswordIcon), - title: BodyMedium( - text: 'Password Name: ${smartDoorBloc.oneTimePasswords![index].pwdName}', - fontWeight: FontWeight.normal, - ), - onTap: () async { - final result = await showDialog( - context: context, - builder: (context) { - return DoorDialog( - title: 'Password Information', - offline: smartDoorBloc.oneTimePasswords![index], - ); - }, - ); - - }, - trailing: const Icon( - Icons.arrow_forward_ios, - color: ColorsManager.greyColor, - size: 15, - ), - ), - ), - ); - }, - ) : Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset(Assets.noValidPasswords), - const SizedBox( - height: 10, - ), - const BodyMedium(text: 'No Valid Passwords') - ], - ), - ); + create: (BuildContext context) => + SmartDoorBloc(deviceId: deviceId!)..add(InitialOneTimePassword()), + child: BlocConsumer(listener: (context, state) { + if (state is FailedState) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errorMessage), + backgroundColor: Colors.red, + ), + ); + } + }, builder: (context, state) { + final smartDoorBloc = BlocProvider.of(context); + return DefaultScaffold( + title: 'Passwords', + actions: [ + IconButton( + onPressed: () { + Navigator.of(context) + .push(MaterialPageRoute( + builder: (context) => OfflineOneTimePasswordPage( + deviceId: deviceId, + ))) + .then((result) { + if (result != null) { + smartDoorBloc.add(InitialOneTimePassword()); + smartDoorBloc.add(InitialOneTimePassword()); + } + }); }, - )); - }) - ); + icon: const Icon(Icons.add)) + ], + child: Builder( + builder: (context) { + return state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Center( + child: smartDoorBloc.oneTimePasswords!.isNotEmpty + ? ListView.builder( + itemCount: smartDoorBloc.oneTimePasswords!.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.all(5.0), + child: DefaultContainer( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 10), + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset(Assets.timeLimitedPasswordIcon), + title: BodyMedium( + text: + 'Password Name: ${smartDoorBloc.oneTimePasswords![index].pwdName}', + fontWeight: FontWeight.normal, + ), + onTap: () async { + await showDialog( + context: context, + builder: (context) { + return DoorDialog( + title: 'Password Information', + offline: smartDoorBloc.oneTimePasswords![index], + ); + }, + ); + }, + trailing: const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ), + ), + ), + ); + }, + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset(Assets.noValidPasswords), + const SizedBox( + height: 10, + ), + const BodyMedium(text: 'No Valid Passwords') + ], + ), + ); + }, + )); + })); } } diff --git a/lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart b/lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart index fce92b9..b07c192 100644 --- a/lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart +++ b/lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart @@ -19,87 +19,88 @@ class TimeLimitedPasswordPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialTimeLimitPassword()), - child: BlocConsumer( - listener: (context, state) { - if (state is FailedState) { - CustomSnackBar.displaySnackBar( - state.errorMessage - ); - } - }, - builder: (context, state) { - final smartDoorBloc = BlocProvider.of(context); - return DefaultScaffold( - title: 'Passwords', - actions: [ - IconButton( - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => CreateOfflineTimeLimitPasswordPage(deviceId: deviceId,) - )).then((result) { - smartDoorBloc.add(InitialTimeLimitPassword()); - smartDoorBloc.add(InitialTimeLimitPassword()); - }); - }, - icon: const Icon(Icons.add) - ) - ], - child: Builder( - builder: (context) { - return state is LoadingInitialState - ? const Center(child: CircularProgressIndicator()) - : Center( - child: smartDoorBloc.timeLimitPasswords!.isNotEmpty - ? ListView.builder( - itemCount: smartDoorBloc.timeLimitPasswords!.length, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.all(5.0), - child: DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), - child: ListTile( - contentPadding: EdgeInsets.zero, - leading: SvgPicture.asset(Assets.timeLimitedPasswordIcon), - title: BodyMedium( - text: 'Password Name: ${smartDoorBloc.timeLimitPasswords![index].pwdName}', - fontWeight: FontWeight.normal, - ), - onTap: () async { - final result = await showDialog( - context: context, - builder: (context) { - return DoorDialog( - title: 'Password Information', - offline: smartDoorBloc.timeLimitPasswords![index], - ); - }, + create: (BuildContext context) => + SmartDoorBloc(deviceId: deviceId!)..add(InitialTimeLimitPassword()), + child: BlocConsumer(listener: (context, state) { + if (state is FailedState) { + CustomSnackBar.displaySnackBar(state.errorMessage); + } + }, builder: (context, state) { + final smartDoorBloc = BlocProvider.of(context); + return DefaultScaffold( + title: 'Passwords', + actions: [ + IconButton( + onPressed: () { + Navigator.of(context) + .push(MaterialPageRoute( + builder: (context) => CreateOfflineTimeLimitPasswordPage( + deviceId: deviceId, + ))) + .then((result) { + smartDoorBloc.add(InitialTimeLimitPassword()); + smartDoorBloc.add(InitialTimeLimitPassword()); + }); + }, + icon: const Icon(Icons.add)) + ], + child: Builder( + builder: (context) { + return state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Center( + child: smartDoorBloc.timeLimitPasswords!.isNotEmpty + ? ListView.builder( + itemCount: smartDoorBloc.timeLimitPasswords!.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.all(5.0), + child: DefaultContainer( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 10), + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset(Assets.timeLimitedPasswordIcon), + title: BodyMedium( + text: + 'Password Name: ${smartDoorBloc.timeLimitPasswords![index].pwdName}', + fontWeight: FontWeight.normal, + ), + onTap: () async { + await showDialog( + context: context, + builder: (context) { + return DoorDialog( + title: 'Password Information', + offline: smartDoorBloc.timeLimitPasswords![index], + ); + }, + ); + }, + trailing: const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ), + ), + ), ); }, - trailing: const Icon( - Icons.arrow_forward_ios, - color: ColorsManager.greyColor, - size: 15, - ), + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset(Assets.noValidPasswords), + const SizedBox( + height: 10, + ), + const BodyMedium(text: 'No Valid Passwords') + ], ), - ), - ); - }, - ) : Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset(Assets.noValidPasswords), - const SizedBox( - height: 10, - ), - const BodyMedium(text: 'No Valid Passwords') - ], - ), - ); - }, - )); - }) - ); + ); + }, + )); + })); } } 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 8b5dc55..61afbc0 100644 --- a/lib/features/devices/view/widgets/three_gang/gang_switch.dart +++ b/lib/features/devices/view/widgets/three_gang/gang_switch.dart @@ -18,7 +18,7 @@ class GangSwitch extends StatelessWidget { @override Widget build(BuildContext context) { return InkWell( - overlayColor: MaterialStateProperty.all(Colors.transparent), + overlayColor: WidgetStateProperty.all(Colors.transparent), onTap: () { action(); // var tempControl = DeviceControlModel( 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 8e1388a..03232c5 100644 --- a/lib/features/devices/view/widgets/three_gang/schedule_screen.dart +++ b/lib/features/devices/view/widgets/three_gang/schedule_screen.dart @@ -63,17 +63,20 @@ class ScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( - switchCode :"switch_1", - device: device, - deviceCode: 'countdown_1', - ))); + pageBuilder: + (context, animation1, animation2) => + TimerScheduleScreen( + switchCode: "switch_1", + device: device, + deviceCode: 'countdown_1', + ))); }, child: Container( - padding: - const EdgeInsets.only(left: 25, right: 15, top: 20, bottom: 20), + padding: const EdgeInsets.only( + left: 25, right: 15, top: 20, bottom: 20), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ BodySmall( text: "Bedside Light", @@ -100,17 +103,20 @@ class ScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( - switchCode :"switch_2", - device: device, - deviceCode: 'countdown_2', - ))); + pageBuilder: + (context, animation1, animation2) => + TimerScheduleScreen( + switchCode: "switch_2", + device: device, + deviceCode: 'countdown_2', + ))); }, child: Container( - padding: - const EdgeInsets.only(left: 25, right: 15, top: 20, bottom: 20), + padding: const EdgeInsets.only( + left: 25, right: 15, top: 20, bottom: 20), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ BodySmall( text: "Ceiling Light", @@ -137,17 +143,20 @@ class ScheduleScreen extends StatelessWidget { Navigator.push( context, PageRouteBuilder( - pageBuilder: (context, animation1, animation2) => TimerScheduleScreen( - switchCode :"switch_3", - device: device, - deviceCode: 'countdown_3', - ))); + pageBuilder: + (context, animation1, animation2) => + TimerScheduleScreen( + switchCode: "switch_3", + device: device, + deviceCode: 'countdown_3', + ))); }, child: Container( - padding: - const EdgeInsets.only(left: 25, right: 15, top: 20, bottom: 20), + padding: const EdgeInsets.only( + left: 25, right: 15, top: 20, bottom: 20), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ BodySmall( text: "Spotlight", 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 d15d4b1..8f49fea 100644 --- a/lib/features/devices/view/widgets/three_gang/timer_screen.dart +++ b/lib/features/devices/view/widgets/three_gang/timer_screen.dart @@ -8,7 +8,6 @@ import 'package:syncrow_app/features/devices/bloc/three_gang_bloc/three_gang_eve 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'; @@ -18,17 +17,15 @@ import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; - - class TimerScheduleScreen extends StatelessWidget { final DeviceModel device; final String deviceCode; final String switchCode; const TimerScheduleScreen( {required this.device, - required this.deviceCode, - required this.switchCode, - super.key}); + required this.deviceCode, + required this.switchCode, + super.key}); @override Widget build(BuildContext context) { @@ -38,8 +35,8 @@ class TimerScheduleScreen extends StatelessWidget { statusBarIconBrightness: Brightness.light, ), child: BlocProvider( - create: (context) => - ThreeGangBloc(switchCode: switchCode, threeGangId: device.uuid ?? '') + create: (context) => ThreeGangBloc( + switchCode: switchCode, threeGangId: device.uuid ?? '') ..add(GetCounterEvent(deviceCode: deviceCode)) ..add(GetScheduleEvent()), child: BlocBuilder( @@ -66,7 +63,7 @@ class TimerScheduleScreen extends StatelessWidget { }, child: DefaultTabController( length: 2, - child: DefaultScaffold( + child: DefaultScaffold( appBar: AppBar( backgroundColor: Colors.transparent, centerTitle: true, @@ -76,177 +73,238 @@ class TimerScheduleScreen extends StatelessWidget { fontWeight: FontsManager.bold, ), 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(), + threeGangBloc.createSchedule == true + ? TextButton( + onPressed: () { + threeGangBloc.add(ThreeGangSave()); + }, + child: const Text('Save')) + : threeGangBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + // oneGangBloc.toggleCreateSchedule(); + threeGangBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), ], ), - child: - state is LoadingInitialState? - const Center(child: CircularProgressIndicator()): - Column( - children: [ - Container( - width: MediaQuery.of(context).size.width, - decoration: const ShapeDecoration( - color: ColorsManager.onPrimaryColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(30)), - ), - ), - child: TabBar( - onTap: (value) { - if(value==0){ - if(threeGangBloc.createSchedule == true){ - threeGangBloc.toggleCreateSchedule(); - } - threeGangBloc.toggleSelectedIndex(0); - - }else{ - threeGangBloc.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)), - ), - ), - tabs: [ - Tab( - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 10), - child: BodySmall( - text: 'Countdown', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - Tab( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Text( - 'Schedule', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ], - ), - - ), - Expanded( - child: TabBarView( + child: state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Column( 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) { - threeGangBloc.add(SetCounterValue( - deviceCode: deviceCode, - duration: duration)); - } - }, - child: SvgPicture.asset(countNum > 0 - ? Assets.pauseIcon - : Assets.playIcon)), - ], + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(30)), ), ), - ), - Column( - mainAxisAlignment:threeGangBloc.listSchedule.isNotEmpty? - MainAxisAlignment.start:MainAxisAlignment.center, - children: [ - SizedBox( - child: threeGangBloc.createSchedule == true ? - CreateSchedule( - onToggleChanged: (bool isOn) { - threeGangBloc.toggleSchedule = isOn; - }, - 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, - )); - }, - ), + child: TabBar( + // onTap: (value) { + // if (value == 0) { + // if (threeGangBloc.createSchedule == + // true) { + // threeGangBloc.toggleCreateSchedule(); + // } + // threeGangBloc.toggleSelectedIndex(0); + // } else { + // threeGangBloc.toggleSelectedIndex(1); + // } + // }, + + onTap: (value) { + if (value == 0) { + if (threeGangBloc.createSchedule == + true) { + // oneGangBloc.toggleCreateSchedule(); + threeGangBloc.add( + const ToggleCreateScheduleEvent( + index: 0)); + } + threeGangBloc.add( + const ToggleSelectedEvent( + index: 0)); + } else { + threeGangBloc.add( + const ToggleSelectedEvent( + index: 1)); + } + }, + indicatorColor: Colors.white, + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20)), ), ), - ], + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + physics: const NeverScrollableScrollPhysics(), // Disable swiping + + children: [ + Center( + child: Container( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + countNum > 0 + ? BodyLarge( + text: _formatDuration( + countNum), + fontColor: ColorsManager + .slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: + CupertinoTimerPickerMode + .hm, + onTimerDurationChanged: + (Duration + newDuration) { + duration = newDuration; + }, + ), + GestureDetector( + onTap: () { + if (state + is LoadingNewSate) { + return; + } + if (countNum > 0) { + threeGangBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: Duration + .zero)); + } else if (duration != + Duration.zero) { + threeGangBloc.add( + SetCounterValue( + deviceCode: + deviceCode, + duration: + duration)); + } + }, + child: SvgPicture.asset( + countNum > 0 + ? Assets.pauseIcon + : Assets.playIcon)), + ], + ), + ), + ), + SizedBox( + child: threeGangBloc.createSchedule == + true + ? CreateSchedule( + onToggleChanged: (bool isOn) { + threeGangBloc.toggleSchedule = + isOn; + }, + onDateTimeChanged: + (DateTime dateTime) { + threeGangBloc.selectedTime = + dateTime; + }, + days: threeGangBloc.days, + selectDays: (List + selectedDays) { + threeGangBloc.selectedDays = + selectedDays; + }, + ) + : Padding( + padding: const EdgeInsets.only( + top: 10), + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + 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_list.dart b/lib/features/devices/view/widgets/two_gang/two_gang_list.dart index 065dd62..a36de97 100644 --- a/lib/features/devices/view/widgets/two_gang/two_gang_list.dart +++ b/lib/features/devices/view/widgets/two_gang/two_gang_list.dart @@ -1,10 +1,9 @@ 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/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/group_two_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'; @@ -16,7 +15,7 @@ class TwoGangList extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { return SingleChildScrollView( child: Column( @@ -28,10 +27,10 @@ class TwoGangList extends StatelessWidget { DevicesDefaultSwitch( switchValue: allSwitches, action: () { - BlocProvider.of(context).add(GroupAllOnEvent()); + BlocProvider.of(context).add(GroupAllOnEvent()); }, secondAction: () { - BlocProvider.of(context).add(GroupAllOffEvent()); + BlocProvider.of(context).add(GroupAllOffEvent()); }, ), ListView.builder( @@ -44,30 +43,27 @@ class TwoGangList extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 10), - BodySmall(text: '${twoGangList[index].deviceName} beside light'), + BodySmall(text: twoGangList[index].deviceName), const SizedBox(height: 5), DevicesDefaultSwitch( switchValue: twoGangList[index].firstSwitch, action: () { - BlocProvider.of(context).add(ChangeFirstSwitchStatusEvent( + BlocProvider.of(context).add(ChangeFirstSwitchStatusEvent( value: twoGangList[index].firstSwitch, deviceId: twoGangList[index].deviceId)); }, ), const SizedBox(height: 10), - BodySmall(text: '${twoGangList[index].deviceName} ceiling light'), + BodySmall(text: twoGangList[index].deviceName), const SizedBox(height: 5), DevicesDefaultSwitch( switchValue: twoGangList[index].secondSwitch, action: () { - BlocProvider.of(context).add(ChangeSecondSwitchStatusEvent( + 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 index d332386..240a5ff 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 @@ -4,7 +4,7 @@ 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_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/group_two_gang_model.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'; @@ -22,18 +22,16 @@ class TwoGangScreen extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => TwoGangBloc( - switchCode: switchCode??'', - twoGangId: device?.uuid ?? '') + create: (context) => TwoGangBloc(switchCode: switchCode ?? '', 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, - ); + firstSwitch: false, + secondSwitch: false, + firstCountDown: 0, + secondCountDown: 0, + ); List groupTwoGangModel = []; bool allSwitchesOn = false; @@ -47,7 +45,9 @@ 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, @@ -116,7 +116,6 @@ class TwoGangScreen extends StatelessWidget { ), ], ), - ], ), ), 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 a86ae8c..c06c4c8 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 @@ -63,7 +63,7 @@ class TimerScheduleScreen extends StatelessWidget { }, child: DefaultTabController( length: 2, - child: DefaultScaffold( + child: DefaultScaffold( appBar: AppBar( backgroundColor: Colors.transparent, centerTitle: true, @@ -73,177 +73,221 @@ class TimerScheduleScreen extends StatelessWidget { 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), - ):SizedBox(), + twoGangBloc.createSchedule == true + ? TextButton( + onPressed: () { + twoGangBloc.add(TwoGangSave()); + }, + child: const Text('Save')) + : twoGangBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + // oneGangBloc.toggleCreateSchedule(); + twoGangBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), ], ), - child: - state is LoadingInitialState? - const Center(child: CircularProgressIndicator()): - Column( - children: [ - Container( - width: MediaQuery.of(context).size.width, - decoration: const ShapeDecoration( - color: ColorsManager.onPrimaryColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(30)), - ), - ), - child: TabBar( - onTap: (value) { - 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)), - ), - ), - tabs: [ - Tab( - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 10), - child: BodySmall( - text: 'Countdown', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - Tab( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Text( - 'Schedule', - style: context.bodySmall.copyWith( - color: ColorsManager.blackColor, - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ], - ), - ), - Expanded( - child: TabBarView( + child: state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Column( 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)), - ], + Container( + width: MediaQuery.of(context).size.width, + decoration: const ShapeDecoration( + color: ColorsManager.onPrimaryColor, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(30)), ), ), - ), - 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, - )); - }, - ), + child: TabBar( + onTap: (value) { + if (value == 0) { + if (twoGangBloc.createSchedule == + true) { + // oneGangBloc.toggleCreateSchedule(); + twoGangBloc.add( + const ToggleCreateScheduleEvent( + index: 0)); + } + twoGangBloc.add( + const ToggleSelectedEvent( + index: 0)); + } else { + twoGangBloc.add( + const ToggleSelectedEvent( + index: 1)); + } + }, + indicatorColor: + Colors.white, // Customize the indicator + dividerHeight: 0, + indicatorSize: TabBarIndicatorSize.tab, + indicator: const ShapeDecoration( + color: ColorsManager.slidingBlueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(20)), ), ), - ], + tabs: [ + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: BodySmall( + text: 'Countdown', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + Tab( + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10), + child: Text( + 'Schedule', + style: context.bodySmall.copyWith( + color: ColorsManager.blackColor, + fontSize: 12, + fontWeight: FontWeight.w400, + ), + ), + ), + ), + ], + ), + ), + Expanded( + child: TabBarView( + physics: const NeverScrollableScrollPhysics(), // Disable swiping + + children: [ + Center( + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + countNum > 0 + ? BodyLarge( + text: _formatDuration( + countNum), + fontColor: ColorsManager + .slidingBlueColor, + fontSize: 40, + ) + : CupertinoTimerPicker( + mode: + CupertinoTimerPickerMode + .hm, + onTimerDurationChanged: + (Duration + newDuration) { + duration = newDuration; + }, + ), + GestureDetector( + onTap: () { + if (state + is LoadingNewSate) { + return; + } + if (countNum > 0) { + 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)), + ], + ), + ), + 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: Column( + children: [ + Expanded( + 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/devices/view/widgets/water_heater/Inching_widget.dart b/lib/features/devices/view/widgets/water_heater/Inching_widget.dart index 948d95c..bd99153 100644 --- a/lib/features/devices/view/widgets/water_heater/Inching_widget.dart +++ b/lib/features/devices/view/widgets/water_heater/Inching_widget.dart @@ -1,13 +1,11 @@ - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.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/utils/resource_manager/color_manager.dart'; - class InchingWidget extends StatefulWidget { - const InchingWidget({super.key,this.onDateTimeChanged}); + const InchingWidget({super.key, this.onDateTimeChanged}); final Function(DateTime)? onDateTimeChanged; @override @@ -22,7 +20,7 @@ class _InchingWidgetState extends State { children: [ DefaultContainer( child: Container( - padding: EdgeInsets.all(10), + padding: const EdgeInsets.all(10), child: Column( children: [ Row( @@ -46,26 +44,29 @@ class _InchingWidgetState extends State { ), ], ), - const SizedBox(height: 10,), - const Text('Once enabled this feature, each time the device is turned on, it will automatically turn of after a period time as pre-set.'), + const SizedBox( + height: 10, + ), + const Text( + 'Once enabled this feature, each time the device is turned on, it will automatically turn of after a period time as pre-set.'), ], ), ), ), const Padding( - padding: EdgeInsets.only(right: 30,left: 30), + padding: EdgeInsets.only(right: 30, left: 30), child: Divider(), ), - Container( - child: MinuteSecondPicker()), + Container(child: MinuteSecondPicker()), const Padding( - padding: EdgeInsets.only(right: 30,left: 30), + padding: EdgeInsets.only(right: 30, left: 30), child: Divider(), ), ], ); } } + class MinuteSecondPicker extends StatefulWidget { const MinuteSecondPicker({super.key}); @@ -79,7 +80,7 @@ class _MinuteSecondPickerState extends State { @override Widget build(BuildContext context) { - return Center( + return Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -98,10 +99,9 @@ class _MinuteSecondPickerState extends State { children: List.generate(61, (int index) { return Center( child: BodyLarge( - text: index.toString().padLeft(2, '0'), + text: index.toString().padLeft(2, '0'), style: const TextStyle( - fontWeight: FontWeight.w400, - fontSize: 30,color: Colors.blue), + fontWeight: FontWeight.w400, fontSize: 30, color: Colors.blue), ), ); }), @@ -124,20 +124,17 @@ class _MinuteSecondPickerState extends State { children: List.generate(60, (int index) { return Center( child: BodyLarge( - text: index.toString().padLeft(2, '0'), + text: index.toString().padLeft(2, '0'), style: const TextStyle( - fontWeight: FontWeight.w400, - fontSize: 30,color: Colors.blue), + fontWeight: FontWeight.w400, fontSize: 30, color: Colors.blue), ), ); }), ), ), const Text('S'), - ], ), - ); } -} \ No newline at end of file +} diff --git a/lib/features/devices/view/widgets/water_heater/circulate_list_view.dart b/lib/features/devices/view/widgets/water_heater/circulate_list_view.dart index cdc2b9a..a6792f9 100644 --- a/lib/features/devices/view/widgets/water_heater/circulate_list_view.dart +++ b/lib/features/devices/view/widgets/water_heater/circulate_list_view.dart @@ -1,8 +1,3 @@ - - - - - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -13,7 +8,6 @@ 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'; - class CirculateListView extends StatelessWidget { final List listSchedule; final Function(String) onDismissed; @@ -30,96 +24,98 @@ class CirculateListView extends StatelessWidget { 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) { + 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); - 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, + 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.5, + child: Row( children: [ - BodyLarge( - text: '12:30 AM - 01:30 PM', - // text: listSchedule[index].time, - fontWeight: FontWeight.w500, - fontColor: Colors.black, - fontSize: 22, + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + BodyLarge( + text: '12:30 AM - 01:30 PM', + // text: listSchedule[index].time, + fontWeight: FontWeight.w500, + fontColor: Colors.black, + fontSize: 18, + ), + ], + ), + Text(listSchedule[index].days.join(' ')), + // Text('Function ${listSchedule[index].function.value ? "ON" : "OFF"}'), + Text( + 'Start Duration: 0h 10m End Duration: 0h 15m'), + ], + ), + ), + 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, + ), + ), + ), ), - Text(listSchedule[index].days.join(' ')), - // Text('Function ${listSchedule[index].function.value ? "ON" : "OFF"}'), - Text('Start Duration: 0h 10m End Duration: 0h 15m'), ], ), ), - 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() - ); + ) + : const EmptySchedule()); } } diff --git a/lib/features/devices/view/widgets/water_heater/water_heater_page.dart b/lib/features/devices/view/widgets/water_heater/water_heater_page.dart index 9678cba..3f3c168 100644 --- a/lib/features/devices/view/widgets/water_heater/water_heater_page.dart +++ b/lib/features/devices/view/widgets/water_heater/water_heater_page.dart @@ -21,10 +21,10 @@ class WaterHeaterPage extends StatelessWidget { @override Widget build(BuildContext context) { return DefaultScaffold( + title: 'Water heater', child: BlocProvider( - create: (context) => - WaterHeaterBloc(switchCode: 'switch_1', whId: device?.uuid ?? '') - ..add(const WaterHeaterInitial()), + create: (context) => WaterHeaterBloc(switchCode: 'switch_1', whId: device?.uuid ?? '') + ..add(const WaterHeaterInitial()), child: BlocBuilder( builder: (context, state) { final waterHeaterBloc = BlocProvider.of(context); @@ -36,10 +36,8 @@ class WaterHeaterPage extends StatelessWidget { } return state is WHLoadingState ? const Center( - child: DefaultContainer( - width: 50, - height: 50, - child: CircularProgressIndicator()), + child: + DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()), ) : RefreshIndicator( onRefresh: () async { @@ -53,13 +51,11 @@ class WaterHeaterPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ InkWell( - overlayColor: MaterialStateProperty.all( - Colors.transparent), + overlayColor: MaterialStateProperty.all(Colors.transparent), onTap: () { waterHeaterBloc.add( - WaterHeaterSwitch( - whSwitch:whModel.firstSwitch - ),); + WaterHeaterSwitch(whSwitch: whModel.firstSwitch), + ); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -68,26 +64,22 @@ class WaterHeaterPage extends StatelessWidget { Container( decoration: BoxDecoration( // color: Colors.white.withOpacity(0.1), - borderRadius: - BorderRadius.circular(890), + borderRadius: BorderRadius.circular(890), boxShadow: [ BoxShadow( - color: - Colors.white.withOpacity(0.1), + color: Colors.white.withOpacity(0.1), blurRadius: 24, offset: const Offset(-5, -5), blurStyle: BlurStyle.outer, ), BoxShadow( - color: - Colors.black.withOpacity(0.11), + color: Colors.black.withOpacity(0.11), blurRadius: 25, offset: const Offset(5, 5), blurStyle: BlurStyle.outer, ), BoxShadow( - color: - Colors.black.withOpacity(0.13), + color: Colors.black.withOpacity(0.13), blurRadius: 30, offset: const Offset(5, 5), blurStyle: BlurStyle.inner, @@ -111,8 +103,7 @@ class WaterHeaterPage extends StatelessWidget { Card( elevation: 3, shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(100), + borderRadius: BorderRadius.circular(100), ), child: GestureDetector( onTap: () { @@ -121,8 +112,10 @@ class WaterHeaterPage extends StatelessWidget { PageRouteBuilder( pageBuilder: (context, animation1, animation2) => WHTimerScheduleScreen( - switchCode: 'switch_1', device: device!, deviceCode: 'countdown_1', - ))); + switchCode: 'switch_1', + device: device!, + deviceCode: 'countdown_1', + ))); }, child: Stack( alignment: Alignment.center, @@ -132,8 +125,7 @@ class WaterHeaterPage extends StatelessWidget { height: 60, decoration: BoxDecoration( color: Colors.grey[300], - borderRadius: - BorderRadius.circular(100), + borderRadius: BorderRadius.circular(100), ), ), Container( @@ -141,14 +133,12 @@ class WaterHeaterPage extends StatelessWidget { height: 40, decoration: BoxDecoration( color: Colors.white, - borderRadius: - BorderRadius.circular(100), + borderRadius: BorderRadius.circular(100), ), child: Center( child: Icon( Icons.access_time, - color: ColorsManager - .primaryColorWithOpacity, + color: ColorsManager.primaryColorWithOpacity, size: 25, ), ), diff --git a/lib/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.dart b/lib/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.dart index f1325ba..1bafc83 100644 --- a/lib/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.dart +++ b/lib/features/devices/view/widgets/water_heater/wh_timer_schedule_screen.dart @@ -76,33 +76,39 @@ class WHTimerScheduleScreen extends StatelessWidget { fontWeight: FontsManager.bold, ), actions: [ - waterHeaterBloc.createSchedule == true && waterHeaterBloc.selectedTabIndex == 1 ? - TextButton( - onPressed: () { - waterHeaterBloc.add(ScheduleSave()); - }, - child: const Text('Save')) : waterHeaterBloc.selectedTabIndex == 1 - ? IconButton( - onPressed: () { - waterHeaterBloc.toggleCreateSchedule(); - }, - icon: const Icon(Icons.add), - ) - : const SizedBox(), - waterHeaterBloc.createCirculate == true &&waterHeaterBloc.selectedTabIndex==2 ? - TextButton( - onPressed: () { - waterHeaterBloc.add(ScheduleSave()); - }, - child: const Text('Save')) - : waterHeaterBloc.selectedTabIndex == 2 - ? IconButton( - onPressed: () { - waterHeaterBloc.toggleCreateCirculate(); + waterHeaterBloc.createSchedule == true && + waterHeaterBloc.selectedTabIndex == 1 + ? TextButton( + onPressed: () { + waterHeaterBloc.add(ScheduleSave()); }, - icon: const Icon(Icons.add), - ) - : const SizedBox(), + child: const Text('Save')) + : waterHeaterBloc.selectedTabIndex == 1 + ? IconButton( + onPressed: () { + // waterHeaterBloc.toggleCreateSchedule(); + waterHeaterBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), + waterHeaterBloc.createCirculate == true && + waterHeaterBloc.selectedTabIndex == 2 + ? TextButton( + onPressed: () { + waterHeaterBloc.add(ScheduleSave()); + }, + child: const Text('Save')) + : waterHeaterBloc.selectedTabIndex == 2 + ? IconButton( + onPressed: () { + waterHeaterBloc.toggleCreateCirculate(); + }, + icon: const Icon(Icons.add), + ) + : const SizedBox(), ], ), child: state is WHLoadingState @@ -124,20 +130,51 @@ class WHTimerScheduleScreen extends StatelessWidget { labelPadding: EdgeInsets.zero, onTap: (value) { if (value == 0) { - if (waterHeaterBloc.createSchedule == true) { - waterHeaterBloc.toggleCreateSchedule(); + if (waterHeaterBloc.createSchedule == + true) { + // waterHeaterBloc + // .toggleCreateSchedule(); + waterHeaterBloc.add( + const ToggleCreateScheduleEvent( + index: 1)); } - waterHeaterBloc.toggleSelectedIndex(value); - } else if (value == 2) { - if (waterHeaterBloc.createCirculate == true) { - waterHeaterBloc.toggleCreateCirculate(); + waterHeaterBloc.add( + ToggleSelectedEvent( + index: value)); + // waterHeaterBloc + // .toggleSelectedIndex(value); + } else if (value == 2) { + if (waterHeaterBloc.createCirculate == + true) { + waterHeaterBloc.add( + ToggleCreateCirculateEvent( + index: value)); + // waterHeaterBloc + // .toggleCreateCirculate(); } - waterHeaterBloc.toggleSelectedIndex(value); + waterHeaterBloc.add( + ToggleSelectedEvent( + index: value)); + // waterHeaterBloc + // .toggleSelectedIndex(value); } else { - if (waterHeaterBloc.createSchedule == true) { - waterHeaterBloc.toggleCreateSchedule(); + if (waterHeaterBloc.createSchedule == + true) { + waterHeaterBloc.add( + ToggleSelectedEvent( + index: value)); + // waterHeaterBloc + // .toggleCreateSchedule(); } - waterHeaterBloc.toggleSelectedIndex(value); + waterHeaterBloc.createCirculate = + false; + waterHeaterBloc.createSchedule = + false; + waterHeaterBloc.add( + ToggleSelectedEvent( + index: value)); + // waterHeaterBloc + // .toggleSelectedIndex(value); } }, indicatorColor: Colors.white, @@ -151,96 +188,128 @@ class WHTimerScheduleScreen extends StatelessWidget { ), ), isScrollable: false, - labelColor: Colors.white, // Text color when selected - unselectedLabelColor: ColorsManager.blackColor, // Text color when not selected + labelColor: Colors + .white, // Text color when selected + unselectedLabelColor: ColorsManager + .blackColor, // Text color when not selected tabs: [ Tab( - icon: SvgPicture.asset( - Assets.scheduleTimeIcon, - color: waterHeaterBloc.selectedTabIndex == 0 - ? Colors.white - : ColorsManager.blackColor, // Change icon color based on selectedIndex + icon: Padding( + padding: const EdgeInsets.only( + top: 10.0), + child: SvgPicture.asset( + Assets.scheduleTimeIcon, + color: waterHeaterBloc + .selectedTabIndex == + 0 + ? Colors.white + : ColorsManager + .blackColor, // Change icon color based on selectedIndex + ), ), child: Container( padding: const EdgeInsets.symmetric( - vertical: 10), + vertical: 5), child: BodySmall( text: 'Countdown', style: context.bodySmall.copyWith( - color: waterHeaterBloc.selectedTabIndex == 0 + color: waterHeaterBloc + .selectedTabIndex == + 0 ? Colors.white : ColorsManager .blackColor, // Text color based on selectedTabIndex - fontSize: 8, + fontSize: 10, fontWeight: FontWeight.w400, ), ), ), ), Tab( - icon: SvgPicture.asset( - Assets.scheduleCelenderIcon, - color: waterHeaterBloc.selectedTabIndex == 1 - ? Colors.white - : ColorsManager.blackColor, + icon: Padding( + padding: + const EdgeInsets.only(top: 10), + child: SvgPicture.asset( + Assets.scheduleCelenderIcon, + color: waterHeaterBloc + .selectedTabIndex == + 1 + ? Colors.white + : ColorsManager.blackColor, + ), ), child: Container( padding: const EdgeInsets.symmetric( - vertical: 10), + vertical: 5), child: Text( 'Schedule', style: context.bodySmall.copyWith( - color: waterHeaterBloc.selectedTabIndex == 1 + color: waterHeaterBloc + .selectedTabIndex == + 1 ? Colors.white : ColorsManager.blackColor, - fontSize: 8, + fontSize: 12, fontWeight: FontWeight.w400, ), ), ), ), Tab( - icon: SvgPicture.asset( - Assets.scheduleCirculateIcon, - color: waterHeaterBloc.selectedTabIndex == 2 - ? Colors.white - : ColorsManager.blackColor, + icon: Padding( + padding: + const EdgeInsets.only(top: 10), + child: SvgPicture.asset( + Assets.scheduleCirculateIcon, + color: waterHeaterBloc + .selectedTabIndex == + 2 + ? Colors.white + : ColorsManager.blackColor, + ), ), child: Container( padding: const EdgeInsets.symmetric( - vertical: 10), + vertical: 5), child: Text( 'Circulate', style: context.bodySmall.copyWith( color: waterHeaterBloc - .selectedTabIndex == 2 + .selectedTabIndex == + 2 ? Colors.white : ColorsManager.blackColor, - fontSize: 8, + fontSize: 12, fontWeight: FontWeight.w400, ), ), ), ), Tab( - icon: SvgPicture.asset( - Assets.scheduleInchingIcon, - color: waterHeaterBloc - .selectedTabIndex == 3 - ? Colors.white - : ColorsManager.blackColor, + icon: Padding( + padding: + const EdgeInsets.only(top: 10), + child: SvgPicture.asset( + Assets.scheduleInchingIcon, + color: waterHeaterBloc + .selectedTabIndex == + 3 + ? Colors.white + : ColorsManager.blackColor, + ), ), child: Container( padding: const EdgeInsets.symmetric( - vertical: 10), + vertical: 5), child: Text( 'Inching', style: context.bodySmall.copyWith( color: waterHeaterBloc - .selectedTabIndex == 3 + .selectedTabIndex == + 3 ? Colors.white : ColorsManager.blackColor, - fontSize: 8, + fontSize: 12, fontWeight: FontWeight.w400, ), ), @@ -308,93 +377,132 @@ class WHTimerScheduleScreen extends StatelessWidget { ), ), ), - Column( - mainAxisAlignment: waterHeaterBloc - .listSchedule.isNotEmpty - ? MainAxisAlignment.start - : MainAxisAlignment.center, - children: [ - SizedBox( - child: waterHeaterBloc.createSchedule == true - ? CreateSchedule( - onToggleChanged: (bool isOn) { - waterHeaterBloc.toggleSchedule = isOn; - }, - onDateTimeChanged: (DateTime dateTime) { - waterHeaterBloc.selectedTime = dateTime; - }, - days: waterHeaterBloc.days, - selectDays: (List - selectedDays) { - waterHeaterBloc - .selectedDays = - selectedDays; - }, - ) - : Padding( - padding: const EdgeInsets.only(top: 10), - child: ScheduleListView( - listSchedule: waterHeaterBloc.listSchedule, // Pass the schedule list here - onDismissed: - (scheduleId) { - waterHeaterBloc.listSchedule.removeWhere( - (schedule) => schedule.scheduleId == scheduleId); - waterHeaterBloc.add( - DeleteScheduleEvent(id: scheduleId)); - }, - onToggleSchedule: (scheduleId, isEnabled) { - - waterHeaterBloc.add(ToggleScheduleEvent( - id: scheduleId, toggle: isEnabled, - )); - }, + SizedBox( + child: waterHeaterBloc.createSchedule == + true + ? CreateSchedule( + onToggleChanged: (bool isOn) { + waterHeaterBloc + .toggleSchedule = isOn; + }, + onDateTimeChanged: + (DateTime dateTime) { + waterHeaterBloc.selectedTime = + dateTime; + }, + days: waterHeaterBloc.days, + selectDays: (List + selectedDays) { + waterHeaterBloc.selectedDays = + selectedDays; + }, + ) + : Padding( + padding: const EdgeInsets.only( + top: 10), + child: Column( + children: [ + Expanded( + child: ScheduleListView( + listSchedule: + waterHeaterBloc + .listSchedule, // Pass the schedule list here + onDismissed: + (scheduleId) { + waterHeaterBloc + .listSchedule + .removeWhere( + (schedule) => + schedule + .scheduleId == + scheduleId); + waterHeaterBloc.add( + DeleteScheduleEvent( + id: scheduleId)); + }, + onToggleSchedule: + (scheduleId, + isEnabled) { + waterHeaterBloc.add( + ToggleScheduleEvent( + id: scheduleId, + toggle: isEnabled, + )); + }, + ), ), - ), - ), - ], + ], + ), + ), ), Center( child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.center, children: [ - waterHeaterBloc.createCirculate == true - ? - CirculateWidget( - endDuration: () { - waterHeaterBloc.add(SelectTimeEvent(context: context, isEffective: false)); - }, - startDuration: () { - waterHeaterBloc.add(SelectTimeEvent(context: context, isEffective: false)); - - }, - isStartEndTime: true, - startTime: DateTime.now(), - endTime: DateTime.now(), - days: waterHeaterBloc.days, - selectedDays: [], - onToggleStartEndTime: (c) {}, - onTimeChanged: (x, f) { - }, - onDaySelected: (p0) {}, - ):CirculateListView( - listSchedule: waterHeaterBloc.listSchedule, // Pass the schedule list here - onDismissed: (scheduleId) { - waterHeaterBloc.listSchedule.removeWhere((schedule) => schedule.scheduleId == scheduleId); - waterHeaterBloc.add(DeleteScheduleEvent(id: scheduleId)); - }, - onToggleSchedule: (scheduleId, isEnabled) { - waterHeaterBloc.add(ToggleScheduleEvent( - id: scheduleId, toggle: isEnabled, - )); - }, - ) + waterHeaterBloc.createCirculate == + true + ? CirculateWidget( + endDuration: () { + waterHeaterBloc.add( + SelectTimeEvent( + context: context, + isEffective: + false)); + }, + startDuration: () { + waterHeaterBloc.add( + SelectTimeEvent( + context: context, + isEffective: + false)); + }, + isStartEndTime: true, + startTime: DateTime.now(), + endTime: DateTime.now(), + days: waterHeaterBloc.days, + selectedDays: [], + onToggleStartEndTime: + (c) {}, + onTimeChanged: (x, f) {}, + onDaySelected: (p0) {}, + ) + : CirculateListView( + listSchedule: [], // Pass the schedule list here + onDismissed: (scheduleId) { + waterHeaterBloc + .listSchedule + .removeWhere((schedule) => + schedule + .scheduleId == + scheduleId); + waterHeaterBloc.add( + DeleteScheduleEvent( + id: scheduleId)); + }, + onToggleSchedule: + (scheduleId, + isEnabled) { + waterHeaterBloc.add( + ToggleScheduleEvent( + id: scheduleId, + toggle: isEnabled, + )); + }, + ) ], ), ), - Column(children: [ - SizedBox(height: 20), - Container(child: InchingWidget(),),],) + Column( + children: [ + SizedBox(height: 20), + Container( + child: InchingWidget(), + ), + ], + ) ], ), ), diff --git a/lib/features/devices/view/widgets/wizard_page.dart b/lib/features/devices/view/widgets/wizard_page.dart index 6639321..fdddf05 100644 --- a/lib/features/devices/view/widgets/wizard_page.dart +++ b/lib/features/devices/view/widgets/wizard_page.dart @@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; +import 'package:syncrow_app/features/devices/view/widgets/one_gang/one_gang_Interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/three_gang/three_gang_interface.dart'; +import 'package:syncrow_app/features/devices/view/widgets/two_gang/two_gang_Interface.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/utils/context_extension.dart'; @@ -39,6 +41,18 @@ class WizardPage extends StatelessWidget { pageBuilder: (context, animation1, animation2) => const ThreeGangInterface())); } + if (groupsList[index].name == '2G') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => const TwoGangInterface())); + } + if (groupsList[index].name == '1G') { + Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (context, animation1, animation2) => const OneGangInterface())); + } }, child: DefaultContainer( padding: const EdgeInsets.all(15), diff --git a/lib/features/shared_widgets/battery_bar.dart b/lib/features/shared_widgets/battery_bar.dart new file mode 100644 index 0000000..9313bab --- /dev/null +++ b/lib/features/shared_widgets/battery_bar.dart @@ -0,0 +1,57 @@ + import 'package:flutter/material.dart'; + + + + +class BatteryBar extends StatelessWidget { + const BatteryBar({ + required this.batteryPercentage, + super.key, + }); + + final int batteryPercentage; + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox(), + Transform.rotate( + angle: 1.5708, // 90 degrees in radians (π/2 or 1.5708) + child: Icon( + _getBatteryIcon(batteryPercentage), + color: _getBatteryColor(batteryPercentage), + size: 30, + ), + ), + ], + ); + } + + IconData _getBatteryIcon(int batteryLevel) { + // if (batteryState == BatteryState.charging) { + // return Icons.battery_charging_full; + // } else + if (batteryLevel >= 80) { + return Icons.battery_full; + } else if (batteryLevel >= 60) { + return Icons.battery_4_bar; + } else if (batteryLevel >= 40) { + return Icons.battery_3_bar; + } else if (batteryLevel >= 20) { + return Icons.battery_2_bar; + } else { + return Icons.battery_alert; + } + } + + Color _getBatteryColor(int batteryLevel) { + if (batteryLevel >= 80) { + return Colors.green; + } else if (batteryLevel >= 40) { + return Colors.yellowAccent; + } else { + return Colors.red; + } + } +} diff --git a/lib/features/shared_widgets/schedule_list.dart b/lib/features/shared_widgets/schedule_list.dart index 0b48f86..11248fa 100644 --- a/lib/features/shared_widgets/schedule_list.dart +++ b/lib/features/shared_widgets/schedule_list.dart @@ -1,16 +1,13 @@ - 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; @@ -24,97 +21,97 @@ class ScheduleListView extends StatelessWidget { @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)), + return 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, + ), + ), ), - ), - 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), + 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].function.value ? "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, + ), + ), + ), + ), + ], + ), + ), ), - ), - ), - 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].function.value ? "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() - ); + ), + ) + : const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center(child: EmptySchedule()), + ], + ); } } diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 20bdb39..2699f62 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -735,7 +735,7 @@ class Assets { //assets/icons/success-white.svg //assets for success white image - static const String assetsSuccessWhite ="assets/icons/success-white.svg"; + static const String assetsSuccessWhite = "assets/icons/success-white.svg"; /// Assets for assetsImagesAutomation /// assets/images/automation.jpg @@ -1044,13 +1044,19 @@ class Assets { static const String waterHeaterOn = "assets/icons/water_heater_on.svg"; static const String waterHeaterOff = "assets/icons/water_heater_off.svg"; - static const String scheduleCelenderIcon = "assets/icons/schedule_celender_icon.svg"; - static const String scheduleCirculateIcon = "assets/icons/schedule_circulate_icon.svg"; - static const String scheduleInchingIcon = "assets/icons/schedule_Inching_icon.svg"; + static const String scheduleCelenderIcon = + "assets/icons/schedule_celender_icon.svg"; + static const String scheduleCirculateIcon = + "assets/icons/schedule_circulate_icon.svg"; + static const String scheduleInchingIcon = + "assets/icons/schedule_Inching_icon.svg"; static const String scheduleTimeIcon = "assets/icons/schedule_time_icon.svg"; static const String waterHeaterIcon = "assets/icons/water_heater_icon.svg"; - - - + static const String doorOpen = "assets/icons/opened_door.svg"; + static const String doorClose = "assets/icons/closed_door.svg"; + static const String doorNotificationSetting = + "assets/icons/door_notification_setting_icon.svg"; + static const String doorRecordsIcon = "assets/icons/door_records_icon.svg"; + static const String doorSensorIcon = "assets/icons/door_sensor_icon.svg"; } diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 1c3e20f..af3e506 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -81,7 +81,8 @@ abstract class ApiEndpoints { static const String controlGroup = '/group/control'; //GET static const String groupBySpace = '/group/{unitUuid}'; - static const String devicesByGroupName = '/group/{unitUuid}/devices/{groupName}'; + static const String devicesByGroupName = + '/group/{unitUuid}/devices/{groupName}'; static const String groupByUuid = '/group/{groupUuid}'; //DELETE @@ -93,7 +94,8 @@ abstract class ApiEndpoints { static const String addDeviceToRoom = '/device/room'; static const String addDeviceToGroup = '/device/group'; static const String controlDevice = '/device/{deviceUuid}/control'; - static const String firmwareDevice = '/device/{deviceUuid}/firmware/{firmwareVersion}'; + static const String firmwareDevice = + '/device/{deviceUuid}/firmware/{firmwareVersion}'; static const String getDevicesByUserId = '/device/user/{userId}'; static const String getDevicesByUnitId = '/device/unit/{unitUuid}'; static const String openDoorLock = '/door-lock/open/{doorLockUuid}'; @@ -103,7 +105,8 @@ abstract class ApiEndpoints { static const String deviceByUuid = '/device/{deviceUuid}'; static const String deviceFunctions = '/device/{deviceUuid}/functions'; static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices'; - static const String deviceFunctionsStatus = '/device/{deviceUuid}/functions/status'; + static const String deviceFunctionsStatus = + '/device/{deviceUuid}/functions/status'; ///Device Permission Module //POST @@ -128,24 +131,29 @@ abstract class ApiEndpoints { static const String getUnitAutomation = '/automation/{unitUuid}'; - static const String getAutomationDetails = '/automation/details/{automationId}'; + static const String getAutomationDetails = + '/automation/details/{automationId}'; /// PUT static const String updateScene = '/scene/tap-to-run/{sceneId}'; static const String updateAutomation = '/automation/{automationId}'; - static const String updateAutomationStatus = '/automation/status/{automationId}'; + static const String updateAutomationStatus = + '/automation/status/{automationId}'; /// DELETE static const String deleteScene = '/scene/tap-to-run/{unitUuid}/{sceneId}'; - static const String deleteAutomation = '/automation/{unitUuid}/{automationId}'; + static const String deleteAutomation = + '/automation/{unitUuid}/{automationId}'; //////////////////////Door Lock ////////////////////// //online - static const String addTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}'; - static const String getTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}'; + static const String addTemporaryPassword = + '/door-lock/temporary-password/online/{doorLockUuid}'; + static const String getTemporaryPassword = + '/door-lock/temporary-password/online/{doorLockUuid}'; //one-time offline static const String addOneTimeTemporaryPassword = @@ -176,11 +184,10 @@ abstract class ApiEndpoints { 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 getSchedule = + '/schedule/{deviceUuid}?category={category}'; static const String changeSchedule = '/schedule/enable/{deviceUuid}'; static const String deleteSchedule = '/schedule/{deviceUuid}/{scheduleId}'; - - + static const String reportLogs = '/device/report-logs/{deviceUuid}?code={code}&startTime={startTime}&endTime={endTime}'; } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index dccb2de..d0c068c 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -3,6 +3,7 @@ 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/device_report_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'; @@ -39,7 +40,6 @@ class DevicesAPI { expectedResponseModel: (json) { return json; }, - ); return response; } catch (e) { @@ -72,7 +72,8 @@ class DevicesAPI { static Future> getDeviceStatus(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId), + path: ApiEndpoints.deviceFunctionsStatus + .replaceAll('{deviceUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -82,7 +83,9 @@ class DevicesAPI { } static Future> renamePass( - {required String name, required String doorLockUuid, required String passwordId}) async { + {required String name, + required String doorLockUuid, + required String passwordId}) async { final response = await _httpService.put( path: ApiEndpoints.renamePassword .replaceAll('{doorLockUuid}', doorLockUuid) @@ -107,7 +110,8 @@ class DevicesAPI { return response; } - static Future> getDeviceByGroupName(String unitId, String groupName) async { + static Future> getDeviceByGroupName( + String unitId, String groupName) async { final response = await _httpService.get( path: ApiEndpoints.devicesByGroupName .replaceAll('{unitUuid}', unitId) @@ -146,7 +150,8 @@ class DevicesAPI { return response; } - static Future> getDevicesByGatewayId(String gatewayId) async { + static Future> getDevicesByGatewayId( + String gatewayId) async { final response = await _httpService.get( path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId), showServerMessage: false, @@ -168,7 +173,8 @@ class DevicesAPI { String deviceId, ) async { final response = await _httpService.get( - path: ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.getTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -179,7 +185,8 @@ class DevicesAPI { static Future getOneTimePasswords(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.getOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.getOneTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -190,7 +197,8 @@ class DevicesAPI { static Future getTimeLimitPasswords(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.getMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.getMultipleTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -215,10 +223,12 @@ class DevicesAPI { "invalidTime": invalidTime, }; if (scheduleList != null) { - body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); + body["scheduleList"] = + scheduleList.map((schedule) => schedule.toJson()).toList(); } final response = await _httpService.post( - path: ApiEndpoints.addTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.addTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), body: body, showServerMessage: false, expectedResponseModel: (json) => json, @@ -229,7 +239,8 @@ class DevicesAPI { static Future generateOneTimePassword({deviceId}) async { try { final response = await _httpService.post( - path: ApiEndpoints.addOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.addOneTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -241,10 +252,12 @@ class DevicesAPI { } } - static Future generateMultiTimePassword({deviceId, effectiveTime, invalidTime}) async { + static Future generateMultiTimePassword( + {deviceId, effectiveTime, invalidTime}) async { try { final response = await _httpService.post( - path: ApiEndpoints.addMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.addMultipleTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), showServerMessage: true, body: {"effectiveTime": effectiveTime, "invalidTime": invalidTime}, expectedResponseModel: (json) { @@ -277,21 +290,22 @@ class DevicesAPI { required String time, required String code, required bool value, - required List days, + required List days, }) async { final response = await _httpService.post( path: ApiEndpoints.saveSchedule.replaceAll('{deviceUuid}', deviceId), showServerMessage: false, - body: - jsonEncode({ - "category": category, - "time": time, - "function":{ - "code": code, - "value":value, + body: jsonEncode( + { + "category": category, + "time": time, + "function": { + "code": code, + "value": value, + }, + "days": days }, - "days": days - },), + ), expectedResponseModel: (json) { return json; }, @@ -304,7 +318,9 @@ class DevicesAPI { required String deviceId, }) async { final response = await _httpService.get( - path: ApiEndpoints.getSchedule.replaceAll('{deviceUuid}', deviceId).replaceAll('{category}', category), + path: ApiEndpoints.getSchedule + .replaceAll('{deviceUuid}', deviceId) + .replaceAll('{category}', category), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -320,10 +336,7 @@ class DevicesAPI { }) async { final response = await _httpService.put( path: ApiEndpoints.changeSchedule.replaceAll('{deviceUuid}', deviceUuid), - body: { - "scheduleId": scheduleId, - "enable": enable - }, + body: {"scheduleId": scheduleId, "enable": enable}, expectedResponseModel: (json) { return json['success']; }, @@ -336,11 +349,33 @@ class DevicesAPI { required String scheduleId, }) async { final response = await _httpService.delete( - path: ApiEndpoints.deleteSchedule.replaceAll('{deviceUuid}', deviceUuid).replaceAll('{scheduleId}', scheduleId), + path: ApiEndpoints.deleteSchedule + .replaceAll('{deviceUuid}', deviceUuid) + .replaceAll('{scheduleId}', scheduleId), expectedResponseModel: (json) { return json; }, ); return response; } + + static Future getReportLogs({ + required String deviceUuid, + required String code, + required String startTime, + required String endTime, + }) async { + final response = await _httpService.get( + path: ApiEndpoints.reportLogs + .replaceAll('{deviceUuid}', deviceUuid) + .replaceAll('{code}', code) + .replaceAll('{startTime}', startTime) + .replaceAll('{endTime}', endTime), + expectedResponseModel: (json) { + return DeviceReport.fromJson(json); + }, + ); + + return response; + } } diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 4f3ba8a..2ce280f 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -1,6 +1,5 @@ //ignore_for_file: constant_identifier_names import 'dart:ui'; - import 'package:syncrow_app/features/devices/model/function_model.dart'; import 'package:syncrow_app/features/menu/view/widgets/create_home/create_home_view.dart'; import 'package:syncrow_app/features/menu/view/widgets/join_home/join_home_view.dart'; @@ -50,6 +49,7 @@ enum DeviceType { CeilingSensor, WallSensor, WH, + DS, Other, } @@ -75,11 +75,14 @@ Map devicesTypesMap = { "1G": DeviceType.OneGang, "CUR": DeviceType.Curtain, "WH": DeviceType.WH, + "DS": DeviceType.DS, }; Map> devicesFunctionsMap = { DeviceType.AC: [ FunctionModel( - code: 'switch', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'mode', type: functionTypesMap['Enum'], @@ -102,7 +105,9 @@ Map> devicesFunctionsMap = { // "range": ["low", "middle", "high", "auto"] })), FunctionModel( - code: 'child_lock', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'child_lock', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), ], DeviceType.Gateway: [ FunctionModel( @@ -116,7 +121,9 @@ Map> devicesFunctionsMap = { "range": ["normal", "alarm"] })), FunctionModel( - code: 'factory_reset', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'factory_reset', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'alarm_active', type: functionTypesMap['String'], @@ -126,7 +133,8 @@ Map> devicesFunctionsMap = { FunctionModel( code: 'sensitivity', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "", "min": 1, "max": 10, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 10, "scale": 0, "step": 1})), ], DeviceType.DoorLock: [ FunctionModel( @@ -134,7 +142,9 @@ Map> devicesFunctionsMap = { type: functionTypesMap['Raw'], values: ValueModel.fromJson({})), FunctionModel( - code: 'remote_no_dp_key', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})), + code: 'remote_no_dp_key', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({})), FunctionModel( code: 'normal_open_switch', type: functionTypesMap['Boolean'], @@ -144,120 +154,143 @@ Map> devicesFunctionsMap = { FunctionModel( code: 'far_detection', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "cm", "min": 75, "max": 600, "scale": 0, "step": 75})), + values: ValueModel.fromJson( + {"unit": "cm", "min": 75, "max": 600, "scale": 0, "step": 75})), FunctionModel( code: 'presence_time', type: functionTypesMap['Integer'], - values: - ValueModel.fromJson({"unit": "Min", "min": 0, "max": 65535, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "Min", "min": 0, "max": 65535, "scale": 0, "step": 1})), FunctionModel( code: 'motion_sensitivity_value', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), FunctionModel( code: 'motionless_sensitivity', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})), FunctionModel( - code: 'indicator', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'indicator', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), ], - DeviceType.OneGang:[ + DeviceType.OneGang: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + 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})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), ], - DeviceType.TwoGang:[ + DeviceType.TwoGang: [ FunctionModel( - code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({}) - ), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({}) - ), + 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}) - ), + 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}) - ), + 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({})), + code: 'switch_1', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_2', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( - code: 'switch_3', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})), + code: 'switch_3', + type: functionTypesMap['Boolean'], + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + 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})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'countdown_3', type: functionTypesMap['Integer'], - values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), + values: ValueModel.fromJson( + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), ], DeviceType.Curtain: [ FunctionModel( code: 'control', type: functionTypesMap['Enum'], - values: ValueModel.fromJson( - {"range": ["open","stop","close"]} - ) - ), + values: ValueModel.fromJson({ + "range": ["open", "stop", "close"] + })), FunctionModel( code: 'percent_control', type: functionTypesMap['Integer'], values: ValueModel.fromJson( - {"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1}) - ), + {"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1})), ], DeviceType.WH: [ FunctionModel( code: 'switch_1', type: functionTypesMap['Boolean'], - values: ValueModel.fromJson({}) - ), + values: ValueModel.fromJson({})), FunctionModel( code: 'countdown_1', type: functionTypesMap['Integer'], values: ValueModel.fromJson( - {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1}) - ), + {"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})), FunctionModel( code: 'relay_status', type: functionTypesMap['Enum'], - values: ValueModel.fromJson( - {"range": [ "off", "on"]}) - ), + values: ValueModel.fromJson({ + "range": ["off", "on"] + })), FunctionModel( code: 'switch_backlight', type: functionTypesMap['Boolean'], - values: ValueModel.fromJson( - {}) - ), + values: ValueModel.fromJson({})), FunctionModel( code: 'switch_inching', type: functionTypesMap['String'], - values: ValueModel.fromJson( - {"maxlen": 255,}) - ), + values: ValueModel.fromJson({ + "maxlen": 255, + })), FunctionModel( code: 'cycle_timing', type: functionTypesMap['Raw'], - values: ValueModel.fromJson( - {"maxlen": 255}) - ), + values: ValueModel.fromJson({"maxlen": 255})), + ], + DeviceType.DS: [ + FunctionModel( + code: 'doorcontact_state', + type: functionTypesMap['Raw'], + values: ValueModel.fromJson({}) + ), + FunctionModel( + code: 'battery_percentage', + type: functionTypesMap['Integer'], + values: ValueModel.fromJson({}) + ), ], }; @@ -404,7 +437,11 @@ List> menuSections = [ 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsMessages, 'page': null }, - {'title': 'FAQs', 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs, 'page': null}, + { + 'title': 'FAQs', + 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs, + 'page': null + }, { 'title': 'Help & Feedback', 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsHelpAndFeedback, @@ -434,7 +471,11 @@ List> menuSections = [ 'title': 'Legal Information', 'color': const Color(0xFF001B72), 'buttons': [ - {'title': 'About', 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout, 'page': null}, + { + 'title': 'About', + 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout, + 'page': null + }, { 'title': 'Privacy Policy', 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsPrivacyPolicy,