mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2026-03-11 03:01:44 +00:00
Compare commits
7 Commits
update_doo
...
online_doo
| Author | SHA1 | Date | |
|---|---|---|---|
| f6fbf452a0 | |||
| 5aec3d37fb | |||
| a32d885e50 | |||
| afe37dd68a | |||
| f83224ce60 | |||
| 3f0fcc79aa | |||
| 33ec234c0d |
@ -1,6 +1,7 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart';
|
||||
@ -12,6 +13,7 @@ import 'package:syncrow_app/features/devices/model/smart_door_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/status_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/create_temporary_password_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/temporary_password_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/hour_picker_dialog.dart';
|
||||
import 'package:syncrow_app/services/api/devices_api.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
@ -32,7 +34,8 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
on<SetStartEndTimeEvent>(setStartEndTime);
|
||||
on<ChangeTimeEvent>(changeTime);
|
||||
on<GeneratePasswordEvent>(generate7DigitNumber);
|
||||
on<SelectTimeEvent>(selectTime);
|
||||
on<SelectTimeEvent>(selectTimeOfLinePassword);
|
||||
on<SelectTimeOnlinePasswordEvent>(selectTimeOnlinePassword);
|
||||
on<DeletePasswordEvent>(deletePassword);
|
||||
on<GenerateAndSavePasswordTimeLimitEvent>(generateAndSavePasswordTimeLimited);
|
||||
on<GenerateAndSavePasswordOneTimeEvent>(generateAndSavePasswordOneTime);
|
||||
@ -66,7 +69,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
return passwordController.text;
|
||||
}
|
||||
|
||||
|
||||
Future generateAndSavePasswordOneTime (GenerateAndSavePasswordOneTimeEvent event, Emitter<SmartDoorState> emit) async {
|
||||
try {
|
||||
if (isSavingPassword) return;
|
||||
@ -76,6 +78,10 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
ApiResponse pass= ApiResponse.fromJson(res);
|
||||
passwordController.text =pass.data.offlineTempPassword;
|
||||
passwordId=pass.data.offlineTempPasswordId;
|
||||
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
Clipboard.setData(ClipboardData(text: passwordController.text));
|
||||
});
|
||||
emit(const GeneratePasswordOneTimestate(generated: true));
|
||||
} catch (_) {
|
||||
emit(FailedState(errorMessage: _.toString()));
|
||||
@ -108,6 +114,8 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
doorLockUuid:deviceId ,
|
||||
passwordId:passwordId
|
||||
);
|
||||
add(InitialOneTimePassword());
|
||||
add(InitialTimeLimitPassword());
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
} catch (e) {
|
||||
emit(FailedState(errorMessage: e.toString()));
|
||||
@ -150,6 +158,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
if (response is List) {
|
||||
timeLimitPasswords = response.map((item) => OfflinePasswordModel.fromJson(item)).toList();
|
||||
}
|
||||
|
||||
emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!));
|
||||
} catch (e) {
|
||||
emit(FailedState(errorMessage: e.toString()));
|
||||
@ -192,7 +201,56 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
emit(UpdateState(smartDoorModel: deviceStatus));
|
||||
}
|
||||
|
||||
Future<void> selectTime(SelectTimeEvent event, Emitter<SmartDoorState> emit) async {
|
||||
|
||||
Future<void> selectTimeOfLinePassword(SelectTimeEvent event, Emitter<SmartDoorState> emit) async {
|
||||
emit(ChangeTimeState());
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: event.context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2101),
|
||||
);
|
||||
if (picked != null) {
|
||||
final TimeOfDay? timePicked = await showHourPicker(
|
||||
context: event.context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
);
|
||||
if (timePicked != null) {
|
||||
final selectedDateTime = DateTime(
|
||||
picked.year,
|
||||
picked.month,
|
||||
picked.day,
|
||||
timePicked.hour,
|
||||
0,
|
||||
);
|
||||
final selectedTimestamp = DateTime(
|
||||
selectedDateTime.year,
|
||||
selectedDateTime.month,
|
||||
selectedDateTime.day,
|
||||
selectedDateTime.hour,
|
||||
selectedDateTime.minute,
|
||||
).millisecondsSinceEpoch ~/ 1000; // Divide by 1000 to remove milliseconds
|
||||
if (event.isEffective) {
|
||||
if (expirationTimeTimeStamp != null && selectedTimestamp > expirationTimeTimeStamp!) {
|
||||
CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.');
|
||||
} else {
|
||||
effectiveTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
|
||||
effectiveTimeTimeStamp = selectedTimestamp;
|
||||
}
|
||||
} else {
|
||||
if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) {
|
||||
CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.');
|
||||
} else {
|
||||
expirationTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
|
||||
expirationTimeTimeStamp = selectedTimestamp;
|
||||
}
|
||||
}
|
||||
emit(TimeSelectedState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> selectTimeOnlinePassword(SelectTimeOnlinePasswordEvent event, Emitter<SmartDoorState> emit) async {
|
||||
emit(ChangeTimeState());
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: event.context,
|
||||
@ -294,16 +352,22 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
try {
|
||||
isSavingPassword = true;
|
||||
emit(LoadingInitialState());
|
||||
var res = await DevicesAPI.generateMultiTimePassword(deviceId: deviceId,
|
||||
var res = await DevicesAPI.generateMultiTimePassword(
|
||||
deviceId: deviceId,
|
||||
effectiveTime: effectiveTimeTimeStamp.toString(),
|
||||
invalidTime: expirationTimeTimeStamp.toString(), );
|
||||
invalidTime: expirationTimeTimeStamp.toString(),
|
||||
);
|
||||
ApiResponse pass= ApiResponse.fromJson(res);
|
||||
passwordController.text =pass.data.offlineTempPassword;
|
||||
passwordId=pass.data.offlineTempPasswordId;
|
||||
CustomSnackBar.displaySnackBar('Save Successfully');
|
||||
add(InitialTimeLimitPassword());
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
Clipboard.setData(ClipboardData(text: passwordController.text));
|
||||
});
|
||||
emit(const GeneratePasswordOneTimestate(generated: true));
|
||||
} catch (_) {
|
||||
emit(FailedState(errorMessage: e.toString()));
|
||||
add(InitialPasswordsPage());
|
||||
}
|
||||
finally {
|
||||
isSavingPassword = false;
|
||||
@ -328,6 +392,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
CustomSnackBar.displaySnackBar('Password less than 7');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (passwordController.text.isEmpty) {
|
||||
CustomSnackBar.displaySnackBar('Password required');
|
||||
return true;
|
||||
@ -336,20 +401,24 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
CustomSnackBar.displaySnackBar('Password name required');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
|
||||
CustomSnackBar.displaySnackBar('Select effective time');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) {
|
||||
CustomSnackBar.displaySnackBar('Select expiration time');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (repeat == true && (endTime == null || startTime == null || selectedDays == null)) {
|
||||
CustomSnackBar.displaySnackBar('Start Time and End time and the days required ');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool timeLimitValidate() {
|
||||
|
||||
if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
|
||||
@ -392,3 +461,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -53,6 +53,14 @@ class SelectTimeEvent extends SmartDoorEvent {
|
||||
List<Object> get props => [context,isEffective];
|
||||
}
|
||||
|
||||
class SelectTimeOnlinePasswordEvent extends SmartDoorEvent {
|
||||
final BuildContext context;
|
||||
final bool isEffective;
|
||||
const SelectTimeOnlinePasswordEvent({required this.context,required this.isEffective});
|
||||
@override
|
||||
List<Object> get props => [context,isEffective];
|
||||
}
|
||||
|
||||
class ToggleRepeatEvent extends SmartDoorEvent {}
|
||||
class SetStartEndTimeEvent extends SmartDoorEvent {
|
||||
final bool val;
|
||||
|
||||
92
lib/features/devices/view/widgets/hour_picker_dialog.dart
Normal file
92
lib/features/devices/view/widgets/hour_picker_dialog.dart
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HourPickerDialog extends StatefulWidget {
|
||||
final TimeOfDay initialTime;
|
||||
HourPickerDialog({required this.initialTime});
|
||||
|
||||
@override
|
||||
_HourPickerDialogState createState() => _HourPickerDialogState();
|
||||
}
|
||||
|
||||
class _HourPickerDialogState extends State<HourPickerDialog> {
|
||||
late int _selectedHour;
|
||||
bool _isPm = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedHour = widget.initialTime.hour > 12 ? widget.initialTime.hour - 12 : widget.initialTime.hour;
|
||||
_isPm = widget.initialTime.period == DayPeriod.pm;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text('Select Hour'),
|
||||
content: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
DropdownButton<int>(
|
||||
value: _selectedHour,
|
||||
items: List.generate(12, (index) {
|
||||
int displayHour = index + 1;
|
||||
return DropdownMenuItem(
|
||||
value: displayHour,
|
||||
child: Text(displayHour.toString()),
|
||||
);
|
||||
}),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedHour = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
SizedBox(width: 16.0),
|
||||
DropdownButton<bool>(
|
||||
value: _isPm,
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: false,
|
||||
child: Text('AM'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: true,
|
||||
child: Text('PM'),
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_isPm = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(null),
|
||||
child: Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
int hour = _isPm ? _selectedHour + 12 : _selectedHour;
|
||||
Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0));
|
||||
},
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<TimeOfDay?> showHourPicker({
|
||||
required BuildContext context,
|
||||
required TimeOfDay initialTime,
|
||||
}) {
|
||||
return showDialog<TimeOfDay>(
|
||||
context: context,
|
||||
builder: (context) => HourPickerDialog(initialTime: initialTime),
|
||||
);
|
||||
}
|
||||
@ -60,7 +60,7 @@ class NameTimeWidget extends StatelessWidget {
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(context: context, isEffective: true));
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeOnlinePasswordEvent(context: context, isEffective: true));
|
||||
},
|
||||
child: Text(
|
||||
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
|
||||
@ -92,7 +92,7 @@ class NameTimeWidget extends StatelessWidget {
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
BlocProvider.of<SmartDoorBloc>(context).add(
|
||||
SelectTimeEvent(
|
||||
SelectTimeOnlinePasswordEvent(
|
||||
context: context, isEffective: false));
|
||||
},
|
||||
child: Text(
|
||||
|
||||
@ -13,6 +13,7 @@ import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/door_lock_button.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
@ -28,11 +29,8 @@ class OfflineOneTimePasswordPage extends StatelessWidget {
|
||||
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
|
||||
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) {
|
||||
if (state is FailedState) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
CustomSnackBar.displaySnackBar(
|
||||
state.errorMessage
|
||||
);
|
||||
}
|
||||
if (state is IsRepeatState){
|
||||
@ -104,6 +102,7 @@ class OfflineOneTimePasswordPage extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
|
||||
Expanded(
|
||||
child: BodyLarge(
|
||||
style: const TextStyle(
|
||||
@ -114,8 +113,9 @@ class OfflineOneTimePasswordPage extends StatelessWidget {
|
||||
wordSpacing: 2),
|
||||
textAlign: TextAlign.center,
|
||||
text: smartDoorBloc.passwordController.text,
|
||||
fontSize: 25,
|
||||
fontSize: 23,
|
||||
),),
|
||||
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await Clipboard.setData(ClipboardData(
|
||||
@ -166,7 +166,6 @@ class OfflineOneTimePasswordPage extends StatelessWidget {
|
||||
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -195,9 +194,6 @@ class OfflineOneTimePasswordPage extends StatelessWidget {
|
||||
onPressed: () async {
|
||||
if(generated==false){
|
||||
smartDoorBloc.add(GenerateAndSavePasswordOneTimeEvent(context: context));
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: smartDoorBloc.passwordController.text)
|
||||
);
|
||||
}else{
|
||||
if(smartDoorBloc.passwordNameController.text.isNotEmpty){
|
||||
smartDoorBloc.add(RenamePasswordEvent());
|
||||
|
||||
@ -11,6 +11,7 @@ import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/door_lock_button.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
@ -27,11 +28,8 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
|
||||
listener: (context, state) {
|
||||
if (state is FailedState) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
CustomSnackBar.displaySnackBar(
|
||||
state.errorMessage
|
||||
);
|
||||
}
|
||||
if (state is IsRepeatState) {
|
||||
@ -240,8 +238,7 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
),
|
||||
const BodyMedium(
|
||||
textAlign: TextAlign.center,
|
||||
text:
|
||||
'Use the time-limited password at least once within 24 hours after the password takes effect. Otherwise, the password becomes invalid.',
|
||||
text: 'Use the time-limited password at least once within 24 hours after the password takes effect. Otherwise, the password becomes invalid.',
|
||||
fontWeight: FontWeight.normal,
|
||||
fontColor: ColorsManager.grayColor,
|
||||
),
|
||||
@ -259,14 +256,10 @@ class CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
onPressed: () async {
|
||||
if (generated == false) {
|
||||
smartDoorBloc.add(
|
||||
GenerateAndSavePasswordTimeLimitEvent(context: context));
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: smartDoorBloc.passwordController.text)
|
||||
);
|
||||
smartDoorBloc.add(GenerateAndSavePasswordTimeLimitEvent(context: context));
|
||||
} else {
|
||||
if(smartDoorBloc.passwordNameController.text.isNotEmpty){
|
||||
smartDoorBloc.add(RenamePasswordEvent());
|
||||
smartDoorBloc.add(RenamePasswordEvent());
|
||||
}
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ 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_medium.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'door_dialog.dart';
|
||||
|
||||
@ -22,11 +23,8 @@ class TimeLimitedPasswordPage extends StatelessWidget {
|
||||
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
|
||||
listener: (context, state) {
|
||||
if (state is FailedState) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
CustomSnackBar.displaySnackBar(
|
||||
state.errorMessage
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -40,10 +38,8 @@ class TimeLimitedPasswordPage extends StatelessWidget {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(builder: (context) => CreateOfflineTimeLimitPasswordPage(deviceId: deviceId,)
|
||||
)).then((result) {
|
||||
if (result != null) {
|
||||
smartDoorBloc.add(InitialTimeLimitPassword());
|
||||
smartDoorBloc.add(InitialTimeLimitPassword());
|
||||
}
|
||||
smartDoorBloc.add(InitialTimeLimitPassword());
|
||||
smartDoorBloc.add(InitialTimeLimitPassword());
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.add)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart';
|
||||
@ -10,6 +9,16 @@ import 'package:syncrow_app/features/scene/model/create_automation_model.dart';
|
||||
import 'package:syncrow_app/navigation/navigation_service.dart';
|
||||
|
||||
class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
|
||||
final daysMap = {
|
||||
'Sun': 'S',
|
||||
'Mon': 'M',
|
||||
'Tue': 'T',
|
||||
'Wed': 'W',
|
||||
'Thu': 'T',
|
||||
'Fri': 'F',
|
||||
'Sat': 'S',
|
||||
};
|
||||
|
||||
EffectPeriodBloc() : super(EffectPeriodState.initial()) {
|
||||
on<SetPeriod>(_onSetPeriod);
|
||||
on<ToggleDay>(_onToggleDay);
|
||||
@ -44,20 +53,17 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
|
||||
break;
|
||||
}
|
||||
|
||||
BlocProvider.of<CreateSceneBloc>(
|
||||
NavigationService.navigatorKey.currentContext!)
|
||||
.add(EffectiveTimePeriodEvent(EffectiveTime(
|
||||
start: startTime, end: endTime, loops: state.selectedDaysBinary)));
|
||||
BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||
EffectiveTimePeriodEvent(
|
||||
EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
|
||||
|
||||
emit(state.copyWith(
|
||||
selectedPeriod: event.period,
|
||||
customStartTime: startTime,
|
||||
customEndTime: endTime));
|
||||
selectedPeriod: event.period, customStartTime: startTime, customEndTime: endTime));
|
||||
}
|
||||
|
||||
void _onToggleDay(ToggleDay event, Emitter<EffectPeriodState> emit) {
|
||||
final daysList = state.selectedDaysBinary.split('');
|
||||
final dayIndex = _getDayIndex(event.day);
|
||||
final dayIndex = getDayIndex(event.day);
|
||||
if (daysList[dayIndex] == '1') {
|
||||
daysList[dayIndex] = '0';
|
||||
} else {
|
||||
@ -66,9 +72,8 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
|
||||
final newDaysBinary = daysList.join();
|
||||
emit(state.copyWith(selectedDaysBinary: newDaysBinary));
|
||||
|
||||
BlocProvider.of<CreateSceneBloc>(
|
||||
NavigationService.navigatorKey.currentContext!)
|
||||
.add(EffectiveTimePeriodEvent(EffectiveTime(
|
||||
BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||
EffectiveTimePeriodEvent(EffectiveTime(
|
||||
start: state.customStartTime ?? '00:00',
|
||||
end: state.customEndTime ?? '23:59',
|
||||
loops: newDaysBinary)));
|
||||
@ -90,37 +95,31 @@ class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {
|
||||
period = EnumEffectivePeriodOptions.custom;
|
||||
}
|
||||
|
||||
emit(state.copyWith(
|
||||
customStartTime: startTime,
|
||||
customEndTime: endTime,
|
||||
selectedPeriod: period));
|
||||
emit(
|
||||
state.copyWith(customStartTime: startTime, customEndTime: endTime, selectedPeriod: period));
|
||||
|
||||
BlocProvider.of<CreateSceneBloc>(
|
||||
NavigationService.navigatorKey.currentContext!)
|
||||
.add(EffectiveTimePeriodEvent(EffectiveTime(
|
||||
start: startTime, end: endTime, loops: state.selectedDaysBinary)));
|
||||
BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||
EffectiveTimePeriodEvent(
|
||||
EffectiveTime(start: startTime, end: endTime, loops: state.selectedDaysBinary)));
|
||||
}
|
||||
|
||||
void _onResetEffectivePeriod(
|
||||
ResetEffectivePeriod event, Emitter<EffectPeriodState> emit) {
|
||||
void _onResetEffectivePeriod(ResetEffectivePeriod event, Emitter<EffectPeriodState> emit) {
|
||||
emit(state.copyWith(
|
||||
selectedPeriod: EnumEffectivePeriodOptions.allDay,
|
||||
customStartTime: '00:00',
|
||||
customEndTime: '23:59',
|
||||
selectedDaysBinary: '1111111'));
|
||||
|
||||
BlocProvider.of<CreateSceneBloc>(
|
||||
NavigationService.navigatorKey.currentContext!)
|
||||
.add(EffectiveTimePeriodEvent(
|
||||
EffectiveTime(start: '00:00', end: '23:59', loops: '1111111')));
|
||||
BlocProvider.of<CreateSceneBloc>(NavigationService.navigatorKey.currentContext!).add(
|
||||
EffectiveTimePeriodEvent(EffectiveTime(start: '00:00', end: '23:59', loops: '1111111')));
|
||||
}
|
||||
|
||||
void _onResetDays(ResetDays event, Emitter<EffectPeriodState> emit) {
|
||||
emit(state.copyWith(selectedDaysBinary: '1111111'));
|
||||
}
|
||||
|
||||
int _getDayIndex(String day) {
|
||||
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
int getDayIndex(String day) {
|
||||
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
return days.indexOf(day);
|
||||
}
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ mixin SceneLogicHelper {
|
||||
required List<SceneStaticFunction> conditions,
|
||||
}) {
|
||||
final sceneBloc = context.read<CreateSceneBloc>();
|
||||
final effectiveTime = sceneBloc.effectiveTime;
|
||||
|
||||
if (isOnlyDelayOrDelayLast(actions)) {
|
||||
context.showCustomSnackbar(
|
||||
@ -46,6 +47,17 @@ mixin SceneLogicHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAutomation == true && effectiveTime?.loops == '0000000') {
|
||||
context.showCustomSnackbar(
|
||||
message: 'At least one day in Effective Period must be selected!',
|
||||
icon: const Icon(
|
||||
Icons.error,
|
||||
color: Colors.red,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAutomation) {
|
||||
final createAutomationModel = CreateAutomationModel(
|
||||
unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '',
|
||||
@ -81,7 +93,7 @@ mixin SceneLogicHelper {
|
||||
executorProperty: CreateSceneExecutorProperty(
|
||||
functionCode: '',
|
||||
functionValue: '',
|
||||
delaySeconds: task.functionValue,
|
||||
delaySeconds: task.functionValue ?? 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -159,21 +171,24 @@ mixin SceneLogicHelper {
|
||||
}
|
||||
}
|
||||
|
||||
Widget getTheCorrectDialogBody(SceneStaticFunction taskItem, dynamic functionValue,
|
||||
Widget getTheCorrectDialogBody(
|
||||
SceneStaticFunction taskItem, dynamic functionValue,
|
||||
{required bool isAutomation}) {
|
||||
if (taskItem.operationDialogType == OperationDialogType.temperature) {
|
||||
return AlertDialogTemperatureBody(
|
||||
taskItem: taskItem,
|
||||
functionValue: functionValue ?? taskItem.functionValue,
|
||||
);
|
||||
} else if ((taskItem.operationDialogType == OperationDialogType.countdown) ||
|
||||
} else if ((taskItem.operationDialogType ==
|
||||
OperationDialogType.countdown) ||
|
||||
(taskItem.operationDialogType == OperationDialogType.delay)) {
|
||||
return AlertDialogCountdown(
|
||||
durationValue: taskItem.functionValue ?? 0,
|
||||
functionValue: functionValue ?? taskItem.functionValue,
|
||||
function: taskItem,
|
||||
);
|
||||
} else if (taskItem.operationDialogType == OperationDialogType.integerSteps) {
|
||||
} else if (taskItem.operationDialogType ==
|
||||
OperationDialogType.integerSteps) {
|
||||
return AlertDialogSliderSteps(
|
||||
taskItem: taskItem,
|
||||
functionValue: functionValue ?? taskItem.functionValue,
|
||||
|
||||
@ -15,10 +15,12 @@ class SceneAutoSettings extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sceneSettings = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>? ?? {};
|
||||
final sceneSettings =
|
||||
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>? ??
|
||||
{};
|
||||
final sceneId = sceneSettings['sceneId'] as String? ?? '';
|
||||
final isAutomation =
|
||||
context.read<CreateSceneBloc>().sceneType == CreateSceneEnum.deviceStatusChanges;
|
||||
final isAutomation = context.read<CreateSceneBloc>().sceneType ==
|
||||
CreateSceneEnum.deviceStatusChanges;
|
||||
final sceneName = sceneSettings['sceneName'] as String? ?? '';
|
||||
|
||||
return DefaultScaffold(
|
||||
@ -48,9 +50,11 @@ class SceneAutoSettings extends StatelessWidget {
|
||||
Visibility(
|
||||
visible: isAutomation,
|
||||
child: SceneListTile(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 8),
|
||||
titleString: "Effective Period",
|
||||
trailingWidget: const Icon(Icons.arrow_forward_ios_rounded),
|
||||
trailingWidget:
|
||||
const Icon(Icons.arrow_forward_ios_rounded),
|
||||
onPressed: () {
|
||||
context.customBottomSheet(
|
||||
child: const EffectPeriodBottomSheetContent(),
|
||||
|
||||
@ -48,12 +48,21 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (widget.functionValue != null) {
|
||||
groupValue = _normalizeValue(widget.functionValue);
|
||||
if (widget.taskItem.code == 'temp_current') {
|
||||
if (widget.functionValue != null) {
|
||||
groupValue = _normalizeValue(
|
||||
double.tryParse(widget.functionValue.toString()) ??
|
||||
widget.taskItem.operationalValues[0].minValue);
|
||||
} else {
|
||||
groupValue = widget.taskItem.operationalValues[0].minValue;
|
||||
}
|
||||
} else {
|
||||
groupValue =
|
||||
_normalizeValue(widget.taskItem.operationalValues[0].minValue);
|
||||
if (widget.functionValue != null) {
|
||||
groupValue = _normalizeValue(widget.functionValue);
|
||||
} else {
|
||||
groupValue =
|
||||
_normalizeValue(widget.taskItem.operationalValues[0].minValue);
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
@ -68,14 +77,6 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
|
||||
);
|
||||
}
|
||||
|
||||
double _normalizeValue(dynamic value) {
|
||||
if (((widget.taskItem.code == "temp_set" && value > 199) ||
|
||||
widget.taskItem.code == "temp_current")) {
|
||||
return (value) / 10;
|
||||
}
|
||||
return value.toDouble();
|
||||
}
|
||||
|
||||
int _comparatorToIndex(String? comparator) {
|
||||
switch (comparator) {
|
||||
case "<":
|
||||
@ -216,7 +217,7 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
|
||||
: null,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
groupValue = normalizeValue(value);
|
||||
groupValue = normalizeToDoubleValue(value);
|
||||
});
|
||||
context.read<CreateSceneBloc>().add(SelectedValueEvent(
|
||||
value: _deNormalizeValue(groupValue),
|
||||
@ -237,8 +238,16 @@ class _AlertDialogSliderStepsState extends State<AlertDialogSliderSteps> {
|
||||
);
|
||||
}
|
||||
|
||||
double normalizeValue(double value) {
|
||||
return double.parse(value.toStringAsFixed(1));
|
||||
double _normalizeValue(dynamic value) {
|
||||
if ((widget.taskItem.code == "temp_set" && value > 199) ||
|
||||
(widget.taskItem.code == "temp_current" && value >= -99.0)) {
|
||||
return (value) / 10;
|
||||
}
|
||||
return value.toDouble();
|
||||
}
|
||||
|
||||
double? normalizeToDoubleValue(double value) {
|
||||
return double.tryParse(value.toStringAsFixed(1));
|
||||
}
|
||||
|
||||
double _deNormalizeValue(double? value) {
|
||||
|
||||
@ -50,10 +50,10 @@ class _AlertDialogTemperatureBodyState
|
||||
});
|
||||
}
|
||||
|
||||
// context.read<CreateSceneBloc>().add(SelectedValueEvent(
|
||||
// value: temperature * 10,
|
||||
// code: widget.taskItem.code,
|
||||
// ));
|
||||
context.read<CreateSceneBloc>().add(SelectedValueEvent(
|
||||
value: temperature * 10,
|
||||
code: widget.taskItem.code,
|
||||
));
|
||||
}
|
||||
|
||||
int _normalizeTemperature(dynamic value) {
|
||||
|
||||
@ -19,18 +19,8 @@ class EffectPeriodBottomSheetContent extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// Expanded(
|
||||
// child: TextButton(
|
||||
// onPressed: () => Navigator.pop(context),
|
||||
// child: BodySmall(
|
||||
// text: 'Save',
|
||||
// style:
|
||||
// context.bodySmall.copyWith(color: ColorsManager.primaryColorWithOpacity),
|
||||
// )),
|
||||
// ),
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
// flex: 2,
|
||||
child: BodyMedium(
|
||||
text: 'Effective Period',
|
||||
fontColor: ColorsManager.primaryColorWithOpacity,
|
||||
|
||||
@ -4,22 +4,16 @@ import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_b
|
||||
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_state.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/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class RepeatDays extends StatelessWidget {
|
||||
const RepeatDays({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final daysMap = {
|
||||
'Mon': 'M',
|
||||
'Tue': 'T',
|
||||
'Wed': 'W',
|
||||
'Thu': 'T',
|
||||
'Fri': 'F',
|
||||
'Sat': 'S',
|
||||
'Sun': 'S',
|
||||
};
|
||||
|
||||
final effectiveBloc = context.read<EffectPeriodBloc>();
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
@ -27,53 +21,61 @@ class RepeatDays extends StatelessWidget {
|
||||
const SizedBox(width: 8),
|
||||
BlocBuilder<EffectPeriodBloc, EffectPeriodState>(
|
||||
builder: (context, state) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: daysMap.entries.map((entry) {
|
||||
final day = entry.key;
|
||||
final abbreviation = entry.value;
|
||||
final dayIndex = _getDayIndex(day);
|
||||
final isSelected = state.selectedDaysBinary[dayIndex] == '1';
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.read<EffectPeriodBloc>().add(ToggleDay(day));
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color:
|
||||
isSelected ? Colors.grey : Colors.grey.shade300,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 15,
|
||||
backgroundColor: Colors.white,
|
||||
child: Text(
|
||||
abbreviation,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color:
|
||||
isSelected ? Colors.grey : Colors.grey.shade300,
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: effectiveBloc.daysMap.entries.map((entry) {
|
||||
final day = entry.key;
|
||||
final abbreviation = entry.value;
|
||||
final dayIndex = effectiveBloc.getDayIndex(day);
|
||||
final isSelected = state.selectedDaysBinary[dayIndex] == '1';
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
effectiveBloc.add(ToggleDay(day));
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: isSelected ? Colors.grey : Colors.grey.shade300,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 15,
|
||||
backgroundColor: Colors.white,
|
||||
child: Text(
|
||||
abbreviation,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: isSelected ? Colors.grey : Colors.grey.shade300,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (state.selectedDaysBinary == '0000000')
|
||||
BodySmall(
|
||||
text: 'At least one day must be selected',
|
||||
style: context.bodyMedium.copyWith(color: ColorsManager.red),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
int _getDayIndex(String day) {
|
||||
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
return days.indexOf(day);
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ class _SmartSceneSelectTabToRunListState extends State<SmartSceneSelectTabToRunL
|
||||
.read<SmartSceneSelectBloc>()
|
||||
.add(SmartSceneEnableEvent(SmartSceneEnable(
|
||||
entityId: scene.id,
|
||||
actionExecutor: 'rule_enable',
|
||||
actionExecutor: 'rule_trigger',
|
||||
sceneORAutomationName: scene.name,
|
||||
type: scene.type,
|
||||
isAutomation: false,
|
||||
@ -109,7 +109,7 @@ class _SmartSceneSelectTabToRunListState extends State<SmartSceneSelectTabToRunL
|
||||
});
|
||||
context.read<SmartSceneSelectBloc>().add(SmartSceneEnableEvent(SmartSceneEnable(
|
||||
entityId: scene.id,
|
||||
actionExecutor: 'rule_enable',
|
||||
actionExecutor: 'rule_trigger',
|
||||
sceneORAutomationName: scene.name,
|
||||
type: scene.type,
|
||||
isAutomation: false,
|
||||
|
||||
@ -197,13 +197,13 @@ class DevicesAPI {
|
||||
required String invalidTime,
|
||||
required String deviceId,
|
||||
List<Schedule>? scheduleList,}) async {
|
||||
|
||||
Map<String, dynamic> body = {
|
||||
"name": name,
|
||||
"password": password,
|
||||
"effectiveTime": effectiveTime,
|
||||
"invalidTime": invalidTime,
|
||||
};
|
||||
print('createPassword =$body');
|
||||
if (scheduleList != null) {
|
||||
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList();
|
||||
}
|
||||
@ -235,7 +235,7 @@ class DevicesAPI {
|
||||
try {
|
||||
final response = await _httpService.post(
|
||||
path: ApiEndpoints.addMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
|
||||
showServerMessage: false,
|
||||
showServerMessage: true,
|
||||
body: {
|
||||
"effectiveTime": effectiveTime,
|
||||
"invalidTime": invalidTime
|
||||
|
||||
@ -5,7 +5,7 @@ description: This is the mobile application project, developed with Flutter for
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
version: 1.0.2+18
|
||||
version: 1.0.2+19
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.6 <4.0.0"
|
||||
|
||||
Reference in New Issue
Block a user