diff --git a/assets/icons/empty_routines.svg b/assets/icons/empty_routines.svg new file mode 100644 index 0000000..8dbf6af --- /dev/null +++ b/assets/icons/empty_routines.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/lib/features/app_layout/bloc/home_cubit.dart b/lib/features/app_layout/bloc/home_cubit.dart index f4ed015..6072e81 100644 --- a/lib/features/app_layout/bloc/home_cubit.dart +++ b/lib/features/app_layout/bloc/home_cubit.dart @@ -22,7 +22,7 @@ import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_e import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; -import 'package:syncrow_app/features/scene/view/scene_view.dart'; +import 'package:syncrow_app/features/scene/view/routines_view.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; @@ -580,7 +580,7 @@ class HomeCubit extends Cubit { create: (context) => DevicesCubit.getInstance(), child: const DevicesViewBody(), ), - const SceneView(), + const RoutinesView(), const MenuView(), ]; diff --git a/lib/features/scene/view/routines_view.dart b/lib/features/scene/view/routines_view.dart new file mode 100644 index 0000000..9a3c46b --- /dev/null +++ b/lib/features/scene/view/routines_view.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart'; +import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart'; +import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; +import 'package:syncrow_app/features/scene/helper/scene_bloc_factory.dart'; +import 'package:syncrow_app/features/scene/widgets/empty_routines_widget.dart'; +import 'package:syncrow_app/features/scene/widgets/routines_expansion_tile.dart'; +import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_header.dart'; +import 'package:syncrow_app/features/shared_widgets/app_loading_indicator.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; + +class RoutinesView extends StatelessWidget { + const RoutinesView({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => SceneBlocFactory.create( + pageType: false, + homeCubit: HomeCubit.getInstance(), + ), + child: BlocBuilder( + builder: (context, state) { + final selectedSpace = HomeCubit.getInstance().selectedSpace; + if (state is DeleteSceneSuccess) { + if (state.success) _loadScenesAndAutomations(context, selectedSpace); + } + if (state is CreateSceneWithTasks) { + if (state.success) { + _loadScenesAndAutomations(context, selectedSpace); + context.read().add(const SmartSceneClearEvent()); + } + } + return BlocListener( + listener: (context, state) { + if (state is SceneTriggerSuccess) { + context.showCustomSnackbar( + message: 'Scene ${state.sceneName} triggered successfully!', + ); + } + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SceneHeader(), + const SizedBox(height: 8), + Expanded( + child: BlocBuilder( + builder: (context, state) { + if (state is SceneLoading) { + return const AppLoadingIndicator(); + } + if (state is SceneError) { + return Center( + child: Text(state.message), + ); + } + if (state is SceneLoaded) { + final scenes = state.scenes; + final automationList = state.automationList; + final routinesIsEmpty = + scenes.isEmpty && automationList.isEmpty; + + if (routinesIsEmpty) return const EmptyRoutinesWidget(); + + return Theme( + data: Theme.of(context).copyWith( + dividerColor: Colors.transparent, + ), + child: Expanded( + child: ListView( + children: [ + RoutinesExpansionTile( + title: 'Tap to run routines', + emptyRoutinesMessage: + 'No scenes have been added yet', + routines: state.scenes, + loadingStates: state.loadingStates, + loadingSceneId: state.loadingSceneId, + ), + RoutinesExpansionTile( + title: 'Automation', + emptyRoutinesMessage: + 'No automations have been added yet', + routines: state.automationList, + loadingStates: state.loadingStates, + loadingSceneId: state.loadingSceneId, + ), + const SizedBox(height: 15), + ], + ), + ), + ); + } + return const SizedBox.shrink(); + }, + ), + ), + ], + ), + ); + }, + ), + ); + } + + void _loadScenesAndAutomations(BuildContext context, SpaceModel? selectedSpace) { + context.read() + ..add(LoadScenes(selectedSpace!.id, selectedSpace, showInDevice: false)) + ..add(LoadAutomation(selectedSpace.id, selectedSpace.community.uuid)); + } +} diff --git a/lib/features/scene/widgets/empty_routines_widget.dart b/lib/features/scene/widgets/empty_routines_widget.dart new file mode 100644 index 0000000..e572bdc --- /dev/null +++ b/lib/features/scene/widgets/empty_routines_widget.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class EmptyRoutinesWidget extends StatelessWidget { + const EmptyRoutinesWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Expanded( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + spacing: 16, + children: [ + SvgPicture.asset( + 'assets/icons/empty_routines.svg', + colorFilter: ColorFilter.mode( + ColorsManager.textGray, + BlendMode.srcIn, + ), + ), + BodyMedium( + text: 'No Routines yet', + fontColor: ColorsManager.textGray, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/scene/widgets/routines_expansion_tile.dart b/lib/features/scene/widgets/routines_expansion_tile.dart new file mode 100644 index 0000000..1a8abea --- /dev/null +++ b/lib/features/scene/widgets/routines_expansion_tile.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/scene/model/scenes_model.dart'; +import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_grid_view.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class RoutinesExpansionTile extends StatelessWidget { + const RoutinesExpansionTile({ + super.key, + required this.title, + required this.emptyRoutinesMessage, + required this.routines, + required this.loadingStates, + this.loadingSceneId, + }); + final String title; + final String emptyRoutinesMessage; + final List routines; + final Map loadingStates; + final String? loadingSceneId; + + @override + Widget build(BuildContext context) { + return ExpansionTile( + tilePadding: const EdgeInsets.symmetric( + horizontal: 6, + ), + initiallyExpanded: true, + iconColor: ColorsManager.grayColor, + title: BodyMedium( + text: title, + ), + childrenPadding: EdgeInsetsDirectional.only(bottom: 10), + children: [ + Visibility( + visible: routines.isNotEmpty, + replacement: Center( + child: BodyMedium( + text: emptyRoutinesMessage, + textAlign: TextAlign.center, + ), + ), + child: SceneGrid( + scenes: routines, + loadingSceneId: loadingSceneId, + disablePlayButton: false, + loadingStates: loadingStates, + ), + ), + ], + ); + } +} diff --git a/lib/features/scene/widgets/scene_view_widget/scene_header.dart b/lib/features/scene/widgets/scene_view_widget/scene_header.dart index 1be7542..cf79681 100644 --- a/lib/features/scene/widgets/scene_view_widget/scene_header.dart +++ b/lib/features/scene/widgets/scene_view_widget/scene_header.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; - import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; - import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; class SceneHeader extends StatelessWidget { @@ -19,10 +17,6 @@ class SceneHeader extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - // SizedBox(height: 20), - // BodySmall( - // text: StringsManager.tapToRunRoutine, - // ), ], ); } diff --git a/lib/features/shared_widgets/default_scaffold.dart b/lib/features/shared_widgets/default_scaffold.dart index a88e828..5832e31 100644 --- a/lib/features/shared_widgets/default_scaffold.dart +++ b/lib/features/shared_widgets/default_scaffold.dart @@ -59,13 +59,16 @@ class DefaultScaffold extends StatelessWidget { height: height ?? MediaQuery.sizeOf(context).height, padding: padding ?? const EdgeInsets.symmetric(horizontal: Constants.defaultPadding), - decoration: const BoxDecoration( - image: DecorationImage( + decoration: BoxDecoration( + image: const DecorationImage( + colorFilter: ColorFilter.mode( + ColorsManager.backgroundGrey, + BlendMode.srcIn, + ), image: AssetImage( Assets.assetsImagesBackground, ), fit: BoxFit.cover, - opacity: 0.4, ), ), child: SafeArea(child: child), diff --git a/lib/utils/resource_manager/color_manager.dart b/lib/utils/resource_manager/color_manager.dart index 052d0e0..fc792a8 100644 --- a/lib/utils/resource_manager/color_manager.dart +++ b/lib/utils/resource_manager/color_manager.dart @@ -35,6 +35,7 @@ abstract class ColorsManager { static const Color blueColor1 = Color(0xff0A7AFF); static const Color grayButtonColors = Color(0xffCCCCCC); static const Color blueButton = Color(0xff85BDFF); + static const Color backgroundGrey = Color(0xFFF3F3F3); } //background: #F5F5F5;background: #85BDFF;