Compare commits

..

35 Commits

Author SHA1 Message Date
f2412aa867 refactor: extract device fetching logic into a separate method for improved readability 2025-04-16 16:10:36 +03:00
e4768c95aa refactor: rename tab change events and states for consistency, and to match blocs naming convention. 2025-04-16 16:07:30 +03:00
f25b4dbf6d implement SceneDevicesBodyTabBar for improved tab management in SceneDevicesBody. 2025-04-16 15:57:21 +03:00
9eff9ab371 refactor: consolidate device loading and error handling in SceneDevicesBody and implement SceneDevicesList widget 2025-04-16 15:46:56 +03:00
a2e68d6194 fix: restore title in DefaultScaffold for SceneRoomsTabBarDevicesView 2025-04-16 15:25:23 +03:00
94c94b170f fix: correct order of dispose method calls in SceneRoomsTabBarDevicesView 2025-04-16 15:24:47 +03:00
253cf15559 refactor: simplify SceneDevicesBody widget structure and improve loading indicator handling 2025-04-16 15:24:37 +03:00
443eea9421 SP-1397 2025-04-16 15:11:43 +03:00
87a2c08f64 Merge pull request #82 from SyncrowIOT/SP-1416
added client token login
2025-04-16 12:35:31 +04:00
dd66e7c747 added client token login 2025-04-16 12:02:19 +04:00
7af61d2f65 added client id and secret 2025-04-16 11:34:24 +04:00
f37eacb0ee Merge pull request #81 from SyncrowIOT/feat/update-additional-device-endpoints
updated endpoint for device
2025-04-14 15:11:36 +03:00
c7dcc297aa fix conflict 2025-04-14 15:11:02 +03:00
aefe5ad081 Merge branch 'dev' into feat/update-additional-device-endpoints 2025-04-14 15:10:42 +03:00
8d9af8519d Merge pull request #78 from SyncrowIOT/SP-1246
updated device endpoint
2025-04-14 14:58:09 +03:00
b1d52937c9 Merge branch 'dev' of https://github.com/SyncrowIOT/syncrow-app into feat/update-additional-device-endpoints 2025-04-14 10:31:09 +04:00
5fedf37421 updated endpoint for device 2025-04-14 10:30:48 +04:00
ccde857c29 Merge pull request #80 from SyncrowIOT/SP-1398-FE-in-the-wizerd-the-batch-control-title-doesn-t-represent-the-the-accurate-device-name
SP-1398-FE-in-the-wizerd-the-batch-control-title-doesn-t-represent-the-the-accurate-device-name
2025-04-13 16:23:49 +03:00
b2a8086e0e Merge pull request #79 from SyncrowIOT/sp-1268-rework-v2
Sp 1268 rework v2
2025-04-13 16:20:10 +03:00
408e78962c SP-1398 2025-04-13 16:18:49 +03:00
dcdbc02ca0 Adds AppLoadingIndicator widget and replaces CircularProgressIndicator in SceneView. 2025-04-13 15:50:52 +03:00
31025e9176 Refactors SceneView constructor and improves conditional rendering for automation list 2025-04-13 15:49:13 +03:00
8219de6821 Injects SceneBloc using the new factory. 2025-04-13 15:28:44 +03:00
fbdf3817ab Created a factory helper to create a SceneBloc. 2025-04-13 15:25:48 +03:00
17422edd0d sp-1268-rework-v2. 2025-04-13 15:21:20 +03:00
4c8f2c72df Merge branch 'dev' of https://github.com/SyncrowIOT/syncrow-app into SP-1246 2025-04-03 10:47:27 +04:00
fb867e5df3 updated device endpoint 2025-04-03 10:47:09 +04:00
9472390284 Merge pull request #76 from SyncrowIOT/bugifx/empty-subspace-routine-creation
fixed issue on empty subspace
2025-03-28 11:38:14 +04:00
731ba0f3d6 Merge pull request #77 from SyncrowIOT/SP-1268-FE-Implement-UX-Behavior-for-No-Tab-to-Run-Scene-in-Device-Screen
Sp 1268 fe implement ux behavior for no tab to run scene in device screen
2025-03-26 15:24:34 +03:00
56407c6426 indentation and trailing commas. 2025-03-26 13:49:54 +03:00
02d61ca0bb Refactor DevicesViewBody and SceneView to improve widget structure and replace CreateUnitWidget with EmptyDevicesWidget for better handling of empty states. 2025-03-26 13:44:49 +03:00
99ee4b9878 Add EmptyDevicesWidget to display message when no routines are available. 2025-03-26 13:34:07 +03:00
19edd0a275 Added /android/app/.cxx/ to .gitIgnore, because they're auto generated files that dont need to be checked into source control. 2025-03-26 13:14:23 +03:00
ef5e7c3154 fixed issue on empty subspace 2025-03-25 14:15:14 +04:00
80dd0f696f Merge pull request #75 from SyncrowIOT/SP-1245
updated endpoint for automation
2025-03-17 09:34:46 +04:00
23 changed files with 579 additions and 363 deletions

View File

@ -1,3 +1,5 @@
ENV_NAME=development ENV_NAME=development
BASE_URL=https://syncrow-dev.azurewebsites.net BASE_URL=https://syncrow-dev.azurewebsites.net
PROJECT_ID=0e62577c-06fa-41b9-8a92-99a21fbaf51c PROJECT_ID=0e62577c-06fa-41b9-8a92-99a21fbaf51c
CLIENT_ID=c024573d191a327ce74db7d4502fdc29
CLIENT_SECRET=fec6ccbc33664f94a3b84a45c7cceef3f3ebd1613ef4307db8946ede530cd1ed

1
.gitignore vendored
View File

@ -42,3 +42,4 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
/android/app/.cxx/

View File

