Skip to content

Commit

Permalink
fixed issues with dynamic routing
Browse files Browse the repository at this point in the history
  • Loading branch information
sudo-which-qp committed Jan 16, 2025
1 parent 926fdd9 commit 5c6da98
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 38 deletions.
22 changes: 10 additions & 12 deletions lib/config/router/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:note_app/helpers/wrapper.dart';
import 'package:note_app/presentation/views.dart';
import 'package:note_app/services/service_locator.dart';
import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
import 'package:note_app/utils/const_values.dart';

class Routes {
static Route<dynamic> generateRoute(RouteSettings settings) {
Expand All @@ -14,27 +15,24 @@ class Routes {


if (RoutesGuard.requiresAuth(settings.name)) {
if (authCubit.state is! AuthAuthenticated) {
authCubit.saveAttemptedRoute(settings.name!);
return MaterialPageRoute(builder: (_) => const LoginScreen());
if (state is AuthEmailUnverified) {
return MaterialPageRoute(
builder: (_) => const VerifyCode(from: 'protected_route'),
maintainState: true, // Preserve route state
);
}
}

if (RoutesGuard.requiresEmailVerification(settings.name)) {
if (state is AuthEmailUnverified) {
if (state is! AuthAuthenticated) {
authCubit.saveAttemptedRoute(settings.name!);
return MaterialPageRoute(
builder: (_) => VerifyCode(from: args!['from']),
builder: (_) => const LoginScreen(),
maintainState: true,
);
}
}

switch (settings.name) {
// Middleware Wrapper
case RoutesName.wrapper:
return MaterialPageRoute(builder: (_) => const Wrapper());
// ends here

switch (settings.name) {
//
case RoutesName.home_screen:
return MaterialPageRoute(builder: (_) => const HomeScreen());
Expand Down
6 changes: 6 additions & 0 deletions lib/data/exceptions/user_suspended_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class UserSuspendedException implements Exception {
final String message;
UserSuspendedException(this.message);
@override
String toString() => message;
}
28 changes: 14 additions & 14 deletions lib/data/network/network_services_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,42 +106,42 @@ class NetworkServicesApi implements BaseApiService {
dynamic returnResponse(http.Response response) {
logger.i(response.statusCode);
logger.i(response.body);

dynamic jsonResponse = json.decode(response.body);
logger.i('Parsed Response: $jsonResponse');

switch (response.statusCode) {
case 200:
dynamic jsonResponse = json.decode(response.body);
return jsonResponse;
case 201:
dynamic jsonResponse = json.decode(response.body);
return jsonResponse;
case 400:
dynamic jsonResponse = json.decode(response.body);
return jsonResponse;
case 401:
dynamic jsonResponse = json.decode(response.body);
logger.i('401 Response: $jsonResponse');
// If it's unverified email case
if (jsonResponse['message'].contains('not yet verified') &&
jsonResponse['data']?['token'] != null) {

if (jsonResponse['message']?.contains('not verified')) {
logger.i('Unverified email case detected');
return jsonResponse; // Just return the response for processing
return jsonResponse;
}

throw UnauthorisedException(jsonResponse['message']);

case 403:
dynamic jsonResponse = json.decode(response.body);
// When user tries to access something without email verification
if (jsonResponse['message'].contains('not yet verified')) {
throw UnauthorisedException(jsonResponse['message']);
if (jsonResponse['message']?.contains('not verified')) {
return jsonResponse;
}
throw UnauthorisedException(jsonResponse['message']);

case 404:
dynamic jsonResponse = json.decode(response.body);
if (jsonResponse['success'] == false) {
throw BadRequestException(jsonResponse['message']);
}
return jsonResponse;

case 500:
dynamic jsonResponse = json.decode(response.body);
throw FetchDataException(jsonResponse['message']);

default:
throw UnauthorisedException();
}
Expand Down
100 changes: 97 additions & 3 deletions lib/data/repositories/auth_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,42 @@ class AuthRepository {
if (response['success'] != true) {
throw response['message'];
}

if (response['success'] == true) {
final currentUser = _hiveManager.userModelBox.get(userKey);
if (currentUser != null) {
// Create new user model with verified status
final updatedUser = UserModel(
id: currentUser.id,
uuid: currentUser.uuid,
firstName: currentUser.firstName,
lastName: currentUser.lastName,
userName: currentUser.userName,
email: currentUser.email,
emailVerified: DateTime.now().toString(), // Update verification status
hasSubscription: currentUser.hasSubscription,
hasExceedTier: currentUser.hasExceedTier,
planDuration: currentUser.planDuration,
planSubDate: currentUser.planSubDate,
);
await _hiveManager.userModelBox.put(userKey, updatedUser);
}
}
}

Future<void> resendVerificationCode() async {
Future<void> resendVerificationCode({String? email}) async {
final user = _hiveManager.userModelBox.get(userKey);
logger.i('Stored User: $user');
logger.i('User Email: ${user?.email}');

if (user?.email == null) {
if (user?.email == null && email == null) {
logger.e('User email is null');
throw 'User email not found';
}
final response = await _api.postApi(
requestEnd: 'user/auth/send_code',
params: {
'email': user!.email,
'email': email ?? user!.email,
},
);

Expand All @@ -120,6 +141,42 @@ class AuthRepository {
}
}

Future<void> forgotPassword(String email) async {
final response = await _api.postApi(
requestEnd: 'user/auth/forgot_password',
params: {
'email': email,
},
);

logger.i(response);

if (response['success'] != true) {
throw response['message'];
}
// return true;
}

Future<void> resetPassword(String otpCode, String newPassword) async {
final response = await _api.postApi(
requestEnd: 'user/auth/reset_password',
params: {
'otp_code': otpCode,
'new_password': newPassword,
},
);

logger.i(response);

if (response['success'] != true) {
throw response['message'];
}

// return true;
}

// ======================================== //

Future<void> logout() async {
await _hiveManager.userModelBox.delete(tokenKey);
}
Expand All @@ -132,4 +189,41 @@ class AuthRepository {
return _hiveManager.userModelBox.get(tokenKey);
}

Future<UserModel?> fetchUserDetails() async {
try {
final token = _hiveManager.userModelBox.get(tokenKey)?.accessToken;

final response = await _api.getApi(
requestEnd: 'user/fetch_user',
bearer: token,
);

if (response['success'] == false) {

if (response['message'].contains('not verified') &&
response['data'] != null &&
response['data']['user'] != null) {

final user = UserModel.fromJsonUserDetails(response);
await _hiveManager.userModelBox.put(userKey, user);

return user;
}
}

if (response['success'] == true) {
logger.i('Inside success == true block');
final user = UserModel.fromJsonUserDetails(response);
await _hiveManager.userModelBox.put(userKey, user);
await _hiveManager.userModelBox.flush();
return user;
}

return null;
} catch (e) {
logger.e('Error in fetchUserDetails: $e');
throw e.toString();
}
}

}
92 changes: 83 additions & 9 deletions lib/state/cubits/auth_cubit/auth_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class AuthCubit extends Cubit<AuthState> {
required AuthRepository authRepository,
}) : _authRepository = authRepository,
super(AuthInitial()) {
checkAuthStatus();
_loadInitialState();
}

void saveAttemptedRoute(String route) {
Expand All @@ -31,14 +31,52 @@ class AuthCubit extends Cubit<AuthState> {
return route;
}

Future<void> _loadInitialState() async {
final token = _authRepository.getCurrentUserToken();
final user = _authRepository.getCurrentUser();

if (token == null) {
emit(AuthUnauthenticated());
return;
}

if (user != null) {
logger.i('Email of user: ${user.emailVerified}');
if (user.emailVerified == null || user.emailVerified == 'null') {
emit(AuthEmailUnverified(user));
} else {
emit(AuthAuthenticated(user));
}
}

checkAuthStatus();
}

Future<void> checkAuthStatus() async {
final token = _authRepository.getCurrentUserToken();
if (token != null) {
final user = _authRepository.getCurrentUser();
if (user?.emailVerified == null || user?.emailVerified == 'null') {
emit(AuthEmailUnverified(user!));
} else {
emit(AuthAuthenticated(user!));
try {
final freshUserData = await _authRepository.fetchUserDetails();

if (freshUserData?.emailVerified == null ||
freshUserData?.emailVerified == 'null') {
emit(AuthEmailUnverified(freshUserData!));
} else {
emit(AuthAuthenticated(freshUserData!));
}
} catch (e) {

if (e.toString().contains('not verified')) {
final user = _authRepository.getCurrentUser();
if (user != null) {
emit(AuthEmailUnverified(user));
}
} else if (e.toString().contains('suspended')) {
emit(const AuthError('Account suspended'));
await _authRepository.logout();
} else {
emit(AuthError(e.toString()));
}
}
} else {
emit(AuthUnauthenticated());
Expand Down Expand Up @@ -116,11 +154,32 @@ class AuthCubit extends Cubit<AuthState> {
}
}

Future<void> resendVerificationCode() async {
Future<void> resendVerificationCode({String? email}) async {
emit(AuthLoading());
try {
await _authRepository.resendVerificationCode();
emit(AuthEmailUnverified(_authRepository.getCurrentUser()!));
if(email != null) {
await _authRepository.resendVerificationCode(email: email);
emit(AuthInitial());
showSuccess('Code sent');
} else {
await _authRepository.resendVerificationCode();
emit(AuthEmailUnverified(_authRepository.getCurrentUser()!));
showSuccess('Code sent');
}
} catch (e) {
if (e is NoInternetException) {
emit(const AuthError('No internet connection'));
} else {
emit(AuthError(e.toString()));
}
}
}

Future<void> forgotPassword(String email) async {
emit(AuthLoading());
try {
await _authRepository.forgotPassword(email);
emit(AuthSuccess());
showSuccess('Code sent');
} catch (e) {
if (e is NoInternetException) {
Expand All @@ -131,6 +190,21 @@ class AuthCubit extends Cubit<AuthState> {
}
}

Future<void> resetPassword(String otpCode, String newPassword) async {
emit(AuthLoading());
try {
await _authRepository.resetPassword(otpCode, newPassword);
emit(AuthSuccess());
showSuccess('Password reset');
} catch (e) {
if (e is NoInternetException) {
emit(const AuthError('No internet connection'));
} else {
emit(AuthError(e.toString()));
}
}
}

Future<void> logout() async {
try {
await _authRepository.logout();
Expand Down
2 changes: 2 additions & 0 deletions lib/state/cubits/auth_cubit/auth_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class AuthAuthenticated extends AuthState {

class AuthUnauthenticated extends AuthState {}

class AuthSuccess extends AuthState {}

class AuthError extends AuthState {
final String message;
const AuthError(this.message);
Expand Down

0 comments on commit 5c6da98

Please sign in to comment.