diff --git a/pocket_cinema/lib/controller/authentication.dart b/pocket_cinema/lib/controller/authentication.dart index 40c9fff..0159478 100644 --- a/pocket_cinema/lib/controller/authentication.dart +++ b/pocket_cinema/lib/controller/authentication.dart @@ -1,7 +1,6 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/cupertino.dart'; import 'package:google_sign_in/google_sign_in.dart'; +import 'package:pocket_cinema/controller/validate.dart'; import 'package:pocket_cinema/model/my_user.dart'; @@ -13,25 +12,29 @@ class Authentication { await googleSignIn.signOut(); await FirebaseAuth.instance.signOut(); } - static Future signIn(TextEditingController userIdTextController, TextEditingController passwordTextController) async { - final userId = userIdTextController.text; - return FirebaseAuth.instance.signInWithEmailAndPassword( - email: FirestoreDatabase.isEmail(userId) ? userId - : await FirestoreDatabase.getEmail(userId).then((email) => email), - password: passwordTextController.text, - ).onError((error, stackTrace) { - throw("Error: ${error.toString()}"); - }); + static Future signIn(String user, String password) async { + try { + final email = Validate.isEmail(user) ? user : await FirestoreDatabase.getEmail(user); + + await FirebaseAuth.instance.signInWithEmailAndPassword( + email: email, + password: password + ); + + return Future.value(); + } on FirebaseAuthException catch (e) { + //print('Sign in failed with error code: ${e.code}'); + return Future.error(e.message ?? "Something went wrong"); + } on Exception catch (e) { + return Future.error(e); + } } static Future signInWithGoogle() async { final googleSignIn = GoogleSignIn(); final googleUser = await googleSignIn.signIn(); if (googleUser == null) { - throw FirebaseAuthException( - message: "Sign in aborted by user", - code: "ERROR_ABORTED_BY_USER", - ); + return Future.error("Sign in aborted by user"); } final googleAuth = await googleUser.authentication; @@ -61,4 +64,12 @@ class Authentication { FirestoreDatabase.createUser(user, currentUser.uid); } + + static Future registerUser(username, email, password) async{ + FirebaseAuth.instance.createUserWithEmailAndPassword(email: email, password: password).then((value) async { + await value.user?.updateDisplayName(username); + final user = MyUser(email: email, username: username); + createUser(user); + }).onError((error, stackTrace) => Future.error("Something went wrong")); + } } diff --git a/pocket_cinema/lib/controller/firestore_database.dart b/pocket_cinema/lib/controller/firestore_database.dart index 7f197ab..6599fd4 100644 --- a/pocket_cinema/lib/controller/firestore_database.dart +++ b/pocket_cinema/lib/controller/firestore_database.dart @@ -25,7 +25,7 @@ class FirestoreDatabase { String email = snapshot.docs.first.get('email'); return email; } - return "User not found"; + return Future.error("User not found"); } static bool isEmail(String str) { @@ -95,6 +95,22 @@ class FirestoreDatabase { return snapshot.docs.isNotEmpty; } + static Future emailExists(String email) async { + final QuerySnapshot snapshot = await FirebaseFirestore.instance + .collection('users') + .where("email", isEqualTo: email) + .get(); + return snapshot.docs.isNotEmpty; + } + + static Future usernameExists(String username) async { + final QuerySnapshot snapshot = await FirebaseFirestore.instance + .collection('users') + .where("username", isEqualTo: username) + .get(); + return snapshot.docs.isNotEmpty; + } + static Future createUser(MyUser user, String userId) async { // Create document and write data to Firebase final docUser = FirebaseFirestore.instance.collection('users').doc(userId); diff --git a/pocket_cinema/lib/controller/validate.dart b/pocket_cinema/lib/controller/validate.dart new file mode 100644 index 0000000..dbc0a63 --- /dev/null +++ b/pocket_cinema/lib/controller/validate.dart @@ -0,0 +1,50 @@ +import 'package:pocket_cinema/controller/firestore_database.dart'; + +class Validate { + static bool isEmail(String email){ + return RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+") + .hasMatch(email); + } + + static Future login(String userId, String password) async{ + if (userId.isEmpty) { + return Future.error("Please enter your email or username"); + } + if (password.isEmpty) { + return Future.error("Please enter your password"); + } + if(!isEmail(userId)){ + if(!await FirestoreDatabase.usernameExists(userId)){ + return Future.error("The username does not exist"); + } + }else{ + if(!await FirestoreDatabase.emailExists(userId)){ + return Future.error("The email does not exist"); + } + } + return ""; + } + + static Future register(String username, String email, String password, String confirmPassword) async{ + if(username.isEmpty || email.isEmpty || password.isEmpty || confirmPassword.isEmpty) { + return Future.error("There are empty fields"); + } + if(!isEmail(email)) { + return Future.error("Please enter a valid email"); + } + if(password.length < 6) { + return Future.error("Password must be at least 6 characters"); + } + if(password != confirmPassword) { + return Future.error("Passwords do not match"); + } + if(await FirestoreDatabase.emailExists(email)){ + return Future.error("The email already exists"); + } + if(await FirestoreDatabase.usernameExists(username)){ + return Future.error("The username already exists"); + } + + return ""; + } +} \ No newline at end of file diff --git a/pocket_cinema/lib/view/common_widgets/validate_lr.dart b/pocket_cinema/lib/view/common_widgets/validate_lr.dart new file mode 100644 index 0000000..220ed7c --- /dev/null +++ b/pocket_cinema/lib/view/common_widgets/validate_lr.dart @@ -0,0 +1,42 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:pocket_cinema/model/my_user.dart'; + +class ValidateLR { + static String? validateLogin(String? userId, String? password) { + if (userId == null || userId.isEmpty) { + return 'Please enter your email or username'; + } + if (password == null || password.isEmpty) { + return 'Please enter your password'; + } + return null; + } + + static String? validateRegister(String? username, String? email, String? password, String? confirmPassword) { + if (email != null && email.isNotEmpty) { + final RegExp emailRegex = RegExp(r'^.+@[a-zA-Z]+\.{1}[a-zA-Z]+(\.{0,1}[a-zA-Z]+)$'); + if (!emailRegex.hasMatch(email)) { + return 'Please enter a valid email'; + } + } + if (email == null || email.isEmpty) { + return 'Please enter your email'; + } + if (username == null || username.isEmpty) { + return 'Please enter your username'; + } + if (password == null || password.isEmpty) { + return 'Please enter your password'; + } + if (confirmPassword == null || confirmPassword.isEmpty) { + return 'Please confirm your password'; + } + if (password != confirmPassword) { + return 'Passwords do not match'; + } + if (password.length < 6) { + return 'Password must be at least 6 characters'; + } + return null; + } +} \ No newline at end of file diff --git a/pocket_cinema/lib/view/login/login.dart b/pocket_cinema/lib/view/login/login.dart index 48c7e2d..7c3b8e6 100644 --- a/pocket_cinema/lib/view/login/login.dart +++ b/pocket_cinema/lib/view/login/login.dart @@ -1,7 +1,5 @@ - - import 'package:flutter/material.dart'; - +import 'package:pocket_cinema/controller/validate.dart'; import 'package:pocket_cinema/controller/authentication.dart'; import 'package:pocket_cinema/view/common_widgets/password_form_field.dart'; import 'package:pocket_cinema/view/common_widgets/login_register_tabs.dart'; @@ -42,35 +40,38 @@ class _LoginPageState extends State { ), ElevatedButton( key: const Key("loginButton"), - onPressed: () { - Authentication.signIn(_userIdTextController, _passwordTextController).then((value) { - Navigator.pushNamed(context, '/'); - }).onError((error, stackTrace) { - print("Error: ${error.toString()}"); + onPressed: () async { + Validate.login(_userIdTextController.text, _passwordTextController.text).then((value) { + Authentication.signIn(_userIdTextController.text, _passwordTextController.text).then((value){ + Navigator.pushNamed(context, '/'); + }).catchError((error) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(error))); + }); + }).catchError((error) { + print("eerer"); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(error))); }); }, child: const Text('Login'), - ), - const Divider(), - ElevatedButton( - onPressed: () { - Authentication.signInWithGoogle().then((user) { - if (user == null || user.displayName == null || user.email == null) return; - Authentication.createUserGoogleSignIn( - MyUser(username: user.displayName, email: user.email), - ); - Navigator.pushNamed(context, '/'); - }).onError((error, stackTrace) { - throw("Error: ${error.toString()}"); - }); - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - foregroundColor: Colors.black, ), - child: const Text('Login with Google')), - ] - ) + const Divider(), + ElevatedButton( + onPressed: () { + Authentication.signInWithGoogle().then((user) { + if (user == null || user.displayName == null || user.email == null) return; + Authentication.createUserGoogleSignIn( + MyUser(username: user.displayName, email: user.email), + ); + Navigator.pushNamed(context, '/'); + }); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: Colors.black, + ), + child: const Text('Login with Google')), + ] + ) ) ); } diff --git a/pocket_cinema/lib/view/register/register.dart b/pocket_cinema/lib/view/register/register.dart index cc6cf5b..3319873 100644 --- a/pocket_cinema/lib/view/register/register.dart +++ b/pocket_cinema/lib/view/register/register.dart @@ -1,11 +1,10 @@ -import 'package:firebase_auth/firebase_auth.dart'; -import 'package:flutter/material.dart'; import 'package:pocket_cinema/controller/authentication.dart'; -import 'package:pocket_cinema/model/my_user.dart'; -import 'package:pocket_cinema/view/common_widgets/password_form_field.dart'; import 'package:pocket_cinema/view/common_widgets/login_register_tabs.dart'; import 'package:pocket_cinema/view/common_widgets/input_field_login_register.dart'; import 'package:pocket_cinema/view/common_widgets/topbar_logo.dart'; +import 'package:pocket_cinema/controller/validate.dart'; +import 'package:pocket_cinema/view/common_widgets/password_form_field.dart'; +import 'package:flutter/material.dart'; class RegisterPage extends StatefulWidget { const RegisterPage({super.key}); @@ -24,13 +23,13 @@ class _RegisterPageState extends State { Widget build(BuildContext context) { return Scaffold( body: Container( - margin: const EdgeInsets.symmetric(horizontal: 40), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const TopBarLogo(), - const LoginRegisterSegmentedButton(selectedPage: LoginRegister.register), + margin: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const TopBarLogo(), + const LoginRegisterSegmentedButton(selectedPage: LoginRegister.register), TextFormFieldLoginRegister( key: const Key("emailField"), hintText: "Email", @@ -53,36 +52,19 @@ class _RegisterPageState extends State { ), ElevatedButton( key: const Key("registerButton"), - onPressed: () { - if (_passwordTextController.text != _confirmPasswordTextController.text) { - _passwordTextController.clear(); - _confirmPasswordTextController.clear(); - // error message - return; - } - FirebaseAuth.instance.createUserWithEmailAndPassword( - email: _emailTextController.text, - password: _passwordTextController.text, - ).then((value) async { - await value.user?.updateDisplayName(_usernameTextController.text); - - Navigator.pushNamed(context, '/'); - final user = MyUser( - username: _usernameTextController.text, - email: _emailTextController.text, - ); - Authentication.createUser(user); - }).onError((error, stackTrace) { - _usernameTextController.clear(); - _emailTextController.clear(); - _passwordTextController.clear(); - _confirmPasswordTextController.clear(); - Text("Error ${error.toString()}"); - print("Error ${error.toString()}"); + onPressed: () async { + Validate.register(_usernameTextController.text, _emailTextController.text, _passwordTextController.text, _confirmPasswordTextController.text).then((value) { + Authentication.registerUser(_usernameTextController.text, _emailTextController.text, _passwordTextController.text).then((value){ + Navigator.pushNamed(context, '/'); + }).catchError((error) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(error))); + }); + }).catchError((error) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(error))); }); }, child: const Text('Create account'), - ), + ), ]))); } -} +} \ No newline at end of file diff --git a/pocket_cinema/test_driver/app_test.dart b/pocket_cinema/test_driver/app_test.dart index 9bd340e..be82483 100644 --- a/pocket_cinema/test_driver/app_test.dart +++ b/pocket_cinema/test_driver/app_test.dart @@ -11,7 +11,6 @@ Future main() { ..features = [Glob(r"test_driver/features/**.feature")] ..reporters = [ ProgressReporter(), - // TestRunReporter(), TestRunSummaryReporter(), JsonReporter(path: './report.json'), ]