Skip to content

Commit

Permalink
refractor app to use riverpods state mgmt
Browse files Browse the repository at this point in the history
  • Loading branch information
youngbryanyu committed Feb 22, 2024
1 parent d990112 commit 9722927
Show file tree
Hide file tree
Showing 17 changed files with 597 additions and 90 deletions.
7 changes: 7 additions & 0 deletions frontend/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,16 @@ PODS:
- GTMSessionFetcher/Core (3.3.1)
- PromisesObjC (2.3.1)
- RecaptchaInterop (100.0.0)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS

DEPENDENCIES:
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- Flutter (from `Flutter`)
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)

SPEC REPOS:
trunk:
Expand All @@ -92,6 +96,8 @@ EXTERNAL SOURCES:
:path: Flutter
google_sign_in_ios:
:path: ".symlinks/plugins/google_sign_in_ios/darwin"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"

SPEC CHECKSUMS:
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
Expand All @@ -110,6 +116,7 @@ SPEC CHECKSUMS:
GTMSessionFetcher: 8a1b34ad97ebe6f909fb8b9b77fba99943007556
PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4
RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695

PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796

Expand Down
15 changes: 8 additions & 7 deletions frontend/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* Entry point into the frontend */
import 'package:flutter/material.dart';
import 'package:frontend/pages/auth/auth_page.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frontend/pages/core/root_page_wrapper.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:frontend/routes/routes_handler.dart';

/* Determine which firebase config file to import depending on environment */
import 'firebase_options_dev.dart'
Expand All @@ -16,18 +18,19 @@ void main() async {
options: firebase_options.DefaultFirebaseOptions.currentPlatform);

/* Start app */
runApp(const App());
runApp(const ProviderScope(child: App()));
}

