update scene debug

This commit is contained in:
ashrafzarkanisala
2024-12-01 00:42:00 +03:00
parent b732b0b957
commit 938f8c6b51
12 changed files with 504 additions and 301 deletions

View File

@ -94,7 +94,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return const RoutinesView(); return const RoutinesView();
} }
if (state is ShowCreateRoutineState && state.showCreateRoutine) { if (state is ShowCreateRoutineState && state.showCreateRoutine) {
return const CreateNewRoutineView(); return CreateNewRoutineView();
} }
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>( return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(

View File

@ -13,6 +13,7 @@ import 'package:syncrow_web/pages/routiens/models/routine_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
import 'package:syncrow_web/services/routines_api.dart'; import 'package:syncrow_web/services/routines_api.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
part 'routine_event.dart'; part 'routine_event.dart';
@ -44,14 +45,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
on<FetchDevicesInRoutine>(_fetchDevices); on<FetchDevicesInRoutine>(_fetchDevices);
on<UpdateScene>(_onUpdateScene); on<UpdateScene>(_onUpdateScene);
on<UpdateAutomation>(_onUpdateAutomation); on<UpdateAutomation>(_onUpdateAutomation);
on<SetAutomationActionExecutor>(_onSetAutomationActionExecutor);
} }
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) { void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems); final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
// Find the index of the item in teh current itemsList // Find the index of the item in teh current itemsList
int index = int index = updatedIfItems.indexWhere(
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid // Replace the map if the index is valid
if (index != -1) { if (index != -1) {
updatedIfItems[index] = event.item; updatedIfItems[index] = event.item;
@ -60,18 +62,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
if (event.isTabToRun) { if (event.isTabToRun) {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
} else { } else {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
} }
} }
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) { void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
final currentItems = List<Map<String, dynamic>>.from(state.thenItems); final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
// Find the index of the item in teh current itemsList // Find the index of the item in teh current itemsList
int index = int index = currentItems.indexWhere(
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid // Replace the map if the index is valid
if (index != -1) { if (index != -1) {
currentItems[index] = event.item; currentItems[index] = event.item;
@ -82,22 +87,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(thenItems: currentItems)); emit(state.copyWith(thenItems: currentItems));
} }
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) { void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
try { try {
if (event.functions.isEmpty) return; if (event.functions.isEmpty) return;
List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions); List<DeviceFunctionData> selectedFunction =
List<DeviceFunctionData>.from(event.functions);
Map<String, List<DeviceFunctionData>> currentSelectedFunctions = Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) { if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
List<DeviceFunctionData> currentFunctions = List<DeviceFunctionData> currentFunctions =
List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []); List<DeviceFunctionData>.from(
currentSelectedFunctions[event.uniqueCustomId] ?? []);
List<String> functionCode = []; List<String> functionCode = [];
for (int i = 0; i < selectedFunction.length; i++) { for (int i = 0; i < selectedFunction.length; i++) {
for (int j = 0; j < currentFunctions.length; j++) { for (int j = 0; j < currentFunctions.length; j++) {
if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) { if (selectedFunction[i].functionCode ==
currentFunctions[j].functionCode) {
currentFunctions[j] = selectedFunction[i]; currentFunctions[j] = selectedFunction[i];
if (!functionCode.contains(currentFunctions[j].functionCode)) { if (!functionCode.contains(currentFunctions[j].functionCode)) {
functionCode.add(currentFunctions[j].functionCode); functionCode.add(currentFunctions[j].functionCode);
@ -107,13 +116,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
for (int i = 0; i < functionCode.length; i++) { for (int i = 0; i < functionCode.length; i++) {
selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]); selectedFunction
.removeWhere((code) => code.functionCode == functionCode[i]);
} }
currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions) currentSelectedFunctions[event.uniqueCustomId] =
..addAll(selectedFunction); List.from(currentFunctions)..addAll(selectedFunction);
} else { } else {
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions); currentSelectedFunctions[event.uniqueCustomId] =
List.from(event.functions);
} }
emit(state.copyWith(selectedFunctions: currentSelectedFunctions)); emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
@ -122,11 +133,13 @@ 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)); emit(state.copyWith(isLoading: true, errorMessage: null));
try { try {
final scenes = await SceneApi.getScenesByUnitId(event.unitId, event.communityId); final scenes =
await SceneApi.getScenesByUnitId(event.unitId, event.communityId);
emit(state.copyWith( emit(state.copyWith(
scenes: scenes, scenes: scenes,
isLoading: false, isLoading: false,
@ -141,7 +154,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)); emit(state.copyWith(isLoading: true, errorMessage: null));
try { try {
@ -169,14 +183,16 @@ 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)); emit(state.copyWith(isLoading: true, errorMessage: null));
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
emit(state.copyWith(isLoading: false, errorMessage: null)); emit(state.copyWith(isLoading: false, errorMessage: null));
emit(state.copyWith(searchText: event.query)); 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)); emit(state.copyWith(selectedIcon: event.icon));
} }
@ -190,7 +206,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return actions.last['deviceId'] == 'delay'; return actions.last['deviceId'] == 'delay';
} }
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async { Future<void> _onCreateScene(
CreateSceneEvent event, Emitter<RoutineState> emit) async {
try { try {
// Check if first action is delay // Check if first action is delay
if (_isFirstActionDelay(state.thenItems)) { if (_isFirstActionDelay(state.thenItems)) {
@ -257,7 +274,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createScene(createSceneModel); final result = await SceneApi.createScene(createSceneModel);
if (result['success']) { if (result['success']) {
emit(_resetState()); Navigator.of(NavigationService.navigatorKey.currentContext!).pop();
add(ResetRoutineState());
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes(spaceId, communityId));
add(const LoadAutomation(spaceId)); add(const LoadAutomation(spaceId));
} else { } else {
@ -274,7 +292,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async { Future<void> _onCreateAutomation(
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
try { try {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith( emit(state.copyWith(
@ -282,7 +301,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
)); ));
return; return;
} }
if (_isFirstActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage: 'Cannot have delay as the first action',
isLoading: false,
));
return;
}
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage: 'Cannot have delay as the last action',
isLoading: false,
));
return;
}
emit(state.copyWith(isLoading: true)); emit(state.copyWith(isLoading: true));
final conditions = state.ifItems.expand((item) { final conditions = state.ifItems.expand((item) {
@ -358,7 +391,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createAutomation(createAutomationModel); final result = await SceneApi.createAutomation(createAutomationModel);
if (result['success']) { if (result['success']) {
emit(_resetState()); Navigator.of(NavigationService.navigatorKey.currentContext!).pop();
add(ResetRoutineState());
add(const LoadAutomation(spaceId)); add(const LoadAutomation(spaceId));
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes(spaceId, communityId));
} else { } else {
@ -375,17 +409,21 @@ 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) { if (event.isFromThen) {
final thenItems = List<Map<String, dynamic>>.from(state.thenItems); final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
thenItems.removeAt(event.index); thenItems.removeAt(event.index);
selectedFunctions.remove(event.key); selectedFunctions.remove(event.key);
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions)); emit(state.copyWith(
thenItems: thenItems, selectedFunctions: selectedFunctions));
} else { } else {
final ifItems = List<Map<String, dynamic>>.from(state.ifItems); final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
ifItems.removeAt(event.index); ifItems.removeAt(event.index);
selectedFunctions.remove(event.key); selectedFunctions.remove(event.key);
@ -396,7 +434,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
isAutomation: false, isAutomation: false,
isTabToRun: false)); isTabToRun: false));
} else { } else {
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions)); emit(state.copyWith(
ifItems: ifItems, selectedFunctions: selectedFunctions));
} }
} }
} }
@ -408,16 +447,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
)); ));
} }
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) { FutureOr<void> _onEffectiveTimeEvent(
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
emit(state.copyWith(effectiveTime: event.effectiveTime)); emit(state.copyWith(effectiveTime: event.effectiveTime));
} }
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) { FutureOr<void> _onSetRoutineName(
SetRoutineName event, Emitter<RoutineState> emit) {
emit(state.copyWith(routineName: event.name)); emit(state.copyWith(routineName: event.name));
} }
(List<Map<String, dynamic>>, List<Map<String, dynamic>>, Map<String, List<DeviceFunctionData>>) (
_createCardData( List<Map<String, dynamic>>,
List<Map<String, dynamic>>,
Map<String, List<DeviceFunctionData>>
) _createCardData(
List<RoutineAction> actions, List<RoutineAction> actions,
List<RoutineCondition>? conditions, List<RoutineCondition>? conditions,
Map<String, List<DeviceFunctionData>> currentFunctions, Map<String, List<DeviceFunctionData>> currentFunctions,
@ -450,7 +494,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
'deviceId': condition.entityId, 'deviceId': condition.entityId,
'title': matchingDevice.name ?? condition.entityId, 'title': matchingDevice.name ?? condition.entityId,
'productType': condition.entityType, 'productType': condition.entityType,
'imagePath': matchingDevice.getDefaultIcon(condition.entityType), 'imagePath':
matchingDevice.getDefaultIcon(condition.entityType),
}; };
final functions = matchingDevice.functions; final functions = matchingDevice.functions;
@ -486,8 +531,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final cardData = { final cardData = {
'entityId': action.entityId, 'entityId': action.entityId,
'uniqueCustomId': const Uuid().v4(), 'uniqueCustomId': const Uuid().v4(),
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, 'deviceId':
'title': action.actionExecutor == 'delay' ? 'Delay' : (matchingDevice.name ?? 'Device'), action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'title': action.actionExecutor == 'delay'
? 'Delay'
: (matchingDevice.name ?? 'Device'),
'productType': action.productType, 'productType': action.productType,
'imagePath': matchingDevice.getDefaultIcon(action.productType), 'imagePath': matchingDevice.getDefaultIcon(action.productType),
}; };
@ -530,16 +578,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return (thenItems, ifItems, currentFunctions); return (thenItems, ifItems, currentFunctions);
} }
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async { Future<void> _onGetSceneDetails(
GetSceneDetails event, Emitter<RoutineState> emit) async {
try { try {
emit(state.copyWith( emit(state.copyWith(
isLoading: true, isLoading: true,
isTabToRun: event.isTabToRun, isTabToRun: true,
isUpdate: true, isUpdate: true,
sceneId: event.sceneId, sceneId: event.sceneId,
routineName: null,
isAutomation: false, isAutomation: false,
selectedFunctions: {},
ifItems: [], ifItems: [],
thenItems: [], thenItems: [],
errorMessage: null,
loadScenesErrorMessage: null,
loadAutomationErrorMessage: null,
searchText: '',
selectedIcon: null,
selectedAutomationOperator: 'or',
effectiveTime: null,
)); ));
final sceneDetails = await SceneApi.getSceneDetails(event.sceneId); final sceneDetails = await SceneApi.getSceneDetails(event.sceneId);
@ -549,20 +607,32 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final Map<String, List<DeviceFunctionData>> updatedFunctions = final Map<String, List<DeviceFunctionData>> updatedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
if (event.isTabToRun) { final Map<String, Map<String, dynamic>> deviceCards = {};
thenItems = sceneDetails.actions.map((action) {
AllDevicesModel? matchingDevice;
for (var device in state.devices) {
if (device.uuid == action.entityId) {
matchingDevice = device;
break;
}
}
final cardData = { for (var action in sceneDetails.actions) {
AllDevicesModel? matchingDevice;
for (var device in state.devices) {
if (device.uuid == action.entityId) {
matchingDevice = device;
break;
}
}
final deviceId = action.type == 'automation'
? '${action.entityId}_automation'
: action.actionExecutor == 'delay'
? '${action.entityId}_delay'
: action.entityId;
if (!deviceCards.containsKey(deviceId)) {
deviceCards[deviceId] = {
'entityId': action.entityId, 'entityId': action.entityId,
'uniqueCustomId': const Uuid().v4(), 'deviceId':
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId':
action.type == 'automation' || action.actionExecutor == 'delay'
? const Uuid().v4()
: action.entityId,
'title': action.actionExecutor == 'delay' 'title': action.actionExecutor == 'delay'
? 'Delay' ? 'Delay'
: action.type == 'automation' : action.type == 'automation'
@ -570,75 +640,85 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
: (matchingDevice?.name ?? 'Device'), : (matchingDevice?.name ?? 'Device'),
'productType': action.productType, 'productType': action.productType,
'functions': matchingDevice?.functions, 'functions': matchingDevice?.functions,
'imagePath': matchingDevice?.getDefaultIcon(action.productType), 'imagePath': action.type == 'automation'
'device': matchingDevice ?? null, ? Assets.automation
: action.actionExecutor == 'delay'
? Assets.delay
: matchingDevice?.getDefaultIcon(action.productType),
'device': matchingDevice,
'name': action.name, 'name': action.name,
'type': action.type, 'type': action.type,
}; };
if (action.type == 'automation') { }
updatedFunctions[cardData['uniqueCustomId'].toString()] = [
DeviceFunctionData( final cardData = deviceCards[deviceId]!;
entityId: action.entityId, final uniqueCustomId = cardData['uniqueCustomId'].toString();
functionCode: 'rule',
value: action.actionExecutor, if (action.type == 'automation') {
operationName: action.name ?? 'Automation', if (!updatedFunctions.containsKey(uniqueCustomId)) {
), updatedFunctions[uniqueCustomId] = [];
]; }
} else if (action.executorProperty != null && action.actionExecutor != 'delay') { updatedFunctions[uniqueCustomId]!.add(
final functions = matchingDevice?.functions; DeviceFunctionData(
final functionCode = action.executorProperty!.functionCode; entityId: action.entityId,
for (var function in functions ?? []) { functionCode: 'automation',
if (function.code == functionCode) { value: action.actionExecutor,
updatedFunctions[cardData['uniqueCustomId'].toString()] = [ operationName: 'Automation',
DeviceFunctionData( ),
entityId: action.entityId, );
functionCode: functionCode ?? '', emit(state.copyWith(automationActionExecutor: action.actionExecutor));
value: action.executorProperty!.functionValue, } else if (action.executorProperty != null &&
operationName: function.operationName, action.actionExecutor != 'delay') {
), if (!updatedFunctions.containsKey(uniqueCustomId)) {
]; updatedFunctions[uniqueCustomId] = [];
break; }
} final functions = matchingDevice?.functions;
final functionCode = action.executorProperty?.functionCode;
for (DeviceFunction function in functions ?? []) {
if (function.code == functionCode) {
updatedFunctions[uniqueCustomId]!.add(
DeviceFunctionData(
entityId: action.entityId,
functionCode: functionCode ?? '',
value: action.executorProperty?.functionValue,
operationName: function.operationName,
),
);
break;
} }
} else if (action.actionExecutor == 'delay') {
final delayFunction = DelayFunction(
deviceId: action.entityId,
deviceName: 'Delay',
);
updatedFunctions[cardData['uniqueCustomId'].toString()] = [
DeviceFunctionData(
entityId: action.entityId,
functionCode: 'delay',
value: action.executorProperty?.delaySeconds ?? 0,
operationName: delayFunction.operationName,
),
];
} }
} else if (action.actionExecutor == 'delay') {
return cardData; if (!updatedFunctions.containsKey(uniqueCustomId)) {
}).toList(); updatedFunctions[uniqueCustomId] = [];
ifItems = [
{
'entityId': 'tab_to_run',
'uniqueCustomId': const Uuid().v4(),
'deviceId': 'tab_to_run',
'title': 'Tab to run',
'productType': 'tab_to_run',
'imagePath': Assets.tabToRun,
} }
]; final delayFunction = DelayFunction(
} else { deviceId: action.entityId,
final result = _createCardData( deviceName: 'Delay',
sceneDetails.actions, );
sceneDetails.conditions, updatedFunctions[uniqueCustomId]!.add(
updatedFunctions, DeviceFunctionData(
false, entityId: action.entityId,
); functionCode: 'delay',
thenItems = result.$1; value: action.executorProperty?.delaySeconds ?? 0,
ifItems = result.$2; operationName: delayFunction.operationName,
),
);
}
} }
thenItems = deviceCards.values.toList();
ifItems = [
{
'entityId': 'tab_to_run',
'uniqueCustomId': const Uuid().v4(),
'deviceId': 'tab_to_run',
'title': 'Tab to run',
'productType': 'tab_to_run',
'imagePath': Assets.tabToRun,
}
];
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
routineName: sceneDetails.name, routineName: sceneDetails.name,
@ -673,7 +753,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
thenItems: [], thenItems: [],
)); ));
final automationDetails = await SceneApi.getAutomationDetails(event.automationId); final automationDetails =
await SceneApi.getAutomationDetails(event.automationId);
final List<Map<String, dynamic>> thenItems; final List<Map<String, dynamic>> thenItems;
final List<Map<String, dynamic>> ifItems; final List<Map<String, dynamic>> ifItems;
@ -692,13 +773,16 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final cardData = { final cardData = {
'entityId': condition.entityId, 'entityId': condition.entityId,
'uniqueCustomId': const Uuid().v4(), 'uniqueCustomId': const Uuid().v4(),
'deviceId': condition.expr.statusCode == 'delay' ? 'delay' : condition.entityId, 'deviceId': condition.expr.statusCode == 'delay'
? 'delay'
: condition.entityId,
'title': condition.expr.statusCode == 'delay' 'title': condition.expr.statusCode == 'delay'
? 'Delay' ? 'Delay'
: (matchingDevice?.name ?? 'Device'), : (matchingDevice?.name ?? 'Device'),
'productType': condition.productType, 'productType': condition.productType,
'functions': matchingDevice?.functions ?? [], 'functions': matchingDevice?.functions ?? [],
'imagePath': matchingDevice?.getDefaultIcon(condition.productType) ?? '', 'imagePath':
matchingDevice?.getDefaultIcon(condition.productType) ?? '',
'device': matchingDevice, 'device': matchingDevice,
}; };
@ -735,15 +819,19 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final cardData = { final cardData = {
'entityId': action.entityId, 'entityId': action.entityId,
'uniqueCustomId': const Uuid().v4(), 'uniqueCustomId': const Uuid().v4(),
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, 'deviceId':
'title': action.actionExecutor == 'delay' ? 'Delay' : (matchingDevice.name ?? 'Device'), action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'title': action.actionExecutor == 'delay'
? 'Delay'
: (matchingDevice.name ?? 'Device'),
'productType': action.productType, 'productType': action.productType,
'functions': matchingDevice.functions, 'functions': matchingDevice.functions,
'imagePath': matchingDevice.getDefaultIcon(action.productType), 'imagePath': matchingDevice.getDefaultIcon(action.productType),
'device': matchingDevice, 'device': matchingDevice,
}; };
if (action.executorProperty != null && action.actionExecutor != 'delay') { if (action.executorProperty != null &&
action.actionExecutor != 'delay') {
final functions = matchingDevice.functions; final functions = matchingDevice.functions;
final functionCode = action.executorProperty!.functionCode; final functionCode = action.executorProperty!.functionCode;
for (var function in functions) { for (var function in functions) {
@ -806,8 +894,9 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
RoutineState _resetState() { FutureOr<void> _onResetRoutineState(
return const RoutineState( ResetRoutineState event, Emitter<RoutineState> emit) {
emit(state.copyWith(
ifItems: [], ifItems: [],
thenItems: [], thenItems: [],
selectedFunctions: {}, selectedFunctions: {},
@ -824,11 +913,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
selectedAutomationOperator: 'or', selectedAutomationOperator: 'or',
effectiveTime: null, effectiveTime: null,
routineName: null, routineName: null,
); ));
}
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
emit(_resetState());
} }
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) { FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) {
@ -842,7 +927,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes(spaceId, communityId));
add(const LoadAutomation(spaceId)); add(const LoadAutomation(spaceId));
emit(_resetState()); add(ResetRoutineState());
} catch (e) { } catch (e) {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -865,7 +950,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
// } // }
// } // }
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async { FutureOr<void> _fetchDevices(
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true)); emit(state.copyWith(isLoading: true));
try { try {
final devices = await DevicesManagementApi().fetchDevices(); final devices = await DevicesManagementApi().fetchDevices();
@ -876,7 +962,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async { FutureOr<void> _onUpdateScene(
UpdateScene event, Emitter<RoutineState> emit) async {
try { try {
// Check if first action is delay // Check if first action is delay
if (_isFirstActionDelay(state.thenItems)) { if (_isFirstActionDelay(state.thenItems)) {
@ -884,6 +971,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
errorMessage: 'Cannot have delay as the first action', errorMessage: 'Cannot have delay as the first action',
isLoading: false, isLoading: false,
)); ));
return;
}
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage: 'Cannot have delay as the last action',
isLoading: false,
));
return; return;
} }
@ -933,10 +1029,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions, actions: actions,
); );
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? ''); final result =
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
if (result['success']) { if (result['success']) {
emit(_resetState()); Navigator.of(NavigationService.navigatorKey.currentContext!).pop();
add(ResetRoutineState());
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes(spaceId, communityId));
add(const LoadAutomation(spaceId));
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -951,7 +1050,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async { FutureOr<void> _onUpdateAutomation(
UpdateAutomation event, Emitter<RoutineState> emit) async {
if (_isFirstActionDelay(state.thenItems)) { if (_isFirstActionDelay(state.thenItems)) {
emit(state.copyWith( emit(state.copyWith(
errorMessage: 'Cannot have delay as the first action', errorMessage: 'Cannot have delay as the first action',
@ -963,7 +1063,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
try { try {
final conditions = state.ifItems final conditions = state.ifItems
.map((item) { .map((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
if (functions.isEmpty) return null; if (functions.isEmpty) return null;
final function = functions.first; final function = functions.first;
@ -983,7 +1084,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final actions = state.thenItems final actions = state.thenItems
.map((item) { .map((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? []; final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
if (functions.isEmpty) return null; if (functions.isEmpty) return null;
final function = functions.first; final function = functions.first;
@ -993,8 +1095,9 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
executorProperty: ExecutorProperty( executorProperty: ExecutorProperty(
functionCode: function.functionCode, functionCode: function.functionCode,
functionValue: function.value, functionValue: function.value,
delaySeconds: delaySeconds: function.functionCode == 'delay'
function.functionCode == 'delay' ? (function.value as num).toInt() : null, ? (function.value as num).toInt()
: null,
), ),
); );
}) })
@ -1005,12 +1108,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
spaceUuid: spaceId, spaceUuid: spaceId,
automationName: state.routineName ?? '', automationName: state.routineName ?? '',
decisionExpr: state.selectedAutomationOperator, decisionExpr: state.selectedAutomationOperator,
effectiveTime: state.effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''), effectiveTime:
state.effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
conditions: conditions, conditions: conditions,
actions: actions, actions: actions,
); );
await SceneApi.updateAutomation(createAutomationModel, state.automationId ?? ''); await SceneApi.updateAutomation(
createAutomationModel, state.automationId ?? '');
add(const LoadAutomation(spaceId)); add(const LoadAutomation(spaceId));
emit(state.copyWith(isLoading: false)); emit(state.copyWith(isLoading: false));
@ -1021,4 +1126,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
)); ));
} }
} }
FutureOr<void> _onSetAutomationActionExecutor(
SetAutomationActionExecutor event, Emitter<RoutineState> emit) {
emit(state.copyWith(
automationActionExecutor: event.automationActionExecutor));
}
} }

