working on automation

This commit is contained in:
ashraf_personal
2024-11-26 00:45:16 +03:00
parent bbe00c0372
commit 31278db437
8 changed files with 307 additions and 71 deletions

View File

@ -14,8 +14,8 @@ part 'routine_state.dart';
const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
bool isAutomation = false;
bool isTabToRun = false;
// bool isAutomation = false;
// bool isTabToRun = false;
RoutineBloc() : super(const RoutineState()) {
on<AddToIfContainer>(_onAddToIfContainer);
@ -27,28 +27,32 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
on<AddSelectedIcon>(_onAddSelectedIcon);
on<CreateSceneEvent>(_onCreateScene);
on<RemoveDragCard>(_onRemoveDragCard);
on<ChangeAutomationOperator>(_changeOperatorOperator);
// on<RemoveFunction>(_onRemoveFunction);
// on<ClearFunctions>(_onClearFunctions);
}
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems)..add(event.item);
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems)
..add(event.item);
if (event.isTabToRun) {
isTabToRun = true;
isAutomation = false;
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
} else {
isTabToRun = false;
isAutomation = true;
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
}
emit(state.copyWith(ifItems: updatedIfItems));
}
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
final updatedThenItems = List<Map<String, dynamic>>.from(state.thenItems)..add(event.item);
void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
final updatedThenItems = List<Map<String, dynamic>>.from(state.thenItems)
..add(event.item);
emit(state.copyWith(thenItems: updatedThenItems));
}
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
try {
if (event.functions.isEmpty) return;
@ -57,9 +61,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
currentSelectedFunctions[event.uniqueCustomId] =
List.from(currentSelectedFunctions[event.uniqueCustomId]!)..addAll(event.functions);
List.from(currentSelectedFunctions[event.uniqueCustomId]!)
..addAll(event.functions);
} else {
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
currentSelectedFunctions[event.uniqueCustomId] =
List.from(event.functions);
}
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
@ -68,7 +74,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
try {
@ -87,7 +94,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
try {
@ -106,13 +114,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
FutureOr<void> _onSearchRoutines(
SearchRoutines event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
await Future.delayed(const Duration(seconds: 1));
emit(state.copyWith(searchText: event.query));
}
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
FutureOr<void> _onAddSelectedIcon(
AddSelectedIcon event, Emitter<RoutineState> emit) {
emit(state.copyWith(selectedIcon: event.icon));
}
@ -121,7 +131,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return actions.first['deviceId'] == 'delay';
}
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
Future<void> _onCreateScene(
CreateSceneEvent event, Emitter<RoutineState> emit) async {
try {
// Check if first action is delay
if (_isFirstActionDelay(state.thenItems)) {
@ -136,7 +147,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final actions = state.thenItems
.map((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
if (functions.isEmpty) return null;
final function = functions.first;
@ -191,7 +203,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
FutureOr<void> _onRemoveDragCard(
RemoveDragCard event, Emitter<RoutineState> emit) {
if (event.isFromThen) {
/// remove element from thenItems at specific index
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
@ -203,4 +216,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(ifItems: ifItems));
}
}
FutureOr<void> _changeOperatorOperator(
ChangeAutomationOperator event, Emitter<RoutineState> emit) {
emit(state.copyWith(
selectedAutomationOperator: event.operator,
));
}
}

View File

@ -87,4 +87,11 @@ class RemoveDragCard extends RoutineEvent {
List<Object> get props => [index];
}
class ChangeAutomationOperator extends RoutineEvent {
final String operator;
const ChangeAutomationOperator({required this.operator});
@override
List<Object> get props => [operator];
}
class ClearFunctions extends RoutineEvent {}

View File

@ -14,9 +14,12 @@ class RoutineState extends Equatable {
final String? routineName;
final String? selectedIcon;
final String? searchText;
final bool isTabToRun;
final bool isAutomation;
final String selectedAutomationOperator;
const RoutineState(
{this.ifItems = const [],
const RoutineState({
this.ifItems = const [],
this.thenItems = const [],
this.availableCards = const [],
this.scenes = const [],
@ -28,7 +31,11 @@ class RoutineState extends Equatable {
this.selectedIcon,
this.loadScenesErrorMessage,
this.loadAutomationErrorMessage,
this.searchText});
this.searchText,
this.isTabToRun = false,
this.isAutomation = false,
this.selectedAutomationOperator = 'or',
});
RoutineState copyWith(
{List<Map<String, dynamic>>? ifItems,
@ -42,7 +49,10 @@ class RoutineState extends Equatable {
String? selectedIcon,
String? loadAutomationErrorMessage,
String? loadScenesErrorMessage,
String? searchText}) {
String? searchText,
bool? isTabToRun,
bool? isAutomation,
String? selectedAutomationOperator}) {
return RoutineState(
ifItems: ifItems ?? this.ifItems,
thenItems: thenItems ?? this.thenItems,
@ -53,9 +63,15 @@ class RoutineState extends Equatable {
errorMessage: errorMessage ?? this.errorMessage,
routineName: routineName ?? this.routineName,
selectedIcon: selectedIcon ?? this.selectedIcon,
loadScenesErrorMessage: loadScenesErrorMessage ?? this.loadScenesErrorMessage,
loadAutomationErrorMessage: loadAutomationErrorMessage ?? this.loadAutomationErrorMessage,
searchText: searchText ?? this.searchText);
loadScenesErrorMessage:
loadScenesErrorMessage ?? this.loadScenesErrorMessage,
loadAutomationErrorMessage:
loadAutomationErrorMessage ?? this.loadAutomationErrorMessage,
searchText: searchText ?? this.searchText,
isTabToRun: isTabToRun ?? this.isTabToRun,
isAutomation: isAutomation ?? this.isAutomation,
selectedAutomationOperator:
selectedAutomationOperator ?? this.selectedAutomationOperator);
}
@override
@ -71,6 +87,9 @@ class RoutineState extends Equatable {
selectedIcon,
loadScenesErrorMessage,
loadAutomationErrorMessage,
searchText
searchText,
isTabToRun,
isAutomation,
selectedAutomationOperator
];
}

View File

@ -43,7 +43,7 @@ class SaveRoutineHelper {
),
),
const SizedBox(height: 8),
if (context.read<RoutineBloc>().isTabToRun)
if (state.isTabToRun)
ListTile(
leading: SvgPicture.asset(
Assets.tabToRun,

View File

@ -3,7 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/dialog_helper/device_dialog_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:uuid/uuid.dart';
class IfContainer extends StatelessWidget {
@ -20,12 +22,20 @@ class IfContainer extends StatelessWidget {
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('IF',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
if (state.isAutomation)
AutomationOperatorSelector(
selectedOperator: state.selectedAutomationOperator),
],
),
const SizedBox(height: 16),
if (context.read<RoutineBloc>().isTabToRun)
if (state.isTabToRun)
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -36,7 +46,7 @@ class IfContainer extends StatelessWidget {
),
],
),
if (!context.read<RoutineBloc>().isTabToRun)
if (!state.isTabToRun)
Wrap(
spacing: 8,
runSpacing: 8,
@ -64,35 +74,12 @@ class IfContainer extends StatelessWidget {
},
onWillAccept: (data) => data != null,
onAccept: (data) async {
// final uniqueCustomId = const Uuid().v4();
// final mutableData = Map<String, dynamic>.from(data);
// mutableData['uniqueCustomId'] = uniqueCustomId;
// if (!context.read<RoutineBloc>().isTabToRun) {
// if (data['deviceId'] == 'tab_to_run') {
// context.read<RoutineBloc>().add(AddToIfContainer(data, true));
// } else {
// final result =
// await DeviceDialogHelper.showDeviceDialog(context, mutableData);
// if (result != null) {
// context
// .read<RoutineBloc>()
// .add(AddToIfContainer(mutableData, false));
// } else if (!['AC', '1G', '2G', '3G']
// .contains(data['productType'])) {
// context
// .read<RoutineBloc>()
// .add(AddToIfContainer(mutableData, false));
// }
// }
//}
final uniqueCustomId = const Uuid().v4();
final mutableData = Map<String, dynamic>.from(data);
mutableData['uniqueCustomId'] = uniqueCustomId;
if (!context.read<RoutineBloc>().isTabToRun) {
if (!state.isTabToRun) {
if (mutableData['deviceId'] == 'tab_to_run') {
context
.read<RoutineBloc>()
@ -119,3 +106,77 @@ class IfContainer extends StatelessWidget {
);
}
}
class AutomationOperatorSelector extends StatelessWidget {
const AutomationOperatorSelector({
super.key,
required this.selectedOperator,
});
final String selectedOperator;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: ColorsManager.dividerColor),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
TextButton(
style: TextButton.styleFrom(
backgroundColor: selectedOperator == 'and'
? ColorsManager.dialogBlueTitle
: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
),
child: Text(
'All condition is met',
style: context.textTheme.bodyMedium?.copyWith(
color: selectedOperator == 'and'
? ColorsManager.whiteColors
: ColorsManager.blackColor,
),
),
onPressed: () {
context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'and'));
},
),
Container(
width: 3,
height: 24,
color: ColorsManager.dividerColor,
),
TextButton(
style: TextButton.styleFrom(
backgroundColor: selectedOperator == 'or'
? ColorsManager.dialogBlueTitle
: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
),
child: Text(
'Any condition is met',
style: context.textTheme.bodyMedium?.copyWith(
color: selectedOperator == 'or'
? ColorsManager.whiteColors
: ColorsManager.blackColor,
),
),
onPressed: () {
context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'or'));
},
),
],
),
);
}
}

