routines and automation Toggle

This commit is contained in:
mohammad
2025-03-21 17:14:05 +03:00
parent c0d53fdf5c
commit 7e1c2ba712
15 changed files with 441 additions and 357 deletions

View File

@ -0,0 +1,30 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_7280_5211)">
<circle cx="18" cy="18" r="15" fill="#F4F4F4"/>
</g>
<g filter="url(#filter1_i_7280_5211)">
<path d="M25.1663 13.187C24.8231 12.8439 24.2666 12.8439 23.9234 13.1871L16.1621 20.9484L12.0766 16.8628C11.7334 16.5196 11.1768 16.5196 10.8336 16.8628C10.4904 17.206 10.4904 17.7625 10.8336 18.1057L15.5406 22.8127C15.7122 22.9844 15.9372 23.0701 16.1621 23.0701C16.3869 23.0701 16.6119 22.9843 16.7835 22.8127L25.1663 14.43C25.5095 14.0868 25.5095 13.5303 25.1663 13.187Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_7280_5211" x="0" y="0" width="36" height="36" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_7280_5211"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_7280_5211" result="shape"/>
</filter>
<filter id="filter1_i_7280_5211" x="10.5762" y="12.9297" width="14.8475" height="10.1406" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_7280_5211"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -33,7 +33,6 @@ Future<void> main() async {
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final GoRouter _router = GoRouter(
@ -55,6 +54,7 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(

View File

@ -55,6 +55,7 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(

View File

@ -1,124 +0,0 @@
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/auth/model/project_model.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_event.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_status.dart';
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
import 'package:syncrow_web/services/routines_api.dart';
class AutomationSceneTriggerBloc extends Bloc<AutomationSceneTriggerEvent, AutomationSceneTriggerStatus> {
AutomationSceneTriggerBloc() : super(AutomationSceneInitial()) {
// on<LoadScenes>(_onLoadScenes);
// on<LoadAutomation>(_onLoadAutomation);
on<SceneTrigger>(_onSceneTrigger);
on<UpdateAutomationStatus>(_onUpdateAutomationStatus);
}
List<ScenesModel> scenes = [];
List<ScenesModel> automationList = [];
// Future<void> _onLoadScenes(LoadScenes event, Emitter<AutomationSceneTriggerStatus> emit) async {
// emit(SceneLoading());
// try {
// Project? project = HomeCubit.getInstance().project;
// if (event.unitId.isNotEmpty) {
// scenes = await SceneApi.getScenesByUnitId(event.unitId,
// event.unit.community.uuid, project?.uuid ?? TempConst.projectIdDev,
// showInDevice: event.showInDevice);
// emit(SceneLoaded(scenes, automationList));
// } else {
// emit(const SceneError(message: 'Unit ID is empty'));
// }
// } catch (e) {
// emit(const SceneError(message: 'Something went wrong'));
// }
// }
// Future<void> _onLoadAutomation(
// LoadAutomation event, Emitter<AutomationSceneTriggerStatus> emit) async {
// emit(SceneLoading());
// try {
// Project? project = HomeCubit.getInstance().project;
// if (event.unitId.isNotEmpty) {
// automationList = await SceneApi.getAutomationByUnitId(
// event.unitId, event.communityId, project?.uuid ?? '');
// emit(SceneLoaded(scenes, automationList));
// } else {
// emit(const SceneError(message: 'Unit ID is empty'));
// }
// } catch (e) {
// emit(const SceneError(message: 'Something went wrong'));
// }
// }
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<AutomationSceneTriggerStatus> emit) async {
final currentState = state;
if (currentState is AutomationSceneLoaded) {
emit(AutomationSceneLoaded(
currentState.scenes,
currentState.automationList,
loadingSceneId: event.sceneId,
));
try {
final success = await SceneApi.triggerScene(event.sceneId);
if (success) {
emit(SceneTriggerSuccess(event.name));
emit(AutomationSceneLoaded(currentState.scenes, currentState.automationList));
} else {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
} catch (e) {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
}
}
Future<void> _onUpdateAutomationStatus(
UpdateAutomationStatus event, Emitter<AutomationSceneTriggerStatus> emit) async {
final currentState = state;
if (currentState is AutomationSceneLoaded) {
final newLoadingStates =
Map<String, bool>.from(currentState.loadingStates)
..[event.automationId] = true;
emit(AutomationSceneLoaded(
currentState.scenes,
currentState.automationList,
loadingStates: newLoadingStates,
));
try {
Project? project = HomeCubit.getInstance().project;
final success = await SceneApi.updateAutomationStatus(
event.automationId,
event.automationStatusUpdate,
project?.uuid ?? '');
if (success) {
automationList = await SceneApi.getAutomationByUnitId(
event.automationStatusUpdate.spaceUuid,
event.communityId,
project?.uuid ?? '');
newLoadingStates[event.automationId] = false;
emit(AutomationSceneLoaded(
currentState.scenes,
automationList,
loadingStates: newLoadingStates,
));
} else {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
} catch (e) {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
}
}
}

View File

@ -1,55 +0,0 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
abstract class AutomationSceneTriggerEvent extends Equatable {
const AutomationSceneTriggerEvent();
@override
List<Object> get props => [];
}
class AutomationSceneScenes extends AutomationSceneTriggerEvent {
final String unitId;
final bool showInDevice;
final SpaceModel unit;
const AutomationSceneScenes(this.unitId, this.unit, {this.showInDevice = false});
@override
List<Object> get props => [unitId, showInDevice];
}
class AutomationSceneAutomation extends AutomationSceneTriggerEvent {
final String unitId;
final String communityId;
const AutomationSceneAutomation(this.unitId, this.communityId);
@override
List<Object> get props => [unitId, communityId];
}
class SceneTrigger extends AutomationSceneTriggerEvent {
final String sceneId;
final String name;
const SceneTrigger(this.sceneId, this.name);
@override
List<Object> get props => [sceneId];
}
//updateAutomationStatus
class UpdateAutomationStatus extends AutomationSceneTriggerEvent {
final String automationId;
final AutomationStatusUpdate automationStatusUpdate;
final String communityId;
const UpdateAutomationStatus({required this.automationStatusUpdate, required this.automationId, required this.communityId});
@override
List<Object> get props => [automationStatusUpdate];
}

View File

@ -1,51 +0,0 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
abstract class AutomationSceneTriggerStatus extends Equatable {
const AutomationSceneTriggerStatus();
@override
List<Object?> get props => [];
}
class AutomationSceneInitial extends AutomationSceneTriggerStatus {}
class AutomationSceneLoading extends AutomationSceneTriggerStatus {}
class AutomationSceneLoaded extends AutomationSceneTriggerStatus {
final List<ScenesModel> scenes;
final List<ScenesModel> automationList;
final String? loadingSceneId;
final Map<String, bool> loadingStates;
const AutomationSceneLoaded(this.scenes, this.automationList,
{this.loadingSceneId, this.loadingStates = const {}});
@override
List<Object?> get props =>
[scenes, loadingSceneId, automationList, loadingStates];
}
class AutomationSceneError extends AutomationSceneTriggerStatus {
final String message;
const AutomationSceneError({required this.message});
@override
List<Object> get props => [message];
}
class SceneTriggerSuccess extends AutomationSceneTriggerStatus {
final String sceneName;
const SceneTriggerSuccess(this.sceneName);
@override
List<Object> get props => [sceneName];
}
class UpdateAutomationStatusLoading extends AutomationSceneTriggerStatus {
const UpdateAutomationStatusLoading();
}

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
import 'package:syncrow_web/pages/routines/models/delay/delay_fucntions.dart';
@ -51,6 +52,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
on<TriggerSwitchTabsEvent>(_triggerSwitchTabsEvent);
on<CreateNewRoutineViewEvent>(_createNewRoutineViewEvent);
on<ResetErrorMessage>(_resetErrorMessage);
on<SceneTrigger>(_onSceneTrigger);
on<UpdateAutomationStatus>(_onUpdateAutomationStatus);
}
FutureOr<void> _triggerSwitchTabsEvent(
@ -1269,4 +1272,79 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
}
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<RoutineState> emit) async {
emit(state.copyWith(loadingSceneId: event.sceneId));
try {
final success = await SceneApi.triggerScene(event.sceneId!);
if (success) {
emit(state.copyWith(
loadingSceneId: null,
// Add success state if needed
));
// Optional: Add delay to show success feedback
await Future.delayed(const Duration(milliseconds: 500));
} else {
emit(state.copyWith(
loadingSceneId: null,
errorMessage: 'Trigger failed',
));
}
} catch (e) {
emit(state.copyWith(
loadingSceneId: null,
errorMessage: 'Trigger error: ${e.toString()}',
));
}
}
Future<void> _onUpdateAutomationStatus(
UpdateAutomationStatus event, Emitter<RoutineState> emit) async {
// Create a new set safely
final currentLoadingIds = state.loadingAutomationIds;
final newLoadingIds = {...currentLoadingIds!}..add(event.automationId);
emit(state.copyWith(loadingAutomationIds: newLoadingIds));
try {
final projectId = await ProjectManager.getProjectUUID() ?? '';
final success = await SceneApi.updateAutomationStatus(
event.automationId, event.automationStatusUpdate, projectId);
if (success) {
final updatedAutomations = await SceneApi.getAutomationByUnitId(
event.automationStatusUpdate.spaceUuid,
event.communityId,
projectId);
// Remove from loading set safely
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
emit(state.copyWith(
automations: updatedAutomations,
loadingAutomationIds: updatedLoadingIds,
));
} else {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update failed',
));
}
} catch (e) {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update error: ${e.toString()}',
));
}
}
}

