two gang schedule

This commit is contained in:
mohammad
2024-09-15 16:48:16 +03:00
parent ad47e34fa2
commit ddaf36797d
10 changed files with 694 additions and 180 deletions

View File

@ -0,0 +1,15 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5419 20.2425C11.1182 19.3486 10.0545 18.9679 9.16647 19.3936L1.38334 23.1269C0.591819 23.3099 0 24.0198 0 24.8727V33.8466C0 34.8381 0.797668 35.6414 1.78229 35.6414C2.76668 35.6414 3.56412 34.8381 3.56412 33.8466V27.6645L9.16647 30.3517C9.41376 30.4705 9.67449 30.5264 9.93112 30.5264C10.5974 30.5264 11.2359 30.1484 11.5419 29.5026C11.9643 28.6076 11.5875 27.5367 10.699 27.1104L6.03295 24.8725L10.699 22.6347C11.5877 22.2085 11.9643 21.1376 11.5419 20.2425Z" fill="#D5D5D5"/>
<path d="M20.6084 17.6893C20.8655 17.6893 21.1257 17.6334 21.3737 17.5145L32.0481 12.395C32.9366 11.9684 33.3141 10.8973 32.891 10.0024C32.4682 9.10855 31.4052 8.72804 30.5156 9.1535L19.8408 14.2735C18.9522 14.6999 18.5754 15.7705 18.998 16.6657C19.3038 17.3111 19.9423 17.6893 20.6084 17.6893Z" fill="#D5D5D5"/>
<path d="M42.7227 7.27445L48.1841 4.65449V10.9114C48.1841 11.9027 48.9818 12.7061 49.9659 12.7061C50.9503 12.7061 51.7478 11.9027 51.7478 10.9114V4.62192L57.2779 7.27445C57.5259 7.39303 57.7862 7.44922 58.0433 7.44922C58.7091 7.44922 59.3478 7.07124 59.6534 6.42537C60.0763 5.53041 59.6994 4.45953 58.8106 4.03316L50.7663 0.174427C50.2813 -0.0581424 49.7189 -0.0581424 49.2338 0.174427L41.19 4.03316C40.3015 4.45953 39.9244 5.53018 40.3472 6.42537C40.771 7.32101 41.8362 7.70198 42.7227 7.27445Z" fill="#D5D5D5"/>
<path d="M67.9533 12.3945L78.6281 17.5147C78.875 17.6335 79.1361 17.6895 79.3932 17.6895C80.059 17.6895 80.6978 17.3115 81.0034 16.6656C81.426 15.7707 81.0494 14.6998 80.1606 14.2734L69.4856 9.15299C68.5982 8.7273 67.5345 9.10735 67.1105 10.0018C66.6875 10.8968 67.0645 11.9677 67.9533 12.3945Z" fill="#D5D5D5"/>
<path d="M81.539 34.8098C82.4275 34.3836 82.8046 33.3128 82.3815 32.4176C81.9587 31.5231 80.8946 31.1423 80.0065 31.5694L73.167 34.8508L51.7477 24.577V16.5765C51.7477 15.5845 50.9503 14.7817 49.9659 14.7817C48.9817 14.7817 48.1841 15.5847 48.1841 16.5765V24.6095L26.8346 34.8499L20.6999 31.9073C19.8093 31.4809 18.7482 31.8612 18.3244 32.7563C17.9016 33.6513 18.2787 34.7224 19.1672 35.1488L25.8031 38.3318V61.6684L19.0593 64.903C18.1707 65.3294 17.7939 66.4 18.2165 67.2955C18.5221 67.9411 19.1606 68.3191 19.8266 68.3191C20.0837 68.3191 20.3447 68.2631 20.5917 68.1443L26.8349 65.1498L48.1841 75.3902V81.5074C48.1841 82.4992 48.9817 83.3022 49.9659 83.3022C50.9503 83.3022 51.7477 82.4989 51.7477 81.5074V75.4232L73.1657 65.15L79.6524 68.2615C79.9002 68.3801 80.1607 68.4363 80.4175 68.4363C81.0834 68.4363 81.7221 68.0583 82.0277 67.4124C82.4508 66.5175 82.0735 65.4466 81.1849 65.02L74.1977 61.6686V38.3318L81.539 34.8098ZM70.634 62.3879L51.7479 71.4466V49.0993L70.634 40.0401V62.3879ZM29.4788 61.8938C29.4472 61.8269 29.4053 61.7691 29.3668 61.708V40.0403L48.1841 49.0663V71.414L29.6409 62.5197C29.6239 62.3085 29.5745 62.0963 29.4788 61.8938ZM50.0003 27.715L69.0207 36.8382L50.0003 45.961L30.9799 36.8382L50.0003 27.715Z" fill="#D5D5D5"/>
<path d="M78.6271 82.4859L67.9525 87.6061C67.0639 88.0327 66.6864 89.1036 67.1095 89.9979C67.4153 90.644 68.054 91.0215 68.7199 91.0215C68.9767 91.0215 69.2379 90.9655 69.485 90.8477L80.1598 85.7272C81.0483 85.3009 81.4252 84.2295 81.0025 83.3346C80.579 82.4401 79.5149 82.0616 78.6271 82.4859Z" fill="#D5D5D5"/>
<path d="M57.2782 92.7267L51.748 95.3794V88.2271C51.748 87.2358 50.9506 86.4324 49.9662 86.4324C48.982 86.4324 48.1844 87.2358 48.1844 88.2271V95.3469L42.723 92.7267C41.8335 92.3024 40.7713 92.6808 40.3475 93.576C39.9246 94.4709 40.3015 95.5414 41.1903 95.9677L49.2346 99.826C49.4769 99.9418 49.7387 100 50.0008 100C50.2629 100 50.5248 99.9418 50.7671 99.826L58.8114 95.9677C59.6999 95.5414 60.077 94.4709 59.6541 93.576C59.2304 92.6808 58.166 92.3033 57.2782 92.7267Z" fill="#D5D5D5"/>
<path d="M32.0476 87.6063L21.3728 82.4861C20.4863 82.0606 19.4217 82.4395 18.9975 83.3345C18.5749 84.2294 18.9517 85.3008 19.8403 85.7271L30.5153 90.8476C30.7622 90.9654 31.0233 91.0214 31.2802 91.0214C31.946 91.0214 32.5847 90.6439 32.8903 89.9978C33.3132 89.1037 32.9361 88.0329 32.0476 87.6063Z" fill="#D5D5D5"/>
<path d="M11.5419 70.4973C11.1182 69.6028 10.0545 69.2239 9.16647 69.6487L3.56412 72.3354V65.6177C3.56412 64.6264 2.76668 63.823 1.78229 63.823C0.797668 63.823 0 64.6264 0 65.6177V74.5918C0 74.8162 0.0457694 75.0283 0.120686 75.2262C0.156892 75.8783 0.53193 76.465 1.12215 76.7485L9.16647 80.6072C9.41376 80.7254 9.67449 80.7818 9.93112 80.7818C10.5974 80.7818 11.2359 80.4038 11.5419 79.7581C11.9643 78.8632 11.5875 77.7923 10.699 77.3655L6.03295 75.1281L10.699 72.8902C11.5877 72.4636 11.9643 71.3923 11.5419 70.4973Z" fill="#D5D5D5"/>
<path d="M1.78229 56.8224C2.76668 56.8224 3.56412 56.0192 3.56412 55.0273V44.4373C3.56412 43.446 2.76668 42.6426 1.78229 42.6426C0.797668 42.6426 0 43.446 0 44.4373V55.0273C0.00022771 56.0192 0.797896 56.8224 1.78229 56.8224Z" fill="#D5D5D5"/>
<path d="M98.6173 23.1267L90.8342 19.3935C89.9443 18.9666 88.8825 19.3478 88.4589 20.2423C88.0363 21.1377 88.4132 22.2084 89.3017 22.635L93.9677 24.8729L89.3017 27.1107C88.4132 27.5371 88.0363 28.6077 88.4589 29.5029C88.7645 30.1488 89.4032 30.5268 90.0691 30.5268C90.3262 30.5268 90.5873 30.4708 90.8342 30.352L96.4365 27.6648V33.847C96.4365 34.8385 97.234 35.6417 98.2184 35.6417C99.2027 35.6417 100 34.8385 100 33.847V24.8731C100 24.0196 99.4088 23.3098 98.6173 23.1267Z" fill="#D5D5D5"/>
<path d="M98.2184 42.6421C97.2337 42.6421 96.4365 43.4455 96.4365 44.4368V55.0268C96.4365 56.0187 97.234 56.8215 98.2184 56.8215C99.2027 56.8215 100 56.0185 100 55.0268V44.4368C100 43.4455 99.203 42.6421 98.2184 42.6421Z" fill="#D5D5D5"/>
<path d="M98.2184 63.8228C97.2337 63.8228 96.4365 64.6262 96.4365 65.6175V72.3352L90.8342 69.6485C89.9443 69.223 88.8825 69.6021 88.4589 70.4971C88.0363 71.392 88.4132 72.4634 89.3017 72.8898L93.9677 75.1276L89.3017 77.365C88.4132 77.7918 88.0363 78.8627 88.4589 79.7577C88.7645 80.4033 89.4032 80.7813 90.0691 80.7813C90.3262 80.7813 90.5873 80.7249 90.8342 80.6068L98.8785 76.748C99.4687 76.4645 99.8438 75.8778 99.88 75.2258C99.9546 75.0278 100 74.8157 100 74.5914V65.6173C100 64.6262 99.203 63.8228 98.2184 63.8228Z" fill="#D5D5D5"/>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -7,6 +7,7 @@ import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.d
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/devices/model/groupTwoGangModel.dart';
import 'package:syncrow_app/features/devices/model/schedule_model.dart';
import 'package:syncrow_app/features/devices/model/status_model.dart';
import 'package:syncrow_app/features/devices/model/two_gang_model.dart';
import 'package:syncrow_app/services/api/devices_api.dart';
@ -14,11 +15,11 @@ import 'package:syncrow_app/services/api/devices_api.dart';
class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
final String twoGangId;
TwoGangModel deviceStatus = TwoGangModel(
firstSwitch: false,
secondSwitch: false,
firstCountDown: 0,
secondCountDown: 0,
);
firstSwitch: false,
secondSwitch: false,
firstCountDown: 0,
secondCountDown: 0,
);
Timer? _timer;
// Timer? _firstSwitchTimer;
// Timer? _secondSwitchTimer;
@ -28,6 +29,20 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
List<DeviceModel> devicesList = [];
List<GroupTwoGangModel> groupTwoGangList = [];
bool allSwitchesOn = true;
List<String> selectedDays = [];
bool createSchedule = false;
List<ScheduleModel> listSchedule = [
ScheduleModel(
id: '1',
category: 'category',
enable: true,
function: FunctionModel(code: 'code', value: true),
time: 'time',
timerId: 'timerId',
timezoneId: 'timezoneId',
days: ['S'])
];
TwoGangBloc({required this.twoGangId}) : super(InitialState()) {
on<InitialEvent>(_fetchTwoGangStatus);
@ -43,9 +58,20 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
on<OnClose>(_onClose);
on<GroupAllOnEvent>(_groupAllOn);
on<GroupAllOffEvent>(_groupAllOff);
on<ToggleDaySelectionEvent>(toggleDaySelection);
}
void _fetchTwoGangStatus(InitialEvent event, Emitter<TwoGangState> emit) async {
DateTime? selectedTime;
void toggleCreateSchedule() {
emit(LoadingInitialState());
createSchedule = !createSchedule;
emit(UpdateCreateScheduleState(createSchedule));
emit(ChangeSlidingSegmentState(value: 1));
}
void _fetchTwoGangStatus(
InitialEvent event, Emitter<TwoGangState> emit) async {
emit(LoadingInitialState());
try {
twoGangGroup = event.groupScreen;
@ -57,7 +83,8 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
HomeCubit.getInstance().selectedSpace?.id ?? '', '2G');
for (int i = 0; i < devicesList.length; i++) {
var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
var response =
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
List<StatusModel> statusModelList = [];
for (var status in response['status']) {
statusModelList.add(StatusModel.fromJson(status));
@ -65,23 +92,23 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
deviceStatus = TwoGangModel.fromJson(statusModelList);
groupTwoGangList.add(GroupTwoGangModel(
deviceId: devicesList[i].uuid ?? '',
deviceName: devicesList[i].name ?? '',
firstSwitch: deviceStatus.firstSwitch,
secondSwitch: deviceStatus.secondSwitch,
)
);
deviceId: devicesList[i].uuid ?? '',
deviceName: devicesList[i].name ?? '',
firstSwitch: deviceStatus.firstSwitch,
secondSwitch: deviceStatus.secondSwitch,
));
}
if (groupTwoGangList.isNotEmpty) {
groupTwoGangList.firstWhere((element) {
if (!element.firstSwitch || !element.secondSwitch ) {
if (!element.firstSwitch || !element.secondSwitch) {
allSwitchesOn = false;
}
return true;
});
}
emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: allSwitchesOn));
emit(UpdateGroupState(
twoGangList: groupTwoGangList, allSwitches: allSwitchesOn));
} else {
var response = await DevicesAPI.getDeviceStatus(twoGangId);
List<StatusModel> statusModelList = [];
@ -100,18 +127,21 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
_listenToChanges() {
try {
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$twoGangId');
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$twoGangId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async {
if (_timer != null) {
await Future.delayed(const Duration(seconds: 2));
}
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<StatusModel> statusList = [];
usersMap['status'].forEach((element) {
statusList.add(StatusModel(code: element['code'], value: element['value']));
statusList
.add(StatusModel(code: element['code'], value: element['value']));
});
deviceStatus = TwoGangModel.fromJson(statusList);
@ -126,7 +156,8 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
emit(UpdateState(twoGangModel: deviceStatus));
}
void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter<TwoGangState> emit) async {
void _changeFirstSwitch(
ChangeFirstSwitchStatusEvent event, Emitter<TwoGangState> emit) async {
emit(LoadingNewSate(twoGangModel: deviceStatus));
try {
deviceStatus.firstSwitch = !event.value;
@ -189,13 +220,16 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: twoGangId, code: 'switch_1', value: deviceStatus.firstSwitch),
deviceId: twoGangId,
code: 'switch_1',
value: deviceStatus.firstSwitch),
twoGangId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: twoGangId, code: 'switch_2', value: deviceStatus.secondSwitch),
deviceId: twoGangId,
code: 'switch_2',
value: deviceStatus.secondSwitch),
twoGangId),
]);
if (response.every((element) => !element['success'])) {
@ -216,16 +250,17 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
emit(UpdateState(twoGangModel: deviceStatus));
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: twoGangId, code: 'switch_1',
value: deviceStatus.firstSwitch),
DeviceControlModel(
deviceId: twoGangId,
code: 'switch_1',
value: deviceStatus.firstSwitch),
twoGangId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: twoGangId,
code: 'switch_2',
value: deviceStatus.secondSwitch),
twoGangId),
DeviceControlModel(
deviceId: twoGangId,
code: 'switch_2',
value: deviceStatus.secondSwitch),
twoGangId),
]);
if (response.every((element) => !element['success'])) {
await Future.delayed(const Duration(milliseconds: 500));
@ -243,7 +278,6 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
for (int i = 0; i < groupTwoGangList.length; i++) {
groupTwoGangList[i].firstSwitch = true;
groupTwoGangList[i].secondSwitch = true;
}
emit(UpdateGroupState(twoGangList: groupTwoGangList, allSwitches: true));
@ -251,11 +285,15 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: true),
deviceId: groupTwoGangList[i].deviceId,
code: 'switch_1',
value: true),
groupTwoGangList[i].deviceId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: true),
deviceId: groupTwoGangList[i].deviceId,
code: 'switch_2',
value: true),
groupTwoGangList[i].deviceId),
]);
@ -284,13 +322,16 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
final response = await Future.wait([
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupTwoGangList[i].deviceId, code: 'switch_1', value: false),
deviceId: groupTwoGangList[i].deviceId,
code: 'switch_1',
value: false),
groupTwoGangList[i].deviceId),
DevicesAPI.controlDevice(
DeviceControlModel(
deviceId: groupTwoGangList[i].deviceId, code: 'switch_2', value: false),
deviceId: groupTwoGangList[i].deviceId,
code: 'switch_2',
value: false),
groupTwoGangList[i].deviceId),
]);
if (response.every((element) => !element['success'])) {
@ -305,17 +346,20 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
}
}
void _changeSliding(ChangeSlidingSegment event, Emitter<TwoGangState> emit) async {
void _changeSliding(
ChangeSlidingSegment event, Emitter<TwoGangState> emit) async {
emit(ChangeSlidingSegmentState(value: event.value));
}
void _setCounterValue(SetCounterValue event, Emitter<TwoGangState> emit) async {
void _setCounterValue(
SetCounterValue event, Emitter<TwoGangState> emit) async {
emit(LoadingNewSate(twoGangModel: deviceStatus));
int seconds = 0;
try {
seconds = event.duration.inSeconds;
final response = await DevicesAPI.controlDevice(
DeviceControlModel(deviceId: twoGangId, code: event.deviceCode, value: seconds),
DeviceControlModel(
deviceId: twoGangId, code: event.deviceCode, value: seconds),
twoGangId);
if (response['success'] ?? false) {
@ -340,7 +384,8 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
}
}
void _getCounterValue(GetCounterEvent event, Emitter<TwoGangState> emit) async {
void _getCounterValue(
GetCounterEvent event, Emitter<TwoGangState> emit) async {
emit(LoadingInitialState());
try {
var response = await DevicesAPI.getDeviceStatus(twoGangId);
@ -387,4 +432,38 @@ class TwoGangBloc extends Bloc<TwoGangEvent, TwoGangState> {
emit(TimerRunComplete());
}
}
List<Map<String, String>> days = [
{"day": "Sun", "key": "Sun"},
{"day": "Mon", "key": "Mon"},
{"day": "Tue", "key": "Tue"},
{"day": "Wed", "key": "Wed"},
{"day": "Thu", "key": "Thu"},
{"day": "Fri", "key": "Fri"},
{"day": "Sat", "key": "Sat"},
];
Future<void> toggleDaySelection(
ToggleDaySelectionEvent event,
Emitter<TwoGangState> emit,
) async {
emit(LoadingInitialState());
if (selectedDays.contains(event.key)) {
selectedDays.remove(event.key);
} else {
selectedDays.add(event.key);
}
emit(ChangeTimeState());
add(ChangeSlidingSegment(value: 1));
}
saveSchedule() async {
final response = await DevicesAPI.postSchedule(
category: 'switch_1',
deviceId: twoGangId,
time: 'selectedTime',
code: 'switch_1',
value: true,
days: selectedDays);
}
}

View File

@ -10,6 +10,14 @@ abstract class TwoGangEvent extends Equatable {
class LoadingEvent extends TwoGangEvent {}
class TwoGangUpdated extends TwoGangEvent {}
class ToggleDaySelectionEvent extends TwoGangEvent {
final String key;
const ToggleDaySelectionEvent({required this.key});
@override
List<Object> get props => [key];
}
class InitialEvent extends TwoGangEvent {
final bool groupScreen;
@ -43,6 +51,13 @@ class GroupAllOnEvent extends TwoGangEvent {}
class GroupAllOffEvent extends TwoGangEvent {}
// two_gang_event.dart
class ToggleCreateScheduleEvent extends TwoGangEvent {}
class ChangeSlidingSegment extends TwoGangEvent {
final int value;
const ChangeSlidingSegment({required this.value});

View File

@ -11,6 +11,7 @@ class TwoGangState extends Equatable {
}
class InitialState extends TwoGangState {}
class ChangeTimeState extends TwoGangState {}
class LoadingInitialState extends TwoGangState {}
@ -76,3 +77,11 @@ class TimerRunInProgress extends TwoGangState {
}
class TimerRunComplete extends TwoGangState {}
// two_gang_state.dart
class UpdateCreateScheduleState extends TwoGangState {
final bool createSchedule;
UpdateCreateScheduleState(this.createSchedule);
}

View File

@ -0,0 +1,74 @@
class FunctionModel {
final String code;
final bool value;
FunctionModel({
required this.code,
required this.value,
});
// Convert a JSON object to a FunctionModel instance
factory FunctionModel.fromJson(Map<String, dynamic> json) {
return FunctionModel(
code: json['code'],
value: json['value'],
);
}
// Convert a FunctionModel instance to a JSON object
Map<String, dynamic> toJson() {
return {
'code': code,
'value': value,
};
}
}
class ScheduleModel {
final String id;
final String category;
final bool enable;
final FunctionModel function;
final String time;
final String timerId;
final String timezoneId;
final List<String> days;
ScheduleModel({
required this.id,
required this.category,
required this.enable,
required this.function,
required this.time,
required this.timerId,
required this.timezoneId,
required this.days,
});
// Convert a JSON object to a Schedule instance
factory ScheduleModel.fromJson(Map<String, dynamic> json) {
return ScheduleModel(
id: json['id'],
category: json['category'],
enable: json['enable'],
function: FunctionModel.fromJson(json['function']),
time: json['time'],
timerId: json['timerId'],
timezoneId: json['timezoneId'],
days: List<String>.from(json['days']),
);
}
// Convert a Schedule instance to a JSON object
Map<String, dynamic> toJson() {
return {
'category': category,
'enable': enable,
'function': function.toJson(),
'time': time,
'timerId': timerId,
'timezoneId': timezoneId,
'days': days,
};
}
}

View File

@ -6,8 +6,11 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_state.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/shared_widgets/create_schedule.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/empty_schedule.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart';
@ -15,11 +18,11 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
import 'package:syncrow_app/features/devices/bloc/two_gang_bloc/two_gang_event.dart';
class TwoTimerScreen extends StatelessWidget {
final DeviceModel device;
final String deviceCode;
const TwoTimerScreen({required this.device, required this.deviceCode, super.key});
const TwoTimerScreen(
{required this.device, required this.deviceCode, super.key});
@override
Widget build(BuildContext context) {
@ -28,151 +31,248 @@ class TwoTimerScreen extends StatelessWidget {
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
statusBarIconBrightness: Brightness.light,
),
child: Scaffold(
backgroundColor: ColorsManager.backgroundColor,
extendBodyBehindAppBar: true,
extendBody: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Schedule',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
),
body: SafeArea(
child: BlocProvider(
create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '')
..add(GetCounterEvent(deviceCode: deviceCode)),
child: BlocBuilder<TwoGangBloc, TwoGangState>(
builder: (context, state) {
Duration duration = Duration.zero;
int countNum = 0;
int tabNum = 0;
if (state is UpdateTimerState) {
countNum = state.seconds;
} else if (state is TimerRunInProgress) {
countNum = state.remainingTime;
} else if (state is TimerRunComplete) {
countNum = 0;
} else if (state is LoadingNewSate) {
countNum = 0;
}
child: BlocProvider(
create: (context) => TwoGangBloc(twoGangId: device.uuid ?? '')
..add(GetCounterEvent(deviceCode: deviceCode)),
child: BlocBuilder<TwoGangBloc, TwoGangState>(
builder: (context, state) {
final twoGangBloc = BlocProvider.of<TwoGangBloc>(context);
if (state is ChangeSlidingSegmentState) {
tabNum = state.value;
}
return PopScope(
canPop: false,
onPopInvoked: (didPop) {
if (!didPop) {
BlocProvider.of<TwoGangBloc>(context).add(OnClose());
Navigator.pop(context);
}
},
child: Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
padding: const EdgeInsets.all(24),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.assetsImagesBackground,
),
fit: BoxFit.cover,
opacity: 0.4,
Duration duration = Duration.zero;
int countNum = 0;
int tabNum = 0;
if (state is UpdateTimerState) {
countNum = state.seconds;
} else if (state is TimerRunInProgress) {
countNum = state.remainingTime;
} else if (state is TimerRunComplete) {
countNum = 0;
} else if (state is LoadingNewSate) {
countNum = 0;
} else if (state is ChangeSlidingSegmentState) {
tabNum = state.value;
}
return Scaffold(
backgroundColor: ColorsManager.backgroundColor,
extendBodyBehindAppBar: true,
extendBody: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Schedule',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
actions: [
if (tabNum == 1)
twoGangBloc.createSchedule == true
? TextButton(onPressed: () {}, child: const Text('Save'))
: IconButton(
onPressed: () {
twoGangBloc.toggleCreateSchedule();
},
icon: const Icon(Icons.add),
),
),
child: state is LoadingInitialState || state is LoadingNewSate
? const Center(
child: DefaultContainer(
width: 50, height: 50, child: CircularProgressIndicator()),
)
: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: MediaQuery.sizeOf(context).width,
decoration: const ShapeDecoration(
color: ColorsManager.slidingBlueColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
],
),
body: SafeArea(
child: PopScope(
canPop: false,
onPopInvoked: (didPop) {
if (!didPop) {
BlocProvider.of<TwoGangBloc>(context).add(OnClose());
Navigator.pop(context);
}
},
child: Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
padding: const EdgeInsets.all(24),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Assets.assetsImagesBackground),
fit: BoxFit.cover,
opacity: 0.4,
),
),
child: state is LoadingInitialState ||
state is LoadingNewSate
? const Center(
child: DefaultContainer(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
)
: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: MediaQuery.sizeOf(context).width,
decoration: const ShapeDecoration(
color: ColorsManager.slidingBlueColor,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(20)),
),
),
child: CupertinoSlidingSegmentedControl<int>(
thumbColor: ColorsManager.slidingBlueColor,
onValueChanged: (value) {
BlocProvider.of<TwoGangBloc>(context).add(
ChangeSlidingSegment(
value: value ?? 0));
},
groupValue: state is ChangeSlidingSegmentState
? state.value
: 0,
backgroundColor: Colors.white,
children: <int, Widget>{
0: Container(
padding: const EdgeInsets.symmetric(
vertical: 10),
child: BodySmall(
text: 'Countdown',
style: context.bodySmall.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
),
child: CupertinoSlidingSegmentedControl<int>(
thumbColor: ColorsManager.slidingBlueColor,
onValueChanged: (value) {
BlocProvider.of<TwoGangBloc>(context)
.add(ChangeSlidingSegment(value: value ?? 0));
},
groupValue:
state is ChangeSlidingSegmentState ? state.value : 0,
backgroundColor: Colors.white,
children: <int, Widget>{
0: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
child: BodySmall(
text: 'Countdown',
style: context.bodySmall.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
1: Container(
padding: const EdgeInsets.symmetric(
vertical: 10),
child: Text(
'Schedule',
style: context.bodySmall.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
fontWeight: FontWeight.w400,
),
1: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(
'Schedule',
style: context.bodySmall.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
),
},
),
),
),
if (tabNum == 0)
countNum > 0
? BodyLarge(
text: _formatDuration(countNum),
fontColor: ColorsManager.slidingBlueColor,
fontSize: 40,
)
: CupertinoTimerPicker(
mode: CupertinoTimerPickerMode.hm,
onTimerDurationChanged: (Duration newDuration) {
duration = newDuration;
},
),
if (tabNum == 0)
GestureDetector(
onTap: () {
if (state is LoadingNewSate) {
return;
}
if (countNum > 0) {
BlocProvider.of<TwoGangBloc>(context).add(
SetCounterValue(
deviceCode: deviceCode,
duration: Duration.zero));
} else if (duration != Duration.zero) {
BlocProvider.of<TwoGangBloc>(context).add(
SetCounterValue(
deviceCode: deviceCode, duration: duration));
}
},
),
),
if (tabNum == 0)
countNum > 0
? BodyLarge(
text: _formatDuration(countNum),
fontColor:
ColorsManager.slidingBlueColor,
fontSize: 40,
)
: CupertinoTimerPicker(
mode: CupertinoTimerPickerMode.hm,
onTimerDurationChanged:
(Duration newDuration) {
duration = newDuration;
},
child: SvgPicture.asset(
countNum > 0 ? Assets.pauseIcon : Assets.playIcon)
)
],
)));
},
),
),
),
if (tabNum == 0)
GestureDetector(
onTap: () {
if (state is LoadingNewSate) {
return;
}
if (countNum > 0) {
BlocProvider.of<TwoGangBloc>(context)
.add(SetCounterValue(
deviceCode: deviceCode,
duration: Duration.zero));
} else if (duration != Duration.zero) {
BlocProvider.of<TwoGangBloc>(context)
.add(SetCounterValue(
deviceCode: deviceCode,
duration: duration));
}
},
child: SvgPicture.asset(countNum > 0
? Assets.pauseIcon
: Assets.playIcon)),
if (tabNum == 1)
SizedBox(
height:
MediaQuery.of(context).size.height / 2,
child: twoGangBloc.createSchedule == true
? CreateSchedule(
onToggleChanged: (bool isOn) {
print(
'Toggle is now: ${isOn ? "ON" : "OFF"}');
},
onDateTimeChanged:
(DateTime dateTime) {
print('Selected Time: $dateTime');
},
days: twoGangBloc.days,
selectDays:
(List<String> selectedDays) {
twoGangBloc.selectedDays =
selectedDays;
print(
'Selected Days: ${twoGangBloc.selectedDays}');
},
)
: Center(
child: twoGangBloc.listSchedule.isNotEmpty?
Container(
height: MediaQuery.of(context).size.height / 2,
child: ListView.builder(
itemCount: twoGangBloc.listSchedule.length,
itemBuilder: (context, index) {
return
Dismissible(
key: Key( twoGangBloc.listSchedule[index].id),
child: InkWell(
onTap: () {},
child:
DefaultContainer(
padding: EdgeInsets.all(20),
height: MediaQuery.of(context).size.height / 8,
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(twoGangBloc.listSchedule[index].time),
Text(twoGangBloc.listSchedule[index].days.toString()),
Text(twoGangBloc.listSchedule[index].enable.toString())
],
),
),
Expanded(child:ListTile(
contentPadding: EdgeInsets.zero,
leading: const BodyMedium(
text: '',
fontWeight: FontWeight.normal,
),
trailing: Transform.scale(
scale: .8,
child: CupertinoSwitch(
value: true,
onChanged: (value) {
// smartDoorBloc.add(ToggleRepeatEvent());
},
applyTheme: true,
)),
),)
],
))));
},),
):
EmptySchedule()),
)
],
),
),
),
),
);
},
),
),
);