View File

@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class AutomationDialog extends StatefulWidget {
final String automationName;
final String automationId;
const AutomationDialog({
Key? key,
required this.automationName,
required this.automationId,
}) : super(key: key);
@override
_AutomationDialogState createState() => _AutomationDialogState();
}
class _AutomationDialogState extends State<AutomationDialog> {
bool _isEnabled = true;
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
width: 400,
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DialogHeader(widget.automationName),
const SizedBox(height: 16),
ListTile(
leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24),
title: const Text('Enable'),
trailing: Radio<bool>(
value: true,
groupValue: _isEnabled,
onChanged: (bool? value) {
setState(() {
_isEnabled = value!;
});
},
),
),
ListTile(
leading:
SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24),
title: const Text('Disable'),
trailing: Radio<bool>(
value: false,
groupValue: _isEnabled,
onChanged: (bool? value) {
setState(() {
_isEnabled = value!;
});
},
),
),
const SizedBox(height: 16),
DialogFooter(
onConfirm: () {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
[
DeviceFunctionData(
entityId: widget.automationId,
functionCode: '',
value: _isEnabled,
operationName: 'Automation',
),
],
widget.automationId,
),
);
Navigator.of(context).pop(true);
},
onCancel: () => Navigator.of(context).pop(false),
isConfirmEnabled: true,
),
],
),
),
);
}
}

