diff --git a/assets/icons/scenesPlayIconCheck.svg b/assets/icons/scenesPlayIconCheck.svg new file mode 100644 index 00000000..9e81869a --- /dev/null +++ b/assets/icons/scenesPlayIconCheck.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/main.dart b/lib/main.dart index f2f640e4..975578cf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -33,7 +33,6 @@ Future 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( diff --git a/lib/main_dev.dart b/lib/main_dev.dart index 9d00ebf7..76261c6a 100644 --- a/lib/main_dev.dart +++ b/lib/main_dev.dart @@ -55,6 +55,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MultiBlocProvider( providers: [ + BlocProvider( create: (context) => HomeBloc()..add(const FetchUserInfo())), BlocProvider( diff --git a/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_bloc.dart b/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_bloc.dart deleted file mode 100644 index b76b06e0..00000000 --- a/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_bloc.dart +++ /dev/null @@ -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 { - AutomationSceneTriggerBloc() : super(AutomationSceneInitial()) { - // on(_onLoadScenes); - // on(_onLoadAutomation); - on(_onSceneTrigger); - on(_onUpdateAutomationStatus); - } - - List scenes = []; - List automationList = []; - - // Future _onLoadScenes(LoadScenes event, Emitter 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 _onLoadAutomation( - // LoadAutomation event, Emitter 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 _onSceneTrigger( - SceneTrigger event, Emitter 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 _onUpdateAutomationStatus( - UpdateAutomationStatus event, Emitter emit) async { - final currentState = state; - if (currentState is AutomationSceneLoaded) { - final newLoadingStates = - Map.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')); - } - } - } -} diff --git a/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_event.dart b/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_event.dart deleted file mode 100644 index b34eb5ed..00000000 --- a/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_event.dart +++ /dev/null @@ -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 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 get props => [unitId, showInDevice]; -} - -class AutomationSceneAutomation extends AutomationSceneTriggerEvent { - final String unitId; - final String communityId; - - - const AutomationSceneAutomation(this.unitId, this.communityId); - - @override - List get props => [unitId, communityId]; -} - -class SceneTrigger extends AutomationSceneTriggerEvent { - final String sceneId; - final String name; - - const SceneTrigger(this.sceneId, this.name); - - @override - List 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 get props => [automationStatusUpdate]; -} diff --git a/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_status.dart b/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_status.dart deleted file mode 100644 index 6753372f..00000000 --- a/lib/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_status.dart +++ /dev/null @@ -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 get props => []; -} - -class AutomationSceneInitial extends AutomationSceneTriggerStatus {} - -class AutomationSceneLoading extends AutomationSceneTriggerStatus {} - -class AutomationSceneLoaded extends AutomationSceneTriggerStatus { - final List scenes; - final List automationList; - final String? loadingSceneId; - final Map loadingStates; - - const AutomationSceneLoaded(this.scenes, this.automationList, - {this.loadingSceneId, this.loadingStates = const {}}); - - @override - List get props => - [scenes, loadingSceneId, automationList, loadingStates]; -} - -class AutomationSceneError extends AutomationSceneTriggerStatus { - final String message; - - const AutomationSceneError({required this.message}); - - @override - List get props => [message]; -} - -class SceneTriggerSuccess extends AutomationSceneTriggerStatus { - final String sceneName; - - const SceneTriggerSuccess(this.sceneName); - - @override - List get props => [sceneName]; -} - -class UpdateAutomationStatusLoading extends AutomationSceneTriggerStatus { - const UpdateAutomationStatusLoading(); -} diff --git a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart index 361810c0..d4f61bae 100644 --- a/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart +++ b/lib/pages/routines/bloc/routine_bloc/routine_bloc.dart @@ -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 { on(_triggerSwitchTabsEvent); on(_createNewRoutineViewEvent); on(_resetErrorMessage); + on(_onSceneTrigger); + on(_onUpdateAutomationStatus); } FutureOr _triggerSwitchTabsEvent( @@ -1269,4 +1272,79 @@ class RoutineBloc extends Bloc { )); } } + + Future _onSceneTrigger( + SceneTrigger event, Emitter 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 _onUpdateAutomationStatus( + UpdateAutomationStatus event, Emitter 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()}', + )); + } + } } diff --git a/lib/pages/routines/bloc/routine_bloc/routine_event.dart b/lib/pages/routines/bloc/routine_bloc/routine_event.dart index c1b2a23f..b532235d 100644 --- a/lib/pages/routines/bloc/routine_bloc/routine_event.dart +++ b/lib/pages/routines/bloc/routine_bloc/routine_event.dart @@ -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 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 get props => [automationStatusUpdate]; +} diff --git a/lib/pages/routines/bloc/routine_bloc/routine_state.dart b/lib/pages/routines/bloc/routine_bloc/routine_state.dart index 4dc26a01..228d6af5 100644 --- a/lib/pages/routines/bloc/routine_bloc/routine_state.dart +++ b/lib/pages/routines/bloc/routine_bloc/routine_state.dart @@ -1,6 +1,7 @@ part of 'routine_bloc.dart'; class RoutineState extends Equatable { + final String? loadingSceneId; final List> ifItems; final List> thenItems; final List> availableCards; @@ -25,6 +26,7 @@ class RoutineState extends Equatable { // final String? automationActionExecutor; final bool routineTab; final bool createRoutineView; + final Set? 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 {}, // Initialize with empty set + this.loadingSceneId, this.devices = const [], // this.automationActionExecutor, this.routineTab = false, this.createRoutineView = false}); RoutineState copyWith({ + String? loadingSceneId, + Set? loadingAutomationIds, List>? ifItems, List>? thenItems, List? 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 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? scenesOrAutomation; + const SceneLoaded({this.scenesOrAutomation}); + @override + List get props => [ + scenesOrAutomation, + ]; +} + +class SceneError extends RoutineState { + final String message; + + const SceneError({required this.message}); + + @override + List get props => [message]; +} + +class SceneTriggerSuccess extends RoutineState { + final String sceneName; + + const SceneTriggerSuccess(this.sceneName); + + @override + List get props => [sceneName]; +} + +class UpdateAutomationStatusLoading extends RoutineState { + const UpdateAutomationStatusLoading(); +} diff --git a/lib/pages/routines/models/routine_model.dart b/lib/pages/routines/models/routine_model.dart index bb3e117b..2f7c2a24 100644 --- a/lib/pages/routines/models/routine_model.dart +++ b/lib/pages/routines/models/routine_model.dart @@ -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, }; } diff --git a/lib/pages/routines/view/routines_view.dart b/lib/pages/routines/view/routines_view.dart index de627a43..3c140a4e 100644 --- a/lib/pages/routines/view/routines_view.dart +++ b/lib/pages/routines/view/routines_view.dart @@ -60,7 +60,13 @@ class _RoutinesViewState extends State { height: 10, ), RoutineViewCard( - isFromScenes: false, + isLoading: false, + onChanged: (v) {}, + status: '', + spaceId: '', + automationId: '', + communityId: '', + sceneId: '', cardType: '', spaceName: '', onTap: () { diff --git a/lib/pages/routines/widgets/main_routine_view/fetch_routine_scenes_automation.dart b/lib/pages/routines/widgets/main_routine_view/fetch_routine_scenes_automation.dart index 08f4318e..809c462c 100644 --- a/lib/pages/routines/widgets/main_routine_view/fetch_routine_scenes_automation.dart +++ b/lib/pages/routines/widgets/main_routine_view/fetch_routine_scenes_automation.dart @@ -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 ), 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(context).add( - const CreateNewRoutineViewEvent( - createRoutineView: true), - ); - context.read().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().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(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().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 ), 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(context).add( - const CreateNewRoutineViewEvent( - createRoutineView: true), - ); - context.read().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(context) + context.read().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(context).add( + const CreateNewRoutineViewEvent( + createRoutineView: true), ); - }, - textString: state.automations[index].name, - icon: state.automations[index].icon ?? - Assets.automation, - ), - ), - ), + context.read().add( + GetAutomationDetails( + automationId: + state.automations[index].id, + isAutomation: true, + isUpdate: true), + ); + }, + textString: state.automations[index].name, + icon: state.automations[index].icon ?? + Assets.automation, + ), + ); + }), ), ], ), diff --git a/lib/pages/routines/widgets/main_routine_view/routine_view_card.dart b/lib/pages/routines/widgets/main_routine_view/routine_view_card.dart index fe94cf83..345a86c8 100644 --- a/lib/pages/routines/widgets/main_routine_view/routine_view_card.dart +++ b/lib/pages/routines/widgets/main_routine_view/routine_view_card.dart @@ -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 createState() => _RoutineViewCardState(); +} + +class _RoutineViewCardState extends State { + 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. instead of just + 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, ), ), ], diff --git a/lib/services/routines_api.dart b/lib/services/routines_api.dart index cbef2bc3..eaa09e27 100644 --- a/lib/services/routines_api.dart +++ b/lib/services/routines_api.dart @@ -12,7 +12,8 @@ class SceneApi { static final HTTPService _httpService = HTTPService(); // //create scene - static Future> createScene(CreateSceneModel createSceneModel) async { + static Future> 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> getScenes(String spaceId, String communityId, String projectId, + static Future> 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 deleteScene({required String unitUuid, required String sceneId}) async { + static Future 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 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 updateAutomationStatus(String automationId, + static Future updateAutomationStatus(String automationId, AutomationStatusUpdate createAutomationEnable, String projectId) async { try { final response = await _httpService.patch( @@ -260,7 +269,8 @@ class SceneApi { rethrow; } } - static Future> getAutomationByUnitId( + + static Future> getAutomationByUnitId( String unitId, String communityId, String projectId, @@ -285,5 +295,4 @@ class SceneApi { rethrow; } } - } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 5d462f28..0e7b0bd2 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -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'; }