@ -1,5 +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:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:syncrow_app/features/auth/model/login_with_email_model.dart'; import 'package:syncrow_app/features/auth/model/login_with_email_model.dart';
import 'package:syncrow_app/features/auth/model/signup_model.dart'; import 'package:syncrow_app/features/auth/model/signup_model.dart';
@ -217,9 +218,22 @@ class AuthCubit extends Cubit<AuthState> {
signUp() async { signUp() async {
emit(AuthLoginLoading()); emit(AuthLoginLoading());
final response; final response;
final clientId = dotenv.env['CLIENT_ID'] ?? '';
final clientSecret = dotenv.env['CLIENT_SECRET'] ?? '';
try { try {
List<String> userFullName = fullName.split(' '); List<String> userFullName = fullName.split(' ');
final clientToken = await AuthenticationAPI.fetchClientToken(
clientId: clientId,
clientSecret: clientSecret,
);
final accessToken = clientToken['accessToken'];
response = await AuthenticationAPI.signUp( response = await AuthenticationAPI.signUp(
accessToken: accessToken,
model: SignUpModel( model: SignUpModel(
hasAcceptedAppAgreement: true, hasAcceptedAppAgreement: true,
email: email.toLowerCase(), email: email.toLowerCase(),

View File

@ -1,8 +1,8 @@
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:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart'; import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
import 'package:syncrow_app/features/devices/view/widgets/all_devices.dart'; import 'package:syncrow_app/features/devices/view/widgets/all_devices.dart';
import 'package:syncrow_app/features/devices/view/widgets/room_page.dart'; import 'package:syncrow_app/features/devices/view/widgets/room_page.dart';
import 'package:syncrow_app/features/devices/view/widgets/rooms_slider.dart'; import 'package:syncrow_app/features/devices/view/widgets/rooms_slider.dart';
@ -15,7 +15,7 @@ import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart'; import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
class DevicesViewBody extends StatelessWidget { class DevicesViewBody extends StatelessWidget {
const DevicesViewBody({Key? key}) : super(key: key); const DevicesViewBody({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<HomeCubit, HomeState>( return BlocBuilder<HomeCubit, HomeState>(
@ -80,10 +80,7 @@ class DevicesViewBody extends StatelessWidget {
), ),
], ],
), ),
SizedBox( const SceneView(pageType: true),
height: MediaQuery.of(context).size.height * 0.1,
child: const SceneView(pageType: true),
),
const SizedBox(height: 20), const SizedBox(height: 20),
const RoomsSlider(), const RoomsSlider(),
const SizedBox(height: 10), const SizedBox(height: 10),

View File

@ -8,8 +8,7 @@ import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
class GarageList extends StatelessWidget { class GarageList extends StatelessWidget {
const GarageList( const GarageList({super.key, required this.garageList, required this.allSwitches});
{super.key, required this.garageList, required this.allSwitches});
final List<GroupGarageModel> garageList; final List<GroupGarageModel> garageList;
final bool allSwitches; final bool allSwitches;
@ -23,43 +22,42 @@ class GarageList extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: 10), const SizedBox(height: 10),
const BodySmall(text: 'All Lights'), const BodySmall(text: 'All Garages'),
const SizedBox(height: 5), const SizedBox(height: 5),
DevicesDefaultSwitch( DevicesDefaultSwitch(
off: 'OFF', off: 'Close',
on: 'ON', on: 'Open',
switchValue: allSwitches, switchValue: allSwitches,
action: () { action: () => BlocProvider.of<GarageDoorBloc>(context).add(
BlocProvider.of<GarageDoorBloc>(context) GroupAllOnEvent(),
.add(GroupAllOnEvent()); ),
}, secondAction: () => BlocProvider.of<GarageDoorBloc>(context).add(
secondAction: () { GroupAllOffEvent(),
BlocProvider.of<GarageDoorBloc>(context) ),
.add(GroupAllOffEvent());
},
), ),
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(0), padding: EdgeInsetsDirectional.zero,
itemCount: garageList.length, itemCount: garageList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final garageDoor = garageList[index];
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 10), const SizedBox(height: 10),
BodySmall(text: garageList[index].deviceName), BodySmall(text: garageDoor.deviceName),
const SizedBox(height: 5), const SizedBox(height: 5),
DevicesDefaultSwitch( DevicesDefaultSwitch(
off: 'OFF', off: 'Close',
on: 'ON', on: 'Open',
switchValue: garageList[index].firstSwitch, switchValue: garageDoor.firstSwitch,
action: () { action: () => BlocProvider.of<GarageDoorBloc>(context).add(
BlocProvider.of<GarageDoorBloc>(context).add(
ChangeFirstWizardSwitchStatusEvent( ChangeFirstWizardSwitchStatusEvent(
value: garageList[index].firstSwitch, value: garageDoor.firstSwitch,
deviceId: garageList[index].deviceId)); deviceId: garageDoor.deviceId,
}, ),
),
), ),
], ],
); );

View File

@ -8,7 +8,11 @@ import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart';
class WHList extends StatelessWidget { class WHList extends StatelessWidget {
const WHList({super.key, required this.whList, required this.allSwitches}); const WHList({
required this.whList,
required this.allSwitches,
super.key,
});
final List<GroupWHModel> whList; final List<GroupWHModel> whList;
final bool allSwitches; final bool allSwitches;
@ -22,43 +26,42 @@ class WHList extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: 10), const SizedBox(height: 10),
const BodySmall(text: 'All Lights'), const BodySmall(text: 'All Water Heaters'),
const SizedBox(height: 5), const SizedBox(height: 5),
DevicesDefaultSwitch( DevicesDefaultSwitch(
off: 'OFF', off: 'OFF',
on: 'ON', on: 'ON',
switchValue: allSwitches, switchValue: allSwitches,
action: () { action: () => context.read<WaterHeaterBloc>().add(
BlocProvider.of<WaterHeaterBloc>(context) GroupAllOnEvent(),
.add(GroupAllOnEvent()); ),
}, secondAction: () => context.read<WaterHeaterBloc>().add(
secondAction: () { GroupAllOffEvent(),
BlocProvider.of<WaterHeaterBloc>(context) ),
.add(GroupAllOffEvent());
},
), ),
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(0), padding: EdgeInsetsDirectional.zero,
itemCount: whList.length, itemCount: whList.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final waterHeater = whList[index];
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 10), const SizedBox(height: 10),
BodySmall(text: whList[index].deviceName), BodySmall(text: waterHeater.deviceName),
const SizedBox(height: 5), const SizedBox(height: 5),
DevicesDefaultSwitch( DevicesDefaultSwitch(
off: 'OFF', off: 'OFF',
on: 'ON', on: 'ON',
switchValue: whList[index].firstSwitch, switchValue: waterHeater.firstSwitch,
action: () { action: () => context.read<WaterHeaterBloc>().add(
BlocProvider.of<WaterHeaterBloc>(context).add(
ChangeFirstWizardSwitchStatusEvent( ChangeFirstWizardSwitchStatusEvent(
value: whList[index].firstSwitch, value: waterHeater.firstSwitch,
deviceId: whList[index].deviceId)); deviceId: waterHeater.deviceId,
}, ),
),
), ),
], ],
); );

View File

@ -1,5 +1,3 @@
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_event.dart'; import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_event.dart';
@ -7,19 +5,31 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart'; import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart';
class TabBarBloc extends Bloc<TabBarEvent, TabBarState> { class TabBarBloc extends Bloc<TabBarEvent, TabBarState> {
final DeviceManagerBloc deviceManagerBloc; TabBarBloc(this.deviceManagerBloc) : super(const TabBarInitialState()) {
TabBarBloc(this.deviceManagerBloc) : super(const Initial()) { on<TabBarTabChangedEvent>(_onTabBarTabChangedEvent);
on<TabChanged>(_handleTabChanged);
} }
FutureOr<void> _handleTabChanged( final DeviceManagerBloc deviceManagerBloc;
TabChanged event, Emitter<TabBarState> emit) {
void _onTabBarTabChangedEvent(
TabBarTabChangedEvent event,
Emitter<TabBarState> emit,
) {
_getDevices(event);
emit(
TabBarTabSelectedState(
roomId: event.roomId,
selectedTabIndex: event.selectedIndex,
),
);
}
void _getDevices(TabBarTabChangedEvent event) {
if (event.roomId == "-1") { if (event.roomId == "-1") {
deviceManagerBloc.add(FetchAllDevices()); deviceManagerBloc.add(FetchAllDevices());
} else { } else {
deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId, event.unit)); deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId, event.unit));
} }
emit(TabSelected(
roomId: event.roomId, selectedTabIndex: event.selectedIndex));
} }
} }

View File

@ -4,10 +4,14 @@ abstract class TabBarEvent {
const TabBarEvent(); const TabBarEvent();
} }
class TabChanged extends TabBarEvent { class TabBarTabChangedEvent extends TabBarEvent {
const TabBarTabChangedEvent({
required this.selectedIndex,
required this.roomId,
required this.unit,
});
final int selectedIndex; final int selectedIndex;
final String roomId; final String roomId;
final SpaceModel unit; final SpaceModel unit;
const TabChanged(
{required this.selectedIndex, required this.roomId, required this.unit});
} }

View File

@ -2,12 +2,16 @@ abstract class TabBarState {
const TabBarState(); const TabBarState();
} }
class Initial extends TabBarState { class TabBarInitialState extends TabBarState {
const Initial(); const TabBarInitialState();
} }
class TabSelected extends TabBarState { class TabBarTabSelectedState extends TabBarState {
const TabBarTabSelectedState({
required this.roomId,
required this.selectedTabIndex,
});
final int selectedTabIndex; final int selectedTabIndex;
final String roomId; final String roomId;
const TabSelected({required this.roomId, required this.selectedTabIndex});
} }

View File

@ -0,0 +1,48 @@
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.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';
abstract final class SceneBlocFactory {
static SceneBloc create({
required bool pageType,
required HomeCubit homeCubit,
}) {
final selectedSpace = homeCubit.selectedSpace;
final defaultSpace = SpaceModel(
id: '-1',
name: '',
community: Community(
uuid: '-1',
name: '',
),
);
final spaceId = selectedSpace?.id ?? defaultSpace.id;
final space = selectedSpace ?? defaultSpace;
final communityUuid =
selectedSpace?.community.uuid ?? defaultSpace.community.uuid;
final sceneBloc = SceneBloc();
sceneBloc.add(
LoadScenes(
spaceId,
space,
showInDevice: pageType,
),
);
if (!pageType) {
sceneBloc.add(
LoadAutomation(
spaceId,
communityUuid,
),
);
}
return sceneBloc;
}
}

View File

@ -20,69 +20,69 @@ class SceneRoomsTabBarDevicesView extends StatefulWidget {
_SceneRoomsTabBarDevicesViewState(); _SceneRoomsTabBarDevicesViewState();
} }
class _SceneRoomsTabBarDevicesViewState class _SceneRoomsTabBarDevicesViewState extends State<SceneRoomsTabBarDevicesView>
extends State<SceneRoomsTabBarDevicesView>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late final TabController _tabController; late final TabController _tabController;
List<SubSpaceModel>? rooms = [];
late final SpaceModel selectedSpace; late final SpaceModel selectedSpace;
var rooms = <SubSpaceModel>[];
@override @override
void initState() { void initState() {
super.initState();
selectedSpace = HomeCubit.getInstance().selectedSpace!; selectedSpace = HomeCubit.getInstance().selectedSpace!;
rooms = List.from(HomeCubit.getInstance().selectedSpace?.subspaces ?? []);
if (rooms != null) { rooms = List.from(selectedSpace.subspaces);
if (rooms![0].id != '-1') { final defaultSubSpaceModel = SubSpaceModel(
rooms?.insert(
0,
SubSpaceModel(
name: 'All Devices', name: 'All Devices',
devices: context.read<DevicesCubit>().allDevices, devices: context.read<DevicesCubit>().allDevices,
id: '-1', id: '-1',
),
); );
if (rooms.isNotEmpty) {
final isFirstRoomIdValid = rooms[0].id != '-1';
if (isFirstRoomIdValid) {
rooms.insert(0, defaultSubSpaceModel);
} }
} else {
rooms = [defaultSubSpaceModel];
} }
_tabController = _tabController = TabController(length: rooms.length, vsync: this);
TabController(length: rooms!.length, vsync: this, initialIndex: 0);
_tabController.addListener(_handleTabSwitched); _tabController.addListener(_handleTabSwitched);
super.initState();
}
void _handleTabSwitched() {
if (_tabController.indexIsChanging) {
final value = _tabController.index;
/// select tab
context.read<TabBarBloc>().add(TabChanged(
selectedIndex: value,
roomId: rooms?[value].id ?? '',
unit: selectedSpace));
return;
}
} }
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
_tabController.dispose();
_tabController.removeListener(() {}); _tabController.removeListener(() {});
_tabController.dispose();
}
void _handleTabSwitched() {
if (_tabController.indexIsChanging) {
final index = _tabController.index;
context.read<TabBarBloc>().add(
TabBarTabChangedEvent(
selectedIndex: index,
roomId: rooms[index].id ?? '',
unit: selectedSpace,
),
);
return;
}
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DefaultScaffold( return DefaultScaffold(
title: StringsManager.createScene, padding: EdgeInsetsDirectional.zero,
padding: EdgeInsets.zero,
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () => navigateToRoute(context, Routes.sceneTasksRoute),
navigateToRoute(context, Routes.sceneTasksRoute); icon: const Icon(Icons.arrow_back_ios),
},
icon: const Icon(
Icons.arrow_back_ios,
),
), ),
title: StringsManager.createScene,
child: SceneDevicesBody(tabController: _tabController, rooms: rooms), child: SceneDevicesBody(tabController: _tabController, rooms: rooms),
); );
} }

View File

@ -1,108 +1,70 @@
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_app/features/app_layout/bloc/home_cubit.dart'; import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/devices/view/widgets/scene_listview.dart'; import 'package:syncrow_app/features/devices/view/widgets/scene_listview.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.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_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.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/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_devices_widget.dart';
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_grid_view.dart'; import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_grid_view.dart';
import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_header.dart'; import 'package:syncrow_app/features/scene/widgets/scene_view_widget/scene_header.dart';
import 'package:syncrow_app/features/shared_widgets/create_unit.dart'; import 'package:syncrow_app/features/shared_widgets/app_loading_indicator.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SceneView extends StatelessWidget { class SceneView extends StatelessWidget {
const SceneView({
this.pageType = false,
super.key,
});
final bool pageType; final bool pageType;
const SceneView({super.key, this.pageType = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) { create: (context) => SceneBlocFactory.create(
if (pageType) { pageType: pageType,
return SceneBloc() homeCubit: HomeCubit.getInstance(),
..add(LoadScenes( ),
HomeCubit.getInstance().selectedSpace?.id ?? '',
HomeCubit.getInstance().selectedSpace ??
SpaceModel(
id: '-1',
name: '',
community: Community(
uuid: '-1',
name: '',
)),
showInDevice: pageType));
} else {
return SceneBloc()
..add(LoadScenes(
HomeCubit.getInstance().selectedSpace?.id ?? '',
HomeCubit.getInstance().selectedSpace ??
SpaceModel(
id: '-1',
name: '',
community: Community(
uuid: '-1',
name: '',
)),
showInDevice: pageType))
..add(LoadAutomation(
HomeCubit.getInstance().selectedSpace?.id ?? '',
HomeCubit.getInstance().selectedSpace?.community.uuid ?? ''));
}
},
child: BlocBuilder<CreateSceneBloc, CreateSceneState>( child: BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) { builder: (context, state) {
final selectedSpace = HomeCubit.getInstance().selectedSpace;
if (state is DeleteSceneSuccess) { if (state is DeleteSceneSuccess) {
if (state.success) { if (state.success) _loadScenesAndAutomations(context, selectedSpace);
BlocProvider.of<SceneBloc>(context).add(LoadScenes(
HomeCubit.getInstance().selectedSpace!.id,
HomeCubit.getInstance().selectedSpace!,
showInDevice: pageType));
BlocProvider.of<SceneBloc>(context).add(LoadAutomation(
HomeCubit.getInstance().selectedSpace!.id,
HomeCubit.getInstance().selectedSpace!.community.uuid));
}
} }
if (state is CreateSceneWithTasks) { if (state is CreateSceneWithTasks) {
if (state.success == true) { if (state.success) {
BlocProvider.of<SceneBloc>(context).add(LoadScenes( _loadScenesAndAutomations(context, selectedSpace);
HomeCubit.getInstance().selectedSpace!.id, context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
HomeCubit.getInstance().selectedSpace!,
showInDevice: pageType));
BlocProvider.of<SceneBloc>(context).add(LoadAutomation(
HomeCubit.getInstance().selectedSpace!.id,
HomeCubit.getInstance().selectedSpace!.community.uuid));
context
.read<SmartSceneSelectBloc>()
.add(const SmartSceneClearEvent());
} }
} }
return BlocListener<SceneBloc, SceneState>( return BlocListener<SceneBloc, SceneState>(
listener: (context, state) { listener: (context, state) {
if (state is SceneTriggerSuccess) { if (state is SceneTriggerSuccess) {
context.showCustomSnackbar( context.showCustomSnackbar(
message: message: 'Scene ${state.sceneName} triggered successfully!',
'Scene ${state.sceneName} triggered successfully!'); );
} }
}, },
child: HomeCubit.getInstance().spaces.isEmpty child: HomeCubit.getInstance().spaces.isEmpty
? const CreateUnitWidget() ? const EmptyDevicesWidget()
: Column( : Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
if (pageType == false) const SceneHeader(), if (!pageType) ...[
if (pageType == false) const SizedBox(height: 8), const SceneHeader(),
const SizedBox(height: 8),
],
BlocBuilder<SceneBloc, SceneState>( BlocBuilder<SceneBloc, SceneState>(
builder: (context, state) { builder: (context, state) {
if (state is SceneLoading) { if (state is SceneLoading) {
return const Center( return const AppLoadingIndicator();
child: CircularProgressIndicator(),
);
} }
if (state is SceneError) { if (state is SceneError) {
return Center( return Center(
@ -112,90 +74,83 @@ class SceneView extends StatelessWidget {
if (state is SceneLoaded) { if (state is SceneLoaded) {
final scenes = state.scenes; final scenes = state.scenes;
final automationList = state.automationList; final automationList = state.automationList;
if (scenes.isEmpty) return const EmptyDevicesWidget();
return pageType if (pageType) {
? Expanded( return SizedBox(
height: context.height * 0.1,
child: SceneListview( child: SceneListview(
scenes: scenes, scenes: scenes,
loadingSceneId: state.loadingSceneId, loadingSceneId: state.loadingSceneId,
)) ),
: Expanded( );
}
return Theme(
data: Theme.of(context).copyWith(
dividerColor: Colors.transparent,
),
child: Expanded(
child: ListView( child: ListView(
children: [ children: [
Theme( ExpansionTile(
data: ThemeData().copyWith( tilePadding: const EdgeInsets.symmetric(
dividerColor: Colors.transparent), horizontal: 6,
child: ExpansionTile( ),
tilePadding:
const EdgeInsets.symmetric(
horizontal: 6),
initiallyExpanded: true, initiallyExpanded: true,
iconColor: ColorsManager.grayColor, iconColor: ColorsManager.grayColor,
title: const BodyMedium( title: const BodyMedium(
text: 'Tap to run routines'), text: 'Tap to run routines',
),
children: [ children: [
scenes.isNotEmpty if (scenes.isNotEmpty)
? SceneGrid( SceneGrid(
scenes: scenes, scenes: scenes,
loadingSceneId: loadingSceneId: state.loadingSceneId,
state.loadingSceneId,
disablePlayButton: false, disablePlayButton: false,
loadingStates: state loadingStates: state.loadingStates,
.loadingStates, // Add this line
) )
: const Center( else
const Center(
child: BodyMedium( child: BodyMedium(
text: text: 'No scenes have been added yet',
'No scenes have been added yet',
), ),
), ),
const SizedBox( const SizedBox(height: 10),
height: 10,
),
], ],
), ),
), ExpansionTile(
Theme(
data: ThemeData().copyWith(
dividerColor: Colors.transparent),
child: ExpansionTile(
initiallyExpanded: true, initiallyExpanded: true,
iconColor: ColorsManager.grayColor, iconColor: ColorsManager.grayColor,
tilePadding: tilePadding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric( horizontal: 6,
horizontal: 6), ),
title: const BodyMedium( title: const BodyMedium(text: 'Automation'),
text: 'Automation'),
children: [ children: [
automationList.isNotEmpty if (automationList.isNotEmpty)
? SceneGrid( SceneGrid(
scenes: automationList, scenes: automationList,
loadingSceneId: loadingSceneId: state.loadingSceneId,
state.loadingSceneId,
disablePlayButton: true, disablePlayButton: true,
loadingStates: state loadingStates: state.loadingStates,
.loadingStates, // Add this line
) )
: const Center( else
const Center(
child: BodyMedium( child: BodyMedium(
text: text:
'No automations have been added yet', 'No automations have been added yet',
), ),
), ),
const SizedBox( const SizedBox(height: 10),
height: 10,
),
], ],
), ),
), const SizedBox(height: 15),
const SizedBox(
height: 15,
),
], ],
), ),
),
); );
} }
return const SizedBox(); return const SizedBox.shrink();
}, },
), ),
], ],
@ -205,4 +160,21 @@ class SceneView extends StatelessWidget {
), ),
); );
} }
void _loadScenesAndAutomations(BuildContext context, SpaceModel? selectedSpace) {
BlocProvider.of<SceneBloc>(context)
..add(
LoadScenes(
selectedSpace!.id,
selectedSpace,
showInDevice: pageType,
),
)
..add(
LoadAutomation(
selectedSpace.id,
selectedSpace.community.uuid,
),
);
}
} }

View File

@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class EmptyDevicesWidget extends StatelessWidget {
const EmptyDevicesWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 48),
child: Text(
"No routines.\nEnable 'Show on Home Screen' to add routines",
textAlign: TextAlign.center,
style: TextStyle(
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400,
fontSize: 12,
),
),
);
}
}

View File

@ -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:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_state.dart'; import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_state.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart'; import 'package:syncrow_app/features/devices/model/subspace_model.dart';
@ -8,60 +7,44 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart'; import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_state.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.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/model/scene_settings_route_arguments.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart'; import 'package:syncrow_app/features/scene/widgets/scene_devices/scene_devices_body_tab_bar.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/scene/widgets/scene_devices/scene_devices_list.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/features/shared_widgets/app_loading_indicator.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SceneDevicesBody extends StatelessWidget { class SceneDevicesBody extends StatelessWidget {
const SceneDevicesBody({ const SceneDevicesBody({
super.key, required this.tabController,
required TabController tabController,
required this.rooms, required this.rooms,
}) : _tabController = tabController; super.key,
});
final TabController _tabController; final TabController tabController;
final List<SubSpaceModel>? rooms; final List<SubSpaceModel> rooms;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isAutomationDeviceStatus =
((ModalRoute.of(context)?.settings.arguments as SceneSettingsRouteArguments?)?.sceneType ==
CreateSceneEnum.deviceStatusChanges.name);
return BlocBuilder<TabBarBloc, TabBarState>( return BlocBuilder<TabBarBloc, TabBarState>(
builder: (context, tabState) { builder: (context, state) {
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
TabBar( SceneDevicesBodyTabBar(
controller: _tabController, tabController: tabController,
dividerColor: Colors.transparent, rooms: rooms,
indicatorColor: Colors.transparent, selectedRoomId: state is TabBarTabSelectedState ? state.roomId : '-1',
tabs: [
...rooms!.map((e) => Tab(
child: BodyLarge(
text: e.name ?? '',
textAlign: TextAlign.start,
style: context.bodyLarge.copyWith(
color: (tabState is TabSelected) && tabState.roomId == e.id
? ColorsManager.textPrimaryColor
: ColorsManager.textPrimaryColor.withOpacity(0.2),
),
),
)),
],
isScrollable: true,
tabAlignment: TabAlignment.start,
), ),
Expanded( Expanded(
child: TabBarView( child: TabBarView(
controller: _tabController,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: controller: tabController,
rooms!.map((e) => _buildRoomTab(e, context, isAutomationDeviceStatus)).toList(), children: rooms
.map(
(room) => _buildRoomTab(
room,
_isAutomationDeviceStatus(context),
),
)
.toList(),
), ),
), ),
], ],
@ -70,52 +53,46 @@ class SceneDevicesBody extends StatelessWidget {
); );
} }
Widget _buildRoomTab(SubSpaceModel room, BuildContext context, bool isAutomationDeviceStatus) { bool _isAutomationDeviceStatus(BuildContext context) {
final routeArguments =
ModalRoute.of(context)?.settings.arguments as SceneSettingsRouteArguments?;
final deviceStatusChangesScene = CreateSceneEnum.deviceStatusChanges.name;
final sceneType = routeArguments?.sceneType;
return sceneType == deviceStatusChangesScene;
}
Widget _buildRoomTab(
SubSpaceModel room,
bool isAutomationDeviceStatus,
) {
return BlocBuilder<DeviceManagerBloc, DeviceManagerState>( return BlocBuilder<DeviceManagerBloc, DeviceManagerState>(
builder: (context, state) { builder: (context, state) {
if (state.loading && state.devices == null) { final isLoading = state.loading && state.devices == null;
return const Center(child: CircularProgressIndicator()); final hasData =
} else if (state.devices != null && state.devices!.isNotEmpty) { state.devices != null && (state.devices?.isNotEmpty ?? false);
return ListView.builder( final hasError = state.error != null;
itemCount: state.devices!.length,
itemBuilder: (context, index) { final widgets = <bool, Widget>{
final device = state.devices![index]; isLoading: const AppLoadingIndicator(),
return DefaultContainer( hasError: Center(child: Text('${state.error}')),
child: SceneListTile( hasData: SceneDevicesList(
minLeadingWidth: 40, devices: state.devices ?? [],
leadingWidget: SvgPicture.asset(device.icon ?? ''), isAutomationDeviceStatus: isAutomationDeviceStatus,
titleWidget: BodyMedium(
text: device.name ?? '',
style: context.titleSmall.copyWith(
color: ColorsManager.secondaryTextColor,
fontWeight: FontWeight.w400,
fontSize: 20,
), ),
), };
trailingWidget: const Icon(
Icons.arrow_forward_ios_rounded, final invalidWidgetEntry = MapEntry(
color: ColorsManager.greyColor, true,
size: 16, Center(child: Text('This subspace has no devices')),
),
onPressed: () {
Navigator.pushNamed(
context,
Routes.deviceFunctionsRoute,
arguments: {
"device": device,
"isAutomationDeviceStatus": isAutomationDeviceStatus
},
); );
},
), final validWidgetEntry = widgets.entries.firstWhere(
(entry) => entry.key == true,
orElse: () => invalidWidgetEntry,
); );
},
); return validWidgetEntry.value;
} else if (state.error != null) {
return const Center(child: Text('Something went wrong'));
} else {
return const SizedBox();
}
}, },
); );
} }

View File

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/devices/model/subspace_model.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SceneDevicesBodyTabBar extends StatelessWidget {
const SceneDevicesBodyTabBar({
required this.tabController,
required this.rooms,
required this.selectedRoomId,
super.key,
});
final String selectedRoomId;
final TabController tabController;
final List<SubSpaceModel> rooms;
@override
Widget build(BuildContext context) {
return TabBar(
controller: tabController,
dividerColor: Colors.transparent,
indicatorColor: Colors.transparent,
isScrollable: true,
tabAlignment: TabAlignment.start,
tabs: rooms.map((e) {
final isSelected = selectedRoomId == e.id;
return Tab(
child: BodyLarge(
text: e.name ?? '',
textAlign: TextAlign.start,
style: context.bodyLarge.copyWith(
color: isSelected
? ColorsManager.textPrimaryColor
: ColorsManager.textPrimaryColor.withValues(
alpha: 0.2,
),
),
),
);
}).toList(),
);
}
}

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/context_extension.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SceneDevicesList extends StatelessWidget {
const SceneDevicesList({
required this.isAutomationDeviceStatus,
required this.devices,
super.key,
});
final List<DeviceModel> devices;
final bool isAutomationDeviceStatus;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: devices.length,
itemBuilder: (context, index) => _buildDeviceTile(context, devices[index]),
);
}
Widget _buildDeviceTile(
BuildContext context,
DeviceModel device,
) {
return DefaultContainer(
child: SceneListTile(
minLeadingWidth: 40,
leadingWidget: SvgPicture.asset(device.icon ?? ''),
titleWidget: BodyMedium(
text: device.name ?? '',
style: context.titleSmall.copyWith(
color: ColorsManager.secondaryTextColor,
fontWeight: FontWeight.w400,
fontSize: 20,
),
),
trailingWidget: const Icon(
Icons.arrow_forward_ios_rounded,
color: ColorsManager.greyColor,
size: 16,
),
onPressed: () => _navigateToDeviceFunctions(context, device),
),
);
}
void _navigateToDeviceFunctions(
BuildContext context,
DeviceModel device,
) {
Navigator.of(context).pushNamed(
Routes.deviceFunctionsRoute,
arguments: {
"device": device,
"isAutomationDeviceStatus": isAutomationDeviceStatus,
},
);
}
}

View File

@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
class AppLoadingIndicator extends StatelessWidget {
const AppLoadingIndicator({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: CircularProgressIndicator.adaptive(),
);
}
}

View File

@ -3,8 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/model/community_model.dart'; import 'package:syncrow_app/features/app_layout/model/community_model.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/app_layout/view/app_layout.dart'; import 'package:syncrow_app/features/app_layout/view/app_layout.dart';
import 'package:syncrow_app/features/auth/view/otp_view.dart';
import 'package:syncrow_app/features/auth/view/login_view.dart'; import 'package:syncrow_app/features/auth/view/login_view.dart';
import 'package:syncrow_app/features/auth/view/otp_view.dart';
import 'package:syncrow_app/features/auth/view/sign_up_view.dart'; import 'package:syncrow_app/features/auth/view/sign_up_view.dart';
import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart'; import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart';
import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart';
@ -17,11 +17,12 @@ import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_bloc.dart'
import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart'; import 'package:syncrow_app/features/scene/bloc/tab_change/tab_change_event.dart';
import 'package:syncrow_app/features/scene/view/device_functions_view.dart'; import 'package:syncrow_app/features/scene/view/device_functions_view.dart';
import 'package:syncrow_app/features/scene/view/scene_auto_settings.dart'; import 'package:syncrow_app/features/scene/view/scene_auto_settings.dart';
import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart';
import 'package:syncrow_app/features/scene/view/scene_rooms_tabbar.dart'; import 'package:syncrow_app/features/scene/view/scene_rooms_tabbar.dart';
import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart';
import 'package:syncrow_app/features/scene/view/scene_view.dart'; import 'package:syncrow_app/features/scene/view/scene_view.dart';
import 'package:syncrow_app/features/scene/view/smart_automation_select_route.dart'; import 'package:syncrow_app/features/scene/view/smart_automation_select_route.dart';
import 'package:syncrow_app/features/splash/view/splash_view.dart'; import 'package:syncrow_app/features/splash/view/splash_view.dart';
import 'routing_constants.dart'; import 'routing_constants.dart';
class Router { class Router {
@ -88,7 +89,7 @@ class Router {
BlocProvider( BlocProvider(
create: (BuildContext context) => create: (BuildContext context) =>
TabBarBloc(context.read<DeviceManagerBloc>()) TabBarBloc(context.read<DeviceManagerBloc>())
..add(TabChanged( ..add(TabBarTabChangedEvent(
selectedIndex: 0, selectedIndex: 0,
roomId: '-1', roomId: '-1',
unit: SpaceModel( unit: SpaceModel(

View File

@ -11,7 +11,7 @@ abstract class ApiEndpoints {
static const String sendOtp = '/authentication/user/send-otp'; static const String sendOtp = '/authentication/user/send-otp';
static const String verifyOtp = '/authentication/user/verify-otp'; static const String verifyOtp = '/authentication/user/verify-otp';
static const String forgetPassword = '/authentication/user/forget-password'; static const String forgetPassword = '/authentication/user/forget-password';
static const String clientLogin = 'client/token';
////////////////////////////////////// Spaces /////////////////////////////////////// ////////////////////////////////////// Spaces ///////////////////////////////////////
///Community Module ///Community Module
@ -111,9 +111,9 @@ abstract class ApiEndpoints {
//POST //POST
static const String addDeviceToRoom = '/device/room'; static const String addDeviceToRoom = '/device/room';
static const String addDeviceToGroup = '/device/group'; static const String addDeviceToGroup = '/device/group';
static const String controlDevice = '/device/{deviceUuid}/control'; static const String controlDevice = '/devices/{deviceUuid}/command';
static const String firmwareDevice = static const String firmwareDevice =
'/device/{deviceUuid}/firmware/{firmwareVersion}'; '/devices/{deviceUuid}/firmware/{firmwareVersion}';
static const String getDevicesByUserId = '/device/user/{userId}'; static const String getDevicesByUserId = '/device/user/{userId}';
static const String getDevicesByUnitId = '/device/unit/{unitUuid}'; static const String getDevicesByUnitId = '/device/unit/{unitUuid}';
static const String openDoorLock = '/door-lock/open/{doorLockUuid}'; static const String openDoorLock = '/door-lock/open/{doorLockUuid}';
@ -121,13 +121,13 @@ abstract class ApiEndpoints {
//GET //GET
static const String deviceByRoom = static const String deviceByRoom =
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices'; '/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices';
static const String deviceByUuid = '/device/{deviceUuid}'; static const String deviceByUuid = '/devices/{deviceUuid}';
static const String deviceFunctions = '/device/{deviceUuid}/functions'; static const String deviceFunctions = '/devices/{deviceUuid}/functions';
static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices'; static const String gatewayApi = '/devices/gateway/{gatewayUuid}/devices';
static const String deviceFunctionsStatus = static const String deviceFunctionsStatus =
'/device/{deviceUuid}/functions/status'; '/devices/{deviceUuid}/functions/status';
static const String powerClamp = static const String powerClamp =
'/device/{powerClampUuid}/power-clamp/status'; '/devices/{deviceUuid}/functions/status';
///Device Permission Module ///Device Permission Module
//POST //POST
@ -153,7 +153,8 @@ abstract class ApiEndpoints {
static const String getScene = '/scene/tap-to-run/{sceneId}'; static const String getScene = '/scene/tap-to-run/{sceneId}';
static const String getIconScene = '/scene/icon'; static const String getIconScene = '/scene/icon';
static const String getUnitAutomation = '/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations'; static const String getUnitAutomation =
'/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations';
static const String getAutomationDetails = static const String getAutomationDetails =
'/projects/{projectId}/automations/{automationId}'; '/projects/{projectId}/automations/{automationId}';
@ -161,7 +162,8 @@ abstract class ApiEndpoints {
/// PUT /// PUT
static const String updateScene = '/scene/tap-to-run/{sceneId}'; static const String updateScene = '/scene/tap-to-run/{sceneId}';
static const String updateAutomation = '/projects/{projectId}/automations/{automationId}'; static const String updateAutomation =
'/projects/{projectId}/automations/{automationId}';
static const String updateAutomationStatus = static const String updateAutomationStatus =
'/projects/{projectId}/automations/{automationId}'; '/projects/{projectId}/automations/{automationId}';
@ -169,7 +171,8 @@ abstract class ApiEndpoints {
/// DELETE /// DELETE
static const String deleteScene = '/scene/tap-to-run/{sceneId}'; static const String deleteScene = '/scene/tap-to-run/{sceneId}';
static const String deleteAutomation = '/projects/{projectId}/automations/{automationId}'; static const String deleteAutomation =
'/projects/{projectId}/automations/{automationId}';
//////////////////////Door Lock ////////////////////// //////////////////////Door Lock //////////////////////
//online //online
@ -213,18 +216,18 @@ abstract class ApiEndpoints {
static const String changeSchedule = '/schedule/enable/{deviceUuid}'; static const String changeSchedule = '/schedule/enable/{deviceUuid}';
static const String deleteSchedule = '/schedule/{deviceUuid}/{scheduleId}'; static const String deleteSchedule = '/schedule/{deviceUuid}/{scheduleId}';
static const String reportLogs = static const String reportLogs =
'/device/report-logs/{deviceUuid}?code={code}&startTime={startTime}&endTime={endTime}'; '/devices/report-logs/{deviceUuid}?code={code}&startTime={startTime}&endTime={endTime}';
static const String controlBatch = '/device/control/batch'; static const String controlBatch = '/devices/batch';
static const String statusBatch = '/device/status/batch'; static const String statusBatch = '/devices/batch';
static const String deviceScene = '/device/{deviceUuid}/scenes'; static const String deviceScene = '/devices/{deviceUuid}/scenes';
static const String fourSceneByName = static const String fourSceneByName =
'/device/{deviceUuid}/scenes?switchName={switchName}'; '/devices/{deviceUuid}/scenes?switchName={switchName}';
static const String resetDevice = '/factory/reset/{deviceUuid}'; static const String resetDevice = '/factory/reset/{deviceUuid}';
static const String unAssignScenesDevice = static const String unAssignScenesDevice =
'/device/{deviceUuid}/scenes?switchName={switchName}'; '/devices/{deviceUuid}/scenes?switchName={switchName}';
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}'; static const String getDeviceLogs = '/devices/report-logs/{uuid}?code={code}';
static const String terms = '/terms'; static const String terms = '/terms';
static const String policy = '/policy'; static const String policy = '/policy';
static const String getPermission = '/permission/{roleUuid}'; static const String getPermission = '/permission/{roleUuid}';

View File

@ -25,11 +25,15 @@ class AuthenticationAPI {
return response; return response;
} }
static Future<bool> signUp({required SignUpModel model}) async { static Future<bool> signUp({
required SignUpModel model,
required String accessToken,
}) async {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.signUp, path: ApiEndpoints.signUp,
body: model.toJson(), body: model.toJson(),
showServerMessage: false, showServerMessage: false,
accessToken: accessToken,
expectedResponseModel: (json) => json['statusCode'] == 201); expectedResponseModel: (json) => json['statusCode'] == 201);
return response; return response;
} }
@ -63,4 +67,20 @@ class AuthenticationAPI {
expectedResponseModel: (json) => json['data']); expectedResponseModel: (json) => json['data']);
return response; return response;
} }
static Future<Map<String, dynamic>> fetchClientToken({
required String clientId,
required String clientSecret,
}) async {
final response = await HTTPService().post(
path: ApiEndpoints.clientLogin,
body: {
'clientId': clientId,
'clientSecret': clientSecret,
},
showServerMessage: false,
expectedResponseModel: (json) => json['data'],
);
return response;
}
} }

View File

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/model/device_category_model.dart';
import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart';
import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart';
@ -8,7 +7,6 @@ import 'package:syncrow_app/features/devices/model/device_report_model.dart';
import 'package:syncrow_app/features/devices/model/function_model.dart'; import 'package:syncrow_app/features/devices/model/function_model.dart';
import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart'; import 'package:syncrow_app/services/api/http_service.dart';
import 'package:syncrow_app/utils/constants/temp_const.dart';
import '../../features/devices/model/create_temporary_password_model.dart'; import '../../features/devices/model/create_temporary_password_model.dart';
class DevicesAPI { class DevicesAPI {
@ -39,7 +37,7 @@ class DevicesAPI {
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId), path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
body: {"deviceName": deviceName}, body: {"deviceName": deviceName},
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json['data'];
}, },
); );
return response; return response;
@ -92,7 +90,7 @@ class DevicesAPI {
.replaceAll('{deviceUuid}', deviceId), .replaceAll('{deviceUuid}', deviceId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json['data'];
}, },
); );
return response; return response;
@ -101,7 +99,7 @@ class DevicesAPI {
static Future<Map<String, dynamic>> getPowerClampStatus( static Future<Map<String, dynamic>> getPowerClampStatus(
String deviceId) async { String deviceId) async {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.powerClamp.replaceAll('{powerClampUuid}', deviceId), path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{powerClampUuid}', deviceId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
@ -132,7 +130,7 @@ class DevicesAPI {
path: ApiEndpoints.deviceFunctions.replaceAll('{deviceUuid}', deviceId), path: ApiEndpoints.deviceFunctions.replaceAll('{deviceUuid}', deviceId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
final functions = FunctionModel.fromJson(json); final functions = FunctionModel.fromJson(json['data']);
return functions; return functions;
}); });
return response; return response;
@ -188,7 +186,7 @@ class DevicesAPI {
path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId), path: ApiEndpoints.deviceByUuid.replaceAll('{deviceUuid}', deviceId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json['data'];
}); });
return response; return response;
} }
@ -264,7 +262,7 @@ class DevicesAPI {
if (json == null || json.isEmpty || json == []) { if (json == null || json.isEmpty || json == []) {
return devices; return devices;
} }
for (var device in json['devices']) { for (var device in json['data']['devices']) {
devices.add(DeviceModel.fromJson(device)); devices.add(DeviceModel.fromJson(device));
} }
return devices; return devices;
@ -491,7 +489,12 @@ class DevicesAPI {
}) async { }) async {
final response = await _httpService.post( final response = await _httpService.post(
path: ApiEndpoints.controlBatch, path: ApiEndpoints.controlBatch,
body: {"devicesUuid": devicesUuid, "code": code, "value": value}, body: {
"devicesUuid": devicesUuid,
"code": code,
"value": value,
"operationType": 'COMMAND',
},
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;

View File

@ -34,7 +34,7 @@ class HomeManagementAPI {
path: ApiEndpoints.devices.replaceAll("{projectUuid}", projectUuid), path: ApiEndpoints.devices.replaceAll("{projectUuid}", projectUuid),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
json.forEach((value) { json['data'].forEach((value) {
list.add(DeviceModel.fromJson(value)); list.add(DeviceModel.fromJson(value));
}); });
}); });

View File

@ -48,13 +48,26 @@ class HTTPService {
Options? options, Options? options,
dynamic body, dynamic body,
bool showServerMessage = true, bool showServerMessage = true,
String? accessToken,
required T Function(dynamic) expectedResponseModel}) async { required T Function(dynamic) expectedResponseModel}) async {
try { try {
final authOptions = options ??
Options(
headers: accessToken != null
? {
'Authorization': 'Bearer $accessToken',
'Content-Type': 'application/json',
}
: {
'Content-Type': 'application/json',
},
);
final response = await client.post( final response = await client.post(
path, path,
data: body, data: body,
queryParameters: queryParameters, queryParameters: queryParameters,
options: options, options: authOptions,
); );
return expectedResponseModel(response.data); return expectedResponseModel(response.data);
} catch (error) { } catch (error) {