View File

@ -0,0 +1,140 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class CreateSchedule extends StatefulWidget {
final List<Map<String, String>> days;
final void Function(List<String> selectedDays)? selectDays; // Callback for selected days
final Function(DateTime) onDateTimeChanged;
final void Function(bool isOn)? onToggleChanged; // Callback for toggle state
const CreateSchedule({
Key? key,
required this.days,
required this.selectDays,
required this.onDateTimeChanged,
this.onToggleChanged, // Optional callback
}) : super(key: key);
@override
_CreateScheduleState createState() => _CreateScheduleState();
}
class _CreateScheduleState extends State<CreateSchedule> {
List<String> selectedDays = []; // List of selected days
bool isOn = false; // Boolean state for the ON/OFF toggle
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: Column(
children: <Widget>[
const Divider(
color: ColorsManager.greyColor,
),
SizedBox(
height: 110,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
initialDateTime: DateTime.now(),
onDateTimeChanged: widget.onDateTimeChanged,
),
),
const Divider(
color: ColorsManager.greyColor,
),
const SizedBox(height: 20),
SizedBox(
height: MediaQuery.of(context).size.height * 0.08,
child: ListView(
scrollDirection: Axis.horizontal,
children: widget.days.map((day) {
bool isSelected = selectedDays.contains(day['day']); // Check if day is selected
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
setState(() {
if (isSelected) {
selectedDays.remove(day['day']); // Deselect day
} else {
selectedDays.add(day['day']!); // Select day
}
if (widget.selectDays != null) {
widget.selectDays!(selectedDays);
}
});
},
child: Container(
width: 50,
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 8),
decoration: BoxDecoration(
border: Border.all(
color: isSelected
? Colors.black
: ColorsManager.grayColor,
),
color: Colors.transparent,
borderRadius: BorderRadius.circular(30),
),
child: Center(
child: Text(
day['day']!,
style: TextStyle(
fontSize: 13,
color: isSelected
? Colors.black
: ColorsManager.grayColor,
),
),
),
),
),
);
}).toList(),
),
),
const SizedBox(height: 20),
InkWell(
onTap: () {
setState(() {
isOn = !isOn; // Toggle the state
if (widget.onToggleChanged != null) {
widget.onToggleChanged!(isOn); // Pass the new toggle state
}
});
},
child: DefaultContainer(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
isOn ? 'ON' : 'OFF', // Change text based on state
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isOn ? Colors.green : Colors.red, // Change color based on state
),
),
CircleAvatar(
backgroundColor: isOn
? ColorsManager.secondaryColor.withOpacity(0.6)
: Colors.red.withOpacity(0.6), // Change background color based on state
child: Icon(
Icons.power_settings_new,
color: isOn ? Colors.green : Colors.red, // Change icon color
),
),
],
),
),
),
],
),
);
}
}