View File

@ -181,6 +181,13 @@ class UpdateAutomation extends RoutineEvent {
List<Object> get props => []; List<Object> get props => [];
} }
class SetAutomationActionExecutor extends RoutineEvent {
final String automationActionExecutor;
const SetAutomationActionExecutor({required this.automationActionExecutor});
@override
List<Object> get props => [automationActionExecutor];
}
class FetchDevicesInRoutine extends RoutineEvent {} class FetchDevicesInRoutine extends RoutineEvent {}
class ResetRoutineState extends RoutineEvent {} class ResetRoutineState extends RoutineEvent {}

View File

@ -22,6 +22,7 @@ class RoutineState extends Equatable {
final String? automationId; final String? automationId;
final bool? isUpdate; final bool? isUpdate;
final List<AllDevicesModel> devices; final List<AllDevicesModel> devices;
final String? automationActionExecutor;
const RoutineState({ const RoutineState({
this.ifItems = const [], this.ifItems = const [],
@ -45,6 +46,7 @@ class RoutineState extends Equatable {
this.automationId, this.automationId,
this.isUpdate, this.isUpdate,
this.devices = const [], this.devices = const [],
this.automationActionExecutor,
}); });
RoutineState copyWith({ RoutineState copyWith({
@ -68,6 +70,7 @@ class RoutineState extends Equatable {
String? automationId, String? automationId,
bool? isUpdate, bool? isUpdate,
List<AllDevicesModel>? devices, List<AllDevicesModel>? devices,
String? automationActionExecutor,
}) { }) {
return RoutineState( return RoutineState(
ifItems: ifItems ?? this.ifItems, ifItems: ifItems ?? this.ifItems,
@ -93,6 +96,8 @@ class RoutineState extends Equatable {
automationId: automationId ?? this.automationId, automationId: automationId ?? this.automationId,
isUpdate: isUpdate ?? this.isUpdate, isUpdate: isUpdate ?? this.isUpdate,
devices: devices ?? this.devices, devices: devices ?? this.devices,
automationActionExecutor:
automationActionExecutor ?? this.automationActionExecutor,
); );
} }
@ -118,5 +123,6 @@ class RoutineState extends Equatable {
automationId, automationId,
isUpdate, isUpdate,
devices, devices,
automationActionExecutor,
]; ];
} }

View File

@ -163,8 +163,6 @@ class SaveRoutineHelper {
.add(const CreateSceneEvent()); .add(const CreateSceneEvent());
} }
} }
Navigator.pop(context, true);
}, },
isConfirmEnabled: true, isConfirmEnabled: true,
), ),