View File

@ -210,3 +210,28 @@ class ResetRoutineState extends RoutineEvent {}
class ClearFunctions extends RoutineEvent {}
class ResetErrorMessage extends RoutineEvent {}
class SceneTrigger extends RoutineEvent {
final String? sceneId;
final String? name;
const SceneTrigger({this.sceneId, this.name});
@override
List<Object> get props => [sceneId!,name!];
}
//updateAutomationStatus
class UpdateAutomationStatus extends RoutineEvent {
final String automationId;
final AutomationStatusUpdate automationStatusUpdate;
final String communityId;
const UpdateAutomationStatus({required this.automationStatusUpdate, required this.automationId, required this.communityId});
@override
List<Object> get props => [automationStatusUpdate];
}

View File

@ -1,6 +1,7 @@
part of 'routine_bloc.dart';
class RoutineState extends Equatable {
final String? loadingSceneId;
final List<Map<String, dynamic>> ifItems;
final List<Map<String, dynamic>> thenItems;
final List<Map<String, String>> availableCards;
@ -25,6 +26,7 @@ class RoutineState extends Equatable {
// final String? automationActionExecutor;
final bool routineTab;
final bool createRoutineView;
final Set<String>? loadingAutomationIds; // Track loading automations
const RoutineState(
{this.ifItems = const [],
@ -47,12 +49,16 @@ class RoutineState extends Equatable {
this.sceneId,
this.automationId,
this.isUpdate,
this.loadingAutomationIds = const <String>{}, // Initialize with empty set
this.loadingSceneId,
this.devices = const [],
// this.automationActionExecutor,
this.routineTab = false,
this.createRoutineView = false});
RoutineState copyWith({
String? loadingSceneId,
Set<String>? loadingAutomationIds,
List<Map<String, dynamic>>? ifItems,
List<Map<String, dynamic>>? thenItems,
List<ScenesModel>? scenes,
@ -79,6 +85,8 @@ class RoutineState extends Equatable {
bool? createRoutineView,
}) {
return RoutineState(
loadingSceneId: loadingSceneId,
loadingAutomationIds: loadingAutomationIds ?? this.loadingAutomationIds,
ifItems: ifItems ?? this.ifItems,
thenItems: thenItems ?? this.thenItems,
scenes: scenes ?? this.scenes,
@ -109,6 +117,7 @@ class RoutineState extends Equatable {
@override
List<Object?> get props => [
loadingAutomationIds,
ifItems,
thenItems,
scenes,
@ -134,3 +143,38 @@ class RoutineState extends Equatable {
createRoutineView
];
}
class SceneInitial extends RoutineState {}
class SceneLoading extends RoutineState {}
class SceneLoaded extends RoutineState {
final List<ScenesModel>? scenesOrAutomation;
const SceneLoaded({this.scenesOrAutomation});
@override
List<Object?> get props => [
scenesOrAutomation,
];
}
class SceneError extends RoutineState {
final String message;
const SceneError({required this.message});
@override
List<Object> get props => [message];
}
class SceneTriggerSuccess extends RoutineState {
final String sceneName;
const SceneTriggerSuccess(this.sceneName);
@override
List<Object> get props => [sceneName];
}
class UpdateAutomationStatusLoading extends RoutineState {
const UpdateAutomationStatusLoading();
}

View File

@ -9,6 +9,9 @@ class ScenesModel {
final String status;
final String type;
final String? icon;
final String spaceName;
final String spaceId;
final String communityId;
ScenesModel({
required this.id,
@ -16,6 +19,9 @@ class ScenesModel {
required this.name,
required this.status,
required this.type,
required this.spaceName,
required this.spaceId,
required this.communityId,
this.icon,
});
@ -41,6 +47,9 @@ class ScenesModel {
name: json["name"] ?? '',
status: json["status"] ?? '',
type: json["type"] ?? '',
spaceName: json["spaceName"] ?? '',
spaceId: json["spaceId"] ?? '',
communityId: json["communityId"] ?? '',
icon:
isAutomation == true ? Assets.automation : (json["icon"] as String?),
);
@ -52,5 +61,8 @@ class ScenesModel {
"name": name,
"status": status,
"type": type,
"spaceName": spaceName,
"spaceId": spaceId,
"communityId": communityId,
};
}

View File

@ -60,7 +60,13 @@ class _RoutinesViewState extends State<RoutinesView> {
height: 10,
),
RoutineViewCard(
isFromScenes: false,
isLoading: false,
onChanged: (v) {},
status: '',
spaceId: '',
automationId: '',
communityId: '',
sceneId: '',
cardType: '',
spaceName: '',
onTap: () {

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -46,48 +46,68 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
),
const SizedBox(height: 10),
if (state.scenes.isEmpty)
Text(
"No scenes found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
Expanded(
child: Text(
"No scenes found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
),
if (state.scenes.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
maxWidth: MediaQuery.sizeOf(context).width * 0.7),
maxHeight: isSmallScreenSize(context) ? 190 : 200,
maxWidth: MediaQuery.sizeOf(context).width * 0.8),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
cardType: 'scenes',
spaceName: 'scenes',
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetSceneDetails(
sceneId: state.scenes[index].id,
isTabToRun: true,
isUpdate: true,
),
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) {
final scene = state.scenes[index];
final isLoading =
state.loadingSceneId == scene.id;
return Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
isLoading: isLoading,
sceneOnTap: () {
context.read<RoutineBloc>().add(
SceneTrigger(
sceneId: scene.id,
name: scene.name));
},
status: state.scenes[index].status,
communityId:
state.scenes[index].communityId ?? '',
spaceId: state.scenes[index].spaceId,
sceneId: state.scenes[index].sceneTuyaId!,
automationId: state.scenes[index].id,
cardType: 'scenes',
spaceName: state.scenes[index].spaceName,
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ??
Assets.logoHorizontal,
isFromScenes: true,
iconInBytes: state.scenes[index].iconInBytes,
),
),
),
context.read<RoutineBloc>().add(
GetSceneDetails(
sceneId: state.scenes[index].id,
isTabToRun: true,
isUpdate: true,
),
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ??
Assets.logoHorizontal,
isFromScenes: true,
iconInBytes: state.scenes[index].iconInBytes,
),
);
}),
),
const SizedBox(height: 15),
Text(
@ -99,46 +119,74 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
),
const SizedBox(height: 10),
if (state.automations.isEmpty)
Text(
"No automations found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
Expanded(
child: Text(
"No automations found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
),
if (state.automations.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
maxHeight: isSmallScreenSize(context) ? 190 : 200,
maxWidth: MediaQuery.sizeOf(context).width * 0.7),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
cardType: 'automations',
spaceName: 'automations',
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId:
state.automations[index].id,
isAutomation: true,
isUpdate: true),
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) {
final isLoading = state.automations!
.contains(state.automations[index].id);
return Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
isLoading: isLoading,
onChanged: (v) {
// BlocProvider.of<RoutineBloc>(context)
context.read<RoutineBloc>().add(
UpdateAutomationStatus(
automationId:
state.automations[index].id,
automationStatusUpdate:
AutomationStatusUpdate(
spaceUuid: state
.automations[index]
.spaceId,
isEnable: v),
communityId:
'aff21a57-2f91-4e5c-b99b-0182c3ab65a9'),
);
},
status: state.automations[index].status,
communityId: '',
spaceId: state.automations[index].spaceId,
sceneId: '',
automationId: state.automations[index].id,
cardType: 'automations',
spaceName: state.scenes[index].spaceName,
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ??
Assets.automation,
),
),
),
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId:
state.automations[index].id,
isAutomation: true,
isUpdate: true),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ??
Assets.automation,
),
);
}),
),
],
),

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -7,42 +8,78 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
class RoutineViewCard extends StatefulWidget with HelperResponsiveLayout {
const RoutineViewCard({
super.key,
required this.onTap,
this.sceneOnTap,
required this.icon,
required this.textString,
required this.spaceName,
required this.cardType,
this.isFromScenes,
this.iconInBytes,
required this.sceneId,
required this.communityId,
required this.spaceId,
required this.automationId,
required this.status,
this.onChanged,
required this.isLoading,
});
final Function() onTap;
final Function()? sceneOnTap;
final dynamic icon;
final String textString;
final String spaceName;
final String cardType;
final String sceneId;
final String spaceId;
final String status;
final bool isLoading;
final void Function(bool)? onChanged;
final String automationId;
final String communityId;
final bool? isFromScenes;
final Uint8List? iconInBytes;
@override
State<RoutineViewCard> createState() => _RoutineViewCardState();
}
class _RoutineViewCardState extends State<RoutineViewCard> {
bool _showTemporaryCheck = false;
void _handleSceneTap() {
if (!_showTemporaryCheck) {
setState(() => _showTemporaryCheck = true);
widget.sceneOnTap?.call();
Timer(const Duration(seconds: 3), () {
if (mounted) setState(() => _showTemporaryCheck = false);
});
}
}
@override
Widget build(BuildContext context) {
final double cardWidth = isSmallScreenSize(context)
// Use widget.<mixinMethod> instead of just <mixinMethod>
final double cardWidth = widget.isSmallScreenSize(context)
? 120
: isMediumScreenSize(context)
: widget.isMediumScreenSize(context)
? 135
: 150;
final double cardHeight = isSmallScreenSize(context) ? 160 : 170;
final double cardHeight = widget.isSmallScreenSize(context) ? 190 : 200;
final double iconSize = isSmallScreenSize(context)
? 50
: isMediumScreenSize(context)
? 60
: 70;
final double iconSize = widget.isSmallScreenSize(context)
? 70
: widget.isMediumScreenSize(context)
? 80
: 90;
return ConstrainedBox(
constraints: BoxConstraints(
@ -62,27 +99,44 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
(isFromScenes ?? false)
? InkWell(
onTap: () {},
child: SvgPicture.asset(
Assets.scenesPlayIcon,
fit: BoxFit.contain,
),
)
: CupertinoSwitch(
activeColor: ColorsManager.primaryColor,
value: false,
onChanged: (value) {},
)
],
),
widget.cardType != ''
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (widget.isFromScenes ?? false)
InkWell(
onTap: _handleSceneTap,
child: SvgPicture.asset(
_showTemporaryCheck
? Assets.scenesPlayIconCheck
: Assets.scenesPlayIcon,
fit: BoxFit.contain,
),
)
else if (widget.isLoading)
const SizedBox(
width: 49,
height: 20,
child: Center(
child: SizedBox(
width: 16,
height: 16,
child:
CircularProgressIndicator(strokeWidth: 2),
),
),
)
else
CupertinoSwitch(
activeColor: ColorsManager.primaryColor,
value: widget.status == 'enable',
onChanged: widget.onChanged,
)
],
)
: const SizedBox(),
InkWell(
onTap: onTap,
onTap: widget.onTap,
child: Column(
children: [
Center(
@ -97,11 +151,11 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
),
height: iconSize,
width: iconSize,
child: (isFromScenes ?? false)
? (iconInBytes != null &&
iconInBytes?.isNotEmpty == true)
child: (widget.isFromScenes ?? false)
? (widget.iconInBytes != null &&
widget.iconInBytes?.isNotEmpty == true)
? Image.memory(
iconInBytes!,
widget.iconInBytes!,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
@ -120,16 +174,18 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
width: iconSize,
fit: BoxFit.contain,
)
: (icon is String && icon.endsWith('.svg'))
: (widget.icon is String &&
widget.icon.endsWith('.svg'))
? SvgPicture.asset(
icon,
widget.icon,
fit: BoxFit.contain,
)
: Icon(
icon,
widget.icon,
color: ColorsManager.dialogBlueTitle,
size:
isSmallScreenSize(context) ? 30 : 40,
size: widget.isSmallScreenSize(context)
? 30
: 40,
),
),
),
@ -139,16 +195,17 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
child: Column(
children: [
Text(
textString,
widget.textString,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: isSmallScreenSize(context) ? 10 : 12,
fontSize:
widget.isSmallScreenSize(context) ? 10 : 12,
),
),
if (spaceName != '')
if (widget.spaceName != '')
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
@ -158,7 +215,7 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
fit: BoxFit.contain,
),
Text(
spaceName,
widget.spaceName,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
@ -166,7 +223,9 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize:
isSmallScreenSize(context) ? 10 : 12,
widget.isSmallScreenSize(context)
? 10
: 12,
),
),
],

