fetch groups

This commit is contained in:
Mohammad Salameh
2024-03-18 11:46:27 +03:00
parent 65cbf10485
commit 13e80fbad7
20 changed files with 273 additions and 164 deletions

48
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,48 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Iphone 15 Pro Max",
"request": "launch",
"type": "dart",
"deviceId": "0147FC23-3D6C-406A-BE2C-9E67BAF3DA9B"
},
{
"name": "Iphone SE",
"request": "launch",
"type": "dart",
"deviceId": "DB3BAF09-EF89-4A31-8DD8-A81DC346CB43",
},
{
"name": "syncrow-app",
"request": "launch",
"type": "dart"
},
{
"name": "syncrow-app (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "syncrow-app (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
],
"compounds": [
{
"name": "All Device",
"configurations": [
"Iphone 15 Pro Max",
"Iphone SE"
]
}
]
}

View File

@ -21,7 +21,7 @@ class SpacesCubit extends Cubit<SpacesState> {
static List<SpaceModel> spaces = [];
SpaceModel? selectedSpace = spaces.isNotEmpty ? spaces.first : null;
static SpaceModel? selectedSpace = spaces.isNotEmpty ? spaces.first : null;
RoomModel? selectedRoom;
@ -47,9 +47,9 @@ class SpacesCubit extends Cubit<SpacesState> {
unselectRoom();
} else {
selectedRoom = selectedSpace!.rooms![index - 1];
}
emit(RoomSelected(selectedRoom!));
}
}
devicesPageChanged(int index) {
roomsPageController.animateToPage(
@ -58,13 +58,13 @@ class SpacesCubit extends Cubit<SpacesState> {
curve: Curves.linear,
);
if (index == 0) {
if (index <= 0) {
unselectRoom();
} else {
selectedRoom = selectedSpace!.rooms![index - 1];
}
emit(RoomSelected(selectedRoom!));
}
}
unselectRoom() {
selectedRoom = null;

View File

@ -26,4 +26,8 @@ class SpaceModel {
rooms: [],
);
}
static List<SpaceModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((item) => SpaceModel.fromJson(item)).toList();
}
}

View File