View File

@ -48,7 +48,8 @@ class RoutineDetailsModel {
spaceUuid: spaceUuid, spaceUuid: spaceUuid,
automationName: name, automationName: name,
decisionExpr: decisionExpr, decisionExpr: decisionExpr,
effectiveTime: effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''), effectiveTime:
effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
conditions: conditions?.map((c) => c.toCondition()).toList() ?? [], conditions: conditions?.map((c) => c.toCondition()).toList() ?? [],
actions: actions.map((a) => a.toAutomationAction()).toList(), actions: actions.map((a) => a.toAutomationAction()).toList(),
); );
@ -63,7 +64,8 @@ class RoutineDetailsModel {
if (iconId != null) 'iconUuid': iconId, if (iconId != null) 'iconUuid': iconId,
if (showInDevice != null) 'showInDevice': showInDevice, if (showInDevice != null) 'showInDevice': showInDevice,
if (effectiveTime != null) 'effectiveTime': effectiveTime!.toMap(), if (effectiveTime != null) 'effectiveTime': effectiveTime!.toMap(),
if (conditions != null) 'conditions': conditions!.map((x) => x.toMap()).toList(), if (conditions != null)
'conditions': conditions!.map((x) => x.toMap()).toList(),
if (type != null) 'type': type, if (type != null) 'type': type,
if (sceneId != null) 'sceneId': sceneId, if (sceneId != null) 'sceneId': sceneId,
if (automationId != null) 'automationId': automationId, if (automationId != null) 'automationId': automationId,
@ -80,10 +82,12 @@ class RoutineDetailsModel {
), ),
iconId: map['iconUuid'], iconId: map['iconUuid'],
showInDevice: map['showInDevice'], showInDevice: map['showInDevice'],
effectiveTime: effectiveTime: map['effectiveTime'] != null
map['effectiveTime'] != null ? EffectiveTime.fromMap(map['effectiveTime']) : null, ? EffectiveTime.fromMap(map['effectiveTime'])
: null,
conditions: map['conditions'] != null conditions: map['conditions'] != null
? List<RoutineCondition>.from(map['conditions'].map((x) => RoutineCondition.fromMap(x))) ? List<RoutineCondition>.from(
map['conditions'].map((x) => RoutineCondition.fromMap(x)))
: null, : null,
type: map['type'], type: map['type'],
sceneId: map['sceneId'], sceneId: map['sceneId'],
@ -136,7 +140,8 @@ class RoutineAction {
'actionExecutor': actionExecutor, 'actionExecutor': actionExecutor,
if (type != null) 'type': type, if (type != null) 'type': type,
if (name != null) 'name': name, if (name != null) 'name': name,
if (executorProperty != null) 'executorProperty': executorProperty!.toMap(), if (executorProperty != null)
'executorProperty': executorProperty!.toMap(),
}; };
} }