View File

@ -12,7 +12,8 @@ class SceneApi {
static final HTTPService _httpService = HTTPService();
// //create scene
static Future<Map<String, dynamic>> createScene(CreateSceneModel createSceneModel) async {
static Future<Map<String, dynamic>> createScene(
CreateSceneModel createSceneModel) async {
try {
debugPrint('create scene model: ${createSceneModel.toMap()}');
final response = await _httpService.post(
@ -37,7 +38,8 @@ class SceneApi {
CreateAutomationModel createAutomationModel, String projectId) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.createAutomation.replaceAll('{projectId}', projectId),
path:
ApiEndpoints.createAutomation.replaceAll('{projectId}', projectId),
body: createAutomationModel.toMap(),
showServerMessage: false,
expectedResponseModel: (json) {
@ -69,7 +71,8 @@ class SceneApi {
//get scenes by community id and space id
static Future<List<ScenesModel>> getScenes(String spaceId, String communityId, String projectId,
static Future<List<ScenesModel>> getScenes(
String spaceId, String communityId, String projectId,
{showInDevice = false}) async {
try {
final response = await _httpService.get(
@ -155,7 +158,8 @@ class SceneApi {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateScene.replaceAll('{sceneId}', sceneId),
body: createSceneModel.toJson(sceneId.isNotEmpty == true ? sceneId : null),
body: createSceneModel
.toJson(sceneId.isNotEmpty == true ? sceneId : null),
expectedResponseModel: (json) {
return json;
},
@ -167,14 +171,15 @@ class SceneApi {
}
//update automation
static updateAutomation(
CreateAutomationModel createAutomationModel, String automationId, String projectId) async {
static updateAutomation(CreateAutomationModel createAutomationModel,
String automationId, String projectId) async {
try {
final response = await _httpService.put(
path: ApiEndpoints.updateAutomation
.replaceAll('{automationId}', automationId)
.replaceAll('{projectId}', projectId),
body: createAutomationModel.toJson(automationId.isNotEmpty == true ? automationId : null),
body: createAutomationModel
.toJson(automationId.isNotEmpty == true ? automationId : null),
expectedResponseModel: (json) {
return json;
},
@ -191,7 +196,8 @@ class SceneApi {
final response = await _httpService.get(
path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false,
expectedResponseModel: (json) => RoutineDetailsModel.fromMap(json['data']),
expectedResponseModel: (json) =>
RoutineDetailsModel.fromMap(json['data']),
);
return response;
} catch (e) {
@ -200,7 +206,8 @@ class SceneApi {
}
//delete Scene
static Future<bool> deleteScene({required String unitUuid, required String sceneId}) async {
static Future<bool> deleteScene(
{required String unitUuid, required String sceneId}) async {
try {
final response = await _httpService.delete(
path: ApiEndpoints.deleteScene
@ -217,7 +224,9 @@ class SceneApi {
// delete automation
static Future<bool> deleteAutomation(
{required String unitUuid, required String automationId, required String projectId}) async {
{required String unitUuid,
required String automationId,
required String projectId}) async {
try {
final response = await _httpService.delete(
path: ApiEndpoints.deleteAutomation
@ -232,7 +241,7 @@ class SceneApi {
}
}
static Future<bool> updateAutomationStatus(String automationId,
static Future<bool> updateAutomationStatus(String automationId,
AutomationStatusUpdate createAutomationEnable, String projectId) async {
try {
final response = await _httpService.patch(
@ -260,7 +269,8 @@ class SceneApi {
rethrow;
}
}
static Future<List<ScenesModel>> getAutomationByUnitId(
static Future<List<ScenesModel>> getAutomationByUnitId(
String unitId,
String communityId,
String projectId,
@ -285,5 +295,4 @@ class SceneApi {
rethrow;
}
}
}

View File

@ -407,6 +407,8 @@ class Assets {
'assets/icons/delete_space_link_icon.svg';
static const String spaceLinkIcon = 'assets/icons/space_link_icon.svg';
static const String successIcon = 'assets/icons/success_icon.svg';
static const String spaceLocationIcon = 'assets/icons/spaceLocationIcon.svg';
static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg';
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.svg';
static const String scenesPlayIconCheck =
'assets/icons/scenesPlayIconCheck.svg';
}