@ -28,7 +28,7 @@ class AppBarHomeDropdown extends StatelessWidget {
underline: const SizedBox.shrink(),
padding: const EdgeInsets.all(0),
borderRadius: BorderRadius.circular(20),
value: SpacesCubit.get(context).selectedSpace!.id,
value: SpacesCubit.selectedSpace!.id,
items: SpacesCubit.spaces.map((space) {
return DropdownMenuItem(
value: space.id,

View File

@ -1,6 +1,7 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/app_layout/bloc/spaces_cubit.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';
@ -18,7 +19,9 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart';
part 'devices_state.dart';
class DevicesCubit extends Cubit<DevicesState> {
DevicesCubit() : super(DevicesInitial());
DevicesCubit() : super(DevicesInitial()) {
fetchGroups(SpacesCubit.selectedSpace!);
}
static DevicesCubit get(context) => BlocProvider.of(context);
@ -435,23 +438,10 @@ class DevicesCubit extends Cubit<DevicesState> {
emit(DevicesCategoryChanged());
}
//fetchRooms(SpaceModel space) async {
// emit(SpaceRoomsLoading());
// try {
// space.rooms = await SpacesAPI.getRooms(space.id!);
// if (space.rooms != null) {
// emit(SpaceRoomsLoaded(space.rooms!));
// } else {
// emit(SpaceRoomsError("No rooms found"));
// }
// } on DioException catch (e) {
// emit(SpacesError(ServerFailure.fromDioError(e).errMessage));
// }
// }
deviceControl(DeviceControlModel control) async {
emit(DeviceControlLoading());
try {
var response = await DevicesAPI.controlDevice(control);
await DevicesAPI.controlDevice(control);
emit(DeviceControlSuccess());
} on DioException catch (e) {
emit(DeviceControlError(ServerFailure.fromDioError(e).errMessage));
@ -461,12 +451,18 @@ class DevicesCubit extends Cubit<DevicesState> {
fetchGroups(
SpaceModel space,
) async {
emit(DevicesCategoriesLoading());
try {
if (state is! DevicesCategoriesLoading) {
emit(DevicesCategoriesLoading());
allCategories = await DevicesAPI.fetchGroups(space.id!);
} else {
return;
}
emit(DevicesCategoriesSuccess());
} on DioException catch (e) {
emit(DevicesCategoriesError(ServerFailure.fromDioError(e).errMessage));
} on DioException catch (error) {
emit(
DevicesCategoriesError(ServerFailure.fromDioError(error).errMessage),
);
}
}

View File

@ -13,6 +13,7 @@ class DevicesFailure extends DevicesState {}
class ChangeIndex extends DevicesState {}
// Devices
class DevicesCategoryChanged extends DevicesState {}
class CategorySwitchChanged extends DevicesState {}
@ -21,6 +22,7 @@ class DeviceSwitchChanged extends DevicesState {}
class DeviceSelected extends DevicesState {}
// Device Control
class DeviceControlLoading extends DevicesState {}
class DeviceControlSuccess extends DevicesState {}
@ -31,6 +33,7 @@ class DeviceControlError extends DevicesState {
DeviceControlError(this.errorMsg);
}
// Categories
class DevicesCategoriesLoading extends DevicesState {}
class DevicesCategoriesSuccess extends DevicesState {}

View File

@ -1,6 +1,7 @@
// ignore_for_file: constant_identifier_names
import 'package:syncrow_app/features/devices/model/device_model.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
class DevicesCategoryModel {
@ -40,10 +41,16 @@ class DevicesCategoryModel {
DevicesCategoryModel.fromJson(Map<String, dynamic> json)
: name = json['groupName'],
id = json['groupId'],
icon = json['icon'],
type = deviceTypeMap[json['groupName']] ?? DeviceType.Other,
icon = deviceTypeIconMap[
deviceTypeMap[json['groupName']] ?? DeviceType.Other] ??
'',
devices = [],
isSelected = false;
static List<DevicesCategoryModel> fromJsonList(List<dynamic> jsonList) {
return jsonList.map((item) => DevicesCategoryModel.fromJson(item)).toList();
}
}
Map<String, DeviceType> deviceTypeMap = {
@ -52,4 +59,16 @@ Map<String, DeviceType> deviceTypeMap = {
'Door Locks': DeviceType.DoorLock,
'Gateways': DeviceType.Gateway,
'ACs': DeviceType.AC,
'3Gang': DeviceType.Gang,
};
Map<DeviceType, String> deviceTypeIconMap = {
DeviceType.AC: Assets.iconsAC,
DeviceType.Lights: Assets.iconsLight,
DeviceType.DoorLock: Assets.iconsDoorLock,
DeviceType.Curtain: Assets.iconsCurtain,
DeviceType.Gateway: Assets.iconsGateway,
DeviceType.Sensors: Assets.iconsSensors,
DeviceType.Gang: Assets.iconsGang,
DeviceType.Other: Assets.iconsAC,
};

View File

@ -23,8 +23,6 @@ class DevicesViewBody extends StatelessWidget {
builder: (context, state) {
return BlocBuilder<DevicesCubit, DevicesState>(
builder: (context, state) {
print(
"length : ${SpacesCubit.get(context).selectedSpace!.rooms!.length}");
//TODO : move to NavigationCubit
if (state is DevicesLoading) {
return const Center(child: CircularProgressIndicator());
@ -51,15 +49,9 @@ class DevicesViewBody extends StatelessWidget {
},
children: [
const WizardPage(),
if (SpacesCubit.get(context).selectedSpace != null)
if (SpacesCubit.get(context)
.selectedSpace!
.rooms !=
null)
...SpacesCubit.get(context)
.selectedSpace!
.rooms!
.map(
if (SpacesCubit.selectedSpace != null)
if (SpacesCubit.selectedSpace!.rooms != null)
...SpacesCubit.selectedSpace!.rooms!.map(
(room) {
return RoomPage(
room: room,
@ -76,11 +68,7 @@ class DevicesViewBody extends StatelessWidget {
child: SmoothPageIndicator(
controller:
SpacesCubit.get(context).devicesPageController,
count: SpacesCubit.get(context)
.selectedSpace!
.rooms!
.length +
1,
count: SpacesCubit.selectedSpace!.rooms!.length + 1,
effect: const WormEffect(
paintStyle: PaintingStyle.stroke,
dotHeight: 8,

View File

@ -26,6 +26,7 @@ class LightSwitch extends StatelessWidget {
DevicesCubit.get(context)
.deviceControl(control)
.then((value) {
print('Device control response: $value');
if (control.value ?? true) {
control.value = false;
} else {

View File

@ -24,6 +24,8 @@ class LightSwitchesBody extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
children: [
LightSwitch(
control: DeviceControlModel(
@ -32,6 +34,19 @@ class LightSwitchesBody extends StatelessWidget {
value: true,
),
),
const SizedBox(height: 20),
const SizedBox(
width: 70,
child: BodySmall(
text: "Bedside Light",
fontColor: ColorsManager.textPrimaryColor,
textAlign: TextAlign.center,
),
),
],
),
Column(
children: [
LightSwitch(
control: DeviceControlModel(
deviceId: 'bfe10693d4fd263206ocq9',
@ -39,6 +54,19 @@ class LightSwitchesBody extends StatelessWidget {
value: true,
),
),
const SizedBox(height: 20),
const SizedBox(
width: 70,
child: BodySmall(
text: "Ceiling Light",
fontColor: ColorsManager.textPrimaryColor,
textAlign: TextAlign.center,
),
),
],
),
Column(
children: [
LightSwitch(
control: DeviceControlModel(
deviceId: 'bfe10693d4fd263206ocq9',
@ -46,6 +74,17 @@ class LightSwitchesBody extends StatelessWidget {
value: true,
),
),
const SizedBox(height: 20),
const SizedBox(
width: 70,
child: BodySmall(
text: "Spotlight",
fontColor: ColorsManager.textPrimaryColor,
textAlign: TextAlign.center,
),
),
],
),
],
),
Expanded(

View File

@ -40,25 +40,21 @@ class RoomsSlider extends StatelessWidget {
),
),
),
if (SpacesCubit.get(context).selectedSpace != null)
if (SpacesCubit.get(context).selectedSpace!.rooms != null)
...SpacesCubit.get(context).selectedSpace!.rooms!.map(
if (SpacesCubit.selectedSpace != null)
if (SpacesCubit.selectedSpace!.rooms != null)
...SpacesCubit.selectedSpace!.rooms!.map(
(room) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: InkWell(
onTap: () {
SpacesCubit.get(context).roomSliderPageChanged(
SpacesCubit.get(context)
.selectedSpace!
.rooms!
.indexOf(room));
SpacesCubit.selectedSpace!.rooms!.indexOf(room));
},
child: TitleMedium(
text: room.name!,
style: context.titleMedium.copyWith(
fontSize: 25,
color: SpacesCubit.get(context).selectedRoom ==
room
color: SpacesCubit.get(context).selectedRoom == room
? ColorsManager.textPrimaryColor
: ColorsManager.textPrimaryColor
.withOpacity(.2),

View File

@ -18,7 +18,9 @@ class WizartSwitches extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<DevicesCubit, DevicesState>(
builder: (context, state) {
return GridView.builder(
return state is DevicesLoading
? const Center(child: CircularProgressIndicator())
: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
@ -35,13 +37,15 @@ class WizartSwitches extends StatelessWidget {
DevicesCubit.get(context).selectCategory(index);
//Navigate to the chosen category view without animation
Navigator.push(context, CustomPageRoute(builder: (context) {
Navigator.push(context,
CustomPageRoute(builder: (context) {
return DevicesCubit.get(context).chosenCategoryView!;
}));
},
child: DefaultContainer(
child: Padding(
padding: const EdgeInsets.only(top: 10, right: 10, left: 10),
padding:
const EdgeInsets.only(top: 10, right: 10, left: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@ -10,6 +10,7 @@ class BodySmall extends StatelessWidget {
this.fontColor,
this.fontSize,
this.fontWeight,
this.textAlign,
});
final String text;
@ -20,6 +21,8 @@ class BodySmall extends StatelessWidget {
final double? fontSize;
final FontWeight? fontWeight;
final TextAlign? textAlign;
@override
Widget build(BuildContext context) => CustomText(
text,
@ -27,5 +30,6 @@ class BodySmall extends StatelessWidget {
fontColor: fontColor,
fontSize: fontSize,
fontWeight: fontWeight,
textAlign: textAlign,
);
}

View File

@ -30,6 +30,7 @@ class Assets {
static const String iconsFan3 = 'assets/icons/fan-3.svg';
static const String iconsFilter = 'assets/icons/filter.png';
static const String iconsFrequency = 'assets/icons/frequency.svg';
static const String iconsGang = 'assets/icons/gang.svg';
static const String iconsGateway = 'assets/icons/Gateway.svg';
static const String iconsGoogle = 'assets/icons/Google.svg';
static const String iconsHome = 'assets/icons/home.svg';
@ -50,6 +51,7 @@ class Assets {
static const String iconsRoutinesFill = 'assets/icons/Routines-fill.svg';
static const String iconsScan = 'assets/icons/Scan.svg';
static const String iconsScreen = 'assets/icons/Screen.svg';
static const String iconsSensors = 'assets/icons/sensors.svg';
static const String iconsSettings = 'assets/icons/settings.png';
static const String iconsSummer = 'assets/icons/Summer.svg';
static const String iconsSummerMode = 'assets/icons/summer_mode.svg';

View File

@ -16,4 +16,7 @@ abstract class ApiEndpoints {
// Devices
static const String control = '$baseUrl/device/control';
//groups
static const String groups = '$baseUrl/group';
}

View File

@ -4,9 +4,11 @@ import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart';
class DevicesAPI {
static final HTTPService _httpService = HTTPService();
static Future<Map<String, dynamic>> controlDevice(
DeviceControlModel controlModel) async {
final response = await HTTPService().post(
final response = await _httpService.post(
path: ApiEndpoints.control,
body: controlModel.toJson(),
showServerMessage: false,
@ -18,18 +20,20 @@ class DevicesAPI {
}
static Future<List<DevicesCategoryModel>> fetchGroups(int spaceId) async {
final response = await HTTPService().get(
path: ApiEndpoints.control,
queryParameters: {'homeId': spaceId, 'pageSize': 100, 'page': 1},
Map<String, dynamic> params = {
"homeId": spaceId,
"pageSize": 100,
"page": 1
};
final response = await _httpService.get(
path:
"https://syncrow.azurewebsites.net/group?homeId=$spaceId&pageSize=100&pageNo=1",
// path: ApiEndpoints.groups,
// queryParameters: params,
showServerMessage: false,
expectedResponseModel: (json) {
List<DevicesCategoryModel> categories = [];
for (var category in json['groups']) {
categories.add(DevicesCategoryModel.fromJson(category));
}
},
expectedResponseModel: (json) =>
DevicesCategoryModel.fromJsonList(json['groups']),
);
print('fetchGroups response: $response');
return response;
}
}

View File

@ -12,10 +12,10 @@ class HTTPInterceptor extends InterceptorsWrapper {
@override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
var storage = FlutterSecureStorage();
var storage = const FlutterSecureStorage();
var token = await storage.read(key: Token.loginAccessTokenKey);
options.headers['Authorization'] = 'Bearer $token';
options.headers['Authorization'] = 'Bearer $token';
super.onRequest(options, handler);
}
//

View File

@ -44,6 +44,7 @@ class HTTPService {
} catch (error) {
debugPrint("******* Error");
debugPrint(error.toString());
debugPrint("******* Error End");
rethrow;
}
}

View File

@ -6,29 +6,25 @@ 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<List<SpaceModel>> getSpaces() async {
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
final response = await HTTPService().get(
final response = await _httpService.get(
path: ApiEndpoints.spaces,
queryParameters: {
"userUuid": uuid,
},
showServerMessage: false,
expectedResponseModel: (json) {
List<SpaceModel> spaces = [];
for (var space in json) {
spaces.add(SpaceModel.fromJson(space));
}
return spaces;
},
expectedResponseModel: (json) => SpaceModel.fromJsonList(json),
);
return response;
}
//get rooms by space id
static Future<List<RoomModel>> getRooms(int spaceId) async {
final response = await HTTPService().get(
final response = await _httpService.get(
path: ApiEndpoints.rooms,
queryParameters: {"homeId": spaceId},
showServerMessage: false,

View File

@ -22,5 +22,6 @@ enum DeviceType {
ThreeGang,
Gateway,
Sensors,
Gang,
Other,
}