diff --git a/android/app/build.gradle b/android/app/build.gradle
index c6a9a9e..4a14eae 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -27,7 +27,7 @@ if (flutterVersionName == null) {
}
android {
- namespace "com.example.syncrow.app"
+ namespace "com.example.syncrow_application"
compileSdkVersion 34
ndkVersion flutter.ndkVersion
@@ -46,7 +46,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId "com.example.syncrow.app"
+ applicationId "com.example.syncrow_application"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 21
diff --git a/android/app/google-services.json b/android/app/google-services.json
index 0c568c5..b304aae 100644
--- a/android/app/google-services.json
+++ b/android/app/google-services.json
@@ -9,7 +9,7 @@
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:550f67441246cb1a0c7e6d",
"android_client_info": {
- "package_name": "com.example.syncrow.app"
+ "package_name": "com.example.syncrow_application"
}
},
"oauth_client": [],
diff --git a/android/app/src/main/kotlin/com/example/syncrow_app/MainActivity.kt b/android/app/src/main/kotlin/com/example/syncrow_app/MainActivity.kt
index ec5d9a5..1e03aee 100644
--- a/android/app/src/main/kotlin/com/example/syncrow_app/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/example/syncrow_app/MainActivity.kt
@@ -1,4 +1,4 @@
-package com.example.syncrow.app
+package com.example.syncrow_application
import io.flutter.embedding.android.FlutterActivity
diff --git a/assets/icons/blue_checkbox_ic.svg b/assets/icons/blue_checkbox_ic.svg
new file mode 100644
index 0000000..46d0160
--- /dev/null
+++ b/assets/icons/blue_checkbox_ic.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/empty_checkbox_ic.svg b/assets/icons/empty_checkbox_ic.svg
new file mode 100644
index 0000000..34522c8
--- /dev/null
+++ b/assets/icons/empty_checkbox_ic.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/features/app_layout/bloc/home_cubit.dart b/lib/features/app_layout/bloc/home_cubit.dart
index 13d33e8..48021cc 100644
--- a/lib/features/app_layout/bloc/home_cubit.dart
+++ b/lib/features/app_layout/bloc/home_cubit.dart
@@ -6,6 +6,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
+import 'package:share_plus/share_plus.dart';
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdown.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
@@ -22,6 +23,7 @@ import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/services/api/spaces_api.dart';
import 'package:syncrow_app/utils/helpers/custom_page_route.dart';
+import 'package:syncrow_app/utils/helpers/snack_bar.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/constants.dart';
@@ -31,11 +33,12 @@ class HomeCubit extends Cubit {
HomeCubit._() : super(HomeInitial()) {
checkIfNotificationPermissionGranted();
if (selectedSpace == null) {
- fetchUnitsByUserId().then((value) {
- if (selectedSpace != null) {
- fetchRoomsByUnitId(selectedSpace!);
- }
- });
+ fetchUnitsByUserId();
+ // .then((value) {
+ // if (selectedSpace != null) {
+ // fetchRoomsByUnitId(selectedSpace!);
+ // }
+ // });
}
}
@@ -143,6 +146,7 @@ class HomeCubit extends Cubit {
changeSelectedSpace(SpaceModel space) {
selectedSpace = space;
emitSafe(SpaceSelected(space));
+ fetchRoomsByUnitId(space);
}
roomSliderPageChanged(int index) {
@@ -193,6 +197,39 @@ class HomeCubit extends Cubit {
}
//////////////////////////////////////// API ////////////////////////////////////////
+ generateInvitation(String unitId) async {
+ try {
+ final invitationCode = await SpacesAPI.generateInvitationCode(unitId);
+ if (invitationCode.isNotEmpty) {
+ Share.share('The invitation code is $invitationCode');
+ CustomSnackBar.displaySnackBar(
+ 'Invitation code generated successfully the code is: $invitationCode');
+ } else {
+ CustomSnackBar.displaySnackBar('Please try again!');
+ }
+ } catch (failure) {
+ CustomSnackBar.displaySnackBar('Something went wrong');
+ return;
+ }
+ }
+
+ Future joinAUnit(String code) async {
+ try {
+ var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? '';
+ Map body = {'userUuid': uuid, 'inviteCode': code};
+
+ final success = await SpacesAPI.joinUnit(body);
+ if (success) {
+ await fetchUnitsByUserId();
+ CustomSnackBar.displaySnackBar('Done successfully');
+ }
+ return true;
+ } catch (failure) {
+ CustomSnackBar.displaySnackBar('Something went wrong');
+ return false;
+ }
+ }
+
fetchUnitsByUserId() async {
emitSafe(GetSpacesLoading());
try {
@@ -204,8 +241,8 @@ class HomeCubit extends Cubit {
if (spaces != null && spaces!.isNotEmpty) {
selectedSpace = spaces!.first;
+ await fetchRoomsByUnitId(selectedSpace!);
emitSafe(GetSpacesSuccess(spaces!));
- // fetchRoomsByUnitId(selectedSpace!);
} else {
emitSafe(GetSpacesError("No spaces found"));
}
diff --git a/lib/features/app_layout/view/widgets/default_app_bar.dart b/lib/features/app_layout/view/widgets/default_app_bar.dart
index 6c67536..332ef19 100644
--- a/lib/features/app_layout/view/widgets/default_app_bar.dart
+++ b/lib/features/app_layout/view/widgets/default_app_bar.dart
@@ -20,11 +20,9 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
leadingWidth: 150,
toolbarHeight: Constants.appBarHeight,
leading: HomeCubit.getInstance().spaces!.isNotEmpty
- ? HomeCubit.appBarLeading[
- HomeCubit.bottomNavItems[HomeCubit.pageIndex].label]
+ ? HomeCubit.appBarLeading[HomeCubit.bottomNavItems[HomeCubit.pageIndex].label]
: null,
- actions: HomeCubit.appBarActions[
- HomeCubit.bottomNavItems[HomeCubit.pageIndex].label],
+ // actions: HomeCubit.appBarActions[HomeCubit.bottomNavItems[HomeCubit.pageIndex].label],
));
},
);
diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart
index c62bc08..2d3f9b1 100644
--- a/lib/features/auth/bloc/auth_cubit.dart
+++ b/lib/features/auth/bloc/auth_cubit.dart
@@ -1,4 +1,3 @@
-import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@@ -9,6 +8,7 @@ import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/services/api/authentication_api.dart';
+import 'package:syncrow_app/utils/helpers/snack_bar.dart';
part 'auth_state.dart';
class AuthCubit extends Cubit {
@@ -26,6 +26,7 @@ class AuthCubit extends Cubit {
final loginFormKey = GlobalKey();
final signUpFormKey = GlobalKey();
bool isPasswordVisible = false;
+ bool showValidationMessage = false;
static GlobalKey formKey = GlobalKey();
@@ -58,7 +59,7 @@ class AuthCubit extends Cubit {
if (value.isNotEmpty) {
if (!RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$')
.hasMatch(value)) {
- return 'Password must contain at least one uppercase letter, one lowercase letter, one number and one special character';
+ return 'Password must contain at least:\n - one uppercase letter.\n - one lowercase letter.\n - one number. \n - special character';
}
}
}
@@ -147,6 +148,12 @@ class AuthCubit extends Cubit {
login() async {
emit(AuthLoginLoading());
try {
+ if (emailController.text.isEmpty || passwordController.text.isEmpty) {
+ CustomSnackBar.displaySnackBar('Please enter your credentials');
+ emit(AuthLoginError(message: 'Something went wrong'));
+ return;
+ }
+
token = await AuthenticationAPI.loginWithEmail(
model: LoginWithEmailModel(
email: emailController.text.toLowerCase(),
diff --git a/lib/features/auth/view/sign_up_view.dart b/lib/features/auth/view/sign_up_view.dart
index e6f27c9..aa5d71e 100644
--- a/lib/features/auth/view/sign_up_view.dart
+++ b/lib/features/auth/view/sign_up_view.dart
@@ -86,7 +86,7 @@ class SignUpView extends StatelessWidget {
),
Form(
key: formKey,
- autovalidateMode: AutovalidateMode.disabled,
+ // autovalidateMode: AutovalidateMode.disabled,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -96,14 +96,15 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
- autovalidateMode: AutovalidateMode.disabled,
+ // autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.name,
scrollPadding: EdgeInsets.zero,
autocorrect: false,
autofillHints: const [AutofillHints.name],
// controller: AuthCubit.get(context).fullNameController,
- validator: AuthCubit.get(context).fullNameValidator,
+ validator: (value) =>
+ AuthCubit.get(context).fullNameValidator(value),
onTapOutside: (event) {
FocusScope.of(context).unfocus();
},
@@ -119,7 +120,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
- autovalidateMode: AutovalidateMode.disabled,
+ // autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
@@ -141,7 +142,7 @@ class SignUpView extends StatelessWidget {
fontColor: Colors.white,
),
TextFormField(
- autovalidateMode: AutovalidateMode.disabled,
+ // autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
@@ -199,6 +200,7 @@ class SignUpView extends StatelessWidget {
'Sign up',
),
onPressed: () {
+ AuthCubit.get(context).showValidationMessage = true;
if (formKey.currentState!.validate()) {
if ((state is! AuthLoading)) {
AuthCubit.get(context).signUp();
diff --git a/lib/features/auth/view/widgets/login_form.dart b/lib/features/auth/view/widgets/login_form.dart
index 26e6844..5432a4d 100644
--- a/lib/features/auth/view/widgets/login_form.dart
+++ b/lib/features/auth/view/widgets/login_form.dart
@@ -65,7 +65,7 @@ class LoginForm extends StatelessWidget {
if (state is AuthTokenError && !pressed) {
return null;
}
- return AuthCubit.get(context).passwordValidator(value);
+ // return AuthCubit.get(context).passwordValidator(value);
},
onTapOutside: (event) {
FocusScope.of(context).unfocus();
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 5a00e20..e31fd2b 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
@@ -1,9 +1,11 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
+import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/features/menu/bloc/create_unit_bloc/create_unit_event.dart';
import 'package:syncrow_app/features/menu/bloc/create_unit_bloc/create_unit_state.dart';
import 'package:syncrow_app/services/api/home_creation_api.dart';
+import 'package:syncrow_app/utils/helpers/snack_bar.dart';
class CreateUnitBloc extends Bloc {
List rooms = [];
@@ -45,11 +47,16 @@ class CreateUnitBloc extends Bloc {
void _save(SaveRooms event, Emitter emit) async {
try {
- emit(LoadingState());
- if (rooms.isEmpty) {
- const ErrorState(message: 'Please add at least one room!');
+ if (event.unitName.isEmpty) {
+ CustomSnackBar.displaySnackBar('Please enter the unit name');
return;
}
+ if (rooms.isEmpty) {
+ CustomSnackBar.displaySnackBar('Please add at least one room!');
+ return;
+ }
+
+ emit(LoadingState());
var storage = const FlutterSecureStorage();
var userId = await storage.read(key: UserModel.userUuidKey) ?? '';
@@ -83,6 +90,7 @@ class CreateUnitBloc extends Bloc {
}
// }
}
+ await HomeCubit.getInstance().fetchUnitsByUserId();
emit(RoomsSavedSuccessfully());
} catch (_) {
emit(const ErrorState(message: 'Something went wrong'));
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
new file mode 100644
index 0000000..bc85498
--- /dev/null
+++ b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_bloc.dart
@@ -0,0 +1,112 @@
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:syncrow_app/features/devices/model/device_model.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';
+import 'package:syncrow_app/services/api/devices_api.dart';
+import 'package:syncrow_app/services/api/home_creation_api.dart';
+import 'package:syncrow_app/services/api/home_management_api.dart';
+import 'package:syncrow_app/services/api/spaces_api.dart';
+
+class ManageUnitBloc extends Bloc {
+ List allDevices = [];
+ String unitName = '';
+
+ ManageUnitBloc() : super(InitialState()) {
+ on(_fetchRoomsAndDevices);
+ on(_fetchDevicesByRoomId);
+ on(_assignDevice);
+ on(_addNewRoom);
+ }
+
+ void _fetchRoomsAndDevices(FetchRoomsEvent event, Emitter emit) async {
+ try {
+ emit(LoadingState());
+ final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId);
+ emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
+ } catch (e) {
+ emit(const ErrorState(message: 'Something went wrong'));
+ return;
+ }
+ }
+
+ void _fetchDevicesByRoomId(FetchDevicesByRoomIdEvent event, Emitter emit) async {
+ try {
+ Map roomDevicesId = {};
+ emit(LoadingState());
+ final devicesList = await DevicesAPI.getDevicesByRoomId(event.roomId);
+ allDevices = await HomeManagementAPI.fetchDevicesByUserId();
+
+ 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;
+ }
+ }
+
+ 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);
+
+ 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};
+ try {
+ emit(LoadingState());
+ final response = await HomeCreation.createRoom(body);
+ if (response['data']['uuid'] != '') {
+ final roomsList = await SpacesAPI.getRoomsBySpaceId(event.unitId);
+ allDevices = await HomeManagementAPI.fetchDevicesByUserId();
+ emit(FetchRoomsState(devicesList: allDevices, roomsList: roomsList));
+ } else {
+ emit(const ErrorState(message: 'Something went wrong'));
+ }
+ } catch (_) {
+ emit(const ErrorState(message: 'Something went wrong'));
+ return;
+ }
+ }
+}
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
new file mode 100644
index 0000000..03c0722
--- /dev/null
+++ b/lib/features/menu/bloc/manage_unit_bloc/manage_unit_event.dart
@@ -0,0 +1,50 @@
+import 'package:equatable/equatable.dart';
+
+abstract class ManageUnitEvent extends Equatable {
+ const ManageUnitEvent();
+
+ @override
+ List