From 4874a3073450c0dfcb1c443342bcae78728a471b Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Sat, 27 May 2023 17:19:44 -0600 Subject: Se implementa verificación de cuentas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/firebase/auth.dart | 34 +++++++++------- lib/main.dart | 81 +++++++++++++++++++++++++------------- lib/screens/dashboard_screen.dart | 5 --- lib/screens/login_screen.dart | 67 +++++++++++++++++++++++++------ lib/screens/onboarding_screen.dart | 2 + lib/screens/register_screen.dart | 49 ++++++++++++++++------- lib/settings/preferences.dart | 9 +++++ pubspec.lock | 8 ++++ pubspec.yaml | 1 + 9 files changed, 185 insertions(+), 71 deletions(-) diff --git a/lib/firebase/auth.dart b/lib/firebase/auth.dart index 35da5bf..da64fb5 100644 --- a/lib/firebase/auth.dart +++ b/lib/firebase/auth.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:dartz/dartz.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart'; import 'package:linkchat/firebase/database.dart'; @@ -17,7 +18,7 @@ class Auth { User? get currentUser => _auth.currentUser; Stream get userChanges => _auth.userChanges(); - Future createUserWithEmailAndPassword({ + Future> createUserWithEmailAndPassword({ required String email, required String password, required String displayName, @@ -28,6 +29,10 @@ class Auth { email: email, password: password, ); + + // Enviar correo de verificación + await cred.user?.sendEmailVerification(); + User? user = cred.user; if (user != null) { String photoUrl = await Storage().uploadAvatar(user.uid, avatar); @@ -41,46 +46,47 @@ class Auth { email: user.email!, )); } - return true; + return Left(user); } catch (e) { if (kDebugMode) print(e); - return false; + return Right(e as FirebaseException); } } - Future signInWithEmailAndPassword({ + Future> signInWithEmailAndPassword({ required String email, required String password, }) async { try { + await _auth.signOut(); UserCredential cred = await _auth.signInWithEmailAndPassword( email: email, password: password, ); - return cred.user?.emailVerified == true; + return Left(cred.user); } catch (e) { if (kDebugMode) print(e); - return false; + return Right(e as FirebaseException); } } - Future signInWithGoogle() async { + Future> signInWithGoogle() async { try { - await _auth.signInWithProvider(_googleAuthProvider); - return true; + UserCredential cred = await _auth.signInWithProvider(_googleAuthProvider); + return Left(cred.user); } catch (e) { if (kDebugMode) print(e); - return false; + return Right(e as FirebaseException); } } - Future signInWithGithub() async { + Future> signInWithGithub() async { try { - await _auth.signInWithProvider(_githubProvider); - return true; + UserCredential cred = await _auth.signInWithProvider(_githubProvider); + return Left(cred.user); } catch (e) { if (kDebugMode) print(e); - return false; + return Right(e as FirebaseException); } } diff --git a/lib/main.dart b/lib/main.dart index b067b07..fa51f9e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,12 @@ import 'package:dynamic_color/dynamic_color.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:linkchat/screens/dashboard_screen.dart'; import 'package:linkchat/screens/login_screen.dart'; +import 'package:linkchat/screens/onboarding_screen.dart'; +import 'package:linkchat/settings/preferences.dart'; +import 'package:linkchat/widgets/loading_modal_widget.dart'; import 'package:provider/provider.dart'; import 'firebase/auth.dart'; @@ -54,32 +58,55 @@ class LinkChat extends StatelessWidget { final ThemeEnum themeEnum = provider.theme; return StreamBuilder( - stream: _auth.userChanges, - builder: (context, snapshot) => - DynamicColorBuilder(builder: (lightDynamic, darkDynamic) { - provider.syncFromPrefs(); - ThemeData lightTheme = ThemeSettings.lightTheme(lightDynamic); - ThemeData darkTheme = ThemeSettings.darkTheme(darkDynamic); - ThemeData? theme; - switch (themeEnum) { - case ThemeEnum.light: - theme = lightTheme; - case ThemeEnum.dark: - theme = darkTheme; - case ThemeEnum.auto: - theme = null; - } - return MaterialApp( - theme: theme ?? lightTheme, - darkTheme: theme ?? darkTheme, - //theme: theme ?? ThemeSettings.lightTheme(lightDynamic), - //darkTheme: theme ?? ThemeSettings.darkTheme(darkDynamic), - themeMode: ThemeMode.system, - routes: getApplicationRoutes(), - home: (snapshot.hasData && !snapshot.data!.isAnonymous) - ? const DashboardScreen() - : const LoginScreen(), - ); - })); + stream: _auth.userChanges, + builder: (context, authSnapshot) => DynamicColorBuilder( + builder: (lightDynamic, darkDynamic) { + provider.syncFromPrefs(); + ThemeData lightTheme = ThemeSettings.lightTheme(lightDynamic); + ThemeData darkTheme = ThemeSettings.darkTheme(darkDynamic); + ThemeData? theme; + switch (themeEnum) { + case ThemeEnum.light: + theme = lightTheme; + case ThemeEnum.dark: + theme = darkTheme; + case ThemeEnum.auto: + theme = null; + } + return MaterialApp( + theme: theme ?? lightTheme, + darkTheme: theme ?? darkTheme, + themeMode: ThemeMode.system, + routes: getApplicationRoutes(), + home: FutureBuilder( + initialData: const LoadingModal(), + future: getHomeScreen(context, authSnapshot.data), + builder: (context, homeSnapshot) { + if (homeSnapshot.hasData) { + return homeSnapshot.data!; + } else if (homeSnapshot.hasError) { + return const LoginScreen(); + } else { + return const LoadingModal(); + } + }, + ), + ); + }, + ), + ); + } + + Future getHomeScreen(BuildContext context, User? user) async { + if (user != null && !user.isAnonymous && user.emailVerified) { + bool showOnboarding = await Preferences.getShowOnboarding(); + if (showOnboarding) { + return const OnboardingScreen(); + } else { + return const DashboardScreen(); + } + } else { + return const LoginScreen(); + } } } diff --git a/lib/screens/dashboard_screen.dart b/lib/screens/dashboard_screen.dart index be3aca0..72bbab7 100644 --- a/lib/screens/dashboard_screen.dart +++ b/lib/screens/dashboard_screen.dart @@ -109,11 +109,6 @@ class _DashboardScreenState extends State { } void signOut(BuildContext context) { - /*_auth.signOut().then((success) { - if (success) { - Navigator.of(context).popUntil(ModalRoute.withName('/login')); - } - });*/ _auth.signOut().then((success) { if (success) { Navigator.of(context).pushReplacementNamed('/login'); diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart index f00328b..c98a565 100644 --- a/lib/screens/login_screen.dart +++ b/lib/screens/login_screen.dart @@ -1,4 +1,6 @@ +import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; +import 'package:linkchat/settings/preferences.dart'; import 'package:social_login_buttons/social_login_buttons.dart'; import '../firebase/auth.dart'; @@ -19,6 +21,7 @@ class _LoginScreenState extends State final Auth _auth = Auth(); bool isLoading = false; + bool isInit = false; final padding = 16.0; final spacer = const SizedBox(height: 16.0); @@ -107,42 +110,84 @@ class _LoginScreenState extends State ); void onLoginClicked(BuildContext context) { + setState(() { + isLoading = true; + }); _auth .signInWithEmailAndPassword( email: _emailController.text, password: _passwordController.text, ) - .then((success) { + .then((result) { setState(() { isLoading = false; }); - // TODO: checar si el resultado es true - Navigator.of(context).pushReplacementNamed('/dash'); + result.fold( + (user) { + if (user != null && user.emailVerified == false) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('El correo no está verificado')), + ); + } else { + Preferences.getShowOnboarding().then((show) { + Navigator.of(context) + .pushReplacementNamed(show ? '/onboard' : '/dash'); + }); + } + }, + (error) => handleError(error), + ); }); } void onGoogleLoginClicked(BuildContext context) { - _auth.signInWithGoogle().then((success) { + setState(() { + isLoading = true; + }); + _auth.signInWithGoogle().then((result) { setState(() { isLoading = false; }); - if (success) { - Navigator.of(context).pushReplacementNamed('/dash'); - } + result.fold( + (user) {}, + (error) => handleError(error), + ); }); } void onGithubLoginClicked(BuildContext context) { - _auth.signInWithGithub().then((success) { + setState(() { + isLoading = true; + }); + _auth.signInWithGithub().then((result) { setState(() { isLoading = false; }); - if (success) { - Navigator.of(context).pushReplacementNamed('/dash'); - } + result.fold( + (user) {}, + (error) => handleError(error), + ); }); } + void handleError(FirebaseException error) { + String message; + switch (error.code) { + case 'invalid-email': + message = 'El correo electrónico es inválido'; + case 'user-disabled': + message = 'El usuario está desactivado'; + case 'user-not-found': + message = 'El usuario no existe'; + case 'wrong-password': + message = 'La contraseña es incorrecta.'; + default: + message = 'Ocurrió un error desconocido'; + } + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(message))); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/screens/onboarding_screen.dart b/lib/screens/onboarding_screen.dart index e1755c4..5d1ff7a 100644 --- a/lib/screens/onboarding_screen.dart +++ b/lib/screens/onboarding_screen.dart @@ -1,5 +1,6 @@ import 'package:concentric_transition/concentric_transition.dart'; import 'package:flutter/material.dart'; +import 'package:linkchat/settings/preferences.dart'; import 'package:lottie/lottie.dart'; import '../widgets/responsive.dart'; @@ -95,6 +96,7 @@ class OnboardingScreen extends StatelessWidget { ); }, onFinish: () { + Preferences.setShowOnboarding(false); Navigator.of(context).pushNamed('/dash'); }, ), diff --git a/lib/screens/register_screen.dart b/lib/screens/register_screen.dart index 9a11923..0ba0cf9 100644 --- a/lib/screens/register_screen.dart +++ b/lib/screens/register_screen.dart @@ -158,21 +158,42 @@ class _RegisterScreenState extends State { void onRegisterClicked(BuildContext context) { setState(() { - isLoading = false; - if (validateForm()) { - Auth() - .createUserWithEmailAndPassword( - email: _emailController.text, - password: _passwordController.text, - displayName: _nameController.text, - avatar: File(_avatar!.path), - ) - .then((success) { - if (success) { - Navigator.of(context).pushReplacementNamed('/onboard'); + isLoading = true; + }); + + if (validateForm()) { + Auth() + .createUserWithEmailAndPassword( + email: _emailController.text, + password: _passwordController.text, + displayName: _nameController.text, + avatar: File(_avatar!.path), + ) + .then((result) { + setState(() { + isLoading = false; + }); + result.fold((user) { + Navigator.of(context).pushReplacementNamed('/login'); + }, (error) { + String message; + switch (error.code) { + case 'email-already-in-use': + message = 'El correo electrónico ya se encuentra en uso'; + case 'invalid-email': + message = 'El correo electrónico es inválido'; + case 'operation-not-allowed': + message = 'La operación no está permitida'; + case 'weak-password': + message = 'La contraseña es muy débil'; + default: + message = 'Ocurrió un error desconocido'; } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(message)), + ); }); - } - }); + }); + } } } diff --git a/lib/settings/preferences.dart b/lib/settings/preferences.dart index 9cfbf6a..715225a 100644 --- a/lib/settings/preferences.dart +++ b/lib/settings/preferences.dart @@ -31,4 +31,13 @@ class Preferences { } }); } + + static Future getShowOnboarding() async => + (await prefs).getBool('show-onboarding') ?? true; + + static void setShowOnboarding(bool show) { + prefs.then((p) { + p.setBool('show-onboarding', show); + }); + } } diff --git a/pubspec.lock b/pubspec.lock index 69c79fb..4244d07 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -161,6 +161,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + dartz: + dependency: "direct main" + description: + name: dartz + sha256: e6acf34ad2e31b1eb00948692468c30ab48ac8250e0f0df661e29f12dd252168 + url: "https://pub.dev" + source: hosted + version: "0.10.1" dynamic_color: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d6b9076..9b1573f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,7 @@ dependencies: dynamic_color: ^1.6.5 provider: ^6.0.5 shared_preferences: ^2.1.1 + dartz: ^0.10.1 lottie: ^2.3.2 concentric_transition: ^1.0.3 social_login_buttons: ^1.0.7 -- cgit v1.2.3