From ac93526cd8550edff5a27e40d3fa755977250273 Mon Sep 17 00:00:00 2001 From: Karagwa Date: Thu, 23 Jan 2025 13:02:10 +0300 Subject: [PATCH 1/8] added forgot password on login page --- .../forgot_password_bloc.dart | 0 .../forgot_password_event.dart | 0 .../forgot_password_state.dart | 0 .../lib/src/app/auth/pages/welcome_screen.dart | 17 +++++++++++++---- 4 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart create mode 100644 mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart create mode 100644 mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart diff --git a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart index 2e5febe4c1..b749641b6d 100644 --- a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart +++ b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart @@ -125,10 +125,19 @@ class _WelcomeScreenState extends State { ), ), SizedBox(height: 16), - Text( - "", - style: TextStyle(color: AppColors.boldHeadlineColor), - ) + SizedBox(height: 16), + Center( + child: InkWell( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + + builder: (context) => ForgotPasswordPage())), + child: Text("Forgot password?", + style: TextStyle( + color: AppColors.primaryColor, + fontWeight: FontWeight.w500)), + ), + ), + ], ), )) From c16352917e3113d27afc8954106cbd3f8a196b74 Mon Sep 17 00:00:00 2001 From: Karagwa Date: Tue, 28 Jan 2025 23:57:34 +0300 Subject: [PATCH 2/8] Implemented the password reset feature. --- .../plugins/GeneratedPluginRegistrant.java | 5 + .../ios/Runner/GeneratedPluginRegistrant.m | 7 + mobile-v3/lib/main.dart | 5 + .../forgot_password_bloc.dart | 35 +++ .../forgot_password_event.dart | 30 +++ .../forgot_password_state.dart | 37 +++ .../lib/src/app/auth/pages/login_page.dart | 18 +- .../pages/password_reset/forgot_password.dart | 208 +++++++++++++++++ .../pages/password_reset/password_reset.dart | 219 ++++++++++++++++++ .../pages/password_reset/reset_link_sent.dart | 159 +++++++++++++ .../pages/password_reset/reset_success.dart | 97 ++++++++ .../src/app/auth/pages/welcome_screen.dart | 14 +- .../app/auth/repository/auth_repository.dart | 66 +++++- mobile-v3/pubspec.lock | 40 ++++ mobile-v3/pubspec.yaml | 1 + 15 files changed, 926 insertions(+), 15 deletions(-) create mode 100644 mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart create mode 100644 mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart create mode 100644 mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart create mode 100644 mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart diff --git a/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index 5f086b275f..5c8bed684b 100644 --- a/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -15,6 +15,11 @@ public final class GeneratedPluginRegistrant { private static final String TAG = "GeneratedPluginRegistrant"; public static void registerWith(@NonNull FlutterEngine flutterEngine) { + try { + flutterEngine.getPlugins().add(new com.llfbandit.app_links.AppLinksPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin app_links, com.llfbandit.app_links.AppLinksPlugin", e); + } try { flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.connectivity.ConnectivityPlugin()); } catch (Exception e) { diff --git a/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m b/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m index 523e1cd8f2..dd8505e00f 100644 --- a/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m +++ b/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m @@ -6,6 +6,12 @@ #import "GeneratedPluginRegistrant.h" +#if __has_include() +#import +#else +@import app_links; +#endif + #if __has_include() #import #else @@ -27,6 +33,7 @@ @implementation GeneratedPluginRegistrant + (void)registerWithRegistry:(NSObject*)registry { + [AppLinksPlugin registerWithRegistrar:[registry registrarForPlugin:@"AppLinksPlugin"]]; [ConnectivityPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"ConnectivityPlusPlugin"]]; [FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]]; [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; diff --git a/mobile-v3/lib/main.dart b/mobile-v3/lib/main.dart index c07d4f841d..5c19655617 100644 --- a/mobile-v3/lib/main.dart +++ b/mobile-v3/lib/main.dart @@ -1,6 +1,8 @@ import 'dart:io'; +import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart'; import 'package:airqo/src/app/auth/bloc/auth_bloc.dart'; +import 'package:airqo/src/app/auth/pages/login_page.dart'; import 'package:airqo/src/app/auth/pages/welcome_screen.dart'; import 'package:airqo/src/app/auth/repository/auth_repository.dart'; import 'package:airqo/src/app/dashboard/bloc/dashboard/dashboard_bloc.dart'; @@ -21,6 +23,7 @@ import 'package:airqo/src/app/shared/bloc/connectivity_bloc.dart'; import 'package:airqo/src/app/shared/pages/nav_page.dart'; import 'package:airqo/src/meta/utils/colors.dart'; +import 'package:app_links/app_links.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -117,6 +120,8 @@ class AirqoMobile extends StatelessWidget { BlocProvider( create: (context) => ConnectivityBloc(connectivity), ), + BlocProvider(create: (context) => PasswordResetBloc(authRepository: authRepository), + ) ], child: BlocBuilder( builder: (context, state) { diff --git a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart index e69de29bb2..a3d00f5aba 100644 --- a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart +++ b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart @@ -0,0 +1,35 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../repository/auth_repository.dart'; +import 'forgot_password_event.dart'; +import 'forgot_password_state.dart'; + + +class PasswordResetBloc extends Bloc { + final AuthRepository authRepository; + + PasswordResetBloc({required this.authRepository}) : super(PasswordResetInitial()) { + on((event, emit) async { + try { + emit(PasswordResetLoading(email: event.email)); + await authRepository.requestPasswordReset(event.email); + emit(PasswordResetSuccess(message: '')); + } catch (e) { + emit(PasswordResetError(message: e.toString())); + } + }); + + on((event, emit) async { + emit(PasswordResetLoading()); + try { + final message = await authRepository.updatePassword( + confirmPassword: event.confirmPassword, + password: event.password, + token: event.token, + ); + emit(PasswordResetSuccess(message: message)); + } catch (error) { + emit(PasswordResetError(message: 'Failed to update password. \nPlease enter the correct code')); + } + }); + } +} diff --git a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart index e69de29bb2..538268e3f0 100644 --- a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart +++ b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; + +abstract class PasswordResetEvent extends Equatable { + @override + List get props => []; +} + +class RequestPasswordReset extends PasswordResetEvent { + final String email; + + RequestPasswordReset(this.email); + + @override + List get props => [email]; +} + +class UpdatePassword extends PasswordResetEvent { + final String confirmPassword; + final String password; + final String token; + + UpdatePassword({ + required this.confirmPassword, + required this.password, + required this.token, + }); + + @override + List get props => [confirmPassword, password, token]; +} diff --git a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart index e69de29bb2..02941d0cb7 100644 --- a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart +++ b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart @@ -0,0 +1,37 @@ +import 'package:equatable/equatable.dart'; + +abstract class PasswordResetState extends Equatable { + final String? email; + const PasswordResetState({this.email}); + + @override + List get props => [email]; +} + +class PasswordResetInitial extends PasswordResetState { + const PasswordResetInitial() : super(); +} + +class PasswordResetLoading extends PasswordResetState { + const PasswordResetLoading({String? email}) : super(email: email); // Optional email +} + +class PasswordResetSuccess extends PasswordResetState { + final String message; + + const PasswordResetSuccess({String? email, required this.message}) : super(email: email); + + @override + List get props => [email, message]; +} + +class PasswordResetError extends PasswordResetState { + final String message; + + const PasswordResetError({String? email, required this.message}) + : super(email: email); // Optional email + + @override + List get props => [email, message]; // Nullable email +} + diff --git a/mobile-v3/lib/src/app/auth/pages/login_page.dart b/mobile-v3/lib/src/app/auth/pages/login_page.dart index d71ab24f04..59865207e5 100644 --- a/mobile-v3/lib/src/app/auth/pages/login_page.dart +++ b/mobile-v3/lib/src/app/auth/pages/login_page.dart @@ -1,4 +1,5 @@ import 'package:airqo/src/app/auth/bloc/auth_bloc.dart'; +import 'package:airqo/src/app/auth/pages/password_reset/forgot_password.dart'; import 'package:airqo/src/app/auth/pages/register_page.dart'; import 'package:airqo/src/app/shared/pages/nav_page.dart'; import 'package:airqo/src/app/shared/widgets/form_field.dart'; @@ -180,14 +181,29 @@ class _LoginPageState extends State { onTap: () => Navigator.of(context).push(MaterialPageRoute( builder: (context) => CreateAccountScreen())), child: Text( - "Create Account", + " Create Account", style: TextStyle( fontWeight: FontWeight.w500, color: AppColors.primaryColor), ), ) ]), + SizedBox(height: 16), + + Center( + child: InkWell( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (context) => ForgotPasswordPage())), + child: Text( + "Forgot password?", + style: TextStyle( + fontWeight: FontWeight.w500, + color: AppColors.primaryColor), + ), + ), + ) ], + ), ), ), diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart new file mode 100644 index 0000000000..226079229e --- /dev/null +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart @@ -0,0 +1,208 @@ +import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart'; +import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart'; +import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_state.dart'; +import 'package:airqo/src/app/auth/bloc/auth_bloc.dart'; +import 'package:airqo/src/app/auth/pages/login_page.dart'; +import 'package:airqo/src/app/auth/pages/password_reset/reset_link_sent.dart'; + +import 'package:airqo/src/app/shared/pages/nav_page.dart'; +import 'package:airqo/src/app/shared/widgets/form_field.dart'; +import 'package:airqo/src/app/shared/widgets/spinner.dart'; +import 'package:airqo/src/meta/utils/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:loggy/loggy.dart'; + +class ForgotPasswordPage extends StatefulWidget { + const ForgotPasswordPage({super.key}); + + @override + State createState() => _ForgotPasswordPage(); +} + +class _ForgotPasswordPage extends State { + String? error; + late PasswordResetBloc passwordResetBloc; + late TextEditingController emailController = TextEditingController(); + + late GlobalKey formKey = GlobalKey(); + + @override + void initState() { + super.initState(); + emailController = TextEditingController(); + + + try { + passwordResetBloc = context.read(); + + } catch (e) { + logError('Failed to initialize PasswordResetBloc: $e'); + } + } + + @override + void dispose() { + emailController.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + if (state is PasswordResetSuccess) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ResetLinkSentPage())); + } else if (state is PasswordResetError) { + setState(() { + error = state.message.replaceAll("Exception: ", ""); + }); + } + }, + child: Scaffold( + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Forgot Password", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: AppColors.boldHeadlineColor), + ), + centerTitle: true, + + + ), + + body: Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Padding( + padding: const EdgeInsets.only(left: 32, right: 32, top: 8), + child: SizedBox( + child: Column( + children: [ + Text("Forgot Your Password?", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + ), + ), + SizedBox( + height: 12, + ), + Text("Enter your email address and we will send you a code to reset your password.", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + SizedBox(height: 32), + FormFieldWidget( + prefixIcon: Container( + padding: const EdgeInsets.all(13.5), + child: SvgPicture.asset( + "assets/icons/email-icon.svg", + height: 10, + ), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return "This field cannot be blank."; + } + return null; + }, + hintText: "Enter your email", + label: "Email*", + controller: emailController), + SizedBox(height: 18), + + ], + ), + ), + ), + ), + if (error != null) + Padding( + padding: const EdgeInsets.only(left: 32.0, top: 8), + child: Text(error!, style: TextStyle(color: Colors.red)), + ), + SizedBox(height: 18), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: BlocBuilder( + builder: (context, state) { + bool loading = state is PasswordResetLoading; + + return InkWell( + onTap: loading + ? null + : () { + final currentForm = formKey.currentState; + if (currentForm != null && + currentForm.validate()) { + passwordResetBloc.add(RequestPasswordReset( + emailController.text.trim() + )); + + emailController.clear(); + + + } + }, + child: Container( + height: 56, + decoration: BoxDecoration( + color: AppColors.primaryColor, + borderRadius: BorderRadius.circular(4)), + child: Center( + child: loading + ? Spinner() + : Text( + "Submit", + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ), + ); + }, + ), + ), + SizedBox( + height: 18 + ), + + InkWell( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (context) => LoginPage())), + child: Center( + child: Text( + "Login", + style: TextStyle( + fontWeight: FontWeight.w500, + color: AppColors.primaryColor), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart new file mode 100644 index 0000000000..16470e6e45 --- /dev/null +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart @@ -0,0 +1,219 @@ +import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart'; + +import 'package:airqo/src/app/auth/pages/password_reset/reset_success.dart'; +import 'package:airqo/src/app/shared/widgets/form_field.dart'; +import 'package:airqo/src/app/shared/widgets/spinner.dart'; +import 'package:airqo/src/meta/utils/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:loggy/loggy.dart'; + +import '../../bloc/ForgotPasswordBloc/forgot_password_bloc.dart'; +import '../../bloc/ForgotPasswordBloc/forgot_password_state.dart'; + +class PasswordResetPage extends StatefulWidget { + final String? token; + const PasswordResetPage({super.key, this.token}); + + @override + State createState() => _PasswordResetPage(); +} + +class _PasswordResetPage extends State { + + String? error; + late PasswordResetBloc passwordResetBloc; + late TextEditingController passwordConfirmController = TextEditingController(); + late TextEditingController passwordController = TextEditingController(); + late TextEditingController resetController= TextEditingController(); + late GlobalKey formKey = GlobalKey(); + + @override + void initState() { + super.initState(); + passwordConfirmController = TextEditingController(); + passwordController = TextEditingController(); + resetController= TextEditingController(); + + try { + passwordResetBloc = context.read(); + + } catch (e) { + logError('Failed to initialize PasswordResetBloc: $e'); + } + } + + @override + void dispose() { + passwordConfirmController.dispose(); + passwordController.dispose(); + resetController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + if (state is PasswordResetSuccess) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ResetSuccessPage())); + } else if (state is PasswordResetError) { + setState(() { + error = state.message.replaceAll("Exception: ", ""); + }); + } + }, + child: Scaffold( + appBar: AppBar( + title: Text( + "Reset Password", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: AppColors.boldHeadlineColor), + ), + centerTitle: true, + ), + body: Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Padding( + padding: const EdgeInsets.only(left: 32, right: 32, top: 8), + child: SizedBox( + child: Column( + children: [ + SizedBox(height: 32), + FormFieldWidget( + prefixIcon: Container( + padding: const EdgeInsets.all(13.5), + child: SvgPicture.asset( + "assets/icons/password.svg", + height: 10, + ), + ), + validator: (value) { + + if (value == null || value.isEmpty) { + return "This field cannot be blank."; + } + return null; + }, + hintText: "Enter your password", + label: "Password", + isPassword: true, + controller: passwordController), + SizedBox(height: 16), + FormFieldWidget( + prefixIcon: Container( + padding: const EdgeInsets.all(13.5), + child: SvgPicture.asset( + "assets/icons/password.svg", + height: 10, + ), + ), + validator: (value) { + if(value != passwordController.text){ + return "Passwords do not match"; + } + + if (value == null || value.isEmpty) { + return "This field cannot be blank."; + } + return null; + }, + hintText: "Re-enter your new password", + label: "Confirm Password", + isPassword: true, + controller: passwordConfirmController), + SizedBox(height: 16), + FormFieldWidget( + prefixIcon: Container( + padding: const EdgeInsets.all(13.5), + child: SvgPicture.asset( + "assets/icons/password.svg", + height: 10, + ), + ), + validator: (value) { + + if (value == null || value.isEmpty) { + return "This field cannot be blank."; + } + return null; + }, + hintText: "Enter the password reset code", + label: "Password Reset Code", + controller: resetController) + ], + ), + ), + ), + ), + if (error != null) + Padding( + padding: const EdgeInsets.only(left: 32.0, top: 8), + child: Text(error!, style: TextStyle(color: Colors.red)), + ), + SizedBox(height: 24), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32.0), + child: BlocBuilder( + builder: (context, state) { + bool loading = state is PasswordResetLoading; + + return InkWell( + onTap: loading + ? null + : () { + + + + final currentForm = formKey.currentState; + if (currentForm != null && + currentForm.validate()) { + passwordResetBloc.add(UpdatePassword( + confirmPassword: passwordConfirmController.text.trim(), + token: resetController.text.trim(), + password:passwordController.text.trim())); + + } + resetController.clear(); + passwordConfirmController.clear(); + passwordController.clear(); + }, + child: Container( + height: 56, + decoration: BoxDecoration( + color: AppColors.primaryColor, + borderRadius: BorderRadius.circular(4)), + child: Center( + child: loading + ? Spinner() + : Text( + "Reset Password", + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ), + ); + }, + ), + ), + SizedBox(height: 16), + + ], + ), + ), + ), + ); + } +} diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart new file mode 100644 index 0000000000..1f2258a907 --- /dev/null +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart @@ -0,0 +1,159 @@ +import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart'; +import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_event.dart'; +import 'package:airqo/src/app/auth/pages/password_reset/password_reset.dart'; + +import 'package:airqo/src/meta/utils/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + + +class ResetLinkSentPage extends StatelessWidget { + const ResetLinkSentPage({super.key}); + + + + + + + @override + Widget build(BuildContext context) { + + + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Forgot Password", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: AppColors.boldHeadlineColor), + ), + centerTitle: true, + + + ), + + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Padding( + padding: const EdgeInsets.only(left: 32, right: 32, top: 8), + child: SizedBox( + child: Column( + children: [ + Text("Password Reset Code Sent!", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + ), + ), + SizedBox( + height: 20, + ), + Text("We’ve successfully sent you an email with a code to reset your password. Please check your inbox (and spam folder, just in case) for the email and copy the code. You'll need this code on the next step to reset your password.", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + + SizedBox( + height: 20, + ), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Not seeing the email?', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + + InkWell( + onTap: (){ + final email = context.read().state.email; + if (email != null && email.isNotEmpty) { + context.read().add( + RequestPasswordReset(email), + ); + print("Resend requested for email: $email"); + } else { + print("Email is null or empty. Cannot resend."); + } + }, + child: Center( + child: Text( + " Resend", + style: TextStyle( + fontWeight: FontWeight.w500, + color: AppColors.primaryColor), + ), + ), + ), + + + + + ], + ), + + SizedBox( + height: 20, + ), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Received the email?', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + + InkWell( + onTap: (){ + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => PasswordResetPage())); + }, + child: Center( + child: Text( + " Continue", + style: TextStyle( + fontWeight: FontWeight.w500, + color: AppColors.primaryColor), + ), + ), + ), + + + + + ], + ), + + + ], + ), + ), + ), + ), + + SizedBox(height: 24), + + + ], + ), + ); + } +} diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart new file mode 100644 index 0000000000..4adcd742f3 --- /dev/null +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart @@ -0,0 +1,97 @@ + +import 'package:airqo/src/meta/utils/colors.dart'; +import 'package:flutter/material.dart'; + + +import '../login_page.dart'; + + +class ResetSuccessPage extends StatelessWidget { + const ResetSuccessPage({super.key}); + + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Reset Password", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: AppColors.boldHeadlineColor), + ), + centerTitle: true, + + + ), + + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Padding( + padding: const EdgeInsets.only(left: 32, right: 32, top: 8), + child: SizedBox( + child: Column( + children: [ + Text("Your password has been reset successfully!", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + ), + ), + SizedBox( + height: 20, + ), + Text("You can now log in to your account using your new password.", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + + SizedBox( + height: 20, + ), + + InkWell( + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (context) => LoginPage())), + child: Container( + height: 56, + decoration: BoxDecoration( + color: AppColors.primaryColor, + borderRadius: BorderRadius.circular(4)), + child: Center( + child:Text( + "Login", + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.white, + ), + ), + ), + ), + ), + + ], + ), + ), + ), + ), + + SizedBox(height: 24), + + + ], + ), + ); + } +} diff --git a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart index b749641b6d..976dad6431 100644 --- a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart +++ b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart @@ -1,4 +1,5 @@ import 'package:airqo/src/app/auth/pages/login_page.dart'; +import 'package:airqo/src/app/auth/pages/password_reset/forgot_password.dart'; import 'package:airqo/src/app/auth/pages/register_page.dart'; import 'package:airqo/src/app/auth/widgets/breathe_clean.dart'; import 'package:airqo/src/app/auth/widgets/know_your_air.dart'; @@ -124,19 +125,8 @@ class _WelcomeScreenState extends State { ), ), ), - SizedBox(height: 16), - SizedBox(height: 16), - Center( - child: InkWell( - onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (context) => ForgotPasswordPage())), - child: Text("Forgot password?", - style: TextStyle( - color: AppColors.primaryColor, - fontWeight: FontWeight.w500)), - ), - ), + ], ), diff --git a/mobile-v3/lib/src/app/auth/repository/auth_repository.dart b/mobile-v3/lib/src/app/auth/repository/auth_repository.dart index 2ea4a92d0f..8625a5dd41 100644 --- a/mobile-v3/lib/src/app/auth/repository/auth_repository.dart +++ b/mobile-v3/lib/src/app/auth/repository/auth_repository.dart @@ -10,12 +10,18 @@ abstract class AuthRepository { //login function Future loginWithEmailAndPassword(String username, String password); Future registerWithEmailAndPassword(RegisterInputModel model); + Future requestPasswordReset(String email); + Future updatePassword({ + required String token, + required String password, + required String confirmPassword, + }); } class AuthImpl extends AuthRepository { @override - Future loginWithEmailAndPassword( - String username, String password) async { + Future loginWithEmailAndPassword(String username, + String password) async { Response loginResponse = await http.post( Uri.parse("https://api.airqo.net/api/v2/users/loginUser"), body: jsonEncode({"userName": username, "password": password}), @@ -51,4 +57,60 @@ class AuthImpl extends AuthRepository { throw Exception(data['errors']['message'] ?? data['message']); } } + + @override + Future requestPasswordReset(String email) async { + final response = await http.post( + Uri.parse('https://api.airqo.net/api/v2/users/reset-password-request'), + headers: { + 'Content-Type': 'application/json', + }, + body: jsonEncode({ + 'email': email, // User's email address + }), + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + if (data['success'] == true) { + return data['message'] ?? 'Password reset link sent.'; + } else { + throw Exception(data['message'] ?? 'Failed to send password reset request.'); + } + } else { + final error = jsonDecode(response.body)['message'] ?? 'Something went wrong.'; + throw Exception(error); + } + } + + + @override + Future updatePassword({ + required String token, + required String password, + required String confirmPassword, + }) async { + final response = await http.post( + Uri.parse('https://api.airqo.net/api/v2/users/reset-password/$token'), + + headers: { + 'Content-Type': 'application/json', + }, + body: jsonEncode({ + 'password': password, + 'confirmPassword': confirmPassword, // Include confirmPassword + }), + ); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + return data['message'] ?? 'Password reset successful.'; + } else { + final error = jsonDecode(response.body)['message'] ?? + 'Failed to reset password.'; + throw Exception(error); + } + } } + + diff --git a/mobile-v3/pubspec.lock b/mobile-v3/pubspec.lock index 1f13bd2d3b..83154b4e96 100644 --- a/mobile-v3/pubspec.lock +++ b/mobile-v3/pubspec.lock @@ -22,6 +22,38 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.0" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: "433df2e61b10519407475d7f69e470789d23d593f28224c38ba1068597be7950" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" args: dependency: transitive description: @@ -421,6 +453,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" hive: dependency: "direct main" description: diff --git a/mobile-v3/pubspec.yaml b/mobile-v3/pubspec.yaml index a8f89d228d..dac293922c 100644 --- a/mobile-v3/pubspec.yaml +++ b/mobile-v3/pubspec.yaml @@ -56,6 +56,7 @@ dependencies: connectivity_plus: ^6.1.0 flutter_loggy: ^2.0.3+1 jwt_decoder: ^2.0.1 + app_links: ^6.3.3 dev_dependencies: flutter_test: From 17e00fe0fba6c5adffc1b8ad463fcd12b367d010 Mon Sep 17 00:00:00 2001 From: Karagwa Date: Thu, 30 Jan 2025 14:00:54 +0300 Subject: [PATCH 3/8] Made improvements on the password reset proces --- mobile-v3/lib/main.dart | 6 +- .../lib/src/app/auth/pages/login_page.dart | 2 +- .../pages/password_reset/forgot_password.dart | 4 +- .../pages/password_reset/password_reset.dart | 45 ++-- .../pages/password_reset/reset_link_sent.dart | 227 ++++++++++-------- .../pages/password_reset/reset_success.dart | 4 +- mobile-v3/lib/src/meta/utils/colors.dart | 7 +- mobile-v3/pubspec.lock | 8 + mobile-v3/pubspec.yaml | 1 + 9 files changed, 169 insertions(+), 135 deletions(-) diff --git a/mobile-v3/lib/main.dart b/mobile-v3/lib/main.dart index 5c19655617..3e9f8cb855 100644 --- a/mobile-v3/lib/main.dart +++ b/mobile-v3/lib/main.dart @@ -2,7 +2,9 @@ import 'dart:io'; import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart'; import 'package:airqo/src/app/auth/bloc/auth_bloc.dart'; -import 'package:airqo/src/app/auth/pages/login_page.dart'; +import 'package:airqo/src/app/auth/pages/password_reset/password_reset.dart'; +import 'package:airqo/src/app/auth/pages/password_reset/reset_link_sent.dart'; + import 'package:airqo/src/app/auth/pages/welcome_screen.dart'; import 'package:airqo/src/app/auth/repository/auth_repository.dart'; import 'package:airqo/src/app/dashboard/bloc/dashboard/dashboard_bloc.dart'; @@ -130,7 +132,7 @@ class AirqoMobile extends StatelessWidget { logDebug('Current theme state: $state'); return MaterialApp( debugShowCheckedModeBanner: false, - theme: isLightTheme ? AppTheme.lightTheme : AppTheme.darkTheme, + theme: isLightTheme ? AppTheme.darkTheme : AppTheme.lightTheme, // theme: isLightTheme ? ThemeData( // splashColor: Colors.transparent, // highlightColor: Colors.transparent, diff --git a/mobile-v3/lib/src/app/auth/pages/login_page.dart b/mobile-v3/lib/src/app/auth/pages/login_page.dart index 59865207e5..e2ae51455e 100644 --- a/mobile-v3/lib/src/app/auth/pages/login_page.dart +++ b/mobile-v3/lib/src/app/auth/pages/login_page.dart @@ -68,7 +68,7 @@ class _LoginPageState extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor), + color: AppColors.boldHeadlineColor2), ), centerTitle: true, ), diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart index 226079229e..5fd6e62b80 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart @@ -76,7 +76,7 @@ class _ForgotPasswordPage extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor), + color: AppColors.boldHeadlineColor2), ), centerTitle: true, @@ -98,6 +98,7 @@ class _ForgotPasswordPage extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, + color: AppColors.highlightColor2 ), ), SizedBox( @@ -107,6 +108,7 @@ class _ForgotPasswordPage extends State { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, + color: AppColors.highlightColor2 ), ), SizedBox(height: 32), diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart index 16470e6e45..3eae6d5934 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart @@ -13,8 +13,8 @@ import '../../bloc/ForgotPasswordBloc/forgot_password_bloc.dart'; import '../../bloc/ForgotPasswordBloc/forgot_password_state.dart'; class PasswordResetPage extends StatefulWidget { - final String? token; - const PasswordResetPage({super.key, this.token}); + final String token; + const PasswordResetPage({super.key, required this.token}); @override State createState() => _PasswordResetPage(); @@ -73,7 +73,7 @@ class _PasswordResetPage extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor), + color: AppColors.boldHeadlineColor2), ), centerTitle: true, ), @@ -88,7 +88,24 @@ class _PasswordResetPage extends State { child: SizedBox( child: Column( children: [ - SizedBox(height: 32), + + Text("Reset your password", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + ), + ), + SizedBox( + height: 20, + ), + Text("Please enter your new password below. Make sure it's something secure that you can remember.", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + + SizedBox(height: 16), FormFieldWidget( prefixIcon: Container( padding: const EdgeInsets.all(13.5), @@ -132,24 +149,6 @@ class _PasswordResetPage extends State { isPassword: true, controller: passwordConfirmController), SizedBox(height: 16), - FormFieldWidget( - prefixIcon: Container( - padding: const EdgeInsets.all(13.5), - child: SvgPicture.asset( - "assets/icons/password.svg", - height: 10, - ), - ), - validator: (value) { - - if (value == null || value.isEmpty) { - return "This field cannot be blank."; - } - return null; - }, - hintText: "Enter the password reset code", - label: "Password Reset Code", - controller: resetController) ], ), ), @@ -179,7 +178,7 @@ class _PasswordResetPage extends State { currentForm.validate()) { passwordResetBloc.add(UpdatePassword( confirmPassword: passwordConfirmController.text.trim(), - token: resetController.text.trim(), + token: widget.token, password:passwordController.text.trim())); } diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart index 1f2258a907..f3378021ae 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart @@ -5,20 +5,20 @@ import 'package:airqo/src/app/auth/pages/password_reset/password_reset.dart'; import 'package:airqo/src/meta/utils/colors.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:pin_code_fields/pin_code_fields.dart'; - -class ResetLinkSentPage extends StatelessWidget { +class ResetLinkSentPage extends StatefulWidget { const ResetLinkSentPage({super.key}); + @override + _ResetLinkSentPageState createState() => _ResetLinkSentPageState(); +} - - - +class _ResetLinkSentPageState extends State { + final TextEditingController _pinController = TextEditingController(); @override Widget build(BuildContext context) { - - return Scaffold( appBar: AppBar( leading: IconButton( @@ -32,126 +32,141 @@ class ResetLinkSentPage extends StatelessWidget { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor), + color: AppColors.boldHeadlineColor2), ), centerTitle: true, - - ), - body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - child: Padding( - padding: const EdgeInsets.only(left: 32, right: 32, top: 8), - child: SizedBox( - child: Column( - children: [ - Text("Password Reset Code Sent!", - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w700, - ), - ), - SizedBox( - height: 20, - ), - Text("We’ve successfully sent you an email with a code to reset your password. Please check your inbox (and spam folder, just in case) for the email and copy the code. You'll need this code on the next step to reset your password.", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - - SizedBox( - height: 20, - ), + padding: const EdgeInsets.only(left: 32, right: 32, top: 8), + child: Column( + children: [ + Text( + "We just sent you a Password Reset Code to your email", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: AppColors.highlightColor2 + ), + ), + SizedBox(height: 20), + Text( + "Enter the verification code sent to ha ******@gmail.com", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: AppColors.highlightColor2 + ), + ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Not seeing the email?', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), + // PIN Code Input + SizedBox(height: 20), + PinCodeTextField( + appContext: context, + length: 5, // Adjust length if needed + controller: _pinController, + keyboardType: TextInputType.number, + animationType: AnimationType.fade, + hintCharacter: "0", + //backgroundColor: AppColors.boldHeadlineColor4, + + + textStyle: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w600, + color: AppColors.boldHeadlineColor3 + + ), + pinTheme: PinTheme( + + shape: PinCodeFieldShape.box, + borderRadius: BorderRadius.circular(4), + + fieldHeight: 64, + fieldWidth: 60, + activeFillColor: AppColors.boldHeadlineColor4, + inactiveFillColor:AppColors.boldHeadlineColor4 , + selectedFillColor: AppColors.darkThemeBackground, + activeColor: AppColors.boldHeadlineColor4, + inactiveColor: AppColors.boldHeadlineColor4, + selectedColor: AppColors.primaryColor, + fieldOuterPadding: EdgeInsets.symmetric(horizontal: 4), + + + ), + enableActiveFill: true, + onChanged: (value) {}, + ), - InkWell( - onTap: (){ - final email = context.read().state.email; - if (email != null && email.isNotEmpty) { - context.read().add( - RequestPasswordReset(email), - ); - print("Resend requested for email: $email"); - } else { - print("Email is null or empty. Cannot resend."); - } - }, - child: Center( - child: Text( - " Resend", - style: TextStyle( - fontWeight: FontWeight.w500, - color: AppColors.primaryColor), - ), - ), + SizedBox(height: 18), + InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => PasswordResetPage(token: _pinController.text.trim()), + ), + ); + }, + child: Container( + height: 56, + decoration: BoxDecoration( + color: AppColors.primaryColor, + borderRadius: BorderRadius.circular(4)), + child: Center( + child: Text( + "Continue", + style: TextStyle( + fontWeight: FontWeight.w500, + color: Colors.white, ), - - - - - ], + ), ), + ), + ), - SizedBox( - height: 20, + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Not seeing the email?', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + ), ), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Received the email?', + InkWell( + onTap: () { + final email = context.read().state.email; + if (email != null && email.isNotEmpty) { + context.read().add( + RequestPasswordReset(email), + ); + print("Resend requested for email: $email"); + } else { + print("Email is null or empty. Cannot resend."); + } + }, + child: Center( + child: Text( + " Resend", style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), + fontWeight: FontWeight.w500, + color: AppColors.primaryColor), ), - - InkWell( - onTap: (){ - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => PasswordResetPage())); - }, - child: Center( - child: Text( - " Continue", - style: TextStyle( - fontWeight: FontWeight.w500, - color: AppColors.primaryColor), - ), - ), - ), - - - - - ], + ), ), - - ], ), - ), + SizedBox(height: 20), + ], ), ), - SizedBox(height: 24), - - ], ), ); diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart index 4adcd742f3..51f8ac04ae 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart @@ -25,7 +25,7 @@ class ResetSuccessPage extends StatelessWidget { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor), + color: AppColors.boldHeadlineColor2), ), centerTitle: true, @@ -45,6 +45,7 @@ class ResetSuccessPage extends StatelessWidget { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, + color: AppColors.highlightColor2 ), ), SizedBox( @@ -54,6 +55,7 @@ class ResetSuccessPage extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, + color: AppColors.highlightColor2 ), ), diff --git a/mobile-v3/lib/src/meta/utils/colors.dart b/mobile-v3/lib/src/meta/utils/colors.dart index 443047ec49..e6c4719e35 100644 --- a/mobile-v3/lib/src/meta/utils/colors.dart +++ b/mobile-v3/lib/src/meta/utils/colors.dart @@ -8,12 +8,17 @@ class AppColors { // static Color highlightColor = Color(0xff2E2F33); // static Color boldHeadlineColor = Color(0xff9EA3AA); // static Color secondaryHeadlineColor = Color(0xff60646C); - + //#E2E3E5 #7A7F87 #2E2F33 #1C1D20 static Color primaryColor = Color(0xff145FFF); static Color backgroundColor = Color(0xffF9FAFB); static Color highlightColor = Color(0xffF3F6F8); static Color boldHeadlineColor = Color(0xff6F87A1); + static Color boldHeadlineColor2 = Color(0xff9EA3AA); + static Color boldHeadlineColor3 = Color(0xff7A7F87); + static Color boldHeadlineColor4 = Color(0xff2E2F33); + static Color highlightColor2= Color(0xffE2E3E5); static Color secondaryHeadlineColor = Color(0xff6F87A1); + static Color darkThemeBackground= Color(0xff1C1D20); } class AppTheme { diff --git a/mobile-v3/pubspec.lock b/mobile-v3/pubspec.lock index 83154b4e96..fae9aae95a 100644 --- a/mobile-v3/pubspec.lock +++ b/mobile-v3/pubspec.lock @@ -757,6 +757,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + pin_code_fields: + dependency: "direct main" + description: + name: pin_code_fields + sha256: "4c0db7fbc889e622e7c71ea54b9ee624bb70c7365b532abea0271b17ea75b729" + url: "https://pub.dev" + source: hosted + version: "8.0.1" platform: dependency: transitive description: diff --git a/mobile-v3/pubspec.yaml b/mobile-v3/pubspec.yaml index dac293922c..d3cd87f253 100644 --- a/mobile-v3/pubspec.yaml +++ b/mobile-v3/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: flutter_loggy: ^2.0.3+1 jwt_decoder: ^2.0.1 app_links: ^6.3.3 + pin_code_fields: ^8.0.1 dev_dependencies: flutter_test: From 7211a31de5cc4a994068db154bcd5eb20fe2748c Mon Sep 17 00:00:00 2001 From: Karagwa Date: Thu, 30 Jan 2025 14:10:02 +0300 Subject: [PATCH 4/8] Removed app links from IOS --- .../io/flutter/plugins/GeneratedPluginRegistrant.java | 5 ----- mobile-v3/ios/Runner/GeneratedPluginRegistrant.m | 8 +------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index 5c8bed684b..5f086b275f 100644 --- a/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/mobile-v3/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -15,11 +15,6 @@ public final class GeneratedPluginRegistrant { private static final String TAG = "GeneratedPluginRegistrant"; public static void registerWith(@NonNull FlutterEngine flutterEngine) { - try { - flutterEngine.getPlugins().add(new com.llfbandit.app_links.AppLinksPlugin()); - } catch (Exception e) { - Log.e(TAG, "Error registering plugin app_links, com.llfbandit.app_links.AppLinksPlugin", e); - } try { flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.connectivity.ConnectivityPlugin()); } catch (Exception e) { diff --git a/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m b/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m index dd8505e00f..a553db10e4 100644 --- a/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m +++ b/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m @@ -6,12 +6,6 @@ #import "GeneratedPluginRegistrant.h" -#if __has_include() -#import -#else -@import app_links; -#endif - #if __has_include() #import #else @@ -33,7 +27,7 @@ @implementation GeneratedPluginRegistrant + (void)registerWithRegistry:(NSObject*)registry { - [AppLinksPlugin registerWithRegistrar:[registry registrarForPlugin:@"AppLinksPlugin"]]; + [ConnectivityPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"ConnectivityPlusPlugin"]]; [FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]]; [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; From c79031f5c570aaa1d615be9c406eaa7628e8ddb7 Mon Sep 17 00:00:00 2001 From: Karagwa Date: Thu, 30 Jan 2025 14:13:16 +0300 Subject: [PATCH 5/8] Removed extra space from the plugin files --- mobile-v3/ios/Runner/GeneratedPluginRegistrant.m | 1 - 1 file changed, 1 deletion(-) diff --git a/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m b/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m index a553db10e4..523e1cd8f2 100644 --- a/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m +++ b/mobile-v3/ios/Runner/GeneratedPluginRegistrant.m @@ -27,7 +27,6 @@ @implementation GeneratedPluginRegistrant + (void)registerWithRegistry:(NSObject*)registry { - [ConnectivityPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"ConnectivityPlusPlugin"]]; [FLTGoogleMapsPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTGoogleMapsPlugin"]]; [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; From 7e2864c5c5f49fd1ff3649d814caf6aecf02d9a1 Mon Sep 17 00:00:00 2001 From: Karagwa Date: Thu, 30 Jan 2025 16:11:55 +0300 Subject: [PATCH 6/8] Corrected the theme implementation logic --- mobile-v3/lib/main.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mobile-v3/lib/main.dart b/mobile-v3/lib/main.dart index 3e9f8cb855..084e34805d 100644 --- a/mobile-v3/lib/main.dart +++ b/mobile-v3/lib/main.dart @@ -2,9 +2,6 @@ import 'dart:io'; import 'package:airqo/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart'; import 'package:airqo/src/app/auth/bloc/auth_bloc.dart'; -import 'package:airqo/src/app/auth/pages/password_reset/password_reset.dart'; -import 'package:airqo/src/app/auth/pages/password_reset/reset_link_sent.dart'; - import 'package:airqo/src/app/auth/pages/welcome_screen.dart'; import 'package:airqo/src/app/auth/repository/auth_repository.dart'; import 'package:airqo/src/app/dashboard/bloc/dashboard/dashboard_bloc.dart'; @@ -25,7 +22,7 @@ import 'package:airqo/src/app/shared/bloc/connectivity_bloc.dart'; import 'package:airqo/src/app/shared/pages/nav_page.dart'; import 'package:airqo/src/meta/utils/colors.dart'; -import 'package:app_links/app_links.dart'; + import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -132,7 +129,7 @@ class AirqoMobile extends StatelessWidget { logDebug('Current theme state: $state'); return MaterialApp( debugShowCheckedModeBanner: false, - theme: isLightTheme ? AppTheme.darkTheme : AppTheme.lightTheme, + theme: isLightTheme ? AppTheme.lightTheme : AppTheme.darkTheme, // theme: isLightTheme ? ThemeData( // splashColor: Colors.transparent, // highlightColor: Colors.transparent, From 8c52ed75bc8f6277d603e84d6089a06efa3fd7cc Mon Sep 17 00:00:00 2001 From: Karagwa Date: Sun, 2 Feb 2025 19:08:56 +0300 Subject: [PATCH 7/8] Worked on the app colors...made sure they were consistent with the themes --- mobile-v3/lib/main.dart | 1 - .../forgot_password_bloc.dart | 2 +- .../lib/src/app/auth/pages/login_page.dart | 6 +-- .../pages/password_reset/forgot_password.dart | 6 +-- .../pages/password_reset/password_reset.dart | 5 ++- .../pages/password_reset/reset_link_sent.dart | 20 +++++----- .../pages/password_reset/reset_success.dart | 7 ++-- .../src/app/auth/pages/welcome_screen.dart | 4 +- .../app/dashboard/pages/dashboard_page.dart | 8 ++-- .../app/dashboard/widgets/analytics_card.dart | 18 ++++++--- .../app/profile/pages/guest_profile page.dart | 4 +- .../profile/pages/widgets/settings_tile.dart | 9 +++-- mobile-v3/lib/src/meta/utils/colors.dart | 16 +++++++- mobile-v3/pubspec.lock | 40 ------------------- 14 files changed, 67 insertions(+), 79 deletions(-) diff --git a/mobile-v3/lib/main.dart b/mobile-v3/lib/main.dart index f774663d63..71126e16a4 100644 --- a/mobile-v3/lib/main.dart +++ b/mobile-v3/lib/main.dart @@ -17,7 +17,6 @@ import 'package:airqo/src/app/other/places/repository/google_places_repository.d import 'package:airqo/src/app/other/theme/bloc/theme_bloc.dart'; import 'package:airqo/src/app/other/theme/repository/theme_repository.dart'; import 'package:airqo/src/app/profile/bloc/user_bloc.dart'; -import 'package:airqo/src/app/profile/pages/guest_profile%20page.dart'; import 'package:airqo/src/app/profile/repository/user_repository.dart'; import 'package:airqo/src/app/shared/bloc/connectivity_bloc.dart'; import 'package:airqo/src/app/shared/pages/nav_page.dart'; diff --git a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart index a3d00f5aba..28e8f795e4 100644 --- a/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart +++ b/mobile-v3/lib/src/app/auth/bloc/ForgotPasswordBloc/forgot_password_bloc.dart @@ -28,7 +28,7 @@ class PasswordResetBloc extends Bloc { ); emit(PasswordResetSuccess(message: message)); } catch (error) { - emit(PasswordResetError(message: 'Failed to update password. \nPlease enter the correct code')); + emit(PasswordResetError(message: 'Failed to update password. \nPlease re-check the code you entered')); } }); } diff --git a/mobile-v3/lib/src/app/auth/pages/login_page.dart b/mobile-v3/lib/src/app/auth/pages/login_page.dart index e2ae51455e..3099d39cf3 100644 --- a/mobile-v3/lib/src/app/auth/pages/login_page.dart +++ b/mobile-v3/lib/src/app/auth/pages/login_page.dart @@ -29,7 +29,7 @@ class _LoginPageState extends State { super.initState(); emailController = TextEditingController(); passwordController = TextEditingController(); - + try { authBloc = context.read(); @@ -68,7 +68,7 @@ class _LoginPageState extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor2), + color: Theme.of(context).textTheme.headlineLarge?.color), ), centerTitle: true, ), @@ -175,7 +175,7 @@ class _LoginPageState extends State { Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Text("Don't have an account?", style: TextStyle( - color: AppColors.boldHeadlineColor, + color: Theme.of(context).textTheme.headlineLarge?.color, fontWeight: FontWeight.w500)), InkWell( onTap: () => Navigator.of(context).push(MaterialPageRoute( diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart index 5fd6e62b80..2c8172e7b7 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/forgot_password.dart @@ -76,7 +76,7 @@ class _ForgotPasswordPage extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor2), + color: Theme.of(context).textTheme.headlineLarge?.color), ), centerTitle: true, @@ -98,7 +98,7 @@ class _ForgotPasswordPage extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.highlightColor2 + color: Theme.of(context).textTheme.titleMedium?.color ), ), SizedBox( @@ -108,7 +108,7 @@ class _ForgotPasswordPage extends State { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: AppColors.highlightColor2 + color: Theme.of(context).textTheme.titleMedium?.color ), ), SizedBox(height: 32), diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart index 3eae6d5934..77ba51018a 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/password_reset.dart @@ -73,7 +73,8 @@ class _PasswordResetPage extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor2), + color: Theme.of(context).textTheme.headlineLarge?.color + ), ), centerTitle: true, ), @@ -93,6 +94,7 @@ class _PasswordResetPage extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, + color: Theme.of(context).textTheme.titleMedium?.color ), ), SizedBox( @@ -102,6 +104,7 @@ class _PasswordResetPage extends State { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, + color: Theme.of(context).textTheme.titleMedium?.color ), ), diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart index f3378021ae..669b71c4db 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart @@ -32,8 +32,9 @@ class _ResetLinkSentPageState extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor2), + color: Theme.of(context).textTheme.headlineLarge?.color), ), + centerTitle: true, ), body: Column( @@ -49,7 +50,7 @@ class _ResetLinkSentPageState extends State { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.highlightColor2 + color: Theme.of(context).textTheme.titleMedium?.color ), ), SizedBox(height: 20), @@ -59,7 +60,7 @@ class _ResetLinkSentPageState extends State { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: AppColors.highlightColor2 + color: Theme.of(context).textTheme.titleMedium?.color ), ), @@ -88,11 +89,11 @@ class _ResetLinkSentPageState extends State { fieldHeight: 64, fieldWidth: 60, - activeFillColor: AppColors.boldHeadlineColor4, - inactiveFillColor:AppColors.boldHeadlineColor4 , - selectedFillColor: AppColors.darkThemeBackground, - activeColor: AppColors.boldHeadlineColor4, - inactiveColor: AppColors.boldHeadlineColor4, + activeFillColor: Theme.of(context).highlightColor, + inactiveFillColor:Theme.of(context).highlightColor , + selectedFillColor: Theme.of(context).appBarTheme.backgroundColor, + activeColor: Theme.of(context).highlightColor, + inactiveColor: Theme.of(context).highlightColor, selectedColor: AppColors.primaryColor, fieldOuterPadding: EdgeInsets.symmetric(horizontal: 4), @@ -133,10 +134,11 @@ class _ResetLinkSentPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'Not seeing the email?', + "Didn't receive the code?", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, + color: Theme.of(context).textTheme.headlineLarge?.color ), ), InkWell( diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart index 51f8ac04ae..03260953c5 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_success.dart @@ -45,7 +45,8 @@ class ResetSuccessPage extends StatelessWidget { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.highlightColor2 + color: Theme.of(context).textTheme.titleMedium?.color + ), ), SizedBox( @@ -55,7 +56,7 @@ class ResetSuccessPage extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: AppColors.highlightColor2 + color: Theme.of(context).textTheme.titleMedium?.color ), ), @@ -89,7 +90,7 @@ class ResetSuccessPage extends StatelessWidget { ), ), - SizedBox(height: 24), + ], diff --git a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart index f0ed1db8ed..98b011098d 100644 --- a/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart +++ b/mobile-v3/lib/src/app/auth/pages/welcome_screen.dart @@ -154,7 +154,7 @@ class _WelcomeScreenState extends State { Text( "Continue as guest ", style: TextStyle( - color: AppColors.boldHeadlineColor2, + color: Theme.of(context).textTheme.headlineLarge?.color, fontWeight: FontWeight.w500, ), ), @@ -162,7 +162,7 @@ class _WelcomeScreenState extends State { 'assets/icons/chevron-right.svg', height: 16.0, width: 16.0, - color: AppColors.boldHeadlineColor2, + color: Theme.of(context).textTheme.headlineLarge?.color, ), ], ), diff --git a/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart b/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart index e22c235614..6d88eaa80b 100644 --- a/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart +++ b/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart @@ -192,7 +192,7 @@ class _DashboardPageState extends State { style: TextStyle( fontSize: 28, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor2, + color: Theme.of(context).textTheme.headlineLarge?.color, ), ); } else { @@ -204,7 +204,7 @@ class _DashboardPageState extends State { style: TextStyle( fontSize: 28, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor, + color: Theme.of(context).textTheme.headlineLarge?.color, ), ); } @@ -213,7 +213,7 @@ class _DashboardPageState extends State { style: TextStyle( fontSize: 28, fontWeight: FontWeight.w700, - color: AppColors.boldHeadlineColor, + color: Theme.of(context).textTheme.headlineLarge?.color, ), ); }, @@ -227,7 +227,7 @@ class _DashboardPageState extends State { style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, - color: Color(0xff60646C), + color: Theme.of(context).textTheme.headlineMedium?.color, ), ), SizedBox(height: 4) diff --git a/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart b/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart index 31dff6608a..eda3d4745f 100644 --- a/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart +++ b/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart @@ -47,7 +47,7 @@ class AnalyticsCard extends StatelessWidget { Text( " PM2.5", style: TextStyle( - color: Color(0xff7A7F87), + color: Theme.of(context).textTheme.headlineSmall?.color, ), ), ], @@ -61,13 +61,17 @@ class AnalyticsCard extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.w700, fontSize: 40, - color: AppColors.boldHeadlineColor2), + color: Theme.of(context).textTheme.headlineLarge?.color + + ), ), Text(" μg/m3", style: TextStyle( fontWeight: FontWeight.w600, fontSize: 20, - color: AppColors.boldHeadlineColor2)) + color: Theme.of(context).textTheme.headlineLarge?.color + ) + ) ]), ]), SizedBox( @@ -99,12 +103,16 @@ class AnalyticsCard extends StatelessWidget { style: TextStyle( fontSize: 28, fontWeight: FontWeight.w700, - color: AppColors.secondaryHeadlineColor3)), + color: Theme.of(context).textTheme.headlineSmall?.color + ) + ), Text(measurement.healthTips![0].description!, style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, - color: AppColors.secondaryHeadlineColor2)) + color: Theme.of(context).textTheme.headlineMedium?.color + ) + ) ], ), ), diff --git a/mobile-v3/lib/src/app/profile/pages/guest_profile page.dart b/mobile-v3/lib/src/app/profile/pages/guest_profile page.dart index 46dfe2fa93..6bd76ba9db 100644 --- a/mobile-v3/lib/src/app/profile/pages/guest_profile page.dart +++ b/mobile-v3/lib/src/app/profile/pages/guest_profile page.dart @@ -74,7 +74,7 @@ class _GuestProfilePageState extends State { Text( "Guest User", style: TextStyle( - color: AppColors.boldHeadlineColor2, + color: Theme.of(context).textTheme.headlineSmall?.color, fontSize: 24, fontWeight: FontWeight.w700, ), @@ -86,7 +86,7 @@ class _GuestProfilePageState extends State { child: Text( 'Settings', style:TextStyle( - color: AppColors.boldHeadlineColor2, + color: Theme.of(context).textTheme.headlineSmall?.color, fontSize: 18, fontWeight: FontWeight.w500, ), diff --git a/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart b/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart index 4911dbec69..70056e173b 100644 --- a/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart +++ b/mobile-v3/lib/src/app/profile/pages/widgets/settings_tile.dart @@ -38,7 +38,7 @@ class SettingsTile extends StatelessWidget { subtitle: description != null ? Text(description!, style: TextStyle( - color: AppColors.secondaryHeadlineColor2, + color: Theme.of(context).textTheme.headlineMedium?.color, fontSize: 14, fontWeight: FontWeight.w400)) : null, @@ -54,11 +54,14 @@ class SettingsTile extends StatelessWidget { ), ], ), - title: Text(title, + title: Text( + title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.w500, - color: AppColors.secondaryHeadlineColor3)), + color: Theme.of(context).textTheme.headlineSmall?.color + ) + ), ), Divider( color:Theme.of(context).highlightColor, diff --git a/mobile-v3/lib/src/meta/utils/colors.dart b/mobile-v3/lib/src/meta/utils/colors.dart index 8e588bc56d..6f1d1e6e84 100644 --- a/mobile-v3/lib/src/meta/utils/colors.dart +++ b/mobile-v3/lib/src/meta/utils/colors.dart @@ -40,12 +40,18 @@ class AppTheme { highlightColor: const Color(0xffF3F6F8), textTheme: TextTheme( headlineLarge: TextStyle( - color: const Color(0xff6F87A1), + color: const Color(0xff000000), fontWeight: FontWeight.bold, ), headlineMedium: TextStyle( - color: const Color(0xff6F87A1), + color: Colors.black, ), + headlineSmall: TextStyle( + color: const Color(0xff000000), + ), + titleMedium:TextStyle( + color: const Color(0xff000000) + ) , titleLarge: TextStyle( fontSize: 40, fontWeight: FontWeight.w700, color: Colors.black), ), @@ -72,6 +78,12 @@ class AppTheme { headlineMedium: TextStyle( color: const Color(0xff60646C), ), + headlineSmall: TextStyle( + color: const Color(0xff7A7F87), + ), + titleMedium:TextStyle( + color: const Color(0xffE2E3E5) + ) , titleLarge: TextStyle( fontSize: 40, fontWeight: FontWeight.w700, color: Colors.white), ), diff --git a/mobile-v3/pubspec.lock b/mobile-v3/pubspec.lock index 61c0c44ff5..dd502aaaf3 100644 --- a/mobile-v3/pubspec.lock +++ b/mobile-v3/pubspec.lock @@ -22,38 +22,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.0" - app_links: - dependency: "direct main" - description: - name: app_links - sha256: "433df2e61b10519407475d7f69e470789d23d593f28224c38ba1068597be7950" - url: "https://pub.dev" - source: hosted - version: "6.3.3" - app_links_linux: - dependency: transitive - description: - name: app_links_linux - sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 - url: "https://pub.dev" - source: hosted - version: "1.0.3" - app_links_platform_interface: - dependency: transitive - description: - name: app_links_platform_interface - sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - app_links_web: - dependency: transitive - description: - name: app_links_web - sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 - url: "https://pub.dev" - source: hosted - version: "1.0.4" args: dependency: transitive description: @@ -453,14 +421,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" - gtk: - dependency: transitive - description: - name: gtk - sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c - url: "https://pub.dev" - source: hosted - version: "2.1.0" hive: dependency: "direct main" description: From 42fec2931bdc6be97c431031f95432a1c26d1f50 Mon Sep 17 00:00:00 2001 From: Karagwa Date: Mon, 3 Feb 2025 21:22:39 +0300 Subject: [PATCH 8/8] Added pin validation --- .../pages/password_reset/reset_link_sent.dart | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart index 669b71c4db..37eef372b9 100644 --- a/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart +++ b/mobile-v3/lib/src/app/auth/pages/password_reset/reset_link_sent.dart @@ -106,11 +106,28 @@ class _ResetLinkSentPageState extends State { SizedBox(height: 18), InkWell( onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => PasswordResetPage(token: _pinController.text.trim()), - ), - ); + final pin = _pinController.text.trim(); + + + if (RegExp(r'^\d{5}$').hasMatch(pin)) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => PasswordResetPage(token: pin), + ), + ); + } else { + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Please enter a valid 5-digit number.', + style: TextStyle( + color: Colors.white + ),), + backgroundColor: AppColors.primaryColor, + ), + ); + } + _pinController.clear(); }, child: Container( height: 56,