View File

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/context_extension.dart';
class EmptySchedule extends StatelessWidget {
const EmptySchedule({super.key});
@override
Widget build(BuildContext context) {
return Center(child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Center(
child: SvgPicture.asset(
height: 100,
width: 100,
Assets.emptySchedule),
),
Column(
children: [
Center(
child: Text(
'Please add',
style: context.displaySmall
.copyWith(
color: const Color(
0XFFD5D5D5),
),
),
),
Center(
child: Text(
'a new schedule',
style: context.displaySmall
.copyWith(
color: const Color(
0XFFD5D5D5),
),
),
),
],
),
],
),);
}
}

View File

@ -1035,4 +1035,6 @@ class Assets {
static const String oneGang = "assets/icons/1gang.svg";
static const String twoGang = "assets/icons/2gang.svg";
static const String emptySchedule = "assets/icons/emptySchedule.svg";
}

View File

@ -270,4 +270,33 @@ class DevicesAPI {
);
return response;
}
static Future<Map<String, dynamic>> postSchedule({
required String category,
required String deviceId,
required String time,
required String code,
required bool value,
required List<String> days,
}) async {
final response = await _httpService.post(
path: ApiEndpoints.deleteTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
body:
{
"category": category,
"time": time,
"function":{
"code": code,
"value":value,
},
"days": days
},
expectedResponseModel: (json) {
return json;
},
);
return response;
}
}