diff --git a/lib/app.dart b/lib/app.dart index 0c30ec6..ce1c1e6 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -12,6 +12,7 @@ import 'package:iiitd_mentorship/app/views/screens/driver.dart'; import 'package:iiitd_mentorship/app/views/screens/home/home.dart'; import 'package:iiitd_mentorship/app/views/screens/profile/profile.dart'; import 'package:iiitd_mentorship/app/views/screens/schedule/schedule.dart'; +import 'package:iiitd_mentorship/app/views/screens/search/search.dart'; import 'package:iiitd_mentorship/theme.dart'; class MyApp extends StatelessWidget { @@ -36,12 +37,13 @@ class MyApp extends StatelessWidget { '/profile': (BuildContext context) => const ProfileScreen(), '/settings': (BuildContext context) => const MyHomePage(), '/notifications': (BuildContext context) => const MyHomePage(), - 'onboarding': (BuildContext context) => const OnBoardsScreen(), + '/onboarding': (BuildContext context) => const OnBoardsScreen(), '/login': (BuildContext context) => const LoginScreen(), '/signup': (BuildContext context) => const SignUpScreen(), '/phoneauth': (BuildContext context) => const PhoneAuthScreen(), '/otpscreen': (BuildContext context) => const OTPScreen(), '/userdetails': (BuildContext context) => const UserDetailsScreen(), + '/search': (BuildContext context) => const SearchScreen(), '/chat': (BuildContext context) => const ChatScreen(title: "Welcome to Chat"), '/home/schedule': (BuildContext context) => diff --git a/lib/app/bloc/auth/auth_bloc.dart b/lib/app/bloc/auth/auth_bloc.dart index ce497ea..b7f20bc 100644 --- a/lib/app/bloc/auth/auth_bloc.dart +++ b/lib/app/bloc/auth/auth_bloc.dart @@ -1,8 +1,9 @@ import 'package:bloc/bloc.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:iiitd_mentorship/app/data/interfaces/responses.dart'; +import 'package:iiitd_mentorship/app/data/model/auth_status.dart'; import 'package:iiitd_mentorship/app/data/model/user_auth.dart'; import 'package:iiitd_mentorship/app/data/repository/auth.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:meta/meta.dart'; part 'auth_event.dart'; @@ -10,50 +11,73 @@ part 'auth_state.dart'; class AuthBloc extends Bloc { final AuthRepository _auth = AuthRepository(); - final currentUser = FirebaseAuth.instance.currentUser; + AuthStatus status = AuthStatus.unknown; AuthBloc() : super(AuthInitial()) { - on((event, emit) async { + on((event, emit) async { + emit(AuthLoading()); + final user = (event).user; + final response = await _auth.login(user); + + status = AuthStatus.authenticated; + + emit(_validateResponse(response, status)); + }); + + on((event, emit) async { + emit(AuthLoading()); + final user = (event).user; + final response = await _auth.signup(user); + status = AuthStatus.pending; + + emit(_validateResponse(response, status)); + }); + + on((event, emit) async { emit(AuthLoading()); - switch (event.runtimeType) { - case const (AuthLogin): - final user = (event as AuthLogin).user; - final response = await _auth.login(user); - - emit(_validateResponse(response)); - break; - case const (AuthSignUp): - final user = (event as AuthSignUp).user; - final response = await _auth.signup(user); - - emit(_validateResponse(response)); - break; - case const (AuthLogout): - final response = await _auth.signout(); - - emit(_validateResponse(response)); - break; - case AuthLoginWithGoogle: - final response = await _auth.loginWithGoogle(); - - emit(_validateResponse(response)); - break; - case AuthPhoneSignIn: - final phoneNumber = (event as AuthPhoneSignIn).phoneNumber; - final response = await _auth.phoneSignIn(phoneNumber); - - emit(_validateResponse(response)); - break; - default: + final response = await _auth.signout(); + + status = AuthStatus.unauthenticated; + + emit(_validateResponse(response, status)); + }); + + on((event, emit) async { + emit(AuthLoading()); + final response = await _auth.loginWithGoogle(); + + // check if user is already signed up + final email = (response.data as UserCredential).user!.email!; + + final isSignedUp = await _auth.isAlreadySignedUp(email); + + if (isSignedUp.hasError) { + status = AuthStatus.unknown; + emit(_validateResponse(isSignedUp, status)); + } else { + if (isSignedUp.data as bool) { + status = AuthStatus.authenticated; + emit(_validateResponse(response, status)); + } else { + status = AuthStatus.pending; + emit(_validateResponse(response, status)); + } } }); + + on((event, emit) async { + emit(AuthLoading()); + final response = await _auth.loginWithGoogle(); + status = AuthStatus.authenticated; + emit(_validateResponse(response, status)); + }); } - _validateResponse(FirebaseResponse response) { + _validateResponse(FirebaseResponse response, AuthStatus status) { if (response.hasError) { - return UnAuthenticated(response.message!); + return UnAuthenticated(response.message!, status: AuthStatus.unknown); } else { - return Authenticated(response); + return Authenticated(response, status: status); } } } diff --git a/lib/app/bloc/auth/auth_event.dart b/lib/app/bloc/auth/auth_event.dart index 4c5a756..4537d8c 100644 --- a/lib/app/bloc/auth/auth_event.dart +++ b/lib/app/bloc/auth/auth_event.dart @@ -19,8 +19,4 @@ final class AuthLogout extends AuthEvent {} final class AuthLoginWithGoogle extends AuthEvent {} -final class AuthPhoneSignIn extends AuthEvent { - final String phoneNumber; - - AuthPhoneSignIn({required this.phoneNumber}); -} +final class AuthSignUpWithGoogle extends AuthEvent {} \ No newline at end of file diff --git a/lib/app/bloc/auth/auth_state.dart b/lib/app/bloc/auth/auth_state.dart index 13d7e54..78e18e7 100644 --- a/lib/app/bloc/auth/auth_state.dart +++ b/lib/app/bloc/auth/auth_state.dart @@ -7,14 +7,16 @@ final class AuthInitial extends AuthState {} final class Authenticated extends AuthState { final FirebaseResponse response; + final AuthStatus status; - Authenticated(this.response); + Authenticated(this.response, {this.status = AuthStatus.authenticated}); } final class UnAuthenticated extends AuthState { final String message; + final AuthStatus status; - UnAuthenticated(this.message); + UnAuthenticated(this.message, {this.status = AuthStatus.unauthenticated}); } final class AuthLoading extends AuthState {} diff --git a/lib/app/data/interfaces/responses.dart b/lib/app/data/interfaces/responses.dart index 8480d5a..d82c8cd 100644 --- a/lib/app/data/interfaces/responses.dart +++ b/lib/app/data/interfaces/responses.dart @@ -18,7 +18,7 @@ sealed class ResponseBase { }; } - bool get hasError => error != null; + bool get hasError => error.runtimeType != Null; bool get hasData => data != null; } diff --git a/lib/app/data/model/auth_status.dart b/lib/app/data/model/auth_status.dart new file mode 100644 index 0000000..19e5c4c --- /dev/null +++ b/lib/app/data/model/auth_status.dart @@ -0,0 +1 @@ +enum AuthStatus { authenticated, unauthenticated, pending, unknown } diff --git a/lib/app/views/screens/chat/message.dart b/lib/app/data/model/message.dart similarity index 100% rename from lib/app/views/screens/chat/message.dart rename to lib/app/data/model/message.dart diff --git a/lib/app/data/model/user_auth.dart b/lib/app/data/model/user_auth.dart index 723d956..8cf9d9b 100644 --- a/lib/app/data/model/user_auth.dart +++ b/lib/app/data/model/user_auth.dart @@ -20,23 +20,16 @@ final class UserAuthLogin extends UserAuth { } final class UserAuthSignUp extends UserAuth { - final bool isMentor; final String name; - UserAuthSignUp( - {String? email, - String? password, - required this.isMentor, - required this.name}) + UserAuthSignUp({String? email, String? password, required this.name}) : super(email!, password!); UserAuthSignUp.fromJson(Map json) - : isMentor = json['isMentor'], - name = json['name'], + : name = json['name'], super.fromJson(json); Map toJson() => { - 'isMentor': isMentor, 'name': name, ...super.toJson(), }; diff --git a/lib/app/data/repository/auth.dart b/lib/app/data/repository/auth.dart index 2080e56..99d2314 100644 --- a/lib/app/data/repository/auth.dart +++ b/lib/app/data/repository/auth.dart @@ -3,29 +3,33 @@ import 'package:iiitd_mentorship/app/data/model/user_auth.dart'; import 'package:iiitd_mentorship/app/data/services/firebase_auth.dart'; class AuthRepository { - final FirebaseServiceAuth _FirebaseServiceAuth = FirebaseServiceAuth(); + final FirebaseServiceAuth _firebaseServiceAuth = FirebaseServiceAuth(); Future login(UserAuthLogin user) async { - return await _FirebaseServiceAuth.signIn(user); + return await _firebaseServiceAuth.signIn(user); } Future loginWithGoogle() async { - return await _FirebaseServiceAuth.signUpWithGoogle(); + return await _firebaseServiceAuth.signUpWithGoogle(); } Future phoneSignIn(String phoneNumber) async { - return await _FirebaseServiceAuth.phoneSignIn(phoneNumber); + return await _firebaseServiceAuth.phoneSignIn(phoneNumber); } Future signup(UserAuthSignUp user) async { - return await _FirebaseServiceAuth.signUp(user); + return await _firebaseServiceAuth.signUp(user); } Future signout() async { - return await _FirebaseServiceAuth.signOut(); + return await _firebaseServiceAuth.signOut(); + } + + Future isAlreadySignedUp(String email) async { + return await _firebaseServiceAuth.userAlreadySignedUp(email); } // Future resetPassword(String email) async { - // return await _FirebaseServiceAuth.rese(email); + // return await _firebaseServiceAuth.rese(email); // } } diff --git a/lib/app/data/repository/meeting.dart b/lib/app/data/repository/meeting.dart index ccdb0dc..d781fcb 100644 --- a/lib/app/data/repository/meeting.dart +++ b/lib/app/data/repository/meeting.dart @@ -1,35 +1,9 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:iiitd_mentorship/app/data/model/meeting.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; - -class MeetingData { - static final FirebaseFirestore _firestore = FirebaseFirestore.instance; - static final FirebaseAuth _auth = FirebaseAuth.instance; - - static Future addMeeting(Meeting meeting) async { - await _firestore.collection('meetings').add(meeting.toMap()); - } - - static Stream> getMeetingsStream() { - return _firestore - .collection('meetings') - .where('userId', isEqualTo: _auth.currentUser?.uid) - .snapshots() - .map((snapshot) => snapshot.docs - .map((doc) => Meeting.fromMap(doc.data(), doc.id)) - .toList()); - } - - static Future deleteMeeting(String eventId) async { - await _firestore.collection('meetings').doc(eventId).delete(); - } -} - -class MeetingDataSource extends CalendarDataSource { - MeetingDataSource(List source) { +class MeetingServiceRepository extends CalendarDataSource { + MeetingServiceRepository(List source) { appointments = source; } diff --git a/lib/app/views/screens/chat/chat_service.dart b/lib/app/data/services/chat_service.dart similarity index 95% rename from lib/app/views/screens/chat/chat_service.dart rename to lib/app/data/services/chat_service.dart index 97e40f3..77c5169 100644 --- a/lib/app/views/screens/chat/chat_service.dart +++ b/lib/app/data/services/chat_service.dart @@ -1,7 +1,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; -import 'package:iiitd_mentorship/app/views/screens/chat/message.dart'; +import 'package:iiitd_mentorship/app/data/model/message.dart'; class ChatService extends ChangeNotifier { final FirebaseAuth _firebaseAuth = FirebaseAuth.instance; diff --git a/lib/app/data/services/firebase_auth.dart b/lib/app/data/services/firebase_auth.dart index 21b0b28..f65aec9 100644 --- a/lib/app/data/services/firebase_auth.dart +++ b/lib/app/data/services/firebase_auth.dart @@ -1,3 +1,4 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:google_sign_in/google_sign_in.dart'; @@ -9,9 +10,6 @@ class FirebaseServiceAuth { bool isSignedUp = false; final FirebaseAuth _auth = FirebaseAuth.instance; final GoogleSignIn _googleSignIn = GoogleSignIn(); - User? _currentUser; - - User? get currentUser => _currentUser; Future signUpWithGoogle() async { try { @@ -33,15 +31,13 @@ class FirebaseServiceAuth { idToken: googleSignInAuthentication.idToken, accessToken: googleSignInAuthentication.accessToken); - UserCredential userCredential = - await _auth.signInWithCredential(credential); - _currentUser = userCredential.user; + final userCreds = await _auth.signInWithCredential(credential); + return FirebaseResponse( - status: true, - message: 'User signed in with Google successfully', - data: _currentUser, - code: 200, - ); + status: true, + message: 'User signed in with Google successfully', + code: 200, + data: userCreds); } catch (exception) { return FirebaseResponse( status: false, @@ -194,7 +190,6 @@ class FirebaseServiceAuth { try { await _auth.signOut(); _googleSignIn.disconnect(); - _currentUser = null; firebaseResponse.status = true; firebaseResponse.message = 'User signed out successfully'; firebaseResponse.statusCode = 200; @@ -239,4 +234,33 @@ class FirebaseServiceAuth { return firebaseResponse; } + + Future userAlreadySignedUp(String email) async { + var firebaseResponse = FirebaseResponse(status: false); + + try { + var response = await FirebaseFirestore.instance + .collection('users') + .where('email', isEqualTo: email) + .get(); + + if (response.docs.isNotEmpty) { + firebaseResponse.status = true; + firebaseResponse.message = 'User already signed up'; + firebaseResponse.statusCode = 200; + firebaseResponse.data = true; + } else { + firebaseResponse.status = true; + firebaseResponse.message = 'User not signed up'; + firebaseResponse.statusCode = 200; + firebaseResponse.data = false; + } + } on FirebaseException catch (e) { + firebaseResponse.message = 'Eror checking if user already signed up'; + firebaseResponse.statusCode = 400; + firebaseResponse.error = e; + } + + return firebaseResponse; + } } diff --git a/lib/app/data/services/meeting.dart b/lib/app/data/services/meeting.dart new file mode 100644 index 0000000..607f097 --- /dev/null +++ b/lib/app/data/services/meeting.dart @@ -0,0 +1,46 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:iiitd_mentorship/app/data/model/meeting.dart'; + +import 'package:rxdart/rxdart.dart'; + +class MeetingService { + static final FirebaseFirestore _firestore = FirebaseFirestore.instance; + static final FirebaseAuth _auth = FirebaseAuth.instance; + + static Future addMeeting(Meeting meeting) async { + await _firestore.collection('meetings').add(meeting.toMap()); + } + + static Stream> getMeetingsStream() async* { + String currentUserEmail = _auth.currentUser?.email ?? ''; + String currentUserId = _auth.currentUser?.uid ?? ''; + + // Stream of meetings created by the user + var userMeetingsStream = _firestore + .collection('meetings') + .where('userId', isEqualTo: currentUserId) + .snapshots(); + + // Stream of all meetings + var allMeetingsStream = _firestore.collection('meetings').snapshots(); + + await for (var combinedSnapshot in Rx.combineLatest2(userMeetingsStream, allMeetingsStream, (userMeetings, allMeetings) { + return userMeetings.docs.map((doc) => Meeting.fromMap(doc.data(), doc.id)).toList() + ..addAll(allMeetings.docs + .where((doc) => doc.data()['emailIDs'].toString().split(',').map((email) => email.trim()).contains(currentUserEmail)) + .map((doc) => Meeting.fromMap(doc.data(), doc.id)) + .toList()); + })) { + yield combinedSnapshot.toSet().toList(); // To remove duplicates + } + } + static Future deleteMeeting(String eventId) async { + await _firestore.collection('meetings').doc(eventId).delete(); + } + + static Future getUserName(String userId) async { + var userDocument = await _firestore.collection('users').doc(userId).get(); + return userDocument.data()?['name'] ?? 'Unknown User'; + } +} \ No newline at end of file diff --git a/lib/app/views/screens/auth/onboarding.dart b/lib/app/views/screens/auth/onboarding.dart index 362f2c2..807b6cf 100644 --- a/lib/app/views/screens/auth/onboarding.dart +++ b/lib/app/views/screens/auth/onboarding.dart @@ -2,6 +2,8 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:iiitd_mentorship/app/bloc/auth/auth_bloc.dart'; +import 'package:iiitd_mentorship/app/data/model/auth_status.dart'; +import 'package:iiitd_mentorship/app/views/screens/auth/user_detail.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_button.dart'; class OnBoardsScreen extends StatefulWidget { @@ -15,9 +17,10 @@ class _OnBoardsScreenState extends State { @override void initState() { super.initState(); + final status = context.read().status; FirebaseAuth.instance.authStateChanges().listen((User? user) { if (mounted) { - if (user != null) { + if (user != null && status == AuthStatus.unknown) { Navigator.pushNamedAndRemoveUntil( context, "/driver", (route) => false); } @@ -44,12 +47,35 @@ class _OnBoardsScreenState extends State { ); break; case Authenticated: - Navigator.pop(context); - Navigator.pushNamedAndRemoveUntil( - context, "/driver", (route) => false); + final user = + (state as Authenticated).response.data as UserCredential; + final status = state.status; + switch (status) { + case AuthStatus.authenticated: + Navigator.pushNamedAndRemoveUntil( + context, "/driver", (route) => false); + break; + case AuthStatus.pending: + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute( + builder: (context) => UserDetailsScreen( + name: user.user!.displayName, + email: user.user!.email)), + (route) => false); + break; + case AuthStatus.unauthenticated: + Navigator.pushNamedAndRemoveUntil( + context, "/onboarding", (route) => false); + break; + case AuthStatus.unknown: + Navigator.pushNamedAndRemoveUntil( + context, "/onboarding", (route) => false); + break; + default: + } break; case UnAuthenticated: - Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text((state as UnAuthenticated).message), @@ -116,7 +142,9 @@ class _OnBoardsScreenState extends State { children: [ MaterialButton( onPressed: () { - context.read().add(AuthLoginWithGoogle()); + context + .read() + .add(AuthSignUpWithGoogle()); }, color: Colors.white, minWidth: 100, diff --git a/lib/app/views/screens/auth/signup.dart b/lib/app/views/screens/auth/signup.dart index ca00bd7..496eaea 100644 --- a/lib/app/views/screens/auth/signup.dart +++ b/lib/app/views/screens/auth/signup.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:iiitd_mentorship/app/bloc/auth/auth_bloc.dart'; +import 'package:iiitd_mentorship/app/data/model/auth_status.dart'; import 'package:iiitd_mentorship/app/data/model/user_auth.dart'; +import 'package:iiitd_mentorship/app/views/screens/auth/user_detail.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_button.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_textbox.dart'; @@ -13,7 +15,6 @@ class SignUpScreen extends StatefulWidget { } class _SignUpScreenState extends State { - bool isMentor = false; bool readterms = false; bool readpolicy = false; @@ -40,13 +41,17 @@ class _SignUpScreenState extends State { ); break; case Authenticated: - // Navigate to UserDetailsScreen when authenticated - Navigator.pop(context); - Navigator.pushNamedAndRemoveUntil( - context, "/userdetails", (route) => false); + if ((state as Authenticated).status == AuthStatus.pending) { + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute( + builder: (context) => UserDetailsScreen( + name: nameController.text, + email: mailController.text)), + (route) => false); + } break; case UnAuthenticated: - Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text((state as UnAuthenticated).message), @@ -102,15 +107,6 @@ class _SignUpScreenState extends State { const SizedBox( height: 20, ), - SwitchListTile( - title: Text(isMentor ? 'Mentor' : 'Student'), - value: isMentor, - onChanged: (bool value) { - setState(() { - isMentor = value; - }); - }, - ), // two checkboxes for reading policy and terms const SizedBox( @@ -175,7 +171,6 @@ class _SignUpScreenState extends State { BlocProvider.of(context).add(AuthSignUp( user: UserAuthSignUp( name: nameController.text, - isMentor: isMentor, email: mailController.text, password: passwordController.text, ), diff --git a/lib/app/views/screens/auth/user_detail.dart b/lib/app/views/screens/auth/user_detail.dart index 0720dde..6162fed 100644 --- a/lib/app/views/screens/auth/user_detail.dart +++ b/lib/app/views/screens/auth/user_detail.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:iiitd_mentorship/app/data/model/user.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_textbox.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_button.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_dropdown.dart'; @@ -10,17 +11,18 @@ class UserDetailsScreen extends StatefulWidget { final String? email; const UserDetailsScreen({ - Key? key, + super.key, this.name, this.email, - }) : super(key: key); + }); @override - _UserDetailsScreenState createState() => _UserDetailsScreenState(); + State createState() => _UserDetailsScreenState(); } class _UserDetailsScreenState extends State { final formKey = GlobalKey(); + late TextEditingController yearOfJoiningController; late TextEditingController yearOfGraduationController; late TextEditingController collegeController; late TextEditingController companyController; @@ -44,13 +46,15 @@ class _UserDetailsScreenState extends State { @override void initState() { super.initState(); + yearOfJoiningController = TextEditingController(); yearOfGraduationController = TextEditingController(); collegeController = TextEditingController(); companyController = TextEditingController(); } @override - void dispose() {; + void dispose() { + yearOfJoiningController.dispose(); yearOfGraduationController.dispose(); collegeController.dispose(); companyController.dispose(); @@ -59,10 +63,18 @@ class _UserDetailsScreenState extends State { bool isMentor = false; + Future completeUserDetails(DBUser user) async { + return await FirebaseFirestore.instance + .collection('users') + .doc(user.uid) + .set(user.toJson()); + } + @override Widget build(BuildContext context) { + User? user = FirebaseAuth.instance.currentUser; return Scaffold( - appBar: AppBar(title: Text('User Details')), + appBar: AppBar(title: const Text('User Details')), body: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Form( @@ -74,7 +86,7 @@ class _UserDetailsScreenState extends State { hintText: 'Year of Graduation', validationMessage: 'Please enter the year of graduation', ), - SizedBox(height: 20), + const SizedBox(height: 20), CustomDropdown( labelText: 'Course', value: selectedCourse, @@ -90,8 +102,8 @@ class _UserDetailsScreenState extends State { validator: (value) => value == null ? 'Please select a course' : null, ), - SizedBox(height: 20), - if (selectedCourse != null) SizedBox(height: 20), + const SizedBox(height: 20), + if (selectedCourse != null) const SizedBox(height: 20), CustomDropdown( labelText: 'Branch', value: selectedBranch, @@ -110,7 +122,7 @@ class _UserDetailsScreenState extends State { validator: (value) => value == null ? 'Please select a branch' : null, ), - SizedBox(height: 20), + const SizedBox(height: 20), SwitchListTile( title: const Text('Are you interested to become a mentor?'), value: isMentor, @@ -121,78 +133,63 @@ class _UserDetailsScreenState extends State { }, ), if (isMentor) ...[ - SizedBox(height: 20), + const SizedBox(height: 20), CustomTextBox( controller: collegeController, hintText: 'College', validationMessage: 'Please enter the college name', ), - SizedBox(height: 20), + const SizedBox(height: 20), CustomTextBox( controller: companyController, hintText: 'Company', validationMessage: 'Please enter the company name', ), ], - SizedBox(height: 20), + const SizedBox(height: 20), CustomButton( - onPressed: () async { + onPressed: () { if (formKey.currentState!.validate()) { - // Fetch the current user - User? user = FirebaseAuth.instance.currentUser; - if (user != null) { - // Prepare the data to be stored - Map userData = { - 'uid': user.uid, // Using Firebase Auth user ID - 'name': widget.name, // Passed from the previous screen - 'email': - widget.email, // Passed from the previous screen - 'yearOfGraduation': yearOfGraduationController.text, - 'course': selectedCourse, - 'branch': selectedBranch, - 'isMentor': isMentor, - 'adminApproval': false, // Default value - }; - - if (isMentor) { - userData['college'] = collegeController.text; - userData['company'] = companyController.text; - } - - // Store in Firestore - try { - await FirebaseFirestore.instance - .collection('users') - .doc(user.uid) // Using Firebase Auth user ID - .set(userData); + DBUser userMentor = DBUser( + uid: user!.uid, + name: widget.name, + email: widget.email, + yearOfGraduation: yearOfGraduationController.text, + yearOfJoining: yearOfJoiningController.text, + course: selectedCourse, + branch: selectedBranch, + isMentor: isMentor, + college: collegeController.text, + company: companyController.text, + adminApproval: false, + isProfileComplete: true); - // Handle successful submission - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Details submitted successfully!')), - ); + DBUser userStudent = DBUser( + uid: user.uid, + name: widget.name, + email: widget.email, + yearOfGraduation: yearOfGraduationController.text, + yearOfJoining: yearOfJoiningController.text, + course: selectedCourse, + branch: selectedBranch, + isMentor: isMentor, + adminApproval: false, + isProfileComplete: true); - // Optionally navigate to another screen or reset the form - // Navigator.pushReplacement(...); - } catch (e) { - // Display error message - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Failed to submit details: ${e.toString()}')), - ); - } + if (isMentor) { + completeUserDetails(userMentor).then((value) { + Navigator.pushNamedAndRemoveUntil( + context, '/driver', (route) => false); + }); } else { - // Handle user not logged in scenario - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'No authenticated user found. Please log in.')), - ); + completeUserDetails(userStudent).then((value) { + Navigator.pushNamedAndRemoveUntil( + context, '/driver', (route) => false); + }); } } }, - child: Text('Submit'), + child: const Text('Submit'), ), ], ), diff --git a/lib/app/views/screens/chat/chat.dart b/lib/app/views/screens/chat/chat.dart index 4de0dc3..d5d2b7d 100644 --- a/lib/app/views/screens/chat/chat.dart +++ b/lib/app/views/screens/chat/chat.dart @@ -14,17 +14,9 @@ class ChatScreen extends StatefulWidget { } class _ChatScreenState extends State { - final TextEditingController _textController = TextEditingController(); final List _connections = []; final FirebaseAuth _auth = FirebaseAuth.instance; - // void _handleSubmittedMessage(String text) { - // setState(() { - // _connections.insert(0, text); // Add the message to the list - // _textController.clear(); // Clear the text input field - // }); - // } - @override void initState() { // TODO: implement initState @@ -36,76 +28,10 @@ class _ChatScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: _userList(), - - // CustomScrollView( - // physics: const BouncingScrollPhysics(), - // slivers: [ - // SliverPadding( - // padding: const EdgeInsets.all(8.0), - // sliver: SliverList( - // delegate: SliverChildListDelegate([ - // SizedBox( - // height: MediaQuery - // .of(context) - // .size - // .height * 0.7, - // child: ListView.builder( - // itemCount: _connections.length, - // itemBuilder: (context, index) { - // return GestureDetector( - // onTap: () => - // { - // Navigator.pushNamed(context, "/Connection1"), - // }, - // child: ConversationTile( - // connectionName: _connections[index],), - // ); - // }, - // ), - // ), - // ]), - // ), - // ) - // ] - // ) - ); - - // return MaterialApp( - // routes: { - // '/Connection1': (BuildContext context) => const MyChats(title: "Connection1"), - // '/Connection2': (BuildContext context) => const MyChats(title: "Connection2"), - // }, - // home: Scaffold( - // appBar: AppBar( - // backgroundColor: Theme - // .of(context) - // .colorScheme - // .inversePrimary, - // title: Text(widget.title), - // ), - // body: Column( - // children: [ - // Flexible( - // child: ListView.builder( - // reverse: false, // Start from the bottom of the list - // itemCount: _connections.length, - // itemBuilder: (context, index) { - // return IconButton( - // onPressed: () => - // Navigator.pushNamed(context, "/Connection1"), - // icon: ConversationTile( - // connectionName: _connections[index],)); - // }, - // ), - // ), - // ], - // ), - // ) - // ); + appBar: AppBar( + title: Text(widget.title), + ), + body: _userList()); } // building a list of user except current user diff --git a/lib/app/views/screens/chat/chat_page.dart b/lib/app/views/screens/chat/chat_page.dart index e936405..e7a3013 100644 --- a/lib/app/views/screens/chat/chat_page.dart +++ b/lib/app/views/screens/chat/chat_page.dart @@ -1,7 +1,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; -import 'package:iiitd_mentorship/app/views/screens/chat/chat_service.dart'; +import 'package:iiitd_mentorship/app/data/services/chat_service.dart'; import 'package:iiitd_mentorship/app/views/widgets/conversation_tile.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_textbox.dart'; import 'package:iiitd_mentorship/app/views/widgets/message_tile.dart'; @@ -101,16 +101,6 @@ class _ChatPageState extends State { message: data['message'], time: DateFormat('kk:mm').format(dateTime), ); - - // Container( - // alignment: alignment, - // child: Column( - // children: [ - // Text(data['senderEmail']), - // Text(data['message']), - // ], - // ), - // ); } // build message input @@ -141,78 +131,4 @@ class _ChatPageState extends State { ), ); } - -// final List _messages = []; - -// void _handleSubmittedMessage(String text) { -// setState(() { -// _messages.insert(0, text); // Add the message to the list -// _messageController.clear(); // Clear the text input field -// }); -// } - -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// // backgroundColor: Theme.of(context).colorScheme.inversePrimary, -// title: const ConversationTile( -// showDetails: false, -// ), //Text(widget.title), -// ), -// body: Column( -// children: [ -// Flexible( -// child: ListView.builder( -// reverse: true, // Start from the bottom of the list -// itemCount: _messages.length, -// itemBuilder: (context, index) { -// return ((_messages.length - index) % 2 == 1) -// ? MessageTile( -// isMe: true, -// message: _messages[index], -// time: "12:00", -// isRead: true, -// ) -// : MessageTile( -// isMe: false, -// message: _messages[index], -// time: "12:00", -// ); -// }, -// ), -// ), -// Divider(height: 1.0), -// Container( -// margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0), -// decoration: BoxDecoration( -// color: Theme.of(context).cardColor, -// ), -// child: _buildTextComposer(), -// ), -// ], -// ), -// ); -// } -// -// Widget _buildTextComposer() { -// return IconTheme( -// data: IconThemeData(color: Theme.of(context).primaryColor), -// child: Row( -// children: [ -// Flexible( -// child: CustomTextBox( -// validationMessage: "Please enter some text", -// controller: _messageController, -// hintText: 'Type a message', -// ), -// ), -// IconButton( -// icon: Icon(Icons.send), -// onPressed: () => sendMessage(), -// ), -// ], -// ), -// ); -// } } diff --git a/lib/app/views/screens/home/home.dart b/lib/app/views/screens/home/home.dart index 83f136c..78aee7b 100644 --- a/lib/app/views/screens/home/home.dart +++ b/lib/app/views/screens/home/home.dart @@ -33,6 +33,7 @@ class _MyHomePageState extends State { return FirebaseFirestore.instance .collection("users") .where("isMentor", isEqualTo: true) + // .where("uid", isNotEqualTo: currentUser!.uid) .snapshots(); } @@ -134,8 +135,8 @@ class _MyHomePageState extends State { !userData.isMentor! ? SessionActionButton( action: "Find a mentor", - onPressed: () => Navigator.pushNamed( - context, "/home/schedule"), + onPressed: () => + Navigator.pushNamed(context, "/search"), ) : Container(), const SizedBox( @@ -163,7 +164,15 @@ class _MyHomePageState extends State { builder: (context, mentorsSnapshot) { if (mentorsSnapshot.hasData) { final mentorsData = mentorsSnapshot.data!; - final mentorsList = mentorsData.docs; + final List mentorsList = mentorsData.docs + .map((e) { + return DBUser.fromJson(e.data()); + }) + .toList() + .where((element) { + return element.uid != currentUser!.uid; + }) + .toList(); return SizedBox( height: 100, @@ -176,8 +185,7 @@ class _MyHomePageState extends State { MediaQuery.of(context).size.width * 0.7, child: MentorTile( - mentor: DBUser.fromJson( - mentorsList[index].data()), + mentor: mentorsList[index], )); }, ), @@ -254,10 +262,6 @@ class _MyHomePageState extends State { .compareTo(b["from"].toDate()); }); - sessionsList.forEach((element) { - print(currentUser!.email); - }); - return ListView.builder( itemCount: sessionsList.length, shrinkWrap: true, diff --git a/lib/app/views/screens/profile/profile.dart b/lib/app/views/screens/profile/profile.dart index 703cdf7..50ef46a 100644 --- a/lib/app/views/screens/profile/profile.dart +++ b/lib/app/views/screens/profile/profile.dart @@ -203,16 +203,20 @@ class _ProfileScreenState extends State { onPressed: _updateProfile, ), IconButton( - onPressed: () => context.read().add(AuthLogout()), + onPressed: () => + BlocProvider.of(context).add(AuthLogout()), icon: const Icon(Icons.logout)) ], ), body: Padding( padding: const EdgeInsets.all(12.0), - child: BlocConsumer( - listener: (context, state) {}, - builder: (context, state) { - Widget widget; + child: BlocConsumer(listener: (context, state) { + if (state is Authenticated) { + Navigator.of(context).pushNamedAndRemoveUntil( + '/onboarding', (Route route) => false); + } + }, builder: (context, state) { + Widget widget; if (state is AuthLoading) { widget = const Center( @@ -395,8 +399,8 @@ class _ProfileScreenState extends State { ); } - return widget; - }), + return widget; + }), ), ); } diff --git a/lib/app/views/screens/schedule/create.dart b/lib/app/views/screens/schedule/create.dart index da4b636..a72f524 100644 --- a/lib/app/views/screens/schedule/create.dart +++ b/lib/app/views/screens/schedule/create.dart @@ -1,7 +1,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:iiitd_mentorship/app/data/model/meeting.dart'; -import 'package:iiitd_mentorship/app/data/repository/meeting.dart'; +import 'package:iiitd_mentorship/app/data/services/meeting.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_button.dart'; import 'package:iiitd_mentorship/app/views/widgets/custom_textbox.dart'; @@ -19,6 +19,7 @@ class _ScheduleMeetingScreenState extends State { DateTime _selectedDate = DateTime.now(); TimeOfDay _startTime = TimeOfDay.now(); TimeOfDay _endTime = TimeOfDay.now(); + final formKey = GlobalKey(); @override Widget build(BuildContext context) { @@ -31,63 +32,66 @@ class _ScheduleMeetingScreenState extends State { fontSize: 15, // Set the font size color: Colors.black, // Set the color ), - child: ListView( - children: [ - const Text('Meeting Title'), - const SizedBox(height: 10), - CustomTextBox( - controller: _titleController, - validationMessage: 'Please enter meeting title', - hintText: 'Meeting Title', - ), - const SizedBox(height: 16), - const Text('Meeting Description'), - const SizedBox(height: 10), - CustomTextBox( - controller: _descriptionController, - validationMessage: 'Please enter meeting description', - hintText: 'Meeting Description', - ), - const SizedBox(height: 16), - const Text('Email IDs'), - const SizedBox(height: 10), - CustomTextBox( - controller: _emailController, - //keyboardType: TextInputType.emailAddress, - validationMessage: 'Please enter email IDs', - hintText: 'e.g. mentor@example.com, mentee@example.com', - ), - const SizedBox(height: 16), - ListTile( - title: const Text('Date'), - subtitle: Text(_selectedDate == null - ? 'Select Date' - : _selectedDate.toLocal().toString().split(' ')[0]), - trailing: const Icon(Icons.calendar_today), - onTap: _pickDate, - ), - ListTile( - title: const Text('Start Time'), - subtitle: Text(_startTime == null - ? 'Select Start Time' - : _startTime.format(context)), - trailing: const Icon(Icons.access_time), - onTap: _pickStartTime, - ), - ListTile( - title: const Text('End Time'), - subtitle: Text(_endTime == null - ? 'Select End Time' - : _endTime.format(context)), - trailing: const Icon(Icons.access_time), - onTap: _pickEndTime, - ), - CustomButton( - rounded: true, - onPressed: _scheduleMeeting, - child: const Text('Schedule Meeting'), - ), - ], + child: Form( + key: formKey, + child: ListView( + children: [ + const Text('Meeting Title'), + const SizedBox(height: 10), + CustomTextBox( + controller: _titleController, + validationMessage: 'Please enter meeting title', + hintText: 'Meeting Title', + ), + const SizedBox(height: 16), + const Text('Meeting Description'), + const SizedBox(height: 10), + CustomTextBox( + controller: _descriptionController, + validationMessage: 'Please enter meeting description', + hintText: 'Meeting Description', + ), + const SizedBox(height: 16), + const Text('Email IDs'), + const SizedBox(height: 10), + CustomTextBox( + controller: _emailController, + //keyboardType: TextInputType.emailAddress, + validationMessage: 'Please enter email IDs', + hintText: 'e.g. mentor@example.com, mentee@example.com', + ), + const SizedBox(height: 16), + ListTile( + title: const Text('Date'), + subtitle: Text(_selectedDate == null + ? 'Select Date' + : _selectedDate.toLocal().toString().split(' ')[0]), + trailing: const Icon(Icons.calendar_today), + onTap: _pickDate, + ), + ListTile( + title: const Text('Start Time'), + subtitle: Text(_startTime == null + ? 'Select Start Time' + : _startTime.format(context)), + trailing: const Icon(Icons.access_time), + onTap: _pickStartTime, + ), + ListTile( + title: const Text('End Time'), + subtitle: Text(_endTime == null + ? 'Select End Time' + : _endTime.format(context)), + trailing: const Icon(Icons.access_time), + onTap: _pickEndTime, + ), + CustomButton( + rounded: true, + onPressed: _scheduleMeeting, + child: const Text('Schedule Meeting'), + ), + ], + ), ), ), ), @@ -133,9 +137,7 @@ class _ScheduleMeetingScreenState extends State { } _scheduleMeeting() async { - if (_titleController.text.isNotEmpty && - _descriptionController.text.isNotEmpty && - _emailController.text.isNotEmpty) { + if (formKey.currentState!.validate()) { DateTime startDateTime = DateTime(_selectedDate.year, _selectedDate.month, _selectedDate.day, _startTime.hour, _startTime.minute); @@ -156,7 +158,7 @@ class _ScheduleMeetingScreenState extends State { currentUserID, // User ID ); - await MeetingData.addMeeting(newMeeting); + await MeetingService.addMeeting(newMeeting); Navigator.pop(context); } else { diff --git a/lib/app/views/screens/schedule/meeting_details_page.dart b/lib/app/views/screens/schedule/meeting_details_page.dart index 044601b..6c51d12 100644 --- a/lib/app/views/screens/schedule/meeting_details_page.dart +++ b/lib/app/views/screens/schedule/meeting_details_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:iiitd_mentorship/app/data/model/meeting.dart'; -import 'package:iiitd_mentorship/app/data/repository/meeting.dart'; +import 'package:iiitd_mentorship/app/data/services/meeting.dart'; class MeetingDetailsPage extends StatelessWidget { final Meeting meeting; @@ -14,38 +14,83 @@ class MeetingDetailsPage extends StatelessWidget { title: const Text('Meeting Details'), elevation: 0, ), - body: SingleChildScrollView( - child: Container( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - meetingDetailCard(Icons.title, 'Title', meeting.title), + body: FutureBuilder( + future: MeetingService.getUserName(meeting.userId), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + if (!snapshot.hasData || snapshot.data == 'Unknown User') { + return Center(child: Text('No Creator Information Available')); + } + return SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + _creatorNameCard(context, snapshot.data!), + meetingDetailCard(context, Icons.title, 'Title', meeting.title), + meetingDetailCard(context, Icons.description, 'Description', + meeting.description), meetingDetailCard( - Icons.description, 'Description', meeting.description), - meetingDetailCard(Icons.email, 'Email IDs', meeting.emailIDs), + context, Icons.email, 'Invitees', meeting.emailIDs), + meetingDetailCard(context, Icons.calendar_today, 'Start', + meeting.from.toString()), meetingDetailCard( - Icons.calendar_today, 'Start', meeting.from.toString()), - meetingDetailCard( - Icons.calendar_today, 'End', meeting.to.toString()), + context, Icons.calendar_today, 'End', meeting.to.toString()), const SizedBox(height: 20), cancelMeetingButton(context), ], ), ), + ); + } ), ); } - Widget meetingDetailCard(IconData icon, String label, String content) { + Widget _creatorNameCard(BuildContext context, String creatorName) { + return Column( + children: [ + Container( + padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20), + decoration: BoxDecoration( + color: Theme.of(context).primaryColorLight, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 4, + offset: Offset(0, 2), + ), + ], + ), + child: Text( + 'Meeting created by: $creatorName', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColorDark, + ), + ), + ), + SizedBox(height: 20), // Adjust the height as needed + ], + ); + } + + Widget meetingDetailCard( + BuildContext context, icon, String label, String content) { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), elevation: 4, margin: const EdgeInsets.symmetric(vertical: 6), child: ListTile( - leading: Icon(icon, color: Colors.deepPurple), + leading: Icon(icon, color: Theme.of(context).primaryColor), title: Text(label), subtitle: Text(content), - contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + contentPadding: const EdgeInsets.symmetric(horizontal: 26, vertical: 4), ), ); } @@ -57,7 +102,7 @@ class MeetingDetailsPage extends StatelessWidget { label: const Text('Cancel Meeting', style: TextStyle(color: Colors.white)), style: ElevatedButton.styleFrom( - primary: Colors.red, + backgroundColor: Colors.red, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15), textStyle: const TextStyle(fontSize: 16), @@ -67,7 +112,7 @@ class MeetingDetailsPage extends StatelessWidget { void _cancelMeeting(BuildContext context) { // Implementation of the cancel meeting functionality - MeetingData.deleteMeeting(meeting.eventId).then((_) { + MeetingService.deleteMeeting(meeting.eventId).then((_) { Navigator.pop(context); }).catchError((error) { ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/app/views/screens/schedule/schedule.dart b/lib/app/views/screens/schedule/schedule.dart index dde0b30..2aea149 100644 --- a/lib/app/views/screens/schedule/schedule.dart +++ b/lib/app/views/screens/schedule/schedule.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:iiitd_mentorship/app/data/model/meeting.dart'; import 'package:iiitd_mentorship/app/data/repository/meeting.dart'; +import 'package:iiitd_mentorship/app/data/services/meeting.dart'; import 'package:syncfusion_flutter_calendar/calendar.dart'; import 'create.dart'; @@ -19,7 +20,7 @@ class _MySchedulesScreenState extends State { return Scaffold( appBar: AppBar(title: const Text('Month Agenda View')), body: StreamBuilder>( - stream: MeetingData.getMeetingsStream(), + stream: MeetingService.getMeetingsStream(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); @@ -29,7 +30,7 @@ class _MySchedulesScreenState extends State { } return SfCalendar( view: CalendarView.month, - dataSource: MeetingDataSource(snapshot.data!), + dataSource: MeetingServiceRepository(snapshot.data!), monthViewSettings: const MonthViewSettings(showAgenda: true), onTap: (CalendarTapDetails details) { if (details.targetElement == CalendarElement.appointment || diff --git a/pubspec.yaml b/pubspec.yaml index 29cea00..7ff0661 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,7 @@ dependencies: # latest version of mockito mockito: ^5.4.3 build_runner: ^2.4.6 + rxdart: ^0.27.7 # The following adds the Cupertino Icons font to your application. diff --git a/test/meeting_test.dart b/test/meeting_test.dart index 6ac5599..e278fb0 100644 --- a/test/meeting_test.dart +++ b/test/meeting_test.dart @@ -1,21 +1,27 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:iiitd_mentorship/app/data/model/meeting.dart'; import 'package:iiitd_mentorship/app/data/repository/meeting.dart'; import 'package:mockito/mockito.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; class MockFirebaseFirestore extends Mock implements FirebaseFirestore {} + class MockFirebaseAuth extends Mock implements FirebaseAuth {} + class MockCollectionReference extends Mock implements CollectionReference {} + class MockDocumentReference extends Mock implements DocumentReference {} + class MockQuerySnapshot extends Mock implements QuerySnapshot {} + class MockDocumentSnapshot extends Mock implements DocumentSnapshot {} + class MockUser extends Mock implements User {} void main() { - group('MeetingData', () { + group('MeetingService', () { late MockFirebaseFirestore mockFirestore; late MockFirebaseAuth mockAuth; late MockCollectionReference mockCollection; @@ -34,17 +40,16 @@ void main() { // Setup mock responses when(mockCollection.doc(any)).thenReturn(mockDocument); when(mockCollection.add(any)).thenAnswer((_) async => mockDocument); - when(mockCollection.snapshots()).thenAnswer((_) => Stream.value(mockQuerySnapshot)); + when(mockCollection.snapshots()) + .thenAnswer((_) => Stream.value(mockQuerySnapshot)); when(mockAuth.currentUser).thenReturn(mockUser); when(mockUser.uid).thenReturn('testUID'); }); - - }); - group('MeetingDataSource', () { + group('MeetingServiceRepository', () { late List meetings; - late MeetingDataSource dataSource; + late MeetingServiceRepository dataSource; setUp(() { meetings = [ @@ -72,7 +77,7 @@ void main() { 'userId', ), ]; - dataSource = MeetingDataSource(meetings); + dataSource = MeetingServiceRepository(meetings); }); test('getStartTime returns correct start time for meetings', () { @@ -101,7 +106,7 @@ void main() { }); test('DataSource handles empty meeting list', () { - final emptyDataSource = MeetingDataSource([]); + final emptyDataSource = MeetingServiceRepository([]); expect(emptyDataSource.appointments?.isEmpty, true); expect(() => emptyDataSource.getStartTime(0), throwsRangeError); expect(() => emptyDataSource.getEndTime(0), throwsRangeError); diff --git a/test/meeting_model_test.dart b/test/model_meeting_test.dart similarity index 97% rename from test/meeting_model_test.dart rename to test/model_meeting_test.dart index 461aa0c..73625e0 100644 --- a/test/meeting_model_test.dart +++ b/test/model_meeting_test.dart @@ -15,8 +15,7 @@ void main() { DateTime(2023, 1, 1, 10, 0), Colors.blue, false, - 'userId123' - ); + 'userId123'); expect(meeting.eventName, 'Event Name'); expect(meeting.title, 'Title'); @@ -54,7 +53,6 @@ void main() { expect(meeting.isAllDay, false); }); - test('toMap converts a Meeting to Map', () { final meeting = Meeting( 'Event Name', @@ -65,8 +63,7 @@ void main() { DateTime(2023, 1, 1, 10, 0), Colors.blue, false, - 'userId123' - ); + 'userId123'); final map = meeting.toMap(); @@ -82,4 +79,3 @@ void main() { }); }); } - diff --git a/test/mentor_test.dart b/test/model_mentor_test.dart similarity index 83% rename from test/mentor_test.dart rename to test/model_mentor_test.dart index 2a5b00d..ae9b5d0 100644 --- a/test/mentor_test.dart +++ b/test/model_mentor_test.dart @@ -1,7 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:iiitd_mentorship/app/data/model/mentor.dart'; - void main() { group('Mentor', () { test('fromJson creates a Mentor from JSON', () { @@ -23,8 +22,18 @@ void main() { test('fromJsonList creates a list of Mentors from JSON list', () { final jsonList = [ - {'bio': 'Bio 1', 'name': 'Name 1', 'photoUrl': 'http://example.com/photo1.jpg', 'stars': 5}, - {'bio': 'Bio 2', 'name': 'Name 2', 'photoUrl': 'http://example.com/photo2.jpg', 'stars': 3} + { + 'bio': 'Bio 1', + 'name': 'Name 1', + 'photoUrl': 'http://example.com/photo1.jpg', + 'stars': 5 + }, + { + 'bio': 'Bio 2', + 'name': 'Name 2', + 'photoUrl': 'http://example.com/photo2.jpg', + 'stars': 3 + } ]; final mentors = Mentor.fromJsonList(jsonList); @@ -40,8 +49,7 @@ void main() { bio: 'Sample Bio', name: 'Sample Name', photoUrl: 'http://example.com/photo.jpg', - stars: 4 - ); + stars: 4); final json = mentor.toJson(); diff --git a/test/topic_test.dart b/test/model_topic_test.dart similarity index 97% rename from test/topic_test.dart rename to test/model_topic_test.dart index 674645c..498614c 100644 --- a/test/topic_test.dart +++ b/test/model_topic_test.dart @@ -8,8 +8,7 @@ void main() { title: 'Sample Title', description: 'Sample Description', imageUrl: 'http://example.com/image.jpg', - id: '1' - ); + id: '1'); expect(topic.title, 'Sample Title'); expect(topic.description, 'Sample Description'); diff --git a/test/model_user_auth_test.dart b/test/model_user_auth_test.dart index 70f1651..92cb5d0 100644 --- a/test/model_user_auth_test.dart +++ b/test/model_user_auth_test.dart @@ -26,21 +26,18 @@ void main() { expect(userAuthSignUp, isA()); expect(userAuthSignUp.email, equals('test@example.com')); expect(userAuthSignUp.password, equals('password')); - expect(userAuthSignUp.isMentor, equals(true)); + expect(userAuthSignUp.name, equals('John Doe')); }); test('toJson() should return a valid JSON object', () { final userAuthSignUp = UserAuthSignUp( - email: 'test@example.com', - password: 'password', - isMentor: true, - name: 'John Doe'); + email: 'test@example.com', password: 'password', name: 'John Doe'); final json = userAuthSignUp.toJson(); expect(json['email'], equals('test@example.com')); expect(json['password'], equals('password')); - expect(json['isMentor'], equals(true)); + expect(json['name'], equals('John Doe')); }); }); diff --git a/test/user_test.dart b/test/model_user_test.dart similarity index 97% rename from test/user_test.dart rename to test/model_user_test.dart index e6efc04..a86123b 100644 --- a/test/user_test.dart +++ b/test/model_user_test.dart @@ -17,8 +17,7 @@ void main() { company: 'Example Company', isMentor: true, email: 'john@example.com', - isProfileComplete: true - ); + isProfileComplete: true); expect(user.college, 'Example College'); expect(user.yearOfGraduation, '2023'); @@ -60,8 +59,6 @@ void main() { expect(user.isMentor, true); expect(user.email, 'john@example.com'); expect(user.isProfileComplete, true); - - }); test('toJson converts a DBUser to JSON', () { @@ -78,8 +75,7 @@ void main() { company: 'Example Company', isMentor: true, email: 'john@example.com', - isProfileComplete: true - ); + isProfileComplete: true); final json = user.toJson(); diff --git a/test/create_test.dart b/test/widget_meeting_create_page_test.dart similarity index 82% rename from test/create_test.dart rename to test/widget_meeting_create_page_test.dart index 54d5f8d..f5d5b6a 100644 --- a/test/create_test.dart +++ b/test/widget_meeting_create_page_test.dart @@ -1,16 +1,14 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:iiitd_mentorship/app/views/screens/schedule/create.dart'; import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:firebase_auth/firebase_auth.dart'; @GenerateMocks([FirebaseAuth, User]) void main() { group('ScheduleMeetingScreen Tests', () { - testWidgets('renders correctly', (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ScheduleMeetingScreen())); + await tester.pumpWidget(const MaterialApp(home: ScheduleMeetingScreen())); expect(find.text('Schedule Meeting'), findsWidgets); expect(find.text('Meeting Title'), findsWidgets); @@ -22,24 +20,27 @@ void main() { expect(find.text('Schedule Meeting'), findsWidgets); }); - testWidgets('opens date picker when date tile is tapped', (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ScheduleMeetingScreen())); + testWidgets('opens date picker when date tile is tapped', + (WidgetTester tester) async { + await tester.pumpWidget(const MaterialApp(home: ScheduleMeetingScreen())); await tester.tap(find.text('Date')); await tester.pumpAndSettle(); expect(find.byType(DatePickerDialog), findsOneWidget); }); - testWidgets('opens time picker when start time tile is tapped', (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ScheduleMeetingScreen())); + testWidgets('opens time picker when start time tile is tapped', + (WidgetTester tester) async { + await tester.pumpWidget(const MaterialApp(home: ScheduleMeetingScreen())); await tester.tap(find.text('Start Time')); await tester.pumpAndSettle(); expect(find.byType(TimePickerDialog), findsOneWidget); }); - testWidgets('opens time picker when end time tile is tapped', (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp(home: ScheduleMeetingScreen())); + testWidgets('opens time picker when end time tile is tapped', + (WidgetTester tester) async { + await tester.pumpWidget(const MaterialApp(home: ScheduleMeetingScreen())); await tester.tap(find.text('End Time')); await tester.pumpAndSettle(); @@ -63,6 +64,5 @@ void main() { // await tester.pump(); // expect(find.text('All fields are required'), findsOneWidget); // }); - }); } diff --git a/test/meeting_details_page_test.dart b/test/widget_meeting_details_page_test.dart similarity index 82% rename from test/meeting_details_page_test.dart rename to test/widget_meeting_details_page_test.dart index d6640af..cb18b69 100644 --- a/test/meeting_details_page_test.dart +++ b/test/widget_meeting_details_page_test.dart @@ -1,33 +1,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:iiitd_mentorship/app/data/model/meeting.dart'; -import 'package:iiitd_mentorship/app/data/repository/meeting.dart'; import 'package:iiitd_mentorship/app/views/screens/schedule/meeting_details_page.dart'; import 'package:mockito/mockito.dart'; class MockNavigatorObserver extends Mock implements NavigatorObserver {} + class MockRoute extends Mock implements Route {} void main() { group('MeetingDetailsPage Tests', () { late Meeting testMeeting; late MockNavigatorObserver mockObserver; - late MockRoute mockRoute; setUp(() { - testMeeting = Meeting( - 'eventName', - 'title', - 'description', - 'emailIDs', - DateTime.now(), - DateTime.now(), - Colors.blue, - true, - 'userId' - ); + testMeeting = Meeting('eventName', 'title', 'description', 'emailIDs', + DateTime.now(), DateTime.now(), Colors.blue, true, 'userId'); mockObserver = MockNavigatorObserver(); - mockRoute = MockRoute(); }); testWidgets('should display meeting details correctly', (tester) async { @@ -59,6 +48,5 @@ void main() { await tester.pumpAndSettle(); }); - }); }