View File

@ -18,7 +18,7 @@ class SettingHelper {
return showDialog<String>(
context: context,
builder: (BuildContext context) {
final isAutomation = context.read<RoutineBloc>().isAutomation;
final isAutomation = context.read<RoutineBloc>().state.isAutomation;
return BlocProvider(
create: (_) =>
SettingBloc()..add(InitialEvent(selectedIcon: iconId ?? '')),

View File

@ -3,6 +3,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/automation_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/delay_dialog.dart';
import 'package:syncrow_web/pages/routiens/helper/dialog_helper/device_dialog_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart';
@ -54,13 +55,48 @@ class ThenContainer extends StatelessWidget {
),
);
},
onWillAccept: (data) => data != null,
onWillAcceptWithDetails: (data) {
if (data == null) return false;
if (state.isTabToRun) {
return data.data['type'] == 'automation';
}
if (state.isAutomation) {
return data.data['type'] == 'scene' ||
data.data['type'] == 'automation';
}
return data.data['deviceId'] != null;
},
onAccept: (data) async {
final uniqueCustomId = const Uuid().v4();
final mutableData = Map<String, dynamic>.from(data);
mutableData['uniqueCustomId'] = uniqueCustomId;
if (mutableData['type'] == 'scene') {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
return;
}
if (mutableData['type'] == 'automation') {
final result = await showDialog<bool>(
context: context,
builder: (BuildContext context) => AutomationDialog(
automationName: mutableData['name'] ?? 'Automation',
automationId: mutableData['deviceId'] ?? '',
),
);
if (result != null) {
context
.read<RoutineBloc>()
.add(AddToThenContainer(mutableData));
}
return;
}
if (mutableData['deviceId'] == 'delay') {
final result = await DelayHelper.showDelayPickerDialog(
context, mutableData['uniqueCustomId']);