mirror of
https://github.com/SyncrowIOT/web.git
synced 2026-10-26 23:47:57 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9ef5b4574 | ||
|
|
c58f2eb24e | ||
|
|
4e94d2df89 | ||
|
|
2f5c5d7da1 | ||
|
|
ff4ce8ed01 | ||
|
|
1aa6b78fb4 | ||
|
|
e003d9b8b3 |
@@ -1,28 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
|
|
||||||
part 'switch_tabs_event.dart';
|
|
||||||
part 'switch_tabs_state.dart';
|
|
||||||
|
|
||||||
class SwitchTabsBloc extends Bloc<SwitchTabsEvent, SwitchTabsState> {
|
|
||||||
SwitchTabsBloc() : super(SwitchTabsInitial()) {
|
|
||||||
on<TriggerSwitchTabsEvent>(_switchTab);
|
|
||||||
on<CreateNewRoutineViewEvent>(_newRoutineView);
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureOr<void> _switchTab(
|
|
||||||
TriggerSwitchTabsEvent event,
|
|
||||||
Emitter<SwitchTabsState> emit,
|
|
||||||
) {
|
|
||||||
emit(SelectedTabState(event.isRoutineView));
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureOr<void> _newRoutineView(
|
|
||||||
CreateNewRoutineViewEvent event,
|
|
||||||
Emitter<SwitchTabsState> emit,
|
|
||||||
) {
|
|
||||||
emit(ShowCreateRoutineState(event.showCreateNewRoutineView));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
part of 'switch_tabs_bloc.dart';
|
|
||||||
|
|
||||||
sealed class SwitchTabsEvent extends Equatable {
|
|
||||||
const SwitchTabsEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class TriggerSwitchTabsEvent extends SwitchTabsEvent {
|
|
||||||
final bool isRoutineView;
|
|
||||||
const TriggerSwitchTabsEvent(this.isRoutineView);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [isRoutineView];
|
|
||||||
}
|
|
||||||
|
|
||||||
class CreateNewRoutineViewEvent extends SwitchTabsEvent {
|
|
||||||
final bool showCreateNewRoutineView;
|
|
||||||
const CreateNewRoutineViewEvent(this.showCreateNewRoutineView);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [showCreateNewRoutineView];
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
part of 'switch_tabs_bloc.dart';
|
|
||||||
|
|
||||||
sealed class SwitchTabsState extends Equatable {
|
|
||||||
const SwitchTabsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
final class SwitchTabsInitial extends SwitchTabsState {
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class SelectedTabState extends SwitchTabsState {
|
|
||||||
final bool selectedTab;
|
|
||||||
const SelectedTabState(this.selectedTab);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [selectedTab];
|
|
||||||
}
|
|
||||||
|
|
||||||
class ShowCreateRoutineState extends SwitchTabsState {
|
|
||||||
final bool showCreateRoutine;
|
|
||||||
const ShowCreateRoutineState(this.showCreateRoutine);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [showCreateRoutine];
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
|
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/view/routines_view.dart';
|
import 'package:syncrow_web/pages/routiens/view/routines_view.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
@@ -18,10 +18,6 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
|
||||||
create: (context) =>
|
|
||||||
SwitchTabsBloc()..add(const TriggerSwitchTabsEvent(false)),
|
|
||||||
),
|
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
|
create: (context) => DeviceManagementBloc()..add(FetchDevices()),
|
||||||
),
|
),
|
||||||
@@ -33,8 +29,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
style: Theme.of(context).textTheme.headlineLarge,
|
style: Theme.of(context).textTheme.headlineLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
centerBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
|
centerBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
|
||||||
builder: (context, state) {
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -44,20 +39,14 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context
|
context
|
||||||
.read<SwitchTabsBloc>()
|
.read<RoutineBloc>()
|
||||||
.add(const TriggerSwitchTabsEvent(false));
|
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Devices',
|
'Devices',
|
||||||
style: context.textTheme.titleMedium?.copyWith(
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
color:
|
color: !state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
|
||||||
state is SelectedTabState && state.selectedTab == false
|
fontWeight: !state.routineTab ? FontWeight.w700 : FontWeight.w400,
|
||||||
? ColorsManager.whiteColors
|
|
||||||
: ColorsManager.grayColor,
|
|
||||||
fontWeight: (state is SelectedTabState) &&
|
|
||||||
state.selectedTab == false
|
|
||||||
? FontWeight.w700
|
|
||||||
: FontWeight.w400,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -66,21 +55,13 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
backgroundColor: null,
|
backgroundColor: null,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context
|
context.read<RoutineBloc>().add(const TriggerSwitchTabsEvent(isRoutineTab: true));
|
||||||
.read<SwitchTabsBloc>()
|
|
||||||
.add(const TriggerSwitchTabsEvent(true));
|
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Routines',
|
'Routines',
|
||||||
style: context.textTheme.titleMedium?.copyWith(
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
color:
|
color: state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
|
||||||
(state is SelectedTabState) && state.selectedTab == true
|
fontWeight: state.routineTab ? FontWeight.w700 : FontWeight.w400,
|
||||||
? ColorsManager.whiteColors
|
|
||||||
: ColorsManager.grayColor,
|
|
||||||
fontWeight:
|
|
||||||
(state is SelectedTabState) && state.selectedTab == true
|
|
||||||
? FontWeight.w700
|
|
||||||
: FontWeight.w400,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -88,13 +69,12 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
rightBody: const NavigateHomeGridView(),
|
rightBody: const NavigateHomeGridView(),
|
||||||
scaffoldBody: BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
|
scaffoldBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
|
||||||
builder: (context, state) {
|
if (state.routineTab) {
|
||||||
if (state is SelectedTabState && state.selectedTab) {
|
|
||||||
return const RoutinesView();
|
return const RoutinesView();
|
||||||
}
|
}
|
||||||
if (state is ShowCreateRoutineState && state.showCreateRoutine) {
|
if (state.createRoutineView) {
|
||||||
return CreateNewRoutineView();
|
return CreateNewRoutineView();
|
||||||
}
|
}
|
||||||
|
|
||||||
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
||||||
@@ -104,8 +84,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
} else if (deviceState is DeviceManagementLoaded) {
|
} else if (deviceState is DeviceManagementLoaded) {
|
||||||
return DeviceManagementBody(devices: deviceState.devices);
|
return DeviceManagementBody(devices: deviceState.devices);
|
||||||
} else if (deviceState is DeviceManagementFiltered) {
|
} else if (deviceState is DeviceManagementFiltered) {
|
||||||
return DeviceManagementBody(
|
return DeviceManagementBody(devices: deviceState.filteredDevices);
|
||||||
devices: deviceState.filteredDevices);
|
|
||||||
} else {
|
} else {
|
||||||
return const Center(child: Text('Error fetching Devices'));
|
return const Center(child: Text('Error fetching Devices'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/auth/model/user_model.dart';
|
|||||||
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
||||||
import 'package:syncrow_web/pages/home/home_model/home_item_model.dart';
|
import 'package:syncrow_web/pages/home/home_model/home_item_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/services/home_api.dart';
|
import 'package:syncrow_web/services/home_api.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
@@ -41,8 +42,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
|
|
||||||
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
|
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
|
||||||
try {
|
try {
|
||||||
var uuid =
|
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
|
||||||
user = await HomeApi().fetchUserInfo(uuid);
|
user = await HomeApi().fetchUserInfo(uuid);
|
||||||
emit(HomeInitial());
|
emit(HomeInitial());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -84,6 +84,8 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
icon: Assets.devicesIcon,
|
icon: Assets.devicesIcon,
|
||||||
active: true,
|
active: true,
|
||||||
onPress: (context) {
|
onPress: (context) {
|
||||||
|
BlocProvider.of<RoutineBloc>(context)
|
||||||
|
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
||||||
context.go(RoutesConst.deviceManagementPage);
|
context.go(RoutesConst.deviceManagementPage);
|
||||||
},
|
},
|
||||||
color: ColorsManager.primaryColor,
|
color: ColorsManager.primaryColor,
|
||||||
|
|||||||
@@ -13,7 +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:syncrow_web/utils/snack_bar.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
part 'routine_event.dart';
|
part 'routine_event.dart';
|
||||||
@@ -45,7 +45,36 @@ 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);
|
on<TriggerSwitchTabsEvent>(_triggerSwitchTabsEvent);
|
||||||
|
on<CreateNewRoutineViewEvent>(_createNewRoutineViewEvent);
|
||||||
|
on<ResetErrorMessage>(_resetErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _triggerSwitchTabsEvent(
|
||||||
|
TriggerSwitchTabsEvent event,
|
||||||
|
Emitter<RoutineState> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(
|
||||||
|
routineTab: event.isRoutineTab, createRoutineView: false));
|
||||||
|
add(ResetRoutineState());
|
||||||
|
if (event.isRoutineTab) {
|
||||||
|
add(const LoadScenes(spaceId, communityId));
|
||||||
|
add(const LoadAutomation(spaceId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetErrorMessage(
|
||||||
|
ResetErrorMessage event,
|
||||||
|
Emitter<RoutineState> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(errorMessage: ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _createNewRoutineViewEvent(
|
||||||
|
CreateNewRoutineViewEvent event,
|
||||||
|
Emitter<RoutineState> emit,
|
||||||
|
) {
|
||||||
|
emit(state.copyWith(createRoutineView: event.createRoutineView));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
|
void _onAddToIfContainer(AddToIfContainer event, Emitter<RoutineState> emit) {
|
||||||
@@ -196,10 +225,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
emit(state.copyWith(selectedIcon: event.icon));
|
emit(state.copyWith(selectedIcon: event.icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isFirstActionDelay(List<Map<String, dynamic>> actions) {
|
// bool _isFirstActionDelay(List<Map<String, dynamic>> actions) {
|
||||||
if (actions.isEmpty) return false;
|
// if (actions.isEmpty) return false;
|
||||||
return actions.first['deviceId'] == 'delay';
|
// return actions.first['deviceId'] == 'delay';
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool _isLastActionDelay(List<Map<String, dynamic>> actions) {
|
bool _isLastActionDelay(List<Map<String, dynamic>> actions) {
|
||||||
if (actions.isEmpty) return false;
|
if (actions.isEmpty) return false;
|
||||||
@@ -210,17 +239,18 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
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)) {
|
||||||
emit(state.copyWith(
|
// emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the first action',
|
// errorMessage: 'Cannot have delay as the first action',
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
));
|
// ));
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the last action',
|
errorMessage:
|
||||||
|
'A delay condition cannot be the only or the last action',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@@ -233,6 +263,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
return functions.map((function) {
|
return functions.map((function) {
|
||||||
if (function.functionCode == 'automation') {
|
if (function.functionCode == 'automation') {
|
||||||
return CreateSceneAction(
|
return CreateSceneAction(
|
||||||
|
actionType: 'automation',
|
||||||
entityId: function.entityId,
|
entityId: function.entityId,
|
||||||
actionExecutor: function.value,
|
actionExecutor: function.value,
|
||||||
executorProperty: null,
|
executorProperty: null,
|
||||||
@@ -252,6 +283,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return CreateSceneAction(
|
return CreateSceneAction(
|
||||||
|
actionType: 'scene',
|
||||||
entityId: function.entityId,
|
entityId: function.entityId,
|
||||||
actionExecutor: 'device_issue',
|
actionExecutor: 'device_issue',
|
||||||
executorProperty: CreateSceneExecutorProperty(
|
executorProperty: CreateSceneExecutorProperty(
|
||||||
@@ -267,21 +299,20 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
spaceUuid: spaceId,
|
spaceUuid: spaceId,
|
||||||
iconId: state.selectedIcon ?? '',
|
iconId: state.selectedIcon ?? '',
|
||||||
showInDevice: true,
|
showInDevice: true,
|
||||||
sceneName: state.routineName!,
|
sceneName: state.routineName ?? '',
|
||||||
decisionExpr: 'and',
|
decisionExpr: 'and',
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await SceneApi.createScene(createSceneModel);
|
final result = await SceneApi.createScene(createSceneModel);
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
Navigator.of(NavigationService.navigatorKey.currentContext!).pop();
|
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadScenes(spaceId, communityId));
|
add(const LoadScenes(spaceId, communityId));
|
||||||
add(const LoadAutomation(spaceId));
|
add(const LoadAutomation(spaceId));
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: result['message'],
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -299,21 +330,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Automation name is required',
|
errorMessage: 'Automation name is required',
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Automation name is required');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
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',
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
));
|
// ));
|
||||||
return;
|
// CustomSnackBar.redSnackBar('Cannot have delay as the first action');
|
||||||
}
|
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the last action',
|
errorMessage:
|
||||||
|
'A delay condition cannot be the only or the last action',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
@@ -346,12 +382,25 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final actions = state.thenItems.expand((item) {
|
final actions = state.thenItems.expand((item) {
|
||||||
|
if (item['type'] == 'tap_to_run') {
|
||||||
|
return [
|
||||||
|
AutomationAction(
|
||||||
|
actionType: 'scene',
|
||||||
|
entityId: item['deviceId'],
|
||||||
|
actionExecutor: 'rule_trigger',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
|
|
||||||
return functions.map((function) {
|
return functions.map((function) {
|
||||||
if (function.functionCode == 'automation') {
|
if (function.functionCode == 'automation') {
|
||||||
return AutomationAction(
|
return AutomationAction(
|
||||||
|
actionType: 'automation',
|
||||||
entityId: function.entityId,
|
entityId: function.entityId,
|
||||||
actionExecutor: function.value,
|
actionExecutor: function.value,
|
||||||
|
executorProperty: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +427,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
final createAutomationModel = CreateAutomationModel(
|
final createAutomationModel = CreateAutomationModel(
|
||||||
spaceUuid: spaceId,
|
spaceUuid: spaceId,
|
||||||
automationName: state.routineName!,
|
automationName: state.routineName ?? '',
|
||||||
decisionExpr: state.selectedAutomationOperator,
|
decisionExpr: state.selectedAutomationOperator,
|
||||||
effectiveTime: EffectiveTime(
|
effectiveTime: EffectiveTime(
|
||||||
start: state.effectiveTime?.start ?? '00:00',
|
start: state.effectiveTime?.start ?? '00:00',
|
||||||
@@ -391,7 +440,6 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
final result = await SceneApi.createAutomation(createAutomationModel);
|
final result = await SceneApi.createAutomation(createAutomationModel);
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
Navigator.of(NavigationService.navigatorKey.currentContext!).pop();
|
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadAutomation(spaceId));
|
add(const LoadAutomation(spaceId));
|
||||||
add(const LoadScenes(spaceId, communityId));
|
add(const LoadScenes(spaceId, communityId));
|
||||||
@@ -400,12 +448,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: result['message'],
|
errorMessage: result['message'],
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Something went wrong');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: 'Something went wrong',
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
|
CustomSnackBar.redSnackBar('Something went wrong');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +504,9 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
FutureOr<void> _onSetRoutineName(
|
FutureOr<void> _onSetRoutineName(
|
||||||
SetRoutineName event, Emitter<RoutineState> emit) {
|
SetRoutineName event, Emitter<RoutineState> emit) {
|
||||||
emit(state.copyWith(routineName: event.name));
|
emit(state.copyWith(
|
||||||
|
routineName: event.name,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
@@ -591,7 +643,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
selectedFunctions: {},
|
selectedFunctions: {},
|
||||||
ifItems: [],
|
ifItems: [],
|
||||||
thenItems: [],
|
thenItems: [],
|
||||||
errorMessage: null,
|
errorMessage: '',
|
||||||
loadScenesErrorMessage: null,
|
loadScenesErrorMessage: null,
|
||||||
loadAutomationErrorMessage: null,
|
loadAutomationErrorMessage: null,
|
||||||
searchText: '',
|
searchText: '',
|
||||||
@@ -666,7 +718,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
operationName: 'Automation',
|
operationName: 'Automation',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
||||||
} else if (action.executorProperty != null &&
|
} else if (action.executorProperty != null &&
|
||||||
action.actionExecutor != 'delay') {
|
action.actionExecutor != 'delay') {
|
||||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||||
@@ -740,180 +792,27 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onGetAutomationDetails(
|
|
||||||
GetAutomationDetails event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
|
||||||
emit(state.copyWith(
|
|
||||||
isLoading: true,
|
|
||||||
isUpdate: true,
|
|
||||||
isTabToRun: false,
|
|
||||||
automationId: event.automationId,
|
|
||||||
isAutomation: true,
|
|
||||||
ifItems: [],
|
|
||||||
thenItems: [],
|
|
||||||
));
|
|
||||||
|
|
||||||
final automationDetails =
|
|
||||||
await SceneApi.getAutomationDetails(event.automationId);
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> thenItems;
|
|
||||||
final List<Map<String, dynamic>> ifItems;
|
|
||||||
final Map<String, List<DeviceFunctionData>> updatedFunctions =
|
|
||||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
||||||
|
|
||||||
ifItems = automationDetails.conditions?.map((condition) {
|
|
||||||
late AllDevicesModel? matchingDevice;
|
|
||||||
for (var device in state.devices) {
|
|
||||||
if (device.uuid == condition.entityId) {
|
|
||||||
matchingDevice = device;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final cardData = {
|
|
||||||
'entityId': condition.entityId,
|
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
|
||||||
'deviceId': condition.expr.statusCode == 'delay'
|
|
||||||
? 'delay'
|
|
||||||
: condition.entityId,
|
|
||||||
'title': condition.expr.statusCode == 'delay'
|
|
||||||
? 'Delay'
|
|
||||||
: (matchingDevice?.name ?? 'Device'),
|
|
||||||
'productType': condition.productType,
|
|
||||||
'functions': matchingDevice?.functions ?? [],
|
|
||||||
'imagePath':
|
|
||||||
matchingDevice?.getDefaultIcon(condition.productType) ?? '',
|
|
||||||
'device': matchingDevice,
|
|
||||||
};
|
|
||||||
|
|
||||||
final functions = matchingDevice?.functions ?? [];
|
|
||||||
for (var function in functions) {
|
|
||||||
if (function.code == condition.expr.statusCode) {
|
|
||||||
updatedFunctions[cardData['uniqueCustomId'].toString()] = [
|
|
||||||
DeviceFunctionData(
|
|
||||||
entityId: condition.entityId,
|
|
||||||
functionCode: condition.expr.statusCode,
|
|
||||||
value: condition.expr.statusValue,
|
|
||||||
operationName: function.operationName,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cardData;
|
|
||||||
}).toList() ??
|
|
||||||
[];
|
|
||||||
|
|
||||||
// Create then items from actions
|
|
||||||
thenItems = automationDetails.actions.map((action) {
|
|
||||||
final matchingDevice = state.devices.firstWhere(
|
|
||||||
(device) => device.uuid == action.entityId,
|
|
||||||
orElse: () => AllDevicesModel(
|
|
||||||
uuid: action.entityId,
|
|
||||||
name: action.entityId,
|
|
||||||
productType: action.productType,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final cardData = {
|
|
||||||
'entityId': action.entityId,
|
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
|
||||||
'deviceId':
|
|
||||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
|
||||||
'title': action.actionExecutor == 'delay'
|
|
||||||
? 'Delay'
|
|
||||||
: (matchingDevice.name ?? 'Device'),
|
|
||||||
'productType': action.productType,
|
|
||||||
'functions': matchingDevice.functions,
|
|
||||||
'imagePath': matchingDevice.getDefaultIcon(action.productType),
|
|
||||||
'device': matchingDevice,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (action.executorProperty != null &&
|
|
||||||
action.actionExecutor != 'delay') {
|
|
||||||
final functions = matchingDevice.functions;
|
|
||||||
final functionCode = action.executorProperty!.functionCode;
|
|
||||||
for (var function in functions) {
|
|
||||||
if (function.code == functionCode) {
|
|
||||||
updatedFunctions[cardData['uniqueCustomId'].toString()] = [
|
|
||||||
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 == 'rule_disable' ||
|
|
||||||
action.actionExecutor == 'rule_enable') {
|
|
||||||
updatedFunctions[cardData['uniqueCustomId'].toString()] = [
|
|
||||||
DeviceFunctionData(
|
|
||||||
entityId: action.entityId,
|
|
||||||
functionCode: 'automation',
|
|
||||||
value: action.actionExecutor,
|
|
||||||
operationName: action.name ?? 'Automation',
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return cardData;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
emit(state.copyWith(
|
|
||||||
isLoading: false,
|
|
||||||
routineName: automationDetails.name,
|
|
||||||
selectedAutomationOperator: automationDetails.decisionExpr,
|
|
||||||
effectiveTime: automationDetails.effectiveTime,
|
|
||||||
isAutomation: true,
|
|
||||||
thenItems: thenItems,
|
|
||||||
ifItems: ifItems,
|
|
||||||
selectedFunctions: updatedFunctions,
|
|
||||||
automationId: automationDetails.automationId,
|
|
||||||
));
|
|
||||||
} catch (e) {
|
|
||||||
emit(state.copyWith(
|
|
||||||
isLoading: false,
|
|
||||||
errorMessage: 'Failed to load automation details: $e',
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FutureOr<void> _onResetRoutineState(
|
FutureOr<void> _onResetRoutineState(
|
||||||
ResetRoutineState event, Emitter<RoutineState> emit) {
|
ResetRoutineState event, Emitter<RoutineState> emit) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
ifItems: [],
|
ifItems: [],
|
||||||
thenItems: [],
|
thenItems: [],
|
||||||
selectedFunctions: {},
|
selectedFunctions: {},
|
||||||
scenes: [],
|
scenes: [],
|
||||||
automations: [],
|
automations: [],
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: null,
|
errorMessage: '',
|
||||||
loadScenesErrorMessage: null,
|
loadScenesErrorMessage: null,
|
||||||
loadAutomationErrorMessage: null,
|
loadAutomationErrorMessage: null,
|
||||||
searchText: '',
|
searchText: '',
|
||||||
selectedIcon: null,
|
selectedIcon: null,
|
||||||
isTabToRun: false,
|
isTabToRun: false,
|
||||||
isAutomation: false,
|
isAutomation: false,
|
||||||
selectedAutomationOperator: 'or',
|
selectedAutomationOperator: 'or',
|
||||||
effectiveTime: null,
|
effectiveTime: null,
|
||||||
routineName: null,
|
routineName: '',
|
||||||
));
|
isUpdate: false,
|
||||||
|
createRoutineView: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) {
|
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) {
|
||||||
@@ -929,6 +828,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));
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
|
emit(state.copyWith(isLoading: false, createRoutineView: false));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
@@ -967,18 +867,19 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
UpdateScene event, Emitter<RoutineState> emit) async {
|
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)) {
|
||||||
emit(state.copyWith(
|
// emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the first action',
|
// errorMessage: 'Cannot have delay as the first action',
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
));
|
// ));
|
||||||
|
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage: 'Cannot have delay as the last action',
|
errorMessage:
|
||||||
|
'A delay condition cannot be the only or the last action',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@@ -991,6 +892,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
return functions.map((function) {
|
return functions.map((function) {
|
||||||
if (function.functionCode == 'automation') {
|
if (function.functionCode == 'automation') {
|
||||||
return CreateSceneAction(
|
return CreateSceneAction(
|
||||||
|
actionType: 'automation',
|
||||||
entityId: function.entityId,
|
entityId: function.entityId,
|
||||||
actionExecutor: function.value,
|
actionExecutor: function.value,
|
||||||
executorProperty: null,
|
executorProperty: null,
|
||||||
@@ -1033,7 +935,6 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
final result =
|
final result =
|
||||||
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
Navigator.of(NavigationService.navigatorKey.currentContext!).pop();
|
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadScenes(spaceId, communityId));
|
add(const LoadScenes(spaceId, communityId));
|
||||||
add(const LoadAutomation(spaceId));
|
add(const LoadAutomation(spaceId));
|
||||||
@@ -1053,84 +954,324 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
FutureOr<void> _onUpdateAutomation(
|
FutureOr<void> _onUpdateAutomation(
|
||||||
UpdateAutomation event, Emitter<RoutineState> emit) async {
|
UpdateAutomation event, Emitter<RoutineState> emit) async {
|
||||||
if (_isFirstActionDelay(state.thenItems)) {
|
|
||||||
emit(state.copyWith(
|
|
||||||
errorMessage: 'Cannot have delay as the first action',
|
|
||||||
isLoading: false,
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emit(state.copyWith(isLoading: true));
|
|
||||||
try {
|
try {
|
||||||
final conditions = state.ifItems
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
.map((item) {
|
emit(state.copyWith(
|
||||||
final functions =
|
errorMessage: 'Automation name is required',
|
||||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
));
|
||||||
if (functions.isEmpty) return null;
|
return;
|
||||||
|
}
|
||||||
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
|
// emit(state.copyWith(
|
||||||
|
// errorMessage: 'Cannot have delay as the first action',
|
||||||
|
// isLoading: false,
|
||||||
|
// ));
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
final function = functions.first;
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
return Condition(
|
emit(state.copyWith(
|
||||||
code: state.ifItems.indexOf(item) + 1,
|
errorMessage: 'Cannot have delay as the last action',
|
||||||
|
isLoading: false,
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
|
|
||||||
|
final conditions = state.ifItems.expand((item) {
|
||||||
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
|
return functions.map((function) {
|
||||||
|
return Condition(
|
||||||
|
code: state.selectedFunctions[item['uniqueCustomId']]!.indexOf(
|
||||||
|
function,
|
||||||
|
) +
|
||||||
|
1,
|
||||||
|
entityId: function.entityId,
|
||||||
|
entityType: 'device_report',
|
||||||
|
expr: ConditionExpr(
|
||||||
|
statusCode: function.functionCode,
|
||||||
|
comparator: function.condition ?? '==',
|
||||||
|
statusValue: function.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
if (conditions.isEmpty) {
|
||||||
|
emit(state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
errorMessage: 'At least one condition is required',
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final actions = state.thenItems.expand((item) {
|
||||||
|
if (item['type'] == 'tap_to_run' || item['type'] == 'scene') {
|
||||||
|
return [
|
||||||
|
AutomationAction(
|
||||||
|
actionType: 'scene',
|
||||||
|
entityId: item['deviceId'],
|
||||||
|
actionExecutor: 'rule_trigger',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
|
|
||||||
|
return functions.map((function) {
|
||||||
|
if (function.functionCode == 'automation') {
|
||||||
|
return AutomationAction(
|
||||||
|
actionType: 'automation',
|
||||||
entityId: function.entityId,
|
entityId: function.entityId,
|
||||||
entityType: item['productType'],
|
actionExecutor: function.value,
|
||||||
expr: ConditionExpr(
|
executorProperty: null,
|
||||||
statusCode: function.functionCode,
|
|
||||||
statusValue: function.value,
|
|
||||||
comparator: function.condition ?? '==',
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
})
|
}
|
||||||
.whereType<Condition>()
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final actions = state.thenItems
|
if (item['deviceId'] == 'delay') {
|
||||||
.map((item) {
|
|
||||||
final functions =
|
|
||||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
||||||
if (functions.isEmpty) return null;
|
|
||||||
|
|
||||||
final function = functions.first;
|
|
||||||
return AutomationAction(
|
return AutomationAction(
|
||||||
entityId: function.entityId,
|
entityId: function.entityId,
|
||||||
actionExecutor: function.actionExecutor,
|
actionType: 'automation',
|
||||||
|
actionExecutor: 'delay',
|
||||||
executorProperty: ExecutorProperty(
|
executorProperty: ExecutorProperty(
|
||||||
functionCode: function.functionCode,
|
delaySeconds: int.tryParse(function.value.toString()) ?? 0,
|
||||||
functionValue: function.value,
|
|
||||||
delaySeconds: function.functionCode == 'delay'
|
|
||||||
? (function.value as num).toInt()
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
})
|
}
|
||||||
.whereType<AutomationAction>()
|
|
||||||
.toList();
|
return AutomationAction(
|
||||||
|
entityId: function.entityId,
|
||||||
|
actionExecutor: 'device_issue',
|
||||||
|
actionType: 'automation',
|
||||||
|
executorProperty: ExecutorProperty(
|
||||||
|
functionCode: function.functionCode,
|
||||||
|
functionValue: function.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}).toList();
|
||||||
|
|
||||||
final createAutomationModel = CreateAutomationModel(
|
final createAutomationModel = CreateAutomationModel(
|
||||||
spaceUuid: spaceId,
|
spaceUuid: spaceId,
|
||||||
automationName: state.routineName ?? '',
|
automationName: state.routineName ?? '',
|
||||||
decisionExpr: state.selectedAutomationOperator,
|
decisionExpr: state.selectedAutomationOperator,
|
||||||
effectiveTime:
|
effectiveTime: EffectiveTime(
|
||||||
state.effectiveTime ?? EffectiveTime(start: '', end: '', loops: ''),
|
start: state.effectiveTime?.start ?? '00:00',
|
||||||
|
end: state.effectiveTime?.end ?? '23:59',
|
||||||
|
loops: state.effectiveTime?.loops ?? '1111111',
|
||||||
|
),
|
||||||
conditions: conditions,
|
conditions: conditions,
|
||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
await SceneApi.updateAutomation(
|
final result = await SceneApi.updateAutomation(
|
||||||
createAutomationModel, state.automationId ?? '');
|
createAutomationModel, state.automationId ?? '');
|
||||||
|
|
||||||
add(const LoadAutomation(spaceId));
|
if (result['success']) {
|
||||||
emit(state.copyWith(isLoading: false));
|
add(ResetRoutineState());
|
||||||
|
add(const LoadAutomation(spaceId));
|
||||||
|
add(const LoadScenes(spaceId, communityId));
|
||||||
|
} else {
|
||||||
|
emit(state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
errorMessage: result['message'],
|
||||||
|
));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: 'Failed to update automation: $e',
|
errorMessage: 'Something went wrong',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSetAutomationActionExecutor(
|
Future<void> _onGetAutomationDetails(
|
||||||
SetAutomationActionExecutor event, Emitter<RoutineState> emit) {
|
GetAutomationDetails event, Emitter<RoutineState> emit) async {
|
||||||
emit(state.copyWith(
|
try {
|
||||||
automationActionExecutor: event.automationActionExecutor));
|
emit(state.copyWith(
|
||||||
|
isLoading: true,
|
||||||
|
isUpdate: true,
|
||||||
|
isTabToRun: false,
|
||||||
|
automationId: event.automationId,
|
||||||
|
isAutomation: true,
|
||||||
|
ifItems: [],
|
||||||
|
thenItems: [],
|
||||||
|
));
|
||||||
|
|
||||||
|
final automationDetails =
|
||||||
|
await SceneApi.getAutomationDetails(event.automationId);
|
||||||
|
|
||||||
|
final Map<String, Map<String, dynamic>> deviceCards = {};
|
||||||
|
final Map<String, List<DeviceFunctionData>> updatedFunctions =
|
||||||
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
|
|
||||||
|
for (RoutineCondition condition in automationDetails.conditions ?? []) {
|
||||||
|
AllDevicesModel? matchingDevice = state.devices.firstWhere(
|
||||||
|
(device) => device.uuid == condition.entityId,
|
||||||
|
orElse: () => AllDevicesModel(
|
||||||
|
uuid: condition.entityId,
|
||||||
|
name: condition.entityId,
|
||||||
|
productType: condition.productType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final deviceId = condition.entityId;
|
||||||
|
|
||||||
|
if (!deviceCards.containsKey(deviceId)) {
|
||||||
|
deviceCards[deviceId] = {
|
||||||
|
'entityId': condition.entityId,
|
||||||
|
'deviceId': condition.entityId,
|
||||||
|
'uniqueCustomId': const Uuid().v4(),
|
||||||
|
'title': matchingDevice.name ?? 'Device',
|
||||||
|
'productType': condition.productType,
|
||||||
|
'functions': matchingDevice.functions,
|
||||||
|
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
|
||||||
|
'device': matchingDevice,
|
||||||
|
'type': 'condition',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
final cardData = deviceCards[deviceId]!;
|
||||||
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||||
|
|
||||||
|
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||||
|
updatedFunctions[uniqueCustomId] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final functions = matchingDevice.functions;
|
||||||
|
for (var function in functions) {
|
||||||
|
if (function.code == condition.expr.statusCode) {
|
||||||
|
updatedFunctions[uniqueCustomId]!.add(
|
||||||
|
DeviceFunctionData(
|
||||||
|
entityId: condition.entityId,
|
||||||
|
functionCode: condition.expr.statusCode,
|
||||||
|
value: condition.expr.statusValue,
|
||||||
|
operationName: function.operationName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process actions (thenItems)
|
||||||
|
for (var action in automationDetails.actions) {
|
||||||
|
AllDevicesModel? matchingDevice = state.devices.firstWhere(
|
||||||
|
(device) => device.uuid == action.entityId,
|
||||||
|
orElse: () => AllDevicesModel(
|
||||||
|
uuid: action.entityId,
|
||||||
|
name: action.entityId,
|
||||||
|
productType: action.productType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final deviceId = action.actionExecutor == 'delay'
|
||||||
|
? '${action.entityId}_delay'
|
||||||
|
: action.entityId;
|
||||||
|
|
||||||
|
if (!deviceCards.containsKey(deviceId)) {
|
||||||
|
deviceCards[deviceId] = {
|
||||||
|
'entityId': action.entityId,
|
||||||
|
'deviceId':
|
||||||
|
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||||
|
'uniqueCustomId': const Uuid().v4(),
|
||||||
|
'title': action.actionExecutor == 'delay'
|
||||||
|
? 'Delay'
|
||||||
|
: (action.type == 'scene' || action.type == 'automation')
|
||||||
|
? action.name
|
||||||
|
: (matchingDevice.name ?? 'Device'),
|
||||||
|
'productType': action.productType,
|
||||||
|
'functions': matchingDevice.functions,
|
||||||
|
'imagePath': action.actionExecutor == 'delay'
|
||||||
|
? Assets.delay
|
||||||
|
: action.type == 'automation'
|
||||||
|
? Assets.automation
|
||||||
|
: matchingDevice.getDefaultIcon(action.productType),
|
||||||
|
'device': matchingDevice,
|
||||||
|
'type': action.type == 'scene'
|
||||||
|
? 'scene'
|
||||||
|
: action.type == 'automation'
|
||||||
|
? 'automation'
|
||||||
|
: 'action',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
final cardData = deviceCards[deviceId]!;
|
||||||
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||||
|
|
||||||
|
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||||
|
updatedFunctions[uniqueCustomId] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.executorProperty != null &&
|
||||||
|
action.actionExecutor != 'delay') {
|
||||||
|
final functions = matchingDevice.functions;
|
||||||
|
final functionCode = action.executorProperty!.functionCode;
|
||||||
|
for (var 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[uniqueCustomId]!.add(
|
||||||
|
DeviceFunctionData(
|
||||||
|
entityId: action.entityId,
|
||||||
|
functionCode: 'delay',
|
||||||
|
value: action.executorProperty?.delaySeconds ?? 0,
|
||||||
|
operationName: delayFunction.operationName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (action.actionExecutor == 'rule_disable' ||
|
||||||
|
action.actionExecutor == 'rule_enable') {
|
||||||
|
updatedFunctions[uniqueCustomId]!.add(
|
||||||
|
DeviceFunctionData(
|
||||||
|
entityId: action.entityId,
|
||||||
|
functionCode: 'automation',
|
||||||
|
value: action.actionExecutor,
|
||||||
|
operationName: action.name ?? 'Automation',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ifItems = deviceCards.values
|
||||||
|
.where((card) => card['type'] == 'condition')
|
||||||
|
.toList();
|
||||||
|
final thenItems = deviceCards.values
|
||||||
|
.where((card) =>
|
||||||
|
card['type'] == 'action' ||
|
||||||
|
card['type'] == 'automation' ||
|
||||||
|
card['type'] == 'scene')
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
emit(state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
routineName: automationDetails.name,
|
||||||
|
selectedAutomationOperator: automationDetails.decisionExpr,
|
||||||
|
effectiveTime: automationDetails.effectiveTime,
|
||||||
|
isAutomation: true,
|
||||||
|
thenItems: thenItems,
|
||||||
|
ifItems: ifItems,
|
||||||
|
selectedFunctions: updatedFunctions,
|
||||||
|
automationId: automationDetails.automationId,
|
||||||
|
));
|
||||||
|
} catch (e) {
|
||||||
|
emit(state.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
errorMessage: 'Failed to load automation details: $e',
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,8 +187,24 @@ class SetAutomationActionExecutor extends RoutineEvent {
|
|||||||
List<Object> get props => [automationActionExecutor];
|
List<Object> get props => [automationActionExecutor];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TriggerSwitchTabsEvent extends RoutineEvent {
|
||||||
|
final bool isRoutineTab;
|
||||||
|
const TriggerSwitchTabsEvent({required this.isRoutineTab});
|
||||||
|
@override
|
||||||
|
List<Object> get props => [isRoutineTab];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateNewRoutineViewEvent extends RoutineEvent {
|
||||||
|
final bool createRoutineView;
|
||||||
|
const CreateNewRoutineViewEvent({required this.createRoutineView});
|
||||||
|
@override
|
||||||
|
List<Object> get props => [createRoutineView];
|
||||||
|
}
|
||||||
|
|
||||||
class FetchDevicesInRoutine extends RoutineEvent {}
|
class FetchDevicesInRoutine extends RoutineEvent {}
|
||||||
|
|
||||||
class ResetRoutineState extends RoutineEvent {}
|
class ResetRoutineState extends RoutineEvent {}
|
||||||
|
|
||||||
class ClearFunctions extends RoutineEvent {}
|
class ClearFunctions extends RoutineEvent {}
|
||||||
|
|
||||||
|
class ResetErrorMessage extends RoutineEvent {}
|
||||||
|
|||||||
@@ -22,32 +22,35 @@ 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;
|
// final String? automationActionExecutor;
|
||||||
|
final bool routineTab;
|
||||||
|
final bool createRoutineView;
|
||||||
|
|
||||||
const RoutineState({
|
const RoutineState(
|
||||||
this.ifItems = const [],
|
{this.ifItems = const [],
|
||||||
this.thenItems = const [],
|
this.thenItems = const [],
|
||||||
this.availableCards = const [],
|
this.availableCards = const [],
|
||||||
this.scenes = const [],
|
this.scenes = const [],
|
||||||
this.automations = const [],
|
this.automations = const [],
|
||||||
this.selectedFunctions = const {},
|
this.selectedFunctions = const {},
|
||||||
this.isLoading = false,
|
this.isLoading = false,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
this.routineName,
|
this.routineName,
|
||||||
this.selectedIcon,
|
this.selectedIcon,
|
||||||
this.loadScenesErrorMessage,
|
this.loadScenesErrorMessage,
|
||||||
this.loadAutomationErrorMessage,
|
this.loadAutomationErrorMessage,
|
||||||
this.searchText,
|
this.searchText,
|
||||||
this.isTabToRun = false,
|
this.isTabToRun = false,
|
||||||
this.isAutomation = false,
|
this.isAutomation = false,
|
||||||
this.selectedAutomationOperator = 'or',
|
this.selectedAutomationOperator = 'or',
|
||||||
this.effectiveTime,
|
this.effectiveTime,
|
||||||
this.sceneId,
|
this.sceneId,
|
||||||
this.automationId,
|
this.automationId,
|
||||||
this.isUpdate,
|
this.isUpdate,
|
||||||
this.devices = const [],
|
this.devices = const [],
|
||||||
this.automationActionExecutor,
|
// this.automationActionExecutor,
|
||||||
});
|
this.routineTab = false,
|
||||||
|
this.createRoutineView = false});
|
||||||
|
|
||||||
RoutineState copyWith({
|
RoutineState copyWith({
|
||||||
List<Map<String, dynamic>>? ifItems,
|
List<Map<String, dynamic>>? ifItems,
|
||||||
@@ -70,35 +73,38 @@ class RoutineState extends Equatable {
|
|||||||
String? automationId,
|
String? automationId,
|
||||||
bool? isUpdate,
|
bool? isUpdate,
|
||||||
List<AllDevicesModel>? devices,
|
List<AllDevicesModel>? devices,
|
||||||
String? automationActionExecutor,
|
// String? automationActionExecutor,
|
||||||
|
TextEditingController? nameController,
|
||||||
|
bool? routineTab,
|
||||||
|
bool? createRoutineView,
|
||||||
}) {
|
}) {
|
||||||
return RoutineState(
|
return RoutineState(
|
||||||
ifItems: ifItems ?? this.ifItems,
|
ifItems: ifItems ?? this.ifItems,
|
||||||
thenItems: thenItems ?? this.thenItems,
|
thenItems: thenItems ?? this.thenItems,
|
||||||
scenes: scenes ?? this.scenes,
|
scenes: scenes ?? this.scenes,
|
||||||
automations: automations ?? this.automations,
|
automations: automations ?? this.automations,
|
||||||
selectedFunctions: selectedFunctions ?? this.selectedFunctions,
|
selectedFunctions: selectedFunctions ?? this.selectedFunctions,
|
||||||
isLoading: isLoading ?? this.isLoading,
|
isLoading: isLoading ?? this.isLoading,
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
errorMessage: errorMessage ?? this.errorMessage,
|
||||||
routineName: routineName ?? this.routineName,
|
routineName: routineName ?? this.routineName,
|
||||||
selectedIcon: selectedIcon ?? this.selectedIcon,
|
selectedIcon: selectedIcon ?? this.selectedIcon,
|
||||||
loadScenesErrorMessage:
|
loadScenesErrorMessage:
|
||||||
loadScenesErrorMessage ?? this.loadScenesErrorMessage,
|
loadScenesErrorMessage ?? this.loadScenesErrorMessage,
|
||||||
loadAutomationErrorMessage:
|
loadAutomationErrorMessage:
|
||||||
loadAutomationErrorMessage ?? this.loadAutomationErrorMessage,
|
loadAutomationErrorMessage ?? this.loadAutomationErrorMessage,
|
||||||
searchText: searchText ?? this.searchText,
|
searchText: searchText ?? this.searchText,
|
||||||
isTabToRun: isTabToRun ?? this.isTabToRun,
|
isTabToRun: isTabToRun ?? this.isTabToRun,
|
||||||
isAutomation: isAutomation ?? this.isAutomation,
|
isAutomation: isAutomation ?? this.isAutomation,
|
||||||
selectedAutomationOperator:
|
selectedAutomationOperator:
|
||||||
selectedAutomationOperator ?? this.selectedAutomationOperator,
|
selectedAutomationOperator ?? this.selectedAutomationOperator,
|
||||||
effectiveTime: effectiveTime ?? this.effectiveTime,
|
effectiveTime: effectiveTime ?? this.effectiveTime,
|
||||||
sceneId: sceneId ?? this.sceneId,
|
sceneId: sceneId ?? this.sceneId,
|
||||||
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: automationActionExecutor ?? this.automationActionExecutor,
|
||||||
automationActionExecutor ?? this.automationActionExecutor,
|
routineTab: routineTab ?? this.routineTab,
|
||||||
);
|
createRoutineView: createRoutineView ?? this.createRoutineView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -123,6 +129,8 @@ class RoutineState extends Equatable {
|
|||||||
automationId,
|
automationId,
|
||||||
isUpdate,
|
isUpdate,
|
||||||
devices,
|
devices,
|
||||||
automationActionExecutor,
|
// automationActionExecutor,
|
||||||
|
routineTab,
|
||||||
|
createRoutineView
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
@@ -6,10 +8,11 @@ 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/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class SaveRoutineHelper {
|
class SaveRoutineHelper {
|
||||||
static Future<bool?> showSaveRoutineDialog(BuildContext context) async {
|
static Future<void> showSaveRoutineDialog(BuildContext context) async {
|
||||||
return showDialog<bool?>(
|
return showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||||
@@ -54,27 +57,23 @@ class SaveRoutineHelper {
|
|||||||
),
|
),
|
||||||
if (state.isAutomation)
|
if (state.isAutomation)
|
||||||
...state.ifItems.map((item) {
|
...state.ifItems.map((item) {
|
||||||
final functions = state.selectedFunctions[
|
final functions =
|
||||||
item['uniqueCustomId']] ??
|
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
[];
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: SvgPicture.asset(
|
leading: SvgPicture.asset(
|
||||||
item['imagePath'],
|
item['imagePath'],
|
||||||
width: 22,
|
width: 22,
|
||||||
height: 22,
|
height: 22,
|
||||||
),
|
),
|
||||||
title: Text(item['title'],
|
title:
|
||||||
style: const TextStyle(fontSize: 14)),
|
Text(item['title'], style: const TextStyle(fontSize: 14)),
|
||||||
subtitle: Wrap(
|
subtitle: Wrap(
|
||||||
children: functions
|
children: functions
|
||||||
.map((f) => Text(
|
.map((f) => Text(
|
||||||
'${f.operationName}: ${f.value}, ',
|
'${f.operationName}: ${f.value}, ',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: ColorsManager
|
color: ColorsManager.grayColor, fontSize: 8),
|
||||||
.grayColor,
|
overflow: TextOverflow.ellipsis,
|
||||||
fontSize: 8),
|
|
||||||
overflow:
|
|
||||||
TextOverflow.ellipsis,
|
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
@@ -99,25 +98,33 @@ class SaveRoutineHelper {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
...state.thenItems.map((item) {
|
...state.thenItems.map((item) {
|
||||||
final functions = state.selectedFunctions[
|
final functions =
|
||||||
item['uniqueCustomId']] ??
|
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
[];
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: SvgPicture.asset(
|
leading: item['type'] == 'tap_to_run'
|
||||||
item['imagePath'],
|
? Image.memory(
|
||||||
width: 22,
|
base64Decode(item['icon']),
|
||||||
height: 22,
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
)
|
||||||
|
: SvgPicture.asset(
|
||||||
|
item['imagePath'],
|
||||||
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
item['title'],
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
title: Text(item['title'],
|
|
||||||
style: const TextStyle(fontSize: 14)),
|
|
||||||
subtitle: Wrap(
|
subtitle: Wrap(
|
||||||
children: functions
|
children: functions
|
||||||
.map((f) => Text(
|
.map((f) => Text(
|
||||||
'${f.operationName}: ${f.value}, ',
|
'${f.operationName}: ${f.value}, ',
|
||||||
style: const TextStyle(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color:
|
color: ColorsManager.grayColor, fontSize: 8),
|
||||||
ColorsManager.grayColor,
|
|
||||||
fontSize: 8),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
))
|
))
|
||||||
@@ -131,42 +138,33 @@ class SaveRoutineHelper {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state.errorMessage != null)
|
// if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
// padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
// child: Text(
|
||||||
state.errorMessage!,
|
// state.errorMessage!,
|
||||||
style: const TextStyle(color: Colors.red),
|
// style: const TextStyle(color: Colors.red),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
DialogFooter(
|
DialogFooter(
|
||||||
onCancel: () => Navigator.pop(context, false),
|
onCancel: () => Navigator.pop(context),
|
||||||
onConfirm: () {
|
onConfirm: () async {
|
||||||
if (state.isAutomation) {
|
if (state.isAutomation) {
|
||||||
if (state.automationId != null) {
|
if (state.automationId != null) {
|
||||||
context
|
context.read<RoutineBloc>().add(const UpdateAutomation());
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(const UpdateAutomation());
|
|
||||||
} else {
|
} else {
|
||||||
context
|
context.read<RoutineBloc>().add(const CreateAutomationEvent());
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(const CreateAutomationEvent());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (state.sceneId != null) {
|
if (state.sceneId != null) {
|
||||||
context
|
context.read<RoutineBloc>().add(const UpdateScene());
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(const UpdateScene());
|
|
||||||
} else {
|
} else {
|
||||||
context
|
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(const CreateSceneEvent());
|
|
||||||
}
|
|
||||||
if (context.read<RoutineBloc>().state.errorMessage ==
|
|
||||||
null) {
|
|
||||||
Navigator.pop(context, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
isConfirmEnabled: true,
|
isConfirmEnabled: true,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class Condition {
|
|||||||
return {
|
return {
|
||||||
'code': code,
|
'code': code,
|
||||||
'entityId': entityId,
|
'entityId': entityId,
|
||||||
'entityType': entityType,
|
'entityType': 'device_report',
|
||||||
'expr': expr.toMap(),
|
'expr': expr.toMap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -137,11 +137,13 @@ class ConditionExpr {
|
|||||||
|
|
||||||
class AutomationAction {
|
class AutomationAction {
|
||||||
String entityId;
|
String entityId;
|
||||||
|
String? actionType;
|
||||||
String actionExecutor;
|
String actionExecutor;
|
||||||
ExecutorProperty? executorProperty;
|
ExecutorProperty? executorProperty;
|
||||||
|
|
||||||
AutomationAction({
|
AutomationAction({
|
||||||
required this.entityId,
|
required this.entityId,
|
||||||
|
this.actionType,
|
||||||
required this.actionExecutor,
|
required this.actionExecutor,
|
||||||
this.executorProperty,
|
this.executorProperty,
|
||||||
});
|
});
|
||||||
@@ -150,12 +152,15 @@ class AutomationAction {
|
|||||||
return {
|
return {
|
||||||
'entityId': entityId,
|
'entityId': entityId,
|
||||||
'actionExecutor': actionExecutor,
|
'actionExecutor': actionExecutor,
|
||||||
'executorProperty': executorProperty?.toMap(),
|
if (executorProperty != null)
|
||||||
|
'executorProperty': executorProperty?.toMap(),
|
||||||
|
'actionType': actionType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
factory AutomationAction.fromMap(Map<String, dynamic> map) {
|
factory AutomationAction.fromMap(Map<String, dynamic> map) {
|
||||||
return AutomationAction(
|
return AutomationAction(
|
||||||
|
actionType: map['actionType'],
|
||||||
entityId: map['entityId'] ?? '',
|
entityId: map['entityId'] ?? '',
|
||||||
actionExecutor: map['actionExecutor'] ?? '',
|
actionExecutor: map['actionExecutor'] ?? '',
|
||||||
executorProperty: map['executorProperty'] != null
|
executorProperty: map['executorProperty'] != null
|
||||||
|
|||||||
@@ -95,10 +95,12 @@ class CreateSceneModel {
|
|||||||
|
|
||||||
class CreateSceneAction {
|
class CreateSceneAction {
|
||||||
String entityId;
|
String entityId;
|
||||||
|
String? actionType;
|
||||||
String actionExecutor;
|
String actionExecutor;
|
||||||
CreateSceneExecutorProperty? executorProperty;
|
CreateSceneExecutorProperty? executorProperty;
|
||||||
|
|
||||||
CreateSceneAction({
|
CreateSceneAction({
|
||||||
|
this.actionType,
|
||||||
required this.entityId,
|
required this.entityId,
|
||||||
required this.actionExecutor,
|
required this.actionExecutor,
|
||||||
required this.executorProperty,
|
required this.executorProperty,
|
||||||
@@ -110,6 +112,7 @@ class CreateSceneAction {
|
|||||||
CreateSceneExecutorProperty? executorProperty,
|
CreateSceneExecutorProperty? executorProperty,
|
||||||
}) {
|
}) {
|
||||||
return CreateSceneAction(
|
return CreateSceneAction(
|
||||||
|
actionType: actionType ?? this.actionType,
|
||||||
entityId: entityId ?? this.entityId,
|
entityId: entityId ?? this.entityId,
|
||||||
actionExecutor: actionExecutor ?? this.actionExecutor,
|
actionExecutor: actionExecutor ?? this.actionExecutor,
|
||||||
executorProperty: executorProperty ?? this.executorProperty,
|
executorProperty: executorProperty ?? this.executorProperty,
|
||||||
@@ -125,6 +128,7 @@ class CreateSceneAction {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
"actionType": actionType,
|
||||||
'entityId': entityId,
|
'entityId': entityId,
|
||||||
'actionExecutor': actionExecutor,
|
'actionExecutor': actionExecutor,
|
||||||
};
|
};
|
||||||
@@ -133,6 +137,7 @@ class CreateSceneAction {
|
|||||||
|
|
||||||
factory CreateSceneAction.fromMap(Map<String, dynamic> map) {
|
factory CreateSceneAction.fromMap(Map<String, dynamic> map) {
|
||||||
return CreateSceneAction(
|
return CreateSceneAction(
|
||||||
|
actionType: map['actionType'],
|
||||||
entityId: map['entityId'] ?? '',
|
entityId: map['entityId'] ?? '',
|
||||||
actionExecutor: map['actionExecutor'] ?? '',
|
actionExecutor: map['actionExecutor'] ?? '',
|
||||||
executorProperty:
|
executorProperty:
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
|
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart';
|
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart';
|
||||||
@@ -23,12 +22,10 @@ class _RoutinesViewState extends State<RoutinesView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SwitchTabsBloc, SwitchTabsState>(
|
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is ShowCreateRoutineState && state.showCreateRoutine) {
|
if (state.createRoutineView) {
|
||||||
return const CreateNewRoutineView(
|
return const CreateNewRoutineView();
|
||||||
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
@@ -49,12 +46,12 @@ class _RoutinesViewState extends State<RoutinesView> {
|
|||||||
),
|
),
|
||||||
RoutineViewCard(
|
RoutineViewCard(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
|
||||||
const CreateNewRoutineViewEvent(true),
|
|
||||||
);
|
|
||||||
context.read<RoutineBloc>().add(
|
context.read<RoutineBloc>().add(
|
||||||
(ResetRoutineState()),
|
(ResetRoutineState()),
|
||||||
);
|
);
|
||||||
|
BlocProvider.of<RoutineBloc>(context).add(
|
||||||
|
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
icon: Icons.add,
|
icon: Icons.add,
|
||||||
textString: '',
|
textString: '',
|
||||||
|
|||||||
@@ -33,31 +33,24 @@ class DeleteSceneWidget extends StatelessWidget {
|
|||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Cancel',
|
'Cancel',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(width: 1, height: 50, color: ColorsManager.greyColor),
|
||||||
width: 1, height: 50, color: ColorsManager.greyColor),
|
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<RoutineBloc>().add(const DeleteScene());
|
context.read<RoutineBloc>().add(const DeleteScene());
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Confirm',
|
'Confirm',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -35,6 +35,20 @@ class DraggableCard extends StatelessWidget {
|
|||||||
final deviceFunctions =
|
final deviceFunctions =
|
||||||
state.selectedFunctions[deviceData['uniqueCustomId']] ?? [];
|
state.selectedFunctions[deviceData['uniqueCustomId']] ?? [];
|
||||||
|
|
||||||
|
int index = state.thenItems.indexWhere(
|
||||||
|
(item) => item['uniqueCustomId'] == deviceData['uniqueCustomId']);
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
return _buildCardContent(context, deviceFunctions, padding: padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ifIndex = state.ifItems.indexWhere(
|
||||||
|
(item) => item['uniqueCustomId'] == deviceData['uniqueCustomId']);
|
||||||
|
|
||||||
|
if (ifIndex != -1) {
|
||||||
|
return _buildCardContent(context, deviceFunctions, padding: padding);
|
||||||
|
}
|
||||||
|
|
||||||
return Draggable<Map<String, dynamic>>(
|
return Draggable<Map<String, dynamic>>(
|
||||||
data: deviceData,
|
data: deviceData,
|
||||||
feedback: Transform.rotate(
|
feedback: Transform.rotate(
|
||||||
@@ -79,17 +93,13 @@ class DraggableCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: imagePath.contains('.svg')
|
child: deviceData['type'] == 'tap_to_run'
|
||||||
? SvgPicture.asset(
|
? Image.memory(
|
||||||
imagePath,
|
base64Decode(deviceData['icon']),
|
||||||
)
|
)
|
||||||
: imagePath.contains('.png')
|
: SvgPicture.asset(
|
||||||
? Image.asset(
|
imagePath,
|
||||||
imagePath,
|
),
|
||||||
)
|
|
||||||
: Image.memory(
|
|
||||||
base64Decode(imagePath),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Padding(
|
Padding(
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
|
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
@@ -69,8 +68,8 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
|
|||||||
),
|
),
|
||||||
child: RoutineViewCard(
|
child: RoutineViewCard(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
BlocProvider.of<RoutineBloc>(context).add(
|
||||||
const CreateNewRoutineViewEvent(true),
|
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||||
);
|
);
|
||||||
context.read<RoutineBloc>().add(
|
context.read<RoutineBloc>().add(
|
||||||
GetSceneDetails(
|
GetSceneDetails(
|
||||||
@@ -118,8 +117,8 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
|
|||||||
),
|
),
|
||||||
child: RoutineViewCard(
|
child: RoutineViewCard(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
BlocProvider.of<RoutineBloc>(context).add(
|
||||||
const CreateNewRoutineViewEvent(true),
|
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||||
);
|
);
|
||||||
context.read<RoutineBloc>().add(
|
context.read<RoutineBloc>().add(
|
||||||
GetAutomationDetails(
|
GetAutomationDetails(
|
||||||
|
|||||||
@@ -7,98 +7,106 @@ 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 StatelessWidget {
|
class AutomationDialog extends StatefulWidget {
|
||||||
final String automationName;
|
final String automationName;
|
||||||
final String automationId;
|
final String automationId;
|
||||||
final String uniqueCustomId;
|
final String uniqueCustomId;
|
||||||
|
final String? passedAutomationActionExecutor;
|
||||||
|
|
||||||
const AutomationDialog({
|
const AutomationDialog({
|
||||||
super.key,
|
super.key,
|
||||||
required this.automationName,
|
required this.automationName,
|
||||||
required this.automationId,
|
required this.automationId,
|
||||||
required this.uniqueCustomId,
|
required this.uniqueCustomId,
|
||||||
|
this.passedAutomationActionExecutor,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<AutomationDialog> createState() => _AutomationDialogState();
|
||||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
}
|
||||||
builder: (context, state) {
|
|
||||||
final isEnabled = state.automationActionExecutor == 'rule_enable';
|
|
||||||
|
|
||||||
return Dialog(
|
class _AutomationDialogState extends State<AutomationDialog> {
|
||||||
shape:
|
String? selectedAutomationActionExecutor;
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
||||||
child: Container(
|
@override
|
||||||
width: 400,
|
void initState() {
|
||||||
padding: const EdgeInsets.all(16),
|
super.initState();
|
||||||
child: Column(
|
List<DeviceFunctionData>? functions = context
|
||||||
mainAxisSize: MainAxisSize.min,
|
.read<RoutineBloc>()
|
||||||
children: [
|
.state
|
||||||
DialogHeader(automationName),
|
.selectedFunctions[widget.uniqueCustomId];
|
||||||
const SizedBox(height: 16),
|
for (DeviceFunctionData data in functions ?? []) {
|
||||||
ListTile(
|
if (data.entityId == widget.automationId) {
|
||||||
leading:
|
selectedAutomationActionExecutor = data.value;
|
||||||
SvgPicture.asset(Assets.acPower, width: 24, height: 24),
|
}
|
||||||
title: const Text('Enable'),
|
}
|
||||||
trailing: Radio<bool>(
|
}
|
||||||
value: true,
|
|
||||||
groupValue: isEnabled,
|
@override
|
||||||
onChanged: (bool? value) {
|
Widget build(BuildContext context) {
|
||||||
if (value == true) {
|
return Dialog(
|
||||||
context.read<RoutineBloc>().add(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
const SetAutomationActionExecutor(
|
child: Container(
|
||||||
automationActionExecutor: 'rule_enable',
|
width: 400,
|
||||||
),
|
padding: const EdgeInsets.all(16),
|
||||||
);
|
child: Column(
|
||||||
}
|
mainAxisSize: MainAxisSize.min,
|
||||||
},
|
children: [
|
||||||
),
|
DialogHeader(widget.automationName),
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: SvgPicture.asset(Assets.acPowerOff,
|
leading: SvgPicture.asset(Assets.acPower, width: 24, height: 24),
|
||||||
width: 24, height: 24),
|
title: const Text('Enable'),
|
||||||
title: const Text('Disable'),
|
trailing: Radio<String?>(
|
||||||
trailing: Radio<bool>(
|
value: 'rule_enable',
|
||||||
value: false,
|
groupValue: selectedAutomationActionExecutor,
|
||||||
groupValue: isEnabled,
|
onChanged: (String? value) {
|
||||||
onChanged: (bool? value) {
|
setState(() {
|
||||||
if (value == false) {
|
selectedAutomationActionExecutor = 'rule_enable';
|
||||||
context.read<RoutineBloc>().add(
|
});
|
||||||
const SetAutomationActionExecutor(
|
}),
|
||||||
automationActionExecutor: 'rule_disable',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
DialogFooter(
|
|
||||||
onConfirm: () {
|
|
||||||
context.read<RoutineBloc>().add(
|
|
||||||
AddFunctionToRoutine(
|
|
||||||
[
|
|
||||||
DeviceFunctionData(
|
|
||||||
entityId: automationId,
|
|
||||||
functionCode: 'automation',
|
|
||||||
value: state.automationActionExecutor,
|
|
||||||
operationName: 'Automation',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
uniqueCustomId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
Navigator.of(context).pop(true);
|
|
||||||
},
|
|
||||||
onCancel: () => Navigator.of(context).pop(false),
|
|
||||||
isConfirmEnabled: true,
|
|
||||||
dialogWidth: 400,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
ListTile(
|
||||||
);
|
leading:
|
||||||
},
|
SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24),
|
||||||
|
title: const Text('Disable'),
|
||||||
|
trailing: Radio<String?>(
|
||||||
|
value: 'rule_disable',
|
||||||
|
groupValue: selectedAutomationActionExecutor,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
setState(() {
|
||||||
|
selectedAutomationActionExecutor = 'rule_disable';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
DialogFooter(
|
||||||
|
onConfirm: () {
|
||||||
|
if (selectedAutomationActionExecutor != null) {
|
||||||
|
context.read<RoutineBloc>().add(
|
||||||
|
AddFunctionToRoutine(
|
||||||
|
[
|
||||||
|
DeviceFunctionData(
|
||||||
|
entityId: widget.automationId,
|
||||||
|
functionCode: 'automation',
|
||||||
|
value: selectedAutomationActionExecutor,
|
||||||
|
operationName: 'Automation',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
widget.uniqueCustomId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
},
|
||||||
|
onCancel: () => Navigator.of(context).pop(),
|
||||||
|
isConfirmEnabled: selectedAutomationActionExecutor != null,
|
||||||
|
dialogWidth: 400,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
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';
|
||||||
@@ -49,11 +48,11 @@ class DiscardDialog {
|
|||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
context.read<RoutineBloc>().add(ResetRoutineState());
|
context.read<RoutineBloc>().add(ResetRoutineState());
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
BlocProvider.of<RoutineBloc>(context).add(
|
||||||
const CreateNewRoutineViewEvent(false),
|
const CreateNewRoutineViewEvent(createRoutineView: false),
|
||||||
);
|
);
|
||||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
BlocProvider.of<RoutineBloc>(context).add(
|
||||||
const TriggerSwitchTabsEvent(true),
|
const TriggerSwitchTabsEvent(isRoutineTab: true),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/switch_tabs/switch_tabs_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/helper/save_routine_helper.dart';
|
import 'package:syncrow_web/pages/routiens/helper/save_routine_helper.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/discard_dialog.dart';
|
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/discard_dialog.dart';
|
||||||
@@ -16,8 +15,7 @@ class RoutineSearchAndButtons extends StatefulWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RoutineSearchAndButtons> createState() =>
|
State<RoutineSearchAndButtons> createState() => _RoutineSearchAndButtonsState();
|
||||||
_RoutineSearchAndButtonsState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
||||||
@@ -37,20 +35,21 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocConsumer<RoutineBloc, RoutineState>(
|
return BlocBuilder<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) {
|
||||||
|
_nameController.text = state.routineName ?? '';
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return Wrap(
|
return Wrap(
|
||||||
runSpacing: 16,
|
runSpacing: 16,
|
||||||
children: [
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
state.errorMessage ?? '',
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
@@ -62,9 +61,8 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: constraints.maxWidth > 700
|
maxWidth:
|
||||||
? 450
|
constraints.maxWidth > 700 ? 450 : constraints.maxWidth - 32),
|
||||||
: constraints.maxWidth - 32),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -73,13 +71,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
children: [
|
children: [
|
||||||
Text('* ',
|
Text('* ',
|
||||||
style: context.textTheme.bodyMedium!
|
style: context.textTheme.bodyMedium!
|
||||||
.copyWith(
|
.copyWith(color: ColorsManager.red, fontSize: 13)),
|
||||||
color: ColorsManager.red,
|
|
||||||
fontSize: 13)),
|
|
||||||
Text(
|
Text(
|
||||||
'Routine Name',
|
'Routine Name',
|
||||||
style: context.textTheme.bodyMedium!
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
.copyWith(
|
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
@@ -93,24 +88,20 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
decoration: containerWhiteDecoration,
|
decoration: containerWhiteDecoration,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
style: context.textTheme.bodyMedium!
|
style: context.textTheme.bodyMedium!
|
||||||
.copyWith(
|
.copyWith(color: ColorsManager.blackColor),
|
||||||
color: ColorsManager.blackColor),
|
|
||||||
controller: _nameController,
|
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(
|
.copyWith(fontSize: 12, color: ColorsManager.grayColor),
|
||||||
fontSize: 12,
|
|
||||||
color: ColorsManager.grayColor),
|
|
||||||
contentPadding:
|
contentPadding:
|
||||||
const EdgeInsets.symmetric(
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
horizontal: 12, vertical: 10),
|
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
),
|
),
|
||||||
onChanged: (value) {
|
onTapOutside: (_) {
|
||||||
context
|
context
|
||||||
.read<RoutineBloc>()
|
.read<RoutineBloc>()
|
||||||
.add(SetRoutineName(value));
|
.add(SetRoutineName(_nameController.text));
|
||||||
},
|
},
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
@@ -130,18 +121,16 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
width: 200,
|
width: 200,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: state.isAutomation ||
|
onPressed: state.isAutomation || state.isTabToRun
|
||||||
state.isTabToRun
|
|
||||||
? () async {
|
? () async {
|
||||||
final result = await SettingHelper
|
final result = await SettingHelper.showSettingDialog(
|
||||||
.showSettingDialog(
|
|
||||||
context: context,
|
context: context,
|
||||||
iconId:
|
iconId: state.selectedIcon ?? '',
|
||||||
state.selectedIcon ?? '',
|
|
||||||
);
|
);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
context.read<RoutineBloc>().add(
|
context
|
||||||
AddSelectedIcon(result));
|
.read<RoutineBloc>()
|
||||||
|
.add(AddSelectedIcon(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
@@ -197,12 +186,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (state.routineName == null ||
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
state.routineName!.isEmpty) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: const Text(
|
content: const Text('Please enter the routine name'),
|
||||||
'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(
|
||||||
@@ -216,12 +203,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.ifItems.isEmpty ||
|
if (state.ifItems.isEmpty || state.thenItems.isEmpty) {
|
||||||
state.thenItems.isEmpty) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: const Text(
|
content: const Text('Please add if and then condition'),
|
||||||
'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(
|
||||||
@@ -234,18 +219,18 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final result = await SaveRoutineHelper
|
// final result =
|
||||||
.showSaveRoutineDialog(context);
|
// await
|
||||||
if (result != null && result) {
|
BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
|
||||||
BlocProvider.of<SwitchTabsBloc>(context)
|
SaveRoutineHelper.showSaveRoutineDialog(context);
|
||||||
.add(
|
// if (result != null && result) {
|
||||||
const CreateNewRoutineViewEvent(false),
|
// BlocProvider.of<RoutineBloc>(context).add(
|
||||||
);
|
// const CreateNewRoutineViewEvent(createRoutineView: false),
|
||||||
BlocProvider.of<SwitchTabsBloc>(context)
|
// );
|
||||||
.add(
|
// BlocProvider.of<RoutineBloc>(context).add(
|
||||||
const TriggerSwitchTabsEvent(true),
|
// const TriggerSwitchTabsEvent(isRoutineTab: true),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
borderRadius: 15,
|
borderRadius: 15,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
@@ -276,14 +261,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: state.isAutomation || state.isTabToRun
|
onPressed: state.isAutomation || state.isTabToRun
|
||||||
? () async {
|
? () async {
|
||||||
final result =
|
final result = await SettingHelper.showSettingDialog(
|
||||||
await SettingHelper.showSettingDialog(
|
context: context, iconId: state.selectedIcon ?? '');
|
||||||
context: context,
|
|
||||||
iconId: state.selectedIcon ?? '');
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
context
|
context.read<RoutineBloc>().add(AddSelectedIcon(result));
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(AddSelectedIcon(result));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
@@ -333,12 +314,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (state.routineName == null ||
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
state.routineName!.isEmpty) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: const Text(
|
content: const Text('Please enter the routine name'),
|
||||||
'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(
|
||||||
@@ -352,12 +331,10 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.ifItems.isEmpty ||
|
if (state.ifItems.isEmpty || state.thenItems.isEmpty) {
|
||||||
state.thenItems.isEmpty) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: const Text(
|
content: const Text('Please add if and then condition'),
|
||||||
'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(
|
||||||
@@ -370,17 +347,18 @@ class _RoutineSearchAndButtonsState extends State<RoutineSearchAndButtons> {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final result =
|
// final result =
|
||||||
await SaveRoutineHelper.showSaveRoutineDialog(
|
// await
|
||||||
context);
|
BlocProvider.of<RoutineBloc>(context).add(ResetErrorMessage());
|
||||||
if (result != null && result) {
|
SaveRoutineHelper.showSaveRoutineDialog(context);
|
||||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
// if (result != null && result) {
|
||||||
const CreateNewRoutineViewEvent(false),
|
// BlocProvider.of<RoutineBloc>(context).add(
|
||||||
);
|
// const CreateNewRoutineViewEvent(createRoutineView: false),
|
||||||
BlocProvider.of<SwitchTabsBloc>(context).add(
|
// );
|
||||||
const TriggerSwitchTabsEvent(true),
|
// BlocProvider.of<RoutineBloc>(context).add(
|
||||||
);
|
// const TriggerSwitchTabsEvent(isRoutineTab: true),
|
||||||
}
|
// );
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
borderRadius: 15,
|
borderRadius: 15,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
|||||||
@@ -87,8 +87,11 @@ class ThenContainer extends StatelessWidget {
|
|||||||
...state.thenItems[index],
|
...state.thenItems[index],
|
||||||
'imagePath':
|
'imagePath':
|
||||||
Assets.automation,
|
Assets.automation,
|
||||||
'title': state
|
'title':
|
||||||
.thenItems[index]['name'],
|
state.thenItems[index]
|
||||||
|
['name'] ??
|
||||||
|
state.thenItems[index]
|
||||||
|
['title'],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -149,6 +152,12 @@ class ThenContainer extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mutableData['type'] == 'automation') {
|
if (mutableData['type'] == 'automation') {
|
||||||
|
int index = state.thenItems.indexWhere(
|
||||||
|
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||||
|
if (index != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final result = await showDialog<bool>(
|
final result = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => AutomationDialog(
|
builder: (BuildContext context) => AutomationDialog(
|
||||||
@@ -169,9 +178,14 @@ class ThenContainer extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
|
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
|
||||||
|
int index = state.thenItems.indexWhere(
|
||||||
|
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||||
|
if (index != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
context.read<RoutineBloc>().add(AddToThenContainer({
|
context.read<RoutineBloc>().add(AddToThenContainer({
|
||||||
...mutableData,
|
...mutableData,
|
||||||
'imagePath': Assets.logo,
|
'imagePath': mutableData['imagePath'] ?? Assets.logo,
|
||||||
'title': mutableData['name'],
|
'title': mutableData['name'],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||||
|
|
||||||
class CustomSnackBar {
|
class CustomSnackBar {
|
||||||
@@ -11,6 +12,35 @@ class CustomSnackBar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static redSnackBar(String message) {
|
||||||
|
final key = NavigationService.snackbarKey;
|
||||||
|
BuildContext? currentContext = key?.currentContext;
|
||||||
|
if (key != null && currentContext != null) {
|
||||||
|
final snackBar = SnackBar(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
backgroundColor: ColorsManager.red,
|
||||||
|
content: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
message,
|
||||||
|
style: Theme.of(currentContext)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
key.currentState?.showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static greenSnackBar(String message) {
|
static greenSnackBar(String message) {
|
||||||
final key = NavigationService.snackbarKey;
|
final key = NavigationService.snackbarKey;
|
||||||
BuildContext? currentContext = key?.currentContext;
|
BuildContext? currentContext = key?.currentContext;
|
||||||
@@ -29,8 +59,10 @@ class CustomSnackBar {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
message,
|
message,
|
||||||
style: Theme.of(currentContext).textTheme.bodySmall!.copyWith(
|
style: Theme.of(currentContext)
|
||||||
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user