/* Main app widget */
class App extends StatelessWidget {
class App extends ConsumerWidget {
const App({super.key});

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const AuthPage(),
onGenerateRoute: generateRoute,
home: const RootPageWrapper(),
themeMode: ThemeMode.system,
/* Use system theme for now */
theme: lightTheme,
Expand All @@ -36,8 +39,6 @@ class App extends StatelessWidget {
}
}

// TODO: allow users to switch themes later in settings

/* Light theme */
final ThemeData lightTheme = ThemeData(
brightness: Brightness.light,
Expand Down
31 changes: 0 additions & 31 deletions frontend/lib/pages/auth/auth_page.dart

This file was deleted.

38 changes: 0 additions & 38 deletions frontend/lib/pages/auth/login_or_register_page.dart

This file was deleted.

19 changes: 13 additions & 6 deletions frontend/lib/pages/auth/login_page.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
/* The login page */
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frontend/components/auth/auth_button.dart';
import 'package:frontend/components/auth/auth_error_popup.dart';
import 'package:frontend/components/auth/auth_text_field.dart';
import 'package:frontend/providers/auth/auth_providers.dart';
// import 'package:frontend/components/auth/auth_logo_tile.dart';
// import 'package:frontend/services/auth/auth_service.dart';

/* Login page widget */
class LoginPage extends StatefulWidget {
final Function()? onTap;
const LoginPage({super.key, required this.onTap});
class LoginPage extends ConsumerStatefulWidget {
const LoginPage({super.key});

@override
State<LoginPage> createState() => _LoginPageState();
ConsumerState<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
class _LoginPageState extends ConsumerState<LoginPage> {
/* Text box controllers */
final emailController = TextEditingController();
final passwordController = TextEditingController();
Expand Down Expand Up @@ -47,6 +48,9 @@ class _LoginPageState extends State<LoginPage> {
if (mounted) {
Navigator.pop(context);
}

/* Switch to home page */
ref.read(authPageStateProvider.notifier).state = AuthPageState.home;
} on FirebaseAuthException catch (error) {
/* Pop loading circle */
if (mounted) {
Expand Down Expand Up @@ -219,7 +223,10 @@ class _LoginPageState extends State<LoginPage> {
),
SizedBox(width: screenWidth * .01),
GestureDetector(
onTap: widget.onTap,
onTap: () {
ref.read(authPageStateProvider.notifier).state =
AuthPageState.register;
},
child: Text(
'Register now',
style: TextStyle(
Expand Down
19 changes: 13 additions & 6 deletions frontend/lib/pages/auth/register_page.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
/* The register page */
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frontend/components/auth/auth_button.dart';
import 'package:frontend/components/auth/auth_error_popup.dart';
import 'package:frontend/components/auth/auth_text_field.dart';
import 'package:frontend/providers/auth/auth_providers.dart';
// import 'package:frontend/components/auth/auth_logo_tile.dart';
// import 'package:frontend/services/auth/auth_service.dart';

/* Login page widget */
class RegisterPage extends StatefulWidget {
final Function()? onTap;
const RegisterPage({super.key, required this.onTap});
class RegisterPage extends ConsumerStatefulWidget {
const RegisterPage({super.key});

@override
State<RegisterPage> createState() => _RegisterPageState();
ConsumerState<RegisterPage> createState() => _RegisterPageState();
}

class _RegisterPageState extends State<RegisterPage> {
class _RegisterPageState extends ConsumerState<RegisterPage> {
/* Text box controllers */
final emailController = TextEditingController();
final passwordController = TextEditingController();
Expand Down Expand Up @@ -54,6 +55,9 @@ class _RegisterPageState extends State<RegisterPage> {
if (mounted) {
Navigator.pop(context);
}

/* Switch to home page */
ref.read(authPageStateProvider.notifier).state = AuthPageState.home;
} on FirebaseAuthException catch (error) {
/* Pop loading circle */
if (mounted) {
Expand Down Expand Up @@ -253,7 +257,10 @@ class _RegisterPageState extends State<RegisterPage> {
),
SizedBox(width: screenWidth * .01),
GestureDetector(
onTap: widget.onTap,
onTap: () {
ref.read(authPageStateProvider.notifier).state =
AuthPageState.login;
},
child: Text(
'Login now',
style: TextStyle(
Expand Down
137 changes: 137 additions & 0 deletions frontend/lib/pages/auth/reset_password_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/* The login page */
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:frontend/components/auth/auth_button.dart';
import 'package:frontend/components/auth/auth_error_popup.dart';
import 'package:frontend/components/auth/auth_text_field.dart';

/* Login page widget */
class ResetPasswordPage extends StatefulWidget {
final Function()? onTap;
const ResetPasswordPage({super.key, required this.onTap});

@override
State<ResetPasswordPage> createState() => _ResetPasswordPageState();
}

class _ResetPasswordPageState extends State<ResetPasswordPage> {
/* Text box controllers */
final emailController = TextEditingController();
final passwordController = TextEditingController();

/* whether to show password */
bool passwordVisible = false;

/* Sign in function */
Future<void> signInUser() async {
/* Show loading circle */
showDialog(
context: context,
builder: (context) {
return const Center(
child: CircularProgressIndicator(),
);
},
);

try {
/* Await sign in */
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: emailController.text,
password: passwordController.text,
);

/* Pop loading circle */
if (mounted) {
Navigator.pop(context);
}
} on FirebaseAuthException catch (error) {
/* Pop loading circle */
if (mounted) {
Navigator.pop(context);
}

/* Handle error codes */
if (error.code == 'too-many-requests') {
showErrorPopup(
'Account temporarily locked',
'Too many failed login attempts. Try again later or reset your password.',
);
} else if (error.code == 'user-disabled') {
showErrorPopup('Account locked', 'Your account is disabled.');
} else if (error.code == 'operation-not-allowed') {
showErrorPopup(
'Login failed',
'Email-based login is currently disabled',
);
} else {
/* Group other errors together (e.g. invalid password, no user found, invalid email)*/
showErrorPopup('Login failed', 'Invalid login credentials');
}
} catch (error) {
showErrorPopup('Login failed', 'Server error occurred');
}
}

/* Shows an error message popup */
void showErrorPopup(String title, String message) {
showDialog(
context: context,
builder: (context) {
return AuthErrorPopup(title: title, message: message);
},
);
}

@override
Widget build(BuildContext context) {
/* Get the screen height */
double screenHeight = MediaQuery.of(context).size.height;
// double screenWidth = MediaQuery.of(context).size.width;

return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: SafeArea(
child: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
/* Welcome text*/
Text(
'Reset Password',
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: screenHeight * .02),

/* Username or email text field */
AuthTextField(
controller: emailController,
hintText: 'Email',
obscureText: false,
),
SizedBox(height: screenHeight * .01),

/* Send email button */
AuthButton(
onTap: signInUser,
message: 'Send password reset email',
),
SizedBox(height: screenHeight * .03),

/* Back button */
AuthButton(
onTap: signInUser,
message: 'Back',
),
SizedBox(height: screenHeight * .03),
]),
),
),
));
}
}
Loading

0 comments on commit 9722927

Please sign in to comment.