diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4..b636303 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/lib/features/app_layout/bloc/home_cubit.dart b/lib/features/app_layout/bloc/home_cubit.dart index 29773da..45f2398 100644 --- a/lib/features/app_layout/bloc/home_cubit.dart +++ b/lib/features/app_layout/bloc/home_cubit.dart @@ -11,7 +11,7 @@ import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdo import 'package:syncrow_app/features/auth/model/user_model.dart'; import 'package:syncrow_app/features/dashboard/view/dashboard_view.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/room_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart'; import 'package:syncrow_app/features/menu/view/menu_view.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; @@ -55,7 +55,8 @@ class HomeCubit extends Cubit { Future fetchUserInfo() async { try { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await ProfileApi().fetchUserInfo(uuid); emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info } catch (e) { @@ -76,9 +77,12 @@ class HomeCubit extends Cubit { selectedSpace = null; selectedRoom = null; pageIndex = 0; - OneSignal.User.pushSubscription.removeObserver((stateChanges) => oneSignalSubscriptionObserver); - OneSignal.Notifications.removePermissionObserver((permission) => oneSignalPermissionObserver); - OneSignal.Notifications.removeClickListener((event) => oneSignalClickListenerObserver); + OneSignal.User.pushSubscription + .removeObserver((stateChanges) => oneSignalSubscriptionObserver); + OneSignal.Notifications.removePermissionObserver( + (permission) => oneSignalPermissionObserver); + OneSignal.Notifications.removeClickListener( + (event) => oneSignalClickListenerObserver); return super.close(); } @@ -88,7 +92,7 @@ class HomeCubit extends Cubit { SpaceModel? selectedSpace; - RoomModel? selectedRoom; + SubSpaceModel? selectedRoom; PageController devicesPageController = PageController(); @@ -120,7 +124,9 @@ class HomeCubit extends Cubit { return; } - var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; + var userUuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? + ''; if (userUuid.isNotEmpty) { await OneSignal.login(userUuid); } @@ -128,21 +134,24 @@ class HomeCubit extends Cubit { await OneSignal.User.pushSubscription.optIn(); //this function will be called once a user is subscribed - oneSignalSubscriptionObserver = OneSignal.User.pushSubscription.addObserver((state) async { + oneSignalSubscriptionObserver = + OneSignal.User.pushSubscription.addObserver((state) async { if (state.current.optedIn) { await _sendSubscriptionId(); } }); // Send the player id when a user allows notifications - oneSignalPermissionObserver = OneSignal.Notifications.addPermissionObserver((state) async { + oneSignalPermissionObserver = + OneSignal.Notifications.addPermissionObserver((state) async { await _sendSubscriptionId(); }); //check if the player id is sent, if not send it again await _sendSubscriptionId(); - oneSignalClickListenerObserver = OneSignal.Notifications.addClickListener((event) async { + oneSignalClickListenerObserver = + OneSignal.Notifications.addClickListener((event) async { //Once the user clicks on the notification }); } catch (err) { @@ -173,7 +182,7 @@ class HomeCubit extends Cubit { if (index == 0) { unselectRoom(); } else { - selectedRoom = selectedSpace!.rooms![index - 1]; + selectedRoom = selectedSpace!.subspaces[index - 1]; emitSafe(RoomSelected(selectedRoom!)); } } @@ -188,7 +197,7 @@ class HomeCubit extends Cubit { if (index <= 0) { unselectRoom(); } else { - selectedRoom = selectedSpace!.rooms![index - 1]; + selectedRoom = selectedSpace!.subspaces[index - 1]; emitSafe(RoomSelected(selectedRoom!)); } } @@ -211,9 +220,10 @@ class HomeCubit extends Cubit { } //////////////////////////////////////// API //////////////////////////////////////// - generateInvitation(String unitId) async { + generateInvitation(SpaceModel unit) async { try { - final invitationCode = await SpacesAPI.generateInvitationCode(unitId); + final invitationCode = + await SpacesAPI.generateInvitationCode(unit.id, unit.community.uuid); if (invitationCode.isNotEmpty) { Share.share('The invitation code is $invitationCode'); CustomSnackBar.displaySnackBar( @@ -229,10 +239,12 @@ class HomeCubit extends Cubit { Future joinAUnit(String code) async { try { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; - Map body = {'userUuid': uuid, 'inviteCode': code}; + var userUuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? + ''; + Map body = {'inviteCode': code}; - final success = await SpacesAPI.joinUnit(body); + final success = await SpacesAPI.joinUnit(userUuid, body); if (success) { await fetchUnitsByUserId(); CustomSnackBar.displaySnackBar('Done successfully'); @@ -247,7 +259,7 @@ class HomeCubit extends Cubit { fetchUnitsByUserId() async { emitSafe(GetSpacesLoading()); try { - spaces = await SpacesAPI.getUnitsByUserId(); + spaces = await SpacesAPI.getSpacesByUserId(); } catch (failure) { emitSafe(GetSpacesError("No units found")); return; @@ -265,13 +277,14 @@ class HomeCubit extends Cubit { fetchRoomsByUnitId(SpaceModel space) async { emitSafe(GetSpaceRoomsLoading()); try { - space.rooms = await SpacesAPI.getRoomsBySpaceId(space.id!); + space.subspaces = + await SpacesAPI.getSubSpaceBySpaceId(space.community.uuid, space.id); } catch (failure) { emitSafe(GetSpaceRoomsError(failure.toString())); return; } - if (space.rooms != null && space.rooms!.isNotEmpty) { - emitSafe(GetSpaceRoomsSuccess(space.rooms!)); + if (space.subspaces != null && space.subspaces!.isNotEmpty) { + emitSafe(GetSpaceRoomsSuccess(space.subspaces!)); } else { emitSafe(GetSpaceRoomsError("No rooms found")); } @@ -347,7 +360,8 @@ class HomeCubit extends Cubit { size: 32, ), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), + foregroundColor: + WidgetStateProperty.all(ColorsManager.textPrimaryColor), ), onPressed: () { Navigator.pushNamed( @@ -368,7 +382,8 @@ class HomeCubit extends Cubit { NavigationService.navigatorKey.currentContext! .read() .add(const SmartSceneClearEvent()); - BlocProvider.of(NavigationService.navigatorKey.currentState!.context) + BlocProvider.of( + NavigationService.navigatorKey.currentState!.context) .add(ResetEffectivePeriod()); NavigationService.navigatorKey.currentContext! .read() @@ -381,7 +396,8 @@ class HomeCubit extends Cubit { size: 28, ), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), + foregroundColor: + WidgetStateProperty.all(ColorsManager.textPrimaryColor), ), onPressed: () {}, ), @@ -414,7 +430,8 @@ class HomeCubit extends Cubit { }; static var bottomNavItems = [ - defaultBottomNavBarItem(icon: Assets.assetsIconsDashboard, label: 'Dashboard'), + defaultBottomNavBarItem( + icon: Assets.assetsIconsDashboard, label: 'Dashboard'), // defaultBottomNavBarItem(icon: Assets.assetsIconslayout, label: 'Layout'), defaultBottomNavBarItem(icon: Assets.assetsIconsDevices, label: 'Devices'), defaultBottomNavBarItem(icon: Assets.assetsIconsRoutines, label: 'Routine'), @@ -440,7 +457,8 @@ class HomeCubit extends Cubit { void updateDevice(String deviceId) async { try { - final response = await DevicesAPI.firmwareDevice(deviceId: deviceId, firmwareVersion: '0'); + final response = await DevicesAPI.firmwareDevice( + deviceId: deviceId, firmwareVersion: '0'); if (response['success'] ?? false) { CustomSnackBar.displaySnackBar('No updates available'); } @@ -448,7 +466,8 @@ class HomeCubit extends Cubit { } } -BottomNavigationBarItem defaultBottomNavBarItem({required String icon, required String label}) { +BottomNavigationBarItem defaultBottomNavBarItem( + {required String icon, required String label}) { return BottomNavigationBarItem( icon: SvgPicture.asset(icon), activeIcon: SvgPicture.asset( diff --git a/lib/features/app_layout/bloc/home_state.dart b/lib/features/app_layout/bloc/home_state.dart index 1125e01..d5e09c6 100644 --- a/lib/features/app_layout/bloc/home_state.dart +++ b/lib/features/app_layout/bloc/home_state.dart @@ -33,7 +33,7 @@ class GetSpacesError extends HomeError { class GetSpaceRoomsLoading extends HomeLoading {} class GetSpaceRoomsSuccess extends HomeSuccess { - final List rooms; + final List rooms; GetSpaceRoomsSuccess(this.rooms); } @@ -50,7 +50,7 @@ class SpaceSelected extends HomeState { } class RoomSelected extends HomeState { - final RoomModel room; + final SubSpaceModel room; RoomSelected(this.room); } diff --git a/lib/features/app_layout/model/community_model.dart b/lib/features/app_layout/model/community_model.dart new file mode 100644 index 0000000..d8b094c --- /dev/null +++ b/lib/features/app_layout/model/community_model.dart @@ -0,0 +1,27 @@ +class Community { + final String uuid; + final String name; + final String description; + + Community({ + required this.uuid, + required this.name, + this.description = '', + }); + + factory Community.fromJson(Map json) { + return Community( + uuid: json['uuid'] ?? '', + name: json['name'] ?? 'Unnamed Community', + description: json['description'] ?? '', + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'name': name, + 'description': description, + }; + } +} diff --git a/lib/features/app_layout/model/space_model.dart b/lib/features/app_layout/model/space_model.dart index 8d7d5b8..944ec7c 100644 --- a/lib/features/app_layout/model/space_model.dart +++ b/lib/features/app_layout/model/space_model.dart @@ -1,37 +1,53 @@ -import 'package:syncrow_app/features/devices/model/room_model.dart'; -import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/features/app_layout/model/community_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; class SpaceModel { - final String? id; - final String? name; - final SpaceType type; - late List? rooms; + final String id; + final String name; + final Community community; + late List subspaces; SpaceModel({ - required this.type, required this.id, required this.name, - required this.rooms, + required this.community, + this.subspaces = const [], // Default to an empty list }); + /// Converts the instance into JSON format. Map toJson() { return { 'id': id, - 'name': name, - 'rooms': rooms, + 'spaceName': name, + 'community': community.toJson(), + 'subspaces': subspaces.map((room) => room.toJson()).toList(), }; } + /// Factory constructor to create an instance from JSON. factory SpaceModel.fromJson(Map json) { + // Extract and log each part of space data + final id = json['uuid'] ?? ''; + final name = json['spaceName'] ?? 'Unnamed Space'; + final communityJson = json['community'] ?? {}; + return SpaceModel( - id: json['uuid'], - name: json['name'], - type: spaceTypesMap[json['type']]!, - rooms: [], + id: id, + name: name, + community: Community.fromJson( + communityJson), // Ensure Community is created correctly + subspaces: (json['subspaces'] as List?) + ?.map((item) => SubSpaceModel.fromJson(item)) + .toList() ?? + [], ); } + /// Helper method to parse a list of SpaceModel from JSON. static List fromJsonList(List jsonList) { - return jsonList.map((item) => SpaceModel.fromJson(item)).toList(); + return jsonList.map((item) { + final spaceData = item['space']; // Extract the `space` object + return SpaceModel.fromJson(spaceData); // Pass to SpaceModel.fromJson + }).toList(); } } diff --git a/lib/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart b/lib/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart index a8b1eb1..41cab5f 100644 --- a/lib/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart +++ b/lib/features/devices/bloc/device_manager_bloc/device_manager_bloc.dart @@ -25,12 +25,14 @@ class DeviceManagerBloc extends Bloc { static List? allCategories; - Future _onFetchAllDevices(FetchAllDevices event, Emitter emit) async { + Future _onFetchAllDevices( + FetchAllDevices event, Emitter emit) async { emit(state.copyWith(loading: true)); try { final allDevices = await HomeManagementAPI.fetchDevicesByUnitId(); emit(state.copyWith(devices: allDevices, loading: false)); } catch (e) { + print(e); emit(state.copyWith(error: e.toString(), loading: false)); } } @@ -39,21 +41,27 @@ class DeviceManagerBloc extends Bloc { FetchDevicesByRoomId event, Emitter emit) async { emit(state.copyWith(loading: true)); try { - final devices = await DevicesAPI.getDevicesByRoomId(event.roomId); + final devices = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId, + ); emit(state.copyWith(devices: devices, loading: false)); } catch (e) { emit(state.copyWith(error: e.toString(), loading: false)); } } - void _onSelectCategory(SelectCategory event, Emitter emit) { + void _onSelectCategory( + SelectCategory event, Emitter emit) { for (var i = 0; i < allCategories!.length; i++) { allCategories![i].isSelected = i == event.index; } emit(state.copyWith(categoryChanged: true)); } - void _onUnselectAllCategories(UnselectAllCategories event, Emitter emit) { + void _onUnselectAllCategories( + UnselectAllCategories event, Emitter emit) { for (var category in allCategories!) { category.isSelected = false; } @@ -97,7 +105,8 @@ class DeviceManagerBloc extends Bloc { _updateDevicesStatus(category, emit); } - void _onTurnOnOffDevice(TurnOnOffDevice event, Emitter emit) { + void _onTurnOnOffDevice( + TurnOnOffDevice event, Emitter emit) { var device = event.device; device.isOnline = !device.isOnline!; DevicesCategoryModel category = allCategories!.firstWhere((category) { @@ -120,7 +129,8 @@ class DeviceManagerBloc extends Bloc { emit(state.copyWith(categoryChanged: true)); // Set category changed state } - void _updateDevicesStatus(DevicesCategoryModel category, Emitter emit) { + void _updateDevicesStatus( + DevicesCategoryModel category, Emitter emit) { if (category.devices != null && category.devices!.isNotEmpty) { bool? tempStatus = category.devices![0].isOnline; for (var device in category.devices!) { @@ -140,7 +150,8 @@ class DeviceManagerBloc extends Bloc { try { final deviceFunctions = await DevicesAPI.deviceFunctions(event.deviceId); - emit(state.copyWith(functionsLoading: false, deviceFunctions: deviceFunctions)); + emit(state.copyWith( + functionsLoading: false, deviceFunctions: deviceFunctions)); } catch (e) { emit(state.copyWith(functionsLoading: false, error: e.toString())); } diff --git a/lib/features/devices/bloc/device_manager_bloc/device_manager_event.dart b/lib/features/devices/bloc/device_manager_bloc/device_manager_event.dart index 867027d..e0abf8c 100644 --- a/lib/features/devices/bloc/device_manager_bloc/device_manager_event.dart +++ b/lib/features/devices/bloc/device_manager_bloc/device_manager_event.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/app_layout/model/space_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_model.dart'; @@ -15,8 +16,9 @@ class FetchAllDevices extends DeviceManagerEvent {} class FetchDevicesByRoomId extends DeviceManagerEvent { final String roomId; + final SpaceModel unit; - const FetchDevicesByRoomId(this.roomId); + const FetchDevicesByRoomId(this.roomId, this.unit); @override List get props => [roomId]; diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 48e9ca0..982f904 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -23,14 +23,22 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart'; part 'devices_state.dart'; class DevicesCubit extends Cubit { + Future _initializeDevices(SpaceModel selectedSpace) async { + // Fetch devices for each room in the selected space + for (var room in selectedSpace.subspaces ?? []) { + await fetchDevicesByRoomId(selectedSpace, room.id!); + } + + // Fetch groups based on the selected space ID + await fetchGroups(selectedSpace.id ?? ''); + } + DevicesCubit._() : super(DevicesInitial()) { - if (HomeCubit.getInstance().selectedSpace != null && - HomeCubit.getInstance().spaces!.isNotEmpty) { - // fetchGroups(HomeCubit.getInstance().selectedSpace!.id!); - for (var room in HomeCubit.getInstance().selectedSpace!.rooms!) { - fetchDevicesByRoomId(room.id!); - } - fetchGroups(HomeCubit.getInstance().selectedSpace?.id ?? ''); + final selectedSpace = HomeCubit.getInstance().selectedSpace; + final spaces = HomeCubit.getInstance().spaces; + + if (selectedSpace != null && spaces != null && spaces.isNotEmpty) { + _initializeDevices(selectedSpace); } } @@ -87,10 +95,10 @@ class DevicesCubit extends Cubit { return const DoorsListView(); case DeviceType.Curtain: return const CurtainListView(); - // case DeviceType.ThreeGang: - // return const ThreeGangSwitchesView(); - // case DeviceType.Gateway: - // return const GateWayView(); + // case DeviceType.ThreeGang: + // return const ThreeGangSwitchesView(); + // case DeviceType.Gateway: + // return const GateWayView(); default: return null; } @@ -104,8 +112,8 @@ class DevicesCubit extends Cubit { List get allDevices { List devices = []; if (HomeCubit.getInstance().selectedSpace != null && - HomeCubit.getInstance().selectedSpace!.rooms != null) { - for (var room in HomeCubit.getInstance().selectedSpace!.rooms!) { + HomeCubit.getInstance().selectedSpace!.subspaces != null) { + for (var room in HomeCubit.getInstance().selectedSpace!.subspaces!) { if (room.devices != null) { devices.addAll(room.devices!); } @@ -275,7 +283,7 @@ class DevicesCubit extends Cubit { deviceId, HomeCubit.getInstance() .selectedSpace! - .rooms! + .subspaces! .indexOf(HomeCubit.getInstance().selectedRoom!), code: control.code); }); @@ -303,20 +311,26 @@ class DevicesCubit extends Cubit { } } - fetchDevicesByRoomId(String? roomId) async { + fetchDevicesByRoomId(SpaceModel? unit, String? roomId) async { if (roomId == null) return; emitSafe(GetDevicesLoading()); - int roomIndex = - HomeCubit.getInstance().selectedSpace!.rooms!.indexWhere((element) => element.id == roomId); + int roomIndex = HomeCubit.getInstance() + .selectedSpace! + .subspaces! + .indexWhere((element) => element.id == roomId); try { - HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices = - await DevicesAPI.getDevicesByRoomId(roomId); + HomeCubit.getInstance().selectedSpace!.subspaces![roomIndex].devices = + await DevicesAPI.getDevicesByRoomId( + communityUuid: unit!.community.uuid, + spaceUuid: unit.id, + roomId: roomId); } catch (e) { emitSafe(GetDevicesError(e.toString())); return; } - final devices = HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices; + final devices = + HomeCubit.getInstance().selectedSpace!.subspaces![roomIndex].devices; emitSafe(GetDevicesSuccess(devices)); //get status for each device @@ -333,7 +347,7 @@ class DevicesCubit extends Cubit { emitSafe(GetDeviceStatusLoading(code: code)); int deviceIndex = HomeCubit.getInstance() .selectedSpace! - .rooms![roomIndex] + .subspaces![roomIndex] .devices! .indexWhere((element) => element.uuid == deviceUuid); List statuses = []; @@ -346,8 +360,11 @@ class DevicesCubit extends Cubit { emitSafe(GetDeviceStatusError(e.toString())); return; } - HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices![deviceIndex].status = - statuses; + HomeCubit.getInstance() + .selectedSpace! + .subspaces![roomIndex] + .devices![deviceIndex] + .status = statuses; emitSafe(GetDeviceStatusSuccess(code: code)); } @@ -396,8 +413,6 @@ class DevicesCubit extends Cubit { // emitSafe(LightBrightnessChanged(value)); // } // } - - } enum LightMode { diff --git a/lib/features/devices/model/subspace_model.dart b/lib/features/devices/model/subspace_model.dart new file mode 100644 index 0000000..f8be0aa --- /dev/null +++ b/lib/features/devices/model/subspace_model.dart @@ -0,0 +1,35 @@ +import 'package:syncrow_app/features/devices/model/device_model.dart'; + +class SubSpaceModel { + final String? id; + final String? name; + List? devices; + + SubSpaceModel({ + required this.id, + required this.name, + required this.devices, + }); + + Map toJson() { + return { + 'id': id, + 'name': name, + 'devices': devices?.map((device) => device.toJson()).toList(), + }; + } + + factory SubSpaceModel.fromJson(Map json) { + List devices = []; + if (json['devices'] != null) { + for (var device in json['devices']) { + devices.add(DeviceModel.fromJson(device)); + } + } + return SubSpaceModel( + id: json['uuid'], + name: json['subspaceName'], + devices: devices, + ); + } +} diff --git a/lib/features/devices/view/widgets/ACs/acs_view.dart b/lib/features/devices/view/widgets/ACs/acs_view.dart index be84084..7680ba7 100644 --- a/lib/features/devices/view/widgets/ACs/acs_view.dart +++ b/lib/features/devices/view/widgets/ACs/acs_view.dart @@ -22,6 +22,8 @@ class ACsView extends StatelessWidget { @override Widget build(BuildContext context) { + print("ACsView deviceModel UUID: ${deviceModel?.uuid}"); + return BlocProvider( create: (context) => ACsBloc(acId: deviceModel?.uuid ?? '') ..add(AcsInitial(allAcs: deviceModel != null ? false : true)), @@ -66,12 +68,14 @@ class ACsView extends StatelessWidget { child: state is AcsLoadingState ? const Center( child: DefaultContainer( - width: 50, height: 50, child: CircularProgressIndicator()), + width: 50, + height: 50, + child: CircularProgressIndicator()), ) : RefreshIndicator( onRefresh: () async { - BlocProvider.of(context) - .add(AcsInitial(allAcs: deviceModel != null ? false : true)); + BlocProvider.of(context).add(AcsInitial( + allAcs: deviceModel != null ? false : true)); }, child: Container( padding: const EdgeInsets.only(top: 40), diff --git a/lib/features/devices/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 53fa8a3..bfa9f7d 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -69,6 +69,7 @@ class DevicesViewBody extends StatelessWidget { Expanded( child: PageView( + controller: HomeCubit.getInstance().devicesPageController, onPageChanged: (index) { HomeCubit.getInstance().devicesPageChanged(index); @@ -78,8 +79,8 @@ class DevicesViewBody extends StatelessWidget { groupsList: DevicesCubit.getInstance().allCategories ?? [], ), if (HomeCubit.getInstance().selectedSpace != null) - if (HomeCubit.getInstance().selectedSpace!.rooms != null) - ...HomeCubit.getInstance().selectedSpace!.rooms!.map((room) { + if (HomeCubit.getInstance().selectedSpace!.subspaces != null) + ...HomeCubit.getInstance().selectedSpace!.subspaces!.map((room) { return RoomPage( room: room, ); @@ -95,7 +96,7 @@ class DevicesViewBody extends StatelessWidget { ), child: SmoothPageIndicator( controller: HomeCubit.getInstance().devicesPageController, - count: HomeCubit.getInstance().selectedSpace!.rooms!.length + 1, + count: HomeCubit.getInstance().selectedSpace!.subspaces!.length + 1, effect: const WormEffect( paintStyle: PaintingStyle.stroke, dotHeight: 8, diff --git a/lib/features/devices/view/widgets/room_page.dart b/lib/features/devices/view/widgets/room_page.dart index e0c54e6..fe8313d 100644 --- a/lib/features/devices/view/widgets/room_page.dart +++ b/lib/features/devices/view/widgets/room_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/devices/model/room_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/room_page_switch.dart'; @@ -8,7 +9,7 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class RoomPage extends StatefulWidget { const RoomPage({super.key, required this.room}); - final RoomModel room; + final SubSpaceModel room; @override _RoomPageState createState() => _RoomPageState(); diff --git a/lib/features/devices/view/widgets/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index 15b86eb..4a0acab 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -87,6 +87,7 @@ class RoomPageSwitch extends StatelessWidget { /// /// The [device] parameter represents the device model. void showDeviceInterface(DeviceModel device, BuildContext context) { + switch (device.productType) { case DeviceType.AC: Navigator.push( diff --git a/lib/features/devices/view/widgets/rooms_slider.dart b/lib/features/devices/view/widgets/rooms_slider.dart index feda17e..f04d7b8 100644 --- a/lib/features/devices/view/widgets/rooms_slider.dart +++ b/lib/features/devices/view/widgets/rooms_slider.dart @@ -39,14 +39,14 @@ class RoomsSlider extends StatelessWidget { ), ), if (HomeCubit.getInstance().selectedSpace != null) - if (HomeCubit.getInstance().selectedSpace!.rooms != null) - ...HomeCubit.getInstance().selectedSpace!.rooms!.map( + if (HomeCubit.getInstance().selectedSpace!.subspaces != null) + ...HomeCubit.getInstance().selectedSpace!.subspaces!.map( (room) => InkWell( onTap: () { HomeCubit.getInstance().roomSliderPageChanged( HomeCubit.getInstance() .selectedSpace! - .rooms! + .subspaces! .indexOf(room)); }, child: TitleMedium( diff --git a/lib/features/devices/view/widgets/scene_listview.dart b/lib/features/devices/view/widgets/scene_listview.dart index 4fa2031..5a19bc5 100644 --- a/lib/features/devices/view/widgets/scene_listview.dart +++ b/lib/features/devices/view/widgets/scene_listview.dart @@ -40,10 +40,13 @@ class SceneListview extends StatelessWidget { sceneName: scene.name, ), ); - context.read().add(const SmartSceneClearEvent()); + context + .read() + .add(const SmartSceneClearEvent()); - BlocProvider.of(context) - .add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false)); + BlocProvider.of(context).add( + FetchSceneTasksEvent( + sceneId: scene.id, isAutomation: false)); /// the state to set the scene type must be after the fetch BlocProvider.of(context) @@ -56,14 +59,27 @@ class SceneListview extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(8.0), - child: Image.memory( - height: 32, - width: 32, - scene.iconInBytes, - fit: BoxFit.fill, - errorBuilder: (context, error, stackTrace) => Image.asset( - height: 32, width: 32, fit: BoxFit.fill, Assets.assetsIconsLogo), - ), + child: scene.iconInBytes != null && + scene.iconInBytes.isNotEmpty + ? Image.memory( + scene.iconInBytes, + height: 32, + width: 32, + fit: BoxFit.fill, + errorBuilder: (context, error, stackTrace) => + Image.asset( + Assets.assetsIconsLogo, + height: 32, + width: 32, + fit: BoxFit.fill, + ), + ) + : Image.asset( + Assets.assetsIconsLogo, + height: 32, + width: 32, + fit: BoxFit.fill, + ), ), Expanded( child: BodyMedium( diff --git a/lib/features/menu/bloc/create_unit_bloc/create_unit_bloc.dart b/lib/features/menu/bloc/create_unit_bloc/create_unit_bloc.dart index e31fd2b..23a1e59 100644 --- a/lib/features/menu/bloc/create_unit_bloc/create_unit_bloc.dart +++ b/lib/features/menu/bloc/create_unit_bloc/create_unit_bloc.dart @@ -61,7 +61,9 @@ class CreateUnitBloc extends Bloc { var storage = const FlutterSecureStorage(); var userId = await storage.read(key: UserModel.userUuidKey) ?? ''; - Map communityBody = {'communityName': event.communityName}; + Map communityBody = { + 'communityName': event.communityName + }; final response = await HomeCreation.createCommunity(communityBody); if (response['data']['uuid'] != '') { // final result = @@ -75,15 +77,22 @@ class CreateUnitBloc extends Bloc { if (buildingId.isNotEmpty) { final floorId = await _createFloor( - floorName: event.floorName, buildingId: buildingId, userId: userId); + floorName: event.floorName, + buildingId: buildingId, + userId: userId); if (floorId.isNotEmpty) { - final unitId = - await _createUnit(unitName: event.unitName, floorId: floorId, userId: userId); + final unitId = await _createUnit( + unitName: event.unitName, floorId: floorId, userId: userId); if (unitId.isNotEmpty && rooms.isNotEmpty) { rooms.forEach((room) async { - await _createNewRoom(roomName: room, unitId: unitId, userId: userId); + await _createNewRoom( + roomName: room, + unitId: unitId, + userId: userId, + communityId: response['data']['uuid'], + ); }); } } @@ -99,7 +108,8 @@ class CreateUnitBloc extends Bloc { } } -Future _assignToCommunity({required String communityId, required String userId}) async { +Future _assignToCommunity( + {required String communityId, required String userId}) async { try { Map body = { 'communityUuid': communityId, @@ -114,9 +124,14 @@ Future _assignToCommunity({required String communityId, required String us } Future _createBuilding( - {required String buildingName, required String communityId, required String userId}) async { + {required String buildingName, + required String communityId, + required String userId}) async { try { - Map body = {'buildingName': buildingName, 'communityUuid': communityId}; + Map body = { + 'buildingName': buildingName, + 'communityUuid': communityId + }; final response = await HomeCreation.createBuilding(body); // if (response['data']['uuid'] != '') { // final result = await _assignToBuilding(buildingId: response['data']['uuid'], userId: userId); @@ -130,7 +145,8 @@ Future _createBuilding( } } -Future _assignToBuilding({required String buildingId, required String userId}) async { +Future _assignToBuilding( + {required String buildingId, required String userId}) async { try { Map body = { 'buildingUuid': buildingId, @@ -145,9 +161,14 @@ Future _assignToBuilding({required String buildingId, required String user } Future _createFloor( - {required String floorName, required String buildingId, required String userId}) async { + {required String floorName, + required String buildingId, + required String userId}) async { try { - Map body = {'floorName': floorName, 'buildingUuid': buildingId}; + Map body = { + 'floorName': floorName, + 'buildingUuid': buildingId + }; final response = await HomeCreation.createFloor(body); // if (response['data']['uuid'] != '') { // final result = await _assignToFloor(buildingId: response['data']['uuid'], userId: userId); @@ -161,7 +182,8 @@ Future _createFloor( } } -Future _assignToFloor({required String buildingId, required String userId}) async { +Future _assignToFloor( + {required String buildingId, required String userId}) async { try { Map body = { 'floorUuid': buildingId, @@ -176,12 +198,15 @@ Future _assignToFloor({required String buildingId, required String userId} } Future _createUnit( - {required String unitName, required String floorId, required String userId}) async { + {required String unitName, + required String floorId, + required String userId}) async { try { Map body = {'unitName': unitName, 'floorUuid': floorId}; final response = await HomeCreation.createUnit(body); if (response['data']['uuid'] != '') { - final result = await _assignToUnit(unitId: response['data']['uuid'], userId: userId); + final result = + await _assignToUnit(unitId: response['data']['uuid'], userId: userId); return result ? response['data']['uuid'] : ''; } else { @@ -192,7 +217,8 @@ Future _createUnit( } } -Future _assignToUnit({required String unitId, required String userId}) async { +Future _assignToUnit( + {required String unitId, required String userId}) async { try { Map body = { 'unitUuid': unitId, @@ -207,10 +233,14 @@ Future _assignToUnit({required String unitId, required String userId}) asy } Future _createNewRoom( - {required String roomName, required String unitId, required String userId}) async { + {required String roomName, + required String unitId, + required String userId, + required String communityId}) async { try { - Map body = {'roomName': roomName, 'unitUuid': unitId}; - final response = await HomeCreation.createRoom(body); + Map body = {'subspaceName': roomName}; + final response = await HomeCreation.createRoom( + communityId: communityId, spaceId: unitId, body: body); // if (response['data']['uuid'] != '') { // final result = await _assignToRoom(roomId: response['data']['uuid'], userId: userId); @@ -223,7 +253,8 @@ Future _createNewRoom( } } -Future _assignToRoom({required String roomId, required String userId}) async { +Future _assignToRoom( + {required String roomId, required String userId}) async { try { Map body = { 'roomUuid': roomId, diff --git a/lib/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart index 46a0c8a..bd6b653 100644 --- a/lib/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart +++ b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart @@ -17,12 +17,15 @@ class ManageUnitBloc extends Bloc { on(_fetchDevicesByRoomId); on(_assignDevice); on(_addNewRoom); + on(_unassignDevice); } - void _fetchRoomsAndDevices(FetchRoomsEvent event, Emitter emit) async { + void _fetchRoomsAndDevices( + FetchRoomsEvent event, Emitter emit) async { try { emit(LoadingState()); - final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId); + final roomsList = await SpacesAPI.getSubSpaceBySpaceId( + event.unit.community.uuid, event.unit.id); emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList)); } catch (e) { emit(const ErrorState(message: 'Something went wrong')); @@ -30,11 +33,15 @@ class ManageUnitBloc extends Bloc { } } - void _fetchDevicesByRoomId(FetchDevicesByRoomIdEvent event, Emitter emit) async { + void _fetchDevicesByRoomId( + FetchDevicesByRoomIdEvent event, Emitter emit) async { try { Map roomDevicesId = {}; emit(LoadingState()); - final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId); + final devicesList = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId); allDevices = await HomeManagementAPI.fetchDevicesByUserId(); List allDevicesIds = []; @@ -61,13 +68,18 @@ class ManageUnitBloc extends Bloc { } } - void _assignDevice(AssignRoomEvent event, Emitter emit) async { + void _assignDevice( + AssignRoomEvent event, Emitter emit) async { try { Map roomDevicesId = {}; emit(LoadingState()); - Map body = {"deviceUuid": event.deviceId, "roomUuid": event.roomId}; - await HomeManagementAPI.assignDeviceToRoom(body); - final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId); + + await HomeManagementAPI.assignDeviceToRoom( + event.unit.community.uuid, event.unit.id, event.roomId, event.deviceId); + final devicesList = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId); List allDevicesIds = []; @@ -93,13 +105,56 @@ class ManageUnitBloc extends Bloc { } } + + void _unassignDevice( + UnassignRoomEvent event, Emitter emit) async { + try { + Map roomDevicesId = {}; + emit(LoadingState()); + + await HomeManagementAPI.unAssignDeviceToRoom( + event.unit.community.uuid, event.unit.id, event.roomId, event.deviceId); + final devicesList = await DevicesAPI.getDevicesByRoomId( + communityUuid: event.unit.community.uuid, + spaceUuid: event.unit.id, + roomId: event.roomId); + + List allDevicesIds = []; + + allDevices.forEach((element) { + allDevicesIds.add(element.uuid!); + }); + + devicesList.forEach((e) { + if (allDevicesIds.contains(e.uuid!)) { + roomDevicesId[e.uuid!] = true; + } else { + roomDevicesId[e.uuid!] = false; + } + }); + emit(FetchDeviceByRoomIdState( + roomDevices: devicesList, + allDevices: allDevices, + roomDevicesId: roomDevicesId, + roomId: event.roomId)); + } catch (e) { + emit(const ErrorState(message: 'Something went wrong')); + return; + } + } + + _addNewRoom(AddNewRoom event, Emitter emit) async { - Map body = {'roomName': event.roomName, 'unitUuid': event.unitId}; + Map body = {'subspaceName': event.roomName}; try { emit(LoadingState()); - final response = await HomeCreation.createRoom(body); + final response = await HomeCreation.createRoom( + communityId: event.unit.community.uuid, + spaceId: event.unit.id, + body: body); if (response['data']['uuid'] != '') { - final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId); + final roomsList = await SpacesAPI.getSubSpaceBySpaceId( + event.unit.community.uuid, event.unit.id); allDevices = await HomeManagementAPI.fetchDevicesByUserId(); emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList)); await HomeCubit.getInstance().fetchUnitsByUserId(); diff --git a/lib/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart index 03c0722..08411c1 100644 --- a/lib/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart +++ b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; abstract class ManageUnitEvent extends Equatable { const ManageUnitEvent(); @@ -12,39 +13,54 @@ class InitialEvent extends ManageUnitEvent {} class LoadingEvent extends ManageUnitEvent {} class FetchRoomsEvent extends ManageUnitEvent { - final String unitId; + final SpaceModel unit; - const FetchRoomsEvent({required this.unitId}); + const FetchRoomsEvent({required this.unit}); @override - List get props => [unitId]; + List get props => [unit]; } class FetchDevicesByRoomIdEvent extends ManageUnitEvent { final String roomId; + final SpaceModel unit; - const FetchDevicesByRoomIdEvent({required this.roomId}); + const FetchDevicesByRoomIdEvent({required this.roomId, required this.unit}); @override - List get props => [roomId]; + List get props => [roomId, unit]; } class AddNewRoom extends ManageUnitEvent { final String roomName; - final String unitId; + final SpaceModel unit; - const AddNewRoom({required this.roomName, required this.unitId}); + const AddNewRoom({required this.roomName, required this.unit}); @override - List get props => [roomName, unitId]; + List get props => [roomName, unit]; } class AssignRoomEvent extends ManageUnitEvent { final String roomId; final String deviceId; + final SpaceModel unit; - const AssignRoomEvent({required this.roomId, required this.deviceId}); + const AssignRoomEvent( + {required this.roomId, required this.deviceId, required this.unit}); @override - List get props => [roomId]; + List get props => [roomId, unit]; +} + +class UnassignRoomEvent extends ManageUnitEvent { + final String roomId; + final String deviceId; + final SpaceModel unit; + + const UnassignRoomEvent( + {required this.roomId, required this.deviceId, required this.unit}); + + @override + List get props => [roomId, unit]; } diff --git a/lib/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart index 56236a5..f27105e 100644 --- a/lib/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart +++ b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart @@ -1,6 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/model/room_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; abstract class ManageUnitState extends Equatable { const ManageUnitState(); @@ -14,7 +15,7 @@ class InitialState extends ManageUnitState {} class LoadingState extends ManageUnitState {} class FetchRoomsState extends ManageUnitState { - final List roomsList; + final List roomsList; final List devicesList; const FetchRoomsState({required this.devicesList, required this.roomsList}); diff --git a/lib/features/menu/view/widgets/join_home/join_home_view.dart b/lib/features/menu/view/widgets/join_home/join_home_view.dart index 69952c9..5ce6c8f 100644 --- a/lib/features/menu/view/widgets/join_home/join_home_view.dart +++ b/lib/features/menu/view/widgets/join_home/join_home_view.dart @@ -44,7 +44,8 @@ class JoinHomeView extends StatelessWidget { controller: textEditingController, decoration: InputDecoration( hintText: 'Invitatoin code', - hintStyle: context.bodyMedium.copyWith(color: Colors.grey), + hintStyle: + context.bodyMedium.copyWith(color: Colors.grey), border: InputBorder.none, ), ), @@ -52,10 +53,12 @@ class JoinHomeView extends StatelessWidget { IconButton( onPressed: () async { if (textEditingController.text.isEmpty) { - CustomSnackBar.displaySnackBar('Please enter the invitation code'); + CustomSnackBar.displaySnackBar( + 'Please enter the invitation code'); return; } - if (await HomeCubit.getInstance().joinAUnit(textEditingController.text)) { + if (await HomeCubit.getInstance() + .joinAUnit(textEditingController.text)) { CustomSnackBar.displaySnackBar('Done successfully'); Navigator.of(context).pop(); } else { diff --git a/lib/features/menu/view/widgets/manage_home/assign_devices.dart b/lib/features/menu/view/widgets/manage_home/assign_devices.dart index bc097fb..12421aa 100644 --- a/lib/features/menu/view/widgets/manage_home/assign_devices.dart +++ b/lib/features/menu/view/widgets/manage_home/assign_devices.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart'; import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart'; import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart'; @@ -11,16 +12,24 @@ import 'package:syncrow_app/utils/helpers/snack_bar.dart'; class AssignDeviceView extends StatelessWidget { final String unitId; final String roomId; - const AssignDeviceView({super.key, required this.unitId, required this.roomId}); + final SpaceModel unit; + const AssignDeviceView( + {super.key, + required this.unitId, + required this.roomId, + required this.unit}); @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => ManageUnitBloc()..add(FetchDevicesByRoomIdEvent(roomId: roomId)), - child: BlocConsumer(listener: (context, state) { + create: (context) => ManageUnitBloc() + ..add(FetchDevicesByRoomIdEvent(roomId: roomId, unit: unit)), + child: BlocConsumer( + listener: (context, state) { if (state is FetchDeviceByRoomIdState) { if (state.allDevices.isEmpty) { - CustomSnackBar.displaySnackBar('You do not have the permission to assign devices'); + CustomSnackBar.displaySnackBar( + 'You do not have the permission to assign devices'); Navigator.of(context).pop(); } } @@ -34,7 +43,8 @@ class AssignDeviceView extends StatelessWidget { width: MediaQuery.sizeOf(context).width, height: MediaQuery.sizeOf(context).height, child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 10.0, mainAxisSpacing: 10.0, @@ -52,11 +62,14 @@ class AssignDeviceView extends StatelessWidget { ), ), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ SvgPicture.asset( state.allDevices[index].icon!, @@ -64,19 +77,42 @@ class AssignDeviceView extends StatelessWidget { ), GestureDetector( onTap: () { - if (state.roomDevicesId[ - state.allDevices[index].uuid!] ?? - false == false) { - BlocProvider.of(context).add( - AssignRoomEvent( - deviceId: - state.allDevices[index].uuid ?? '', + bool isAssigned = + state.roomDevicesId[state + .allDevices[index] + .uuid!] ?? + false; + if (isAssigned) { + BlocProvider.of< + ManageUnitBloc>( + context) + .add(UnassignRoomEvent( + deviceId: state + .allDevices[ + index] + .uuid ?? + '', + unit: unit, + roomId: roomId)); + } else { + // Tick (assign) the device + BlocProvider.of< + ManageUnitBloc>( + context) + .add(AssignRoomEvent( + deviceId: state + .allDevices[ + index] + .uuid ?? + '', + unit: unit, roomId: roomId)); } }, child: SvgPicture.asset( - state.roomDevicesId[ - state.allDevices[index].uuid!] ?? + state.roomDevicesId[state + .allDevices[index] + .uuid!] ?? false ? Assets.blueCheckboxIcon : Assets.emptyCheckboxIcon, diff --git a/lib/features/menu/view/widgets/manage_home/home_settings.dart b/lib/features/menu/view/widgets/manage_home/home_settings.dart index 751c04b..8584479 100644 --- a/lib/features/menu/view/widgets/manage_home/home_settings.dart +++ b/lib/features/menu/view/widgets/manage_home/home_settings.dart @@ -60,7 +60,7 @@ class HomeSettingsView extends StatelessWidget { onTap: () { Navigator.of(context).push(CustomPageRoute( builder: (context) => RoomsView( - unitId: space?.id ?? '', + unit: space!, ))); }, child: Container( @@ -112,7 +112,7 @@ class HomeSettingsView extends StatelessWidget { padding: const EdgeInsets.only(bottom: 10), child: GestureDetector( onTap: () async { - await HomeCubit.getInstance().generateInvitation(space?.id ?? ''); + await HomeCubit.getInstance().generateInvitation(space!); }, child: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/features/menu/view/widgets/manage_home/room_screen.dart b/lib/features/menu/view/widgets/manage_home/room_screen.dart index 34f2326..f8fe951 100644 --- a/lib/features/menu/view/widgets/manage_home/room_screen.dart +++ b/lib/features/menu/view/widgets/manage_home/room_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart'; import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart'; import 'package:syncrow_app/features/menu/bloc/manage_unit_bloc/manage_unit_state.dart'; @@ -11,14 +12,14 @@ import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class RoomsView extends StatelessWidget { - final String unitId; - const RoomsView({super.key, required this.unitId}); + final SpaceModel unit; + const RoomsView({super.key, required this.unit}); @override Widget build(BuildContext context) { TextEditingController textEditingController = TextEditingController(); return BlocProvider( - create: (context) => ManageUnitBloc()..add(FetchRoomsEvent(unitId: unitId)), + create: (context) => ManageUnitBloc()..add(FetchRoomsEvent(unit:unit )), child: BlocConsumer( listener: (context, state) {}, builder: (context, state) { @@ -62,7 +63,8 @@ class RoomsView extends StatelessWidget { MaterialPageRoute( builder: (context) => AssignDeviceView( roomId: state.roomsList[index].id ?? '', - unitId: unitId, + unitId: unit.id, + unit: unit, )), ); }, @@ -100,14 +102,14 @@ class RoomsView extends StatelessWidget { BlocProvider.of(context).add( AddNewRoom( roomName: textEditingController.text, - unitId: unitId)); + unit: unit)); textEditingController.clear(); }, child: const Row( mainAxisAlignment: MainAxisAlignment.start, children: [ BodyMedium( - text: 'Add Space', + text: 'Add Room', fontColor: ColorsManager.primaryColor, ), ], diff --git a/lib/features/scene/bloc/create_scene/create_scene_bloc.dart b/lib/features/scene/bloc/create_scene/create_scene_bloc.dart index 024faf4..ac6072e 100644 --- a/lib/features/scene/bloc/create_scene/create_scene_bloc.dart +++ b/lib/features/scene/bloc/create_scene/create_scene_bloc.dart @@ -60,7 +60,8 @@ class CreateSceneBloc extends Bloc String selectedIcon = ''; bool showInDeviceScreen = false; - FutureOr _onAddSceneTask(AddTaskEvent event, Emitter emit) { + FutureOr _onAddSceneTask( + AddTaskEvent event, Emitter emit) { emit(CreateSceneLoading()); if (event.isAutomation == true) { final copyList = List.from(automationTempTasksList); @@ -95,7 +96,8 @@ class CreateSceneBloc extends Bloc } } - void addToTempTaskList(TempHoldSceneTasksEvent event, Emitter emit) { + void addToTempTaskList( + TempHoldSceneTasksEvent event, Emitter emit) { emit(CreateSceneLoading()); bool updated = false; @@ -180,7 +182,8 @@ class CreateSceneBloc extends Bloc )); } - void addToTempAutomationTaskList(TempHoldSceneTasksEvent event, Emitter emit) { + void addToTempAutomationTaskList( + TempHoldSceneTasksEvent event, Emitter emit) { emit(CreateSceneLoading()); bool updated = false; for (var element in automationTempTasksList) { @@ -202,8 +205,10 @@ class CreateSceneBloc extends Bloc ], comparator: automationComparatorValues[element.code], ); - automationTempTasksList[automationTempTasksList.indexOf(element)] = updatedElement; - automationSelectedValues[updatedElement.code] = event.deviceControlModel.value; + automationTempTasksList[automationTempTasksList.indexOf(element)] = + updatedElement; + automationSelectedValues[updatedElement.code] = + event.deviceControlModel.value; updated = true; break; } @@ -223,10 +228,12 @@ class CreateSceneBloc extends Bloc icon: '', ), ], - comparator: automationComparatorValues[event.deviceControlModel.code] ?? '==', + comparator: + automationComparatorValues[event.deviceControlModel.code] ?? '==', ); automationTempTasksList.add(newElement); - automationSelectedValues[newElement.code] = event.deviceControlModel.value; + automationSelectedValues[newElement.code] = + event.deviceControlModel.value; } emit(AddSceneTask( tasksList: tasksList, @@ -235,7 +242,8 @@ class CreateSceneBloc extends Bloc )); } - FutureOr _selectedValue(SelectedValueEvent event, Emitter emit) { + FutureOr _selectedValue( + SelectedValueEvent event, Emitter emit) { if (event.isAutomation == true) { automationSelectedValues[event.code] = event.value; automationComparatorValues[event.code] = event.comparator ?? '=='; @@ -272,7 +280,8 @@ class CreateSceneBloc extends Bloc )); } - FutureOr _removeTaskById(RemoveTaskByIdEvent event, Emitter emit) { + FutureOr _removeTaskById( + RemoveTaskByIdEvent event, Emitter emit) { emit(CreateSceneLoading()); if (event.isAutomation == true) { for (var element in automationTasksList) { @@ -345,7 +354,8 @@ class CreateSceneBloc extends Bloc : await SceneApi.createScene(event.createSceneModel!); } else if (event.createAutomationModel != null) { response = event.updateScene - ? await SceneApi.updateAutomation(event.createAutomationModel!, event.sceneId) + ? await SceneApi.updateAutomation( + event.createAutomationModel!, event.sceneId) : await SceneApi.createAutomation(event.createAutomationModel!); } @@ -359,12 +369,14 @@ class CreateSceneBloc extends Bloc automationComparatorValues.clear(); selectedIcon = ''; showInDeviceScreen = false; - effectiveTime = EffectiveTime(start: '00:00', end: '23:59', loops: '1111111'); + effectiveTime = + EffectiveTime(start: '00:00', end: '23:59', loops: '1111111'); sceneType = CreateSceneEnum.none; conditionRule = 'or'; emit(const CreateSceneWithTasks(success: true)); - CustomSnackBar.greenSnackBar( - event.updateScene ? 'Scene updated successfully' : 'Scene created successfully'); + CustomSnackBar.greenSnackBar(event.updateScene + ? 'Scene updated successfully' + : 'Scene created successfully'); } else { emit(const CreateSceneError(message: 'Something went wrong')); } @@ -378,7 +390,8 @@ class CreateSceneBloc extends Bloc } } - FutureOr _clearTaskList(ClearTaskListEvent event, Emitter emit) { + FutureOr _clearTaskList( + ClearTaskListEvent event, Emitter emit) { emit(CreateSceneLoading()); automationTasksList.clear(); tasksList.clear(); @@ -389,7 +402,8 @@ class CreateSceneBloc extends Bloc )); } - FutureOr _clearTabToRunSetting(ClearTabToRunSetting event, Emitter emit) { + FutureOr _clearTabToRunSetting( + ClearTabToRunSetting event, Emitter emit) { emit(CreateSceneLoading()); selectedIcon = ''; showInDeviceScreen = false; @@ -416,7 +430,8 @@ class CreateSceneBloc extends Bloc automationComparatorValues.clear(); selectedIcon = ''; showInDeviceScreen = false; - effectiveTime = EffectiveTime(start: '00:00', end: '23:59', loops: '1111111'); + effectiveTime = + EffectiveTime(start: '00:00', end: '23:59', loops: '1111111'); sceneType = CreateSceneEnum.none; conditionRule = 'or'; @@ -425,10 +440,14 @@ class CreateSceneBloc extends Bloc : await SceneApi.getSceneDetails(event.sceneId); if (response.id.isNotEmpty) { if (event.isAutomation) { - automationTasksList = List.from(getTaskListFunctionsFromApi( - actions: [], isAutomation: true, conditions: response.conditions)); + automationTasksList = List.from( + getTaskListFunctionsFromApi( + actions: [], + isAutomation: true, + conditions: response.conditions)); tasksList = List.from( - getTaskListFunctionsFromApi(actions: response.actions, isAutomation: false)); + getTaskListFunctionsFromApi( + actions: response.actions, isAutomation: false)); conditionRule = response.decisionExpr ?? conditionRule; @@ -441,11 +460,13 @@ class CreateSceneBloc extends Bloc : EffectiveTime(start: '00:00', end: '23:59', loops: '1111111'); // Set the days directly from the API response - BlocProvider.of(NavigationService.navigatorKey.currentContext!) + BlocProvider.of( + NavigationService.navigatorKey.currentContext!) .add(SetDays(response.effectiveTime?.loops ?? '1111111')); // Set Custom Time and reset days first - BlocProvider.of(NavigationService.navigatorKey.currentContext!) + BlocProvider.of( + NavigationService.navigatorKey.currentContext!) .add(SetCustomTime(effectiveTime!.start, effectiveTime!.end)); emit(AddSceneTask( @@ -457,7 +478,8 @@ class CreateSceneBloc extends Bloc showInDevice: showInDeviceScreen)); } else { tasksList = List.from( - getTaskListFunctionsFromApi(actions: response.actions, isAutomation: false)); + getTaskListFunctionsFromApi( + actions: response.actions, isAutomation: false)); selectedIcon = response.icon!; showInDeviceScreen = response.showInDevice!; emit(AddSceneTask( @@ -475,7 +497,8 @@ class CreateSceneBloc extends Bloc } } - FutureOr _fetchIconScene(SceneIconEvent event, Emitter emit) async { + FutureOr _fetchIconScene( + SceneIconEvent event, Emitter emit) async { emit(CreateSceneLoading()); try { iconModelList = await SceneApi.getIcon(); @@ -491,7 +514,8 @@ class CreateSceneBloc extends Bloc } } - FutureOr _iconSelected(IconSelected event, Emitter emit) async { + FutureOr _iconSelected( + IconSelected event, Emitter emit) async { try { if (event.confirmSelection) { selectedIcon = event.iconId; @@ -531,7 +555,8 @@ class CreateSceneBloc extends Bloc return days[index]; } - FutureOr _clearTempTaskList(ClearTempTaskListEvent event, Emitter emit) { + FutureOr _clearTempTaskList( + ClearTempTaskListEvent event, Emitter emit) { emit(CreateSceneLoading()); if (event.isAutomation == true) { automationTempTasksList.clear(); @@ -575,13 +600,18 @@ class CreateSceneBloc extends Bloc } } - FutureOr _deleteScene(DeleteSceneEvent event, Emitter emit) async { + FutureOr _deleteScene( + DeleteSceneEvent event, Emitter emit) async { emit(DeleteSceneLoading()); try { - final response = sceneType.name == CreateSceneEnum.deviceStatusChanges.name - ? await SceneApi.deleteAutomation(automationId: event.sceneId, unitUuid: event.unitUuid) - : await SceneApi.deleteScene(sceneId: event.sceneId, unitUuid: event.unitUuid); + final response = + sceneType.name == CreateSceneEnum.deviceStatusChanges.name + ? await SceneApi.deleteAutomation( + automationId: event.sceneId, unitUuid: event.unitUuid) + : await SceneApi.deleteScene( + sceneId: event.sceneId, + ); if (response == true) { emit(const DeleteSceneSuccess(true)); } else { @@ -592,7 +622,8 @@ class CreateSceneBloc extends Bloc } } - FutureOr _updateTaskValue(UpdateTaskEvent event, Emitter emit) { + FutureOr _updateTaskValue( + UpdateTaskEvent event, Emitter emit) { emit(CreateSceneLoading()); if (event.isAutomation == true) { for (var i = 0; i < automationTasksList.length; i++) { @@ -628,7 +659,8 @@ class CreateSceneBloc extends Bloc )); } - FutureOr _selectConditionRule(SelectConditionEvent event, Emitter emit) { + FutureOr _selectConditionRule( + SelectConditionEvent event, Emitter emit) { emit(CreateSceneInitial()); if (event.condition.contains('any')) { conditionRule = 'or'; @@ -643,7 +675,8 @@ class CreateSceneBloc extends Bloc )); } - FutureOr _sceneTypeEvent(SceneTypeEvent event, Emitter emit) { + FutureOr _sceneTypeEvent( + SceneTypeEvent event, Emitter emit) { // emit(CreateSceneInitial()); if (event.type == CreateSceneEnum.tabToRun) { diff --git a/lib/features/scene/bloc/scene_bloc/scene_bloc.dart b/lib/features/scene/bloc/scene_bloc/scene_bloc.dart index 701e30a..4837d32 100644 --- a/lib/features/scene/bloc/scene_bloc/scene_bloc.dart +++ b/lib/features/scene/bloc/scene_bloc/scene_bloc.dart @@ -24,7 +24,9 @@ class SceneBloc extends Bloc { try { if (event.unitId.isNotEmpty) { - scenes = await SceneApi.getScenesByUnitId(event.unitId, showInDevice: event.showInDevice); + scenes = await SceneApi.getScenesByUnitId( + event.unitId, event.unit.community.uuid, + showInDevice: event.showInDevice); emit(SceneLoaded(scenes, automationList)); } else { emit(const SceneError(message: 'Unit ID is empty')); @@ -34,7 +36,8 @@ class SceneBloc extends Bloc { } } - Future _onLoadAutomation(LoadAutomation event, Emitter emit) async { + Future _onLoadAutomation( + LoadAutomation event, Emitter emit) async { emit(SceneLoading()); try { @@ -49,7 +52,8 @@ class SceneBloc extends Bloc { } } - Future _onSceneTrigger(SceneTrigger event, Emitter emit) async { + Future _onSceneTrigger( + SceneTrigger event, Emitter emit) async { final currentState = state; if (currentState is SceneLoaded) { emit(SceneLoaded( @@ -76,8 +80,9 @@ class SceneBloc extends Bloc { UpdateAutomationStatus event, Emitter emit) async { final currentState = state; if (currentState is SceneLoaded) { - final newLoadingStates = Map.from(currentState.loadingStates) - ..[event.automationId] = true; + final newLoadingStates = + Map.from(currentState.loadingStates) + ..[event.automationId] = true; emit(SceneLoaded( currentState.scenes, @@ -86,11 +91,11 @@ class SceneBloc extends Bloc { )); try { - final success = - await SceneApi.updateAutomationStatus(event.automationId, event.automationStatusUpdate); + final success = await SceneApi.updateAutomationStatus( + event.automationId, event.automationStatusUpdate); if (success) { - automationList = - await SceneApi.getAutomationByUnitId(event.automationStatusUpdate.unitUuid); + automationList = await SceneApi.getAutomationByUnitId( + event.automationStatusUpdate.spaceUuid); newLoadingStates[event.automationId] = false; emit(SceneLoaded( currentState.scenes, diff --git a/lib/features/scene/bloc/scene_bloc/scene_event.dart b/lib/features/scene/bloc/scene_bloc/scene_event.dart index d72f3a7..7807bbb 100644 --- a/lib/features/scene/bloc/scene_bloc/scene_event.dart +++ b/lib/features/scene/bloc/scene_bloc/scene_event.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/scene/model/update_automation.dart'; abstract class SceneEvent extends Equatable { @@ -11,8 +12,9 @@ abstract class SceneEvent extends Equatable { class LoadScenes extends SceneEvent { final String unitId; final bool showInDevice; + final SpaceModel unit; - const LoadScenes(this.unitId, {this.showInDevice = false}); + const LoadScenes(this.unitId, this.unit, {this.showInDevice = false}); @override List get props => [unitId, showInDevice]; diff --git a/lib/features/scene/bloc/tab_change/tab_change_bloc.dart b/lib/features/scene/bloc/tab_change/tab_change_bloc.dart index a7c6481..7fc18db 100644 --- a/lib/features/scene/bloc/tab_change/tab_change_bloc.dart +++ b/lib/features/scene/bloc/tab_change/tab_change_bloc.dart @@ -17,7 +17,7 @@ class TabBarBloc extends Bloc { if (event.roomId == "-1") { deviceManagerBloc.add(FetchAllDevices()); } else { - deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId)); + deviceManagerBloc.add(FetchDevicesByRoomId(event.roomId,event.unit)); } emit(TabSelected( roomId: event.roomId, selectedTabIndex: event.selectedIndex)); diff --git a/lib/features/scene/bloc/tab_change/tab_change_event.dart b/lib/features/scene/bloc/tab_change/tab_change_event.dart index c052280..f39d823 100644 --- a/lib/features/scene/bloc/tab_change/tab_change_event.dart +++ b/lib/features/scene/bloc/tab_change/tab_change_event.dart @@ -1,3 +1,5 @@ +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; + abstract class TabBarEvent { const TabBarEvent(); } @@ -5,5 +7,7 @@ abstract class TabBarEvent { class TabChanged extends TabBarEvent { final int selectedIndex; final String roomId; - const TabChanged({required this.selectedIndex, required this.roomId}); + final SpaceModel unit; + const TabChanged( + {required this.selectedIndex, required this.roomId, required this.unit}); } diff --git a/lib/features/scene/helper/scene_logic_helper.dart b/lib/features/scene/helper/scene_logic_helper.dart index e6b8d33..8db714e 100644 --- a/lib/features/scene/helper/scene_logic_helper.dart +++ b/lib/features/scene/helper/scene_logic_helper.dart @@ -124,7 +124,7 @@ mixin SceneLogicHelper { )); } else { final createSceneModel = CreateSceneModel( - unitUuid: HomeCubit.getInstance().selectedSpace!.id ?? '', + spaceUuid: HomeCubit.getInstance().selectedSpace!.id ?? '', iconId: sceneBloc.selectedIcon, showInDevice: sceneBloc.showInDeviceScreen, sceneName: sceneName.text, diff --git a/lib/features/scene/model/create_automation_model.dart b/lib/features/scene/model/create_automation_model.dart index a349dc2..e54c3e4 100644 --- a/lib/features/scene/model/create_automation_model.dart +++ b/lib/features/scene/model/create_automation_model.dart @@ -39,7 +39,7 @@ class CreateAutomationModel { Map toMap([String? automationId]) { return { - if (automationId == null) 'unitUuid': unitUuid, + if (automationId == null) 'spaceUuid': unitUuid, 'automationName': automationName, 'decisionExpr': decisionExpr, 'effectiveTime': effectiveTime.toMap(), @@ -50,7 +50,7 @@ class CreateAutomationModel { factory CreateAutomationModel.fromMap(Map map) { return CreateAutomationModel( - unitUuid: map['unitUuid'] ?? '', + unitUuid: map['spaceUuid'] ?? '', automationName: map['automationName'] ?? '', decisionExpr: map['decisionExpr'] ?? '', effectiveTime: EffectiveTime.fromMap(map['effectiveTime']), diff --git a/lib/features/scene/model/create_scene_model.dart b/lib/features/scene/model/create_scene_model.dart index 43f62cb..c669aa9 100644 --- a/lib/features/scene/model/create_scene_model.dart +++ b/lib/features/scene/model/create_scene_model.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; class CreateSceneModel { - String unitUuid; + String spaceUuid; String iconId; bool showInDevice; String sceneName; @@ -11,7 +11,7 @@ class CreateSceneModel { List actions; CreateSceneModel({ - required this.unitUuid, + required this.spaceUuid, required this.iconId, required this.showInDevice, required this.sceneName, @@ -20,15 +20,16 @@ class CreateSceneModel { }); CreateSceneModel copyWith({ - String? unitUuid, + String? spaceUuid, String? iconId, bool? showInDevice, String? sceneName, String? decisionExpr, List? actions, + bool? showInHomePage, }) { return CreateSceneModel( - unitUuid: unitUuid ?? this.unitUuid, + spaceUuid: spaceUuid ?? this.spaceUuid, iconId: iconId ?? this.iconId, showInDevice: showInDevice ?? this.showInDevice, sceneName: sceneName ?? this.sceneName, @@ -39,7 +40,7 @@ class CreateSceneModel { Map toMap([String? sceneId]) { return { - if (sceneId == null) 'unitUuid': unitUuid, + if (sceneId == null) 'spaceUuid': spaceUuid, if (iconId.isNotEmpty) 'iconUuid': iconId, 'showInHomePage': showInDevice, 'sceneName': sceneName, @@ -50,23 +51,24 @@ class CreateSceneModel { factory CreateSceneModel.fromMap(Map map) { return CreateSceneModel( - unitUuid: map['unitUuid'] ?? '', - iconId: map['iconUuid'] ?? '', + spaceUuid: map['spaceUuid'] ?? '', showInDevice: map['showInHomePage'] ?? false, + iconId: map['iconUuid'] ?? '', sceneName: map['sceneName'] ?? '', decisionExpr: map['decisionExpr'] ?? '', - actions: - List.from(map['actions']?.map((x) => CreateSceneAction.fromMap(x))), + actions: List.from( + map['actions']?.map((x) => CreateSceneAction.fromMap(x))), ); } String toJson([String? sceneId]) => json.encode(toMap(sceneId)); - factory CreateSceneModel.fromJson(String source) => CreateSceneModel.fromMap(json.decode(source)); + factory CreateSceneModel.fromJson(String source) => + CreateSceneModel.fromMap(json.decode(source)); @override String toString() { - return 'CreateSceneModel(unitUuid: $unitUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)'; + return 'CreateSceneModel(unitUuid: $spaceUuid, sceneName: $sceneName, decisionExpr: $decisionExpr, actions: $actions)'; } @override @@ -74,7 +76,7 @@ class CreateSceneModel { if (identical(this, other)) return true; return other is CreateSceneModel && - other.unitUuid == unitUuid && + other.spaceUuid == spaceUuid && other.iconId == iconId && other.showInDevice == showInDevice && other.sceneName == sceneName && @@ -84,7 +86,10 @@ class CreateSceneModel { @override int get hashCode { - return unitUuid.hashCode ^ sceneName.hashCode ^ decisionExpr.hashCode ^ actions.hashCode; + return spaceUuid.hashCode ^ + sceneName.hashCode ^ + decisionExpr.hashCode ^ + actions.hashCode; } } @@ -130,7 +135,8 @@ class CreateSceneAction { return CreateSceneAction( entityId: map['entityId'] ?? '', actionExecutor: map['actionExecutor'] ?? '', - executorProperty: CreateSceneExecutorProperty.fromMap(map['executorProperty']), + executorProperty: + CreateSceneExecutorProperty.fromMap(map['executorProperty']), ); } @@ -154,7 +160,8 @@ class CreateSceneAction { } @override - int get hashCode => entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode; + int get hashCode => + entityId.hashCode ^ actionExecutor.hashCode ^ executorProperty.hashCode; } class CreateSceneExecutorProperty { @@ -218,5 +225,6 @@ class CreateSceneExecutorProperty { } @override - int get hashCode => functionCode.hashCode ^ functionValue.hashCode ^ delaySeconds.hashCode; + int get hashCode => + functionCode.hashCode ^ functionValue.hashCode ^ delaySeconds.hashCode; } diff --git a/lib/features/scene/model/scene_details_model.dart b/lib/features/scene/model/scene_details_model.dart index 99a2c5f..d356d43 100644 --- a/lib/features/scene/model/scene_details_model.dart +++ b/lib/features/scene/model/scene_details_model.dart @@ -25,28 +25,34 @@ class SceneDetailsModel { this.effectiveTime, }); - factory SceneDetailsModel.fromRawJson(String str) => SceneDetailsModel.fromJson(json.decode(str)); + factory SceneDetailsModel.fromRawJson(String str) => + SceneDetailsModel.fromJson(json.decode(str)); String toRawJson() => json.encode(toJson()); - factory SceneDetailsModel.fromJson(Map json) => SceneDetailsModel( - id: json["id"], - name: json["name"], - status: json["status"], - type: json["type"], - actions: (json["actions"] as List) - .map((x) => Action.fromJson(x)) - .where((x) => x != null) - .toList() - .cast(), - conditions: json["conditions"] != null - ? (json["conditions"] as List).map((x) => Condition.fromJson(x)).toList() - : null, - decisionExpr: json["decisionExpr"], - effectiveTime: - json["effectiveTime"] != null ? EffectiveTime.fromJson(json["effectiveTime"]) : null, - icon: json["iconUuid"] != null ? json["iconUuid"] ?? '' : '', - showInDevice: json['showInHome'] != null ? json['showInHome'] ?? false : false); + factory SceneDetailsModel.fromJson(Map json) => + SceneDetailsModel( + id: json["uuid"] ?? json["id"], + name: json["name"], + status: json["status"], + type: json["type"], + actions: (json["actions"] as List) + .map((x) => Action.fromJson(x)) + .where((x) => x != null) + .toList() + .cast(), + conditions: json["conditions"] != null + ? (json["conditions"] as List) + .map((x) => Condition.fromJson(x)) + .toList() + : null, + decisionExpr: json["decisionExpr"], + effectiveTime: json["effectiveTime"] != null + ? EffectiveTime.fromJson(json["effectiveTime"]) + : null, + icon: json["iconUuid"] != null ? json["iconUuid"] ?? '' : '', + showInDevice: + json['showInHome'] != null ? json['showInHome'] ?? false : false); Map toJson() => { "id": id, @@ -54,8 +60,9 @@ class SceneDetailsModel { "status": status, "type": type, "actions": List.from(actions.map((x) => x.toJson())), - "conditions": - conditions != null ? List.from(conditions!.map((x) => x.toJson())) : null, + "conditions": conditions != null + ? List.from(conditions!.map((x) => x.toJson())) + : null, "decisionExpr": decisionExpr, "effectiveTime": effectiveTime?.toJson(), }; @@ -116,7 +123,8 @@ class ExecutorProperty { this.delaySeconds, }); - factory ExecutorProperty.fromJson(Map json) => ExecutorProperty( + factory ExecutorProperty.fromJson(Map json) => + ExecutorProperty( functionCode: json["functionCode"] ?? '', functionValue: json["functionValue"] ?? '', delaySeconds: json["delaySeconds"] ?? 0, @@ -142,7 +150,8 @@ class Condition { required this.expr, }); - factory Condition.fromRawJson(String str) => Condition.fromJson(json.decode(str)); + factory Condition.fromRawJson(String str) => + Condition.fromJson(json.decode(str)); String toRawJson() => json.encode(toJson()); @@ -200,7 +209,8 @@ class EffectiveTime { required this.loops, }); - factory EffectiveTime.fromRawJson(String str) => EffectiveTime.fromJson(json.decode(str)); + factory EffectiveTime.fromRawJson(String str) => + EffectiveTime.fromJson(json.decode(str)); String toRawJson() => json.encode(toJson()); diff --git a/lib/features/scene/model/scenes_model.dart b/lib/features/scene/model/scenes_model.dart index 0881ba9..7f9d654 100644 --- a/lib/features/scene/model/scenes_model.dart +++ b/lib/features/scene/model/scenes_model.dart @@ -3,32 +3,40 @@ import 'dart:typed_data'; class ScenesModel { final String id; + final String? sceneTuyaId; final String name; final String status; final String type; - final String icon; + final String? icon; ScenesModel( {required this.id, + this.sceneTuyaId, required this.name, required this.status, required this.type, - required this.icon}); + this.icon}); - factory ScenesModel.fromRawJson(String str) => ScenesModel.fromJson(json.decode(str)); + factory ScenesModel.fromRawJson(String str) => + ScenesModel.fromJson(json.decode(str)); String toRawJson() => json.encode(toJson()); - Uint8List get iconInBytes => base64Decode(icon); - - factory ScenesModel.fromJson(Map json) => ScenesModel( - id: json["id"], - name: json["name"] ?? '', - status: json["status"] ?? '', - type: json["type"] ?? '', - icon: json["icon"] ?? ''); + Uint8List get iconInBytes => base64Decode(icon ?? ''); + factory ScenesModel.fromJson(Map json) { + return ScenesModel( + id: json["id"] ?? json["uuid"] ?? '', // Fallback to empty string if id is null + sceneTuyaId: json["sceneTuyaId"] as String?, // Nullable + name: json["name"] ?? '', // Fallback to empty string if name is null + status: + json["status"] ?? '', // Fallback to empty string if status is null + type: json["type"] ?? '', // Fallback to empty string if type is null + icon: json["icon"] as String?, // Nullable + ); + } Map toJson() => { "id": id, + "sceneTuyaId": sceneTuyaId ?? '', "name": name, "status": status, "type": type, diff --git a/lib/features/scene/model/update_automation.dart b/lib/features/scene/model/update_automation.dart index d15d191..86aefbb 100644 --- a/lib/features/scene/model/update_automation.dart +++ b/lib/features/scene/model/update_automation.dart @@ -1,11 +1,11 @@ import 'dart:convert'; class AutomationStatusUpdate { - final String unitUuid; + final String spaceUuid; final bool isEnable; AutomationStatusUpdate({ - required this.unitUuid, + required this.spaceUuid, required this.isEnable, }); @@ -16,23 +16,23 @@ class AutomationStatusUpdate { factory AutomationStatusUpdate.fromJson(Map json) => AutomationStatusUpdate( - unitUuid: json["unitUuid"], + spaceUuid: json["spaceUuid"], isEnable: json["isEnable"], ); Map toJson() => { - "unitUuid": unitUuid, + "spaceUuid": spaceUuid, "isEnable": isEnable, }; factory AutomationStatusUpdate.fromMap(Map map) => AutomationStatusUpdate( - unitUuid: map["unitUuid"], + spaceUuid: map["spaceUuid"], isEnable: map["isEnable"], ); Map toMap() => { - "unitUuid": unitUuid, + "spaceUuid": spaceUuid, "isEnable": isEnable, }; } diff --git a/lib/features/scene/view/scene_rooms_tabbar.dart b/lib/features/scene/view/scene_rooms_tabbar.dart index a97d668..d9dcb80 100644 --- a/lib/features/scene/view/scene_rooms_tabbar.dart +++ b/lib/features/scene/view/scene_rooms_tabbar.dart @@ -1,8 +1,9 @@ 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/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/room_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; 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/widgets/scene_devices/scene_devices_body.dart'; @@ -26,20 +27,21 @@ class _SceneRoomsTabBarDevicesViewState extends State with SingleTickerProviderStateMixin { late final TabController _tabController; - List? rooms = []; + List? rooms = []; + late final SpaceModel selectedSpace; @override void initState() { - rooms = List.from(HomeCubit.getInstance().selectedSpace?.rooms ?? []); + selectedSpace = HomeCubit.getInstance().selectedSpace!; + rooms = List.from(HomeCubit.getInstance().selectedSpace?.subspaces ?? []); if (rooms != null) { if (rooms![0].id != '-1') { rooms?.insert( 0, - RoomModel( + SubSpaceModel( name: 'All Devices', devices: DevicesCubit.getInstance().allDevices, id: '-1', - type: SpaceType.Room, ), ); } @@ -56,8 +58,10 @@ class _SceneRoomsTabBarDevicesViewState final value = _tabController.index; /// select tab - context.read().add( - TabChanged(selectedIndex: value, roomId: rooms?[value].id ?? '')); + context.read().add(TabChanged( + selectedIndex: value, + roomId: rooms?[value].id ?? '', + unit: selectedSpace)); return; } } diff --git a/lib/features/scene/view/scene_view.dart b/lib/features/scene/view/scene_view.dart index a2d1881..41d1fdd 100644 --- a/lib/features/scene/view/scene_view.dart +++ b/lib/features/scene/view/scene_view.dart @@ -1,6 +1,8 @@ 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/community_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/scene/bloc/create_scene/create_scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart'; @@ -21,32 +23,47 @@ class SceneView extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => SceneBloc() - ..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '', showInDevice: pageType)) + ..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 ?? '')), child: BlocBuilder( builder: (context, state) { if (state is DeleteSceneSuccess) { if (state.success) { + BlocProvider.of(context).add(LoadScenes( + HomeCubit.getInstance().selectedSpace!.id!,HomeCubit.getInstance().selectedSpace!, + showInDevice: pageType)); BlocProvider.of(context).add( - LoadScenes(HomeCubit.getInstance().selectedSpace!.id!, showInDevice: pageType)); - BlocProvider.of(context) - .add(LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); + LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); } } if (state is CreateSceneWithTasks) { if (state.success == true) { + BlocProvider.of(context).add(LoadScenes( + HomeCubit.getInstance().selectedSpace!.id!,HomeCubit.getInstance().selectedSpace!, + showInDevice: pageType)); BlocProvider.of(context).add( - LoadScenes(HomeCubit.getInstance().selectedSpace!.id!, showInDevice: pageType)); - BlocProvider.of(context) - .add(LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); - context.read().add(const SmartSceneClearEvent()); + LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); + context + .read() + .add(const SmartSceneClearEvent()); } } return BlocListener( listener: (context, state) { if (state is SceneTriggerSuccess) { context.showCustomSnackbar( - message: 'Scene ${state.sceneName} triggered successfully!'); + message: + 'Scene ${state.sceneName} triggered successfully!'); } }, child: HomeCubit.getInstance().spaces?.isEmpty ?? true @@ -83,25 +100,30 @@ class SceneView extends StatelessWidget { child: ListView( children: [ Theme( - data: ThemeData() - .copyWith(dividerColor: Colors.transparent), + data: ThemeData().copyWith( + dividerColor: Colors.transparent), child: ExpansionTile( - tilePadding: const EdgeInsets.symmetric(horizontal: 6), + tilePadding: + const EdgeInsets.symmetric( + horizontal: 6), initiallyExpanded: true, iconColor: ColorsManager.grayColor, - title: const BodyMedium(text: 'Tap to run routines'), + title: const BodyMedium( + text: 'Tap to run routines'), children: [ scenes.isNotEmpty ? SceneGrid( scenes: scenes, - loadingSceneId: state.loadingSceneId, + loadingSceneId: + state.loadingSceneId, disablePlayButton: false, - loadingStates: - state.loadingStates, // Add this line + loadingStates: state + .loadingStates, // Add this line ) : const Center( child: BodyMedium( - text: 'No scenes have been added yet', + text: + 'No scenes have been added yet', ), ), const SizedBox( @@ -111,25 +133,30 @@ class SceneView extends StatelessWidget { ), ), Theme( - data: ThemeData() - .copyWith(dividerColor: Colors.transparent), + data: ThemeData().copyWith( + dividerColor: Colors.transparent), child: ExpansionTile( initiallyExpanded: true, iconColor: ColorsManager.grayColor, - tilePadding: const EdgeInsets.symmetric(horizontal: 6), - title: const BodyMedium(text: 'Automation'), + tilePadding: + const EdgeInsets.symmetric( + horizontal: 6), + title: const BodyMedium( + text: 'Automation'), children: [ automationList.isNotEmpty ? SceneGrid( scenes: automationList, - loadingSceneId: state.loadingSceneId, + loadingSceneId: + state.loadingSceneId, disablePlayButton: true, - loadingStates: - state.loadingStates, // Add this line + loadingStates: state + .loadingStates, // Add this line ) : const Center( child: BodyMedium( - text: 'No automations have been added yet', + text: + 'No automations have been added yet', ), ), const SizedBox( diff --git a/lib/features/scene/widgets/delete_routine_b.dart b/lib/features/scene/widgets/delete_routine_b.dart index 18293b1..b344683 100644 --- a/lib/features/scene/widgets/delete_routine_b.dart +++ b/lib/features/scene/widgets/delete_routine_b.dart @@ -24,7 +24,7 @@ class DeleteRoutineButton extends StatelessWidget { if (state.success) { navigateToRoute(context, Routes.homeRoute); BlocProvider.of(context) - .add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!)); + .add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!,HomeCubit.getInstance().selectedSpace!)); BlocProvider.of(context).add( LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!)); } diff --git a/lib/features/scene/widgets/scene_devices/scene_devices_body.dart b/lib/features/scene/widgets/scene_devices/scene_devices_body.dart index b5296b7..a57919b 100644 --- a/lib/features/scene/widgets/scene_devices/scene_devices_body.dart +++ b/lib/features/scene/widgets/scene_devices/scene_devices_body.dart @@ -4,6 +4,7 @@ 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_state.dart'; import 'package:syncrow_app/features/devices/model/room_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; 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/enum/create_scene_enum.dart'; @@ -24,7 +25,7 @@ class SceneDevicesBody extends StatelessWidget { }) : _tabController = tabController; final TabController _tabController; - final List? rooms; + final List? rooms; @override Widget build(BuildContext context) { @@ -76,7 +77,7 @@ class SceneDevicesBody extends StatelessWidget { } Widget _buildRoomTab( - RoomModel room, BuildContext context, bool isAutomationDeviceStatus) { + SubSpaceModel room, BuildContext context, bool isAutomationDeviceStatus) { return BlocBuilder( builder: (context, state) { if (state.loading && state.devices == null) { diff --git a/lib/features/scene/widgets/scene_view_widget/scene_item.dart b/lib/features/scene/widgets/scene_view_widget/scene_item.dart index 78f4200..220cd96 100644 --- a/lib/features/scene/widgets/scene_view_widget/scene_item.dart +++ b/lib/features/scene/widgets/scene_view_widget/scene_item.dart @@ -38,8 +38,8 @@ class SceneItem extends StatelessWidget { onTap: () { context.read().add(const SmartSceneClearEvent()); if (disablePlayButton == false) { - BlocProvider.of(context) - .add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false)); + BlocProvider.of(context).add( + FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false)); /// the state to set the scene type must be after the fetch BlocProvider.of(context) @@ -73,22 +73,32 @@ class SceneItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (!disablePlayButton) + if (!disablePlayButton && scene.iconInBytes != null && scene.iconInBytes.isNotEmpty) Image.memory( + scene.iconInBytes, + height: 32, + width: 32, + fit: BoxFit.fill, + errorBuilder: (context, error, stackTrace) => Image.asset( + Assets.assetsIconsLogo, + height: 32, + width: 32, + fit: BoxFit.fill), + ), + if (disablePlayButton || scene.iconInBytes == null || scene.iconInBytes.isEmpty) + SvgPicture.asset( + Assets.automationIcon, height: 32, width: 32, - scene.iconInBytes, fit: BoxFit.fill, - errorBuilder: (context, error, stackTrace) => - Image.asset(height: 32, width: 32, fit: BoxFit.fill, Assets.assetsIconsLogo), ), - if (disablePlayButton) - SvgPicture.asset(height: 32, width: 32, fit: BoxFit.fill, Assets.automationIcon), disablePlayButton == false ? IconButton( padding: EdgeInsets.zero, onPressed: () { - context.read().add(SceneTrigger(scene.id, scene.name)); + context + .read() + .add(SceneTrigger(scene.id, scene.name)); }, icon: isLoading ? const Center( @@ -110,11 +120,15 @@ class SceneItem extends StatelessWidget { activeColor: ColorsManager.primaryColor, value: scene.status == 'enable' ? true : false, onChanged: (value) { - context.read().add(UpdateAutomationStatus( - automationStatusUpdate: AutomationStatusUpdate( - isEnable: value, - unitUuid: HomeCubit.getInstance().selectedSpace!.id!), - automationId: scene.id)); + context.read().add( + UpdateAutomationStatus( + automationStatusUpdate: + AutomationStatusUpdate( + isEnable: value, + spaceUuid: HomeCubit.getInstance() + .selectedSpace! + .id!), + automationId: scene.id)); }, ), ], diff --git a/lib/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart b/lib/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart index 37a5e7c..a50aa6f 100644 --- a/lib/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart +++ b/lib/features/scene/widgets/select_smart_scene/smart_enable_tab_run.dart @@ -17,7 +17,7 @@ class SmartEnableTabRun extends StatelessWidget { width: double.infinity, child: BlocBuilder( bloc: context.read() - ..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '')), + ..add(LoadScenes(HomeCubit.getInstance().selectedSpace?.id ?? '',HomeCubit.getInstance().selectedSpace!)), builder: (context, state) { if (state is SceneLoading) { return const Align( diff --git a/lib/navigation/router.dart b/lib/navigation/router.dart index 990b7fb..c09a307 100644 --- a/lib/navigation/router.dart +++ b/lib/navigation/router.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; 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/space_model.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'; @@ -84,9 +86,18 @@ class Router { DeviceManagerBloc()..add(FetchAllDevices()), ), BlocProvider( - create: (BuildContext context) => TabBarBloc( - context.read()) - ..add(const TabChanged(selectedIndex: 0, roomId: '-1')), + create: (BuildContext context) => + TabBarBloc(context.read()) + ..add(TabChanged( + selectedIndex: 0, + roomId: '-1', + unit: SpaceModel( + id: '-1', + name: '', + community: Community( + uuid: '-1', + name: '', + )))), ), ], child: const SceneRoomsTabBarDevicesView(), diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index dfdb653..5d9528a 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -53,17 +53,26 @@ abstract class ApiEndpoints { //POST static const String addUnit = '/unit'; static const String addUnitToUser = '/unit/user'; + static const String verifyInvitationCode = + '/user/{userUuid}/spaces/verify-code'; + //GET static const String unitByUuid = '/unit/'; - static const String unitChild = '/unit/child/'; + static const String listSubspace = + '/communities/{communityUuid}/spaces/{spaceUuid}/subspaces'; static const String unitParent = '/unit/parent/{unitUuid}'; static const String unitUser = '/unit/user/'; - static const String invitationCode = '/unit/{unitUuid}/invitation-code'; - static const String verifyInvitationCode = '/unit/user/verify-code'; + static const String invitationCode = + '/communities/{communityUuid}/spaces/{unitUuid}/invitation-code'; //PUT static const String renameUnit = '/unit/{unitUuid}'; + //Subspace Module +//POST + static const String addSubSpace = + '/communities/{communityUuid}/spaces/{spaceUuid}/subspaces'; + ///Room Module //POST static const String addRoom = '/room'; @@ -75,6 +84,12 @@ abstract class ApiEndpoints { //PUT static const String renameRoom = '/room/{roomUuid}'; + //SPACE Module +//GET + static const String userSpaces = '/user/{userUuid}/spaces'; + static const String spaceDevices = + '/communities/{communityUuid}/spaces/{spaceUuid}/devices'; + ///Group Module //POST static const String addGroup = '/group'; @@ -101,12 +116,15 @@ abstract class ApiEndpoints { static const String openDoorLock = '/door-lock/open/{doorLockUuid}'; //GET - static const String deviceByRoom = '/device/room'; + static const String deviceByRoom = + '/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices'; static const String deviceByUuid = '/device/{deviceUuid}'; static const String deviceFunctions = '/device/{deviceUuid}/functions'; static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices'; - static const String deviceFunctionsStatus = '/device/{deviceUuid}/functions/status'; - static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status'; + static const String deviceFunctionsStatus = + '/device/{deviceUuid}/functions/status'; + static const String powerClamp = + '/device/{powerClampUuid}/power-clamp/status'; ///Device Permission Module //POST @@ -116,18 +134,20 @@ abstract class ApiEndpoints { //PUT static const String editDevicePermission = '/device-permission/edit/{userId}'; - static const String assignDeviceToRoom = '/device/room'; + static const String assignDeviceToRoom = + '/communities/{communityUuid}/spaces/{spaceUuid}/subspaces/{subSpaceUuid}/devices/{deviceUuid}'; /// Scene & Automation API //////////////////// /// POST static const String createScene = '/scene/tap-to-run'; - static const String triggerScene = '/scene/tap-to-run/trigger/{sceneId}'; + static const String triggerScene = '/scene/tap-to-run/{sceneId}/trigger'; static const String createAutomation = '/automation'; /// GET - static const String getUnitScenes = '/scene/tap-to-run/{unitUuid}'; + static const String getUnitScenes = + '/communities/{communityUuid}/spaces/{spaceUuid}/scenes'; - static const String getScene = '/scene/tap-to-run/details/{sceneId}'; + static const String getScene = '/scene/tap-to-run/{sceneId}'; static const String getIconScene = '/scene/icon'; static const String getUnitAutomation = '/automation/{unitUuid}'; @@ -144,10 +164,9 @@ abstract class ApiEndpoints { '/automation/status/{automationId}'; /// DELETE - static const String deleteScene = '/scene/tap-to-run/{unitUuid}/{sceneId}'; + static const String deleteScene = '/scene/tap-to-run/{sceneId}'; - static const String deleteAutomation = - '/automation/{unitUuid}/{automationId}'; + static const String deleteAutomation = '/automation/{automationId}'; //////////////////////Door Lock ////////////////////// //online diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 87fcdb6..fe6cc56 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -65,6 +65,7 @@ class DevicesAPI { showServerMessage: false, expectedResponseModel: (json) => DevicesCategoryModel.fromJsonList(json), ); + return response; } @@ -142,23 +143,41 @@ class DevicesAPI { return response; } - static Future> getDevicesByRoomId(String roomId) async { - final response = await _httpService.get( - path: ApiEndpoints.deviceByRoom, - queryParameters: {"roomUuid": roomId}, - showServerMessage: false, - expectedResponseModel: (json) { - if (json == null || json.isEmpty || json == []) { - return []; - } - List devices = []; - for (var device in json) { - devices.add(DeviceModel.fromJson(device)); - } - return devices; - }, - ); - return response; + static Future> getDevicesByRoomId({ + required String communityUuid, + required String spaceUuid, + required String roomId, + }) async { + try { + final String path = ApiEndpoints.deviceByRoom + .replaceAll('{communityUuid}', communityUuid) + .replaceAll('{spaceUuid}', spaceUuid) + .replaceAll('{subSpaceUuid}', roomId); + + final response = await _httpService.get( + path: path, + showServerMessage: false, + expectedResponseModel: (json) { + final data = json['data']; + + if (data == null || data.isEmpty) { + return []; + } + if (json == null || json.isEmpty || json == []) { + return []; + } + return data + .map((device) => DeviceModel.fromJson(device)) + .toList(); + }, + ); + + return response; + } catch (e) { + // Log the error if needed + // Return an empty list in case of error + return []; + } } static Future> getDevicesByGatewayId( diff --git a/lib/services/api/home_creation_api.dart b/lib/services/api/home_creation_api.dart index 5ac6d5a..5b2816b 100644 --- a/lib/services/api/home_creation_api.dart +++ b/lib/services/api/home_creation_api.dart @@ -4,7 +4,8 @@ import 'package:syncrow_app/services/api/http_service.dart'; class HomeCreation { static final HTTPService _httpService = HTTPService(); - static Future> createCommunity(Map body) async { + static Future> createCommunity( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addCommunity, @@ -20,7 +21,8 @@ class HomeCreation { } } - static Future> assignUserToCommunity(Map body) async { + static Future> assignUserToCommunity( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addCommunityToUser, @@ -36,7 +38,8 @@ class HomeCreation { } } - static Future> createBuilding(Map body) async { + static Future> createBuilding( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addBuilding, @@ -52,7 +55,8 @@ class HomeCreation { } } - static Future> assignUserToBuilding(Map body) async { + static Future> assignUserToBuilding( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addBuildingToUser, @@ -68,7 +72,8 @@ class HomeCreation { } } - static Future> createFloor(Map body) async { + static Future> createFloor( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addFloor, @@ -84,7 +89,8 @@ class HomeCreation { } } - static Future> assignUserToFloor(Map body) async { + static Future> assignUserToFloor( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addBuildingToUser, @@ -100,7 +106,8 @@ class HomeCreation { } } - static Future> createUnit(Map body) async { + static Future> createUnit( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addUnit, @@ -116,7 +123,8 @@ class HomeCreation { } } - static Future> assignUserToUnit(Map body) async { + static Future> assignUserToUnit( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addUnitToUser, @@ -132,10 +140,17 @@ class HomeCreation { } } - static Future> createRoom(Map body) async { + static Future> createRoom({ + required String communityId, + required String spaceId, + required Map body, + }) async { try { + final fullPath = ApiEndpoints.addSubSpace + .replaceAll('{communityUuid}', communityId) + .replaceAll('{spaceUuid}', spaceId); final response = await _httpService.post( - path: ApiEndpoints.addRoom, + path: fullPath, body: body, showServerMessage: false, expectedResponseModel: (json) { @@ -148,7 +163,8 @@ class HomeCreation { } } - static Future> assignUserToRoom(Map body) async { + static Future> assignUserToRoom( + Map body) async { try { final response = await _httpService.post( path: ApiEndpoints.addRoomToUser, diff --git a/lib/services/api/home_management_api.dart b/lib/services/api/home_management_api.dart index 9eb35b2..4151d0f 100644 --- a/lib/services/api/home_management_api.dart +++ b/lib/services/api/home_management_api.dart @@ -28,24 +28,46 @@ class HomeManagementAPI { static Future> fetchDevicesByUnitId() async { List list = []; - await _httpService.get( - path: ApiEndpoints.getDevicesByUnitId.replaceAll( - "{unitUuid}", HomeCubit.getInstance().selectedSpace?.id ?? ''), + + try { + // Retrieve selected space details + final selectedSpace = HomeCubit.getInstance().selectedSpace; + final communityUuid = selectedSpace?.community?.uuid ?? ''; + final spaceUuid = selectedSpace?.id ?? ''; + + // Ensure both placeholders are replaced + final path = ApiEndpoints.spaceDevices + .replaceAll('{communityUuid}', communityUuid) + .replaceAll('{spaceUuid}', spaceUuid); + await _httpService.get( + path: path, showServerMessage: false, expectedResponseModel: (json) { - json.forEach((value) { - list.add(DeviceModel.fromJson(value)); - }); - }); + if (json['data'] != null) { + json['data'].forEach((value) { + list.add(DeviceModel.fromJson(value)); + }); + } + }, + ); + + // Debugging: Log successful fetch + } catch (e) { + // Log the error for debugging + } + return list; } - static Future> assignDeviceToRoom( - Map body) async { + static Future> assignDeviceToRoom(String communityId, + String spaceId, String subSpaceId, String deviceId) async { try { - final response = await _httpService.put( - path: ApiEndpoints.assignDeviceToRoom, - body: body, + final response = await _httpService.post( + path: ApiEndpoints.assignDeviceToRoom + .replaceAll('{communityUuid}', communityId) + .replaceAll('{spaceUuid}', spaceId) + .replaceAll('{subSpaceUuid}', subSpaceId) + .replaceAll('{deviceUuid}', deviceId), expectedResponseModel: (json) { return json; }, @@ -55,4 +77,24 @@ class HomeManagementAPI { rethrow; } } + + static Future> unAssignDeviceToRoom(String communityId, + String spaceId, String subSpaceId, String deviceId) async { + try { + final response = await _httpService.delete( + path: ApiEndpoints.assignDeviceToRoom + .replaceAll('{communityUuid}', communityId) + .replaceAll('{spaceUuid}', spaceId) + .replaceAll('{subSpaceUuid}', subSpaceId) + .replaceAll('{deviceUuid}', deviceId), + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } catch (e) { + rethrow; + } + } + } diff --git a/lib/services/api/scene_api.dart b/lib/services/api/scene_api.dart index 6e0d981..e2040e9 100644 --- a/lib/services/api/scene_api.dart +++ b/lib/services/api/scene_api.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:syncrow_app/features/scene/model/create_automation_model.dart'; import 'package:syncrow_app/features/scene/model/create_scene_model.dart'; import 'package:syncrow_app/features/scene/model/icon_model.dart'; @@ -11,7 +13,8 @@ class SceneApi { static final HTTPService _httpService = HTTPService(); //create scene - static Future> createScene(CreateSceneModel createSceneModel) async { + static Future> createScene( + CreateSceneModel createSceneModel) async { try { final response = await _httpService.post( path: ApiEndpoints.createScene, @@ -47,15 +50,21 @@ class SceneApi { //get scene by unit id - static Future> getScenesByUnitId(String unitId, {showInDevice = false}) async { + static Future> getScenesByUnitId( + String unitId, String communityId, + {showInDevice = false}) async { try { final response = await _httpService.get( - path: ApiEndpoints.getUnitScenes.replaceAll('{unitUuid}', unitId), + path: ApiEndpoints.getUnitScenes + .replaceAll('{spaceUuid}', unitId) + .replaceAll('{communityUuid}', communityId), queryParameters: {'showInHomePage': showInDevice}, showServerMessage: false, expectedResponseModel: (json) { + final scenesJson = json['data'] as List; + List scenes = []; - for (var scene in json) { + for (var scene in scenesJson) { scenes.add(ScenesModel.fromJson(scene)); } return scenes; @@ -102,10 +111,12 @@ class SceneApi { } //automation details - static Future getAutomationDetails(String automationId) async { + static Future getAutomationDetails( + String automationId) async { try { final response = await _httpService.get( - path: ApiEndpoints.getAutomationDetails.replaceAll('{automationId}', automationId), + path: ApiEndpoints.getAutomationDetails + .replaceAll('{automationId}', automationId), showServerMessage: false, expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), ); @@ -116,11 +127,12 @@ class SceneApi { } //updateAutomationStatus - static Future updateAutomationStatus( - String automationId, AutomationStatusUpdate createAutomationEnable) async { + static Future updateAutomationStatus(String automationId, + AutomationStatusUpdate createAutomationEnable) async { try { final response = await _httpService.put( - path: ApiEndpoints.updateAutomationStatus.replaceAll('{automationId}', automationId), + path: ApiEndpoints.updateAutomationStatus + .replaceAll('{automationId}', automationId), body: createAutomationEnable.toMap(), expectedResponseModel: (json) => json['success'], ); @@ -135,7 +147,13 @@ class SceneApi { final response = await _httpService.get( path: ApiEndpoints.getScene.replaceAll('{sceneId}', sceneId), showServerMessage: false, - expectedResponseModel: (json) => SceneDetailsModel.fromJson(json), + expectedResponseModel: (json) { + if (json != null && json['data'] != null) { + return SceneDetailsModel.fromJson(json['data']); + } else { + throw Exception('Data field is null'); + } + }, ); return response; } catch (e) { @@ -163,7 +181,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; }, @@ -175,11 +194,14 @@ class SceneApi { } //update automation - static updateAutomation(CreateAutomationModel createAutomationModel, String automationId) async { + static updateAutomation( + CreateAutomationModel createAutomationModel, String automationId) async { try { final response = await _httpService.put( - path: ApiEndpoints.updateAutomation.replaceAll('{automationId}', automationId), - body: createAutomationModel.toJson(automationId.isNotEmpty == true ? automationId : null), + path: ApiEndpoints.updateAutomation + .replaceAll('{automationId}', automationId), + body: createAutomationModel + .toJson(automationId.isNotEmpty == true ? automationId : null), expectedResponseModel: (json) { return json; }, @@ -192,12 +214,10 @@ class SceneApi { //delete Scene - static Future deleteScene({required String unitUuid, required String sceneId}) async { + static Future deleteScene({required String sceneId}) async { try { final response = await _httpService.delete( - path: ApiEndpoints.deleteScene - .replaceAll('{sceneId}', sceneId) - .replaceAll('{unitUuid}', unitUuid), + path: ApiEndpoints.deleteScene.replaceAll('{sceneId}', sceneId), showServerMessage: false, expectedResponseModel: (json) => json['statusCode'] == 200, ); @@ -213,8 +233,7 @@ class SceneApi { try { final response = await _httpService.delete( path: ApiEndpoints.deleteAutomation - .replaceAll('{automationId}', automationId) - .replaceAll('{unitUuid}', unitUuid), + .replaceAll('{automationId}', automationId), showServerMessage: false, expectedResponseModel: (json) => json['statusCode'] == 200, ); diff --git a/lib/services/api/spaces_api.dart b/lib/services/api/spaces_api.dart index bd02075..7a27bf1 100644 --- a/lib/services/api/spaces_api.dart +++ b/lib/services/api/spaces_api.dart @@ -1,55 +1,89 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/auth/model/user_model.dart'; -import 'package:syncrow_app/features/devices/model/room_model.dart'; +import 'package:syncrow_app/features/devices/model/subspace_model.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/http_service.dart'; class SpacesAPI { static final HTTPService _httpService = HTTPService(); - static Future> getUnitsByUserId() async { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); - final response = await _httpService.get( - path: "${ApiEndpoints.unitUser}$uuid", - showServerMessage: false, - expectedResponseModel: (json) => SpaceModel.fromJsonList(json), - ); - return response; + static Future> getSpacesByUserId() async { + try { + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + if (uuid == null) throw Exception("User UUID is missing"); + + final path = ApiEndpoints.userSpaces.replaceFirst('{userUuid}', uuid); + final response = await _httpService.get( + path: path, + showServerMessage: false, + expectedResponseModel: (json) { + return SpaceModel.fromJsonList(json['data']); + }, + ); + + return response; + } catch (error) { + rethrow; // Rethrow the error to be caught by `fetchUnitsByUserId` + } } - static Future> getRoomsBySpaceId(String unitId) async { + static Future> getSubSpaceBySpaceId( + String communityId, String spaceId) async { + try { + // Construct the API path + final path = ApiEndpoints.listSubspace + .replaceFirst('{communityUuid}', communityId) + .replaceFirst('{spaceUuid}', spaceId); + + final response = await _httpService.get( + path: path, + queryParameters: {"page": 1, "pageSize": 10}, + showServerMessage: false, + expectedResponseModel: (json) { + List rooms = []; + if (json['data'] != null) { + for (var subspace in json['data']) { + rooms.add(SubSpaceModel.fromJson(subspace)); + } + } else { + print("Warning: 'data' key is missing or null in response JSON."); + } + return rooms; + }, + ); + + return response; + } catch (error, stackTrace) { + return []; // Return an empty list if there's an error + } + } + + static Future generateInvitationCode( + String unitId, String communityId) async { final response = await _httpService.get( - path: "${ApiEndpoints.unitChild}$unitId", - queryParameters: {"page": 1, "pageSize": 10}, + path: ApiEndpoints.invitationCode + .replaceAll('{unitUuid}', unitId) + .replaceAll('{communityUuid}', communityId), showServerMessage: false, expectedResponseModel: (json) { - List rooms = []; - for (var room in json['children']) { - rooms.add(RoomModel.fromJson(room)); + if (json != null && json['data'] != null) { + return json['data']['invitationCode']; + } else { + throw Exception('Data field is null'); } - return rooms; }, ); return response; } - static Future generateInvitationCode( - String unitId, - ) async { - final response = await _httpService.get( - path: ApiEndpoints.invitationCode.replaceAll('{unitUuid}', unitId), - showServerMessage: false, - expectedResponseModel: (json) => json['invitationCode'], - ); - return response; - } - static Future joinUnit( + String userId, Map body, ) async { final response = await _httpService.post( - path: ApiEndpoints.verifyInvitationCode, + path: ApiEndpoints.verifyInvitationCode.replaceAll('{userUuid}', userId), showServerMessage: false, body: body, expectedResponseModel: (json) => json['success'],