View File

@ -26,7 +26,9 @@ class _RoutinesViewState extends State<RoutinesView> {
return BlocBuilder<SwitchTabsBloc, SwitchTabsState>( return BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
builder: (context, state) { builder: (context, state) {
if (state is ShowCreateRoutineState && state.showCreateRoutine) { if (state is ShowCreateRoutineState && state.showCreateRoutine) {
return const CreateNewRoutineView(); return const CreateNewRoutineView(
);
} }
return Padding( return Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),

View File

@ -7,7 +7,7 @@ import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class AutomationDialog extends StatefulWidget { class AutomationDialog extends StatelessWidget {
final String automationName; final String automationName;
final String automationId; final String automationId;
final String uniqueCustomId; final String uniqueCustomId;
@ -19,77 +19,86 @@ class AutomationDialog extends StatefulWidget {
required this.uniqueCustomId, required this.uniqueCustomId,
}); });
@override
_AutomationDialogState createState() => _AutomationDialogState();
}
class _AutomationDialogState extends State<AutomationDialog> {
bool _isEnabled = true;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Dialog( return BlocBuilder<RoutineBloc, RoutineState>(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), builder: (context, state) {
child: Container( final isEnabled = state.automationActionExecutor == 'rule_enable';
width: 400,
padding: const EdgeInsets.all(16), return Dialog(
child: Column( shape:
mainAxisSize: MainAxisSize.min, RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
children: [ child: Container(
DialogHeader(widget.automationName), width: 400,
const SizedBox(height: 16), padding: const EdgeInsets.all(16),
ListTile( child: Column(
leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24), mainAxisSize: MainAxisSize.min,
title: const Text('Enable'), children: [
trailing: Radio<bool>( DialogHeader(automationName),
value: true, const SizedBox(height: 16),
groupValue: _isEnabled, ListTile(
onChanged: (bool? value) { leading:
setState(() { SvgPicture.asset(Assets.acPower, width: 24, height: 24),
_isEnabled = value!; title: const Text('Enable'),
}); trailing: Radio<bool>(
}, value: true,
), groupValue: isEnabled,
), onChanged: (bool? value) {
ListTile( if (value == true) {
leading: context.read<RoutineBloc>().add(
SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24), const SetAutomationActionExecutor(
title: const Text('Disable'), automationActionExecutor: 'rule_enable',
trailing: Radio<bool>( ),
value: false, );
groupValue: _isEnabled, }
onChanged: (bool? value) { },
setState(() { ),
_isEnabled = value!; ),
}); ListTile(
}, leading: SvgPicture.asset(Assets.acPowerOff,
), width: 24, height: 24),
), title: const Text('Disable'),
const SizedBox(height: 16), trailing: Radio<bool>(
DialogFooter( value: false,
onConfirm: () { groupValue: isEnabled,
context.read<RoutineBloc>().add( onChanged: (bool? value) {
AddFunctionToRoutine( if (value == false) {
[ context.read<RoutineBloc>().add(
DeviceFunctionData( const SetAutomationActionExecutor(
entityId: widget.automationId, automationActionExecutor: 'rule_disable',
functionCode: 'automation', ),
value: _isEnabled ? 'rule_enable' : 'rule_disable', );
operationName: 'Automation', }
},
),
),
const SizedBox(height: 16),
DialogFooter(
onConfirm: () {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
[
DeviceFunctionData(
entityId: automationId,
functionCode: 'automation',
value: state.automationActionExecutor,
operationName: 'Automation',
),
],
uniqueCustomId,
), ),
], );
widget.uniqueCustomId, Navigator.of(context).pop(true);
), },
); onCancel: () => Navigator.of(context).pop(false),
Navigator.of(context).pop(true); isConfirmEnabled: true,
}, dialogWidth: 400,
onCancel: () => Navigator.of(context).pop(false), ),
isConfirmEnabled: true, ],
dialogWidth: 400,
), ),
], ),
), );
), },
); );
} }
} }

View File

@ -10,14 +10,41 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class RoutineSearchAndButtons extends StatelessWidget { class RoutineSearchAndButtons extends StatefulWidget {
const RoutineSearchAndButtons({ const RoutineSearchAndButtons({
super.key, super.key,
}); });
@override
State<RoutineSearchAndButtons> createState() =>
_RoutineSearchAndButtonsState();
}
class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
late TextEditingController _nameController;
@override
void initState() {
super.initState();
_nameController = TextEditingController();
}
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocConsumer<RoutineBloc, RoutineState>(
listenWhen: (previous, current) =>
previous.routineName != current.routineName,
listener: (context, state) {
if (state.routineName != _nameController.text) {
_nameController.text = state.routineName ?? '';
}
},
builder: (context, state) { builder: (context, state) {
return LayoutBuilder( return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) { builder: (BuildContext context, BoxConstraints constraints) {
@ -35,26 +62,9 @@ class RoutineSearchAndButtons extends StatelessWidget {
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: maxWidth: constraints.maxWidth > 700
constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32), ? 450
// child: StatefulTextField( : constraints.maxWidth - 32),
// title: 'Routine Name',
// initialValue: state.routineName ?? '',
// height: 40,
// // controller: TextEditingController(),
// hintText: 'Please enter the name',
// boxDecoration: containerWhiteDecoration,
// elevation: 0,
// borderRadius: 15,
// isRequired: true,
// width: 450,
// onSubmitted: (value) {
// // context.read<RoutineBloc>().add(SetRoutineName(value));
// },
// onChanged: (value) {
// context.read<RoutineBloc>().add(SetRoutineName(value));
// },
// ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -63,10 +73,13 @@ class RoutineSearchAndButtons extends StatelessWidget {
children: [ children: [
Text('* ', Text('* ',
style: context.textTheme.bodyMedium! style: context.textTheme.bodyMedium!
.copyWith(color: ColorsManager.red, fontSize: 13)), .copyWith(
color: ColorsManager.red,
fontSize: 13)),
Text( Text(
'Routine Name', 'Routine Name',
style: context.textTheme.bodyMedium!.copyWith( style: context.textTheme.bodyMedium!
.copyWith(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: ColorsManager.blackColor, color: ColorsManager.blackColor,
@ -80,18 +93,24 @@ class RoutineSearchAndButtons extends StatelessWidget {
decoration: containerWhiteDecoration, decoration: containerWhiteDecoration,
child: TextFormField( child: TextFormField(
style: context.textTheme.bodyMedium! style: context.textTheme.bodyMedium!
.copyWith(color: ColorsManager.blackColor), .copyWith(
initialValue: state.routineName, color: ColorsManager.blackColor),
controller: _nameController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Please enter the name', hintText: 'Please enter the name',
hintStyle: context.textTheme.bodyMedium! hintStyle: context.textTheme.bodyMedium!
.copyWith(fontSize: 12, color: ColorsManager.grayColor), .copyWith(
fontSize: 12,
color: ColorsManager.grayColor),
contentPadding: contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10), const EdgeInsets.symmetric(
horizontal: 12, vertical: 10),
border: InputBorder.none, border: InputBorder.none,
), ),
onChanged: (value) { onChanged: (value) {
context.read<RoutineBloc>().add(SetRoutineName(value)); context
.read<RoutineBloc>()
.add(SetRoutineName(value));
}, },
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
@ -111,15 +130,17 @@ class RoutineSearchAndButtons extends StatelessWidget {
width: 200, width: 200,
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: state.isAutomation || state.isTabToRun onPressed: state.isAutomation ||
state.isTabToRun
? () async { ? () async {
final result = await SettingHelper.showSettingDialog( final result = await SettingHelper
.showSettingDialog(
context: context, context: context,
iconId: state.selectedIcon,
); );
if (result != null) { if (result != null) {
context context.read<RoutineBloc>().add(
.read<RoutineBloc>() AddSelectedIcon(result));
.add(AddSelectedIcon(result));
} }
} }
: null, : null,
@ -175,10 +196,12 @@ class RoutineSearchAndButtons extends StatelessWidget {
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: () async { onPressed: () async {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null ||
state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text('Please enter the routine name'), content: const Text(
'Please enter the routine name'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@ -192,10 +215,12 @@ class RoutineSearchAndButtons extends StatelessWidget {
return; return;
} }
if (state.ifItems.isEmpty || state.thenItems.isEmpty) { if (state.ifItems.isEmpty ||
state.thenItems.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text('Please add if and then condition'), content: const Text(
'Please add if and then condition'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@ -208,13 +233,15 @@ class RoutineSearchAndButtons extends StatelessWidget {
); );
return; return;
} }
final result = final result = await SaveRoutineHelper
await SaveRoutineHelper.showSaveRoutineDialog(context); .showSaveRoutineDialog(context);
if (result != null && result) { if (result != null && result) {
BlocProvider.of<SwitchTabsBloc>(context).add( BlocProvider.of<SwitchTabsBloc>(context)
.add(
const CreateNewRoutineViewEvent(false), const CreateNewRoutineViewEvent(false),
); );
BlocProvider.of<SwitchTabsBloc>(context).add( BlocProvider.of<SwitchTabsBloc>(context)
.add(
const TriggerSwitchTabsEvent(true), const TriggerSwitchTabsEvent(true),
); );
} }
@ -248,11 +275,15 @@ class RoutineSearchAndButtons extends StatelessWidget {
child: DefaultButton( child: DefaultButton(
onPressed: state.isAutomation || state.isTabToRun onPressed: state.isAutomation || state.isTabToRun
? () async { ? () async {
final result = await SettingHelper.showSettingDialog( final result =
await SettingHelper.showSettingDialog(
context: context, context: context,
iconId: state.selectedIcon,
); );
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddSelectedIcon(result)); context
.read<RoutineBloc>()
.add(AddSelectedIcon(result));
} }
} }
: null, : null,
@ -302,10 +333,12 @@ class RoutineSearchAndButtons extends StatelessWidget {
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: () async { onPressed: () async {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null ||
state.routineName!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text('Please enter the routine name'), content: const Text(
'Please enter the routine name'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@ -319,10 +352,12 @@ class RoutineSearchAndButtons extends StatelessWidget {
return; return;
} }
if (state.ifItems.isEmpty || state.thenItems.isEmpty) { if (state.ifItems.isEmpty ||
state.thenItems.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: const Text('Please add if and then condition'), content: const Text(
'Please add if and then condition'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red, backgroundColor: ColorsManager.red,
action: SnackBarAction( action: SnackBarAction(
@ -335,7 +370,9 @@ class RoutineSearchAndButtons extends StatelessWidget {
); );
return; return;
} }
final result = await SaveRoutineHelper.showSaveRoutineDialog(context); final result =
await SaveRoutineHelper.showSaveRoutineDialog(
context);
if (result != null && result) { if (result != null && result) {
BlocProvider.of<SwitchTabsBloc>(context).add( BlocProvider.of<SwitchTabsBloc>(context).add(
const CreateNewRoutineViewEvent(false), const CreateNewRoutineViewEvent(false),

View File

@ -26,7 +26,9 @@ class ThenContainer extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text('THEN', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), const Text('THEN',
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16), const SizedBox(height: 16),
state.isLoading && state.isUpdate == true state.isLoading && state.isUpdate == true
? const Center( ? const Center(
@ -39,12 +41,17 @@ class ThenContainer extends StatelessWidget {
state.thenItems.length, state.thenItems.length,
(index) => GestureDetector( (index) => GestureDetector(
onTap: () async { onTap: () async {
if (state.thenItems[index]['deviceId'] == 'delay') { if (state.thenItems[index]
final result = await DelayHelper.showDelayPickerDialog( ['deviceId'] ==
context, state.thenItems[index]); 'delay') {
final result = await DelayHelper
.showDelayPickerDialog(context,
state.thenItems[index]);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({ context
.read<RoutineBloc>()
.add(AddToThenContainer({
...state.thenItems[index], ...state.thenItems[index],
'imagePath': Assets.delay, 'imagePath': Assets.delay,
'title': 'Delay', 'title': 'Delay',
@ -53,57 +60,76 @@ class ThenContainer extends StatelessWidget {
return; return;
} }
if (state.thenItems[index]['type'] == 'automation') { if (state.thenItems[index]['type'] ==
'automation') {
final result = await showDialog<bool>( final result = await showDialog<bool>(
context: context, context: context,
builder: (BuildContext context) => AutomationDialog( builder: (BuildContext context) =>
AutomationDialog(
automationName: automationName:
state.thenItems[index]['name'] ?? 'Automation', state.thenItems[index]
['name'] ??
'Automation',
automationId: automationId:
state.thenItems[index]['deviceId'] ?? '', state.thenItems[index]
uniqueCustomId: state.thenItems[index] ['deviceId'] ??
['uniqueCustomId'], '',
uniqueCustomId:
state.thenItems[index]
['uniqueCustomId'],
), ),
); );
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({ context
.read<RoutineBloc>()
.add(AddToThenContainer({
...state.thenItems[index], ...state.thenItems[index],
'imagePath': Assets.automation, 'imagePath':
'title': state.thenItems[index]['name'], Assets.automation,
'title': state
.thenItems[index]['name'],
})); }));
} }
return; return;
} }
final result = await DeviceDialogHelper.showDeviceDialog( final result = await DeviceDialogHelper
context, state.thenItems[index], .showDeviceDialog(
removeComparetors: true); context, state.thenItems[index],
removeComparetors: true);
if (result != null) { if (result != null) {
context context.read<RoutineBloc>().add(
.read<RoutineBloc>() AddToThenContainer(
.add(AddToThenContainer(state.thenItems[index])); state.thenItems[index]));
} else if (!['AC', '1G', '2G', '3G'] } else if (!['AC', '1G', '2G', '3G']
.contains(state.thenItems[index]['productType'])) { .contains(state.thenItems[index]
context ['productType'])) {
.read<RoutineBloc>() context.read<RoutineBloc>().add(
.add(AddToThenContainer(state.thenItems[index])); AddToThenContainer(
state.thenItems[index]));
} }
}, },
child: DraggableCard( child: DraggableCard(
imagePath: state.thenItems[index]['imagePath'] ?? '', imagePath: state.thenItems[index]
title: state.thenItems[index]['title'] ?? '', ['imagePath'] ??
'',
title: state.thenItems[index]
['title'] ??
'',
deviceData: state.thenItems[index], deviceData: state.thenItems[index],
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(horizontal: 4, vertical: 8), horizontal: 4, vertical: 8),
isFromThen: true, isFromThen: true,
isFromIf: false, isFromIf: false,
onRemove: () { onRemove: () {
context.read<RoutineBloc>().add(RemoveDragCard( context.read<RoutineBloc>().add(
index: index, RemoveDragCard(
isFromThen: true, index: index,
key: state.thenItems[index]['uniqueCustomId'])); isFromThen: true,
key: state.thenItems[index]
['uniqueCustomId']));
}, },
), ),
))), ))),
@ -157,7 +183,8 @@ class ThenContainer extends StatelessWidget {
} }
if (mutableData['deviceId'] == 'delay') { if (mutableData['deviceId'] == 'delay') {
final result = await DelayHelper.showDelayPickerDialog(context, mutableData); final result =
await DelayHelper.showDelayPickerDialog(context, mutableData);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer({ context.read<RoutineBloc>().add(AddToThenContainer({
@ -169,11 +196,13 @@ class ThenContainer extends StatelessWidget {
return; return;
} }
final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData, final result = await DeviceDialogHelper.showDeviceDialog(
context, mutableData,
removeComparetors: true); removeComparetors: true);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) { } else if (!['AC', '1G', '2G', '3G']
.contains(mutableData['productType'])) {
context.read<RoutineBloc>().add(AddToThenContainer(mutableData)); context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
} }
}, },

View File

@ -36,7 +36,6 @@ class SceneApi {
static Future<Map<String, dynamic>> createAutomation( static Future<Map<String, dynamic>> createAutomation(
CreateAutomationModel createAutomationModel) async { CreateAutomationModel createAutomationModel) async {
try { try {
debugPrint("automation body ${createAutomationModel.toMap()}");
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.createAutomation, path: ApiEndpoints.createAutomation,
body: createAutomationModel.toMap(), body: createAutomationModel.toMap(),