Updated theme colors, added error code handling in user provider, and modified signin and signup screens to handle specific errors and timeouts.

This commit is contained in:
Naresh Pratista 2024-11-13 14:21:56 +07:00
parent 8a2aa421e8
commit 64b0b39deb
4 changed files with 85 additions and 26 deletions

View File

@ -83,9 +83,9 @@ class AppTextStyles {
ThemeData appTheme() { ThemeData appTheme() {
return ThemeData( return ThemeData(
textSelectionTheme: const TextSelectionThemeData( textSelectionTheme: const TextSelectionThemeData(
cursorColor: AppColors.primaryColor, cursorColor: AppColors.blackColor,
selectionColor: AppColors.primaryColor, selectionColor: AppColors.blueColor,
selectionHandleColor: AppColors.primaryColor, selectionHandleColor: AppColors.blueColor,
), ),
textTheme: GoogleFonts.interTextTheme(), textTheme: GoogleFonts.interTextTheme(),
fontFamily: GoogleFonts.inter().fontFamily, fontFamily: GoogleFonts.inter().fontFamily,

View File

@ -10,12 +10,14 @@ class UserProvider with ChangeNotifier {
Map<String, dynamic>? _userData; Map<String, dynamic>? _userData;
File? _selectedImage; File? _selectedImage;
bool _isLoading = false; bool _isLoading = false;
int? _errorCode;
bool get isLoading => _isLoading; bool get isLoading => _isLoading;
bool get isLoggedIn => _isLoggedIn; bool get isLoggedIn => _isLoggedIn;
String? get jwtToken => _jwtToken; String? get jwtToken => _jwtToken;
Map<String, dynamic>? get userData => _userData; Map<String, dynamic>? get userData => _userData;
File? get selectedImage => _selectedImage; File? get selectedImage => _selectedImage;
int? get errorCode => _errorCode;
UserProvider() { UserProvider() {
_loadLoginStatus(); _loadLoginStatus();
@ -68,12 +70,15 @@ class UserProvider with ChangeNotifier {
Future<bool> login({required String email, required String password}) async { Future<bool> login({required String email, required String password}) async {
setLoading(true); setLoading(true);
_errorCode = null;
try { try {
final response = await _userRepository.loginUser({ final response = await _userRepository.loginUser({
'EMAIL': email, 'EMAIL': email,
'PASSWORD': password, 'PASSWORD': password,
}); });
_errorCode = response.statusCode;
if (response.statusCode == 200 && if (response.statusCode == 200 &&
response.data['payload']['TOKEN'] != null) { response.data['payload']['TOKEN'] != null) {
String token = response.data['payload']['TOKEN']; String token = response.data['payload']['TOKEN'];
@ -87,7 +92,12 @@ class UserProvider with ChangeNotifier {
_isLoggedIn = true; _isLoggedIn = true;
await refreshUserData(); await refreshUserData();
return true; return true;
} else if (response.statusCode == 403) {
// Khusus untuk error 403 (user belum divalidasi)
print('User is not validated: ${response.data['message']}');
return false;
} }
return false; return false;
} catch (e) { } catch (e) {
print('Login error: $e'); print('Login error: $e');

View File

@ -169,6 +169,15 @@ class SigninScreen extends StatelessWidget {
_emailController.clear(); _emailController.clear();
_passwordController.clear(); _passwordController.clear();
}); });
} else {
// Handle specific error for unverified user
if (userProvider.errorCode == 403) {
CustomSnackBar.show(
context,
message:
'User is not validated! Please verify your email first.',
isError: true,
);
} else { } else {
CustomSnackBar.show( CustomSnackBar.show(
context, context,
@ -178,6 +187,7 @@ class SigninScreen extends StatelessWidget {
); );
} }
} }
}
}, },
), ),
const SizedBox(height: 8), const SizedBox(height: 8),

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:english_learning/core/widgets/custom_snackbar.dart'; import 'package:english_learning/core/widgets/custom_snackbar.dart';
import 'package:english_learning/features/auth/provider/user_provider.dart'; import 'package:english_learning/features/auth/provider/user_provider.dart';
import 'package:english_learning/features/auth/provider/validator_provider.dart'; import 'package:english_learning/features/auth/provider/validator_provider.dart';
@ -38,46 +40,83 @@ class _SignupScreenState extends State<SignupScreen> {
BuildContext context, BuildContext context,
ValidatorProvider validatorProvider, ValidatorProvider validatorProvider,
) async { ) async {
if (isLoading) return; // Prevent multiple submissions
setState(() { setState(() {
isLoading = true; isLoading = true;
}); });
try { try {
// Attempt registration first // Attempt registration with timeout
final isSuccess = await userProvider.register( final isSuccess = await userProvider
.register(
name: _nameController.text, name: _nameController.text,
email: _emailController.text, email: _emailController.text,
nisn: _nisnController.text, nisn: _nisnController.text,
password: _passwordController.text, password: _passwordController.text,
confirmPassword: _passwordController.text, confirmPassword: _passwordController.text,
)
.timeout(
const Duration(seconds: 30),
onTimeout: () {
throw TimeoutException(
'Registration request timed out. Please try again.');
},
); );
// Remove loading dialog
Navigator.of(context).pop();
if (isSuccess) { if (isSuccess) {
// Show verification dialog // Show success verification dialog
showDialog( if (!context.mounted) return;
await showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (BuildContext context) { builder: (BuildContext dialogContext) {
return SignupVerification( return PopScope(
canPop: false,
child: SignupVerification(
onSubmit: () { onSubmit: () {
// Navigate to sign in screen // Clear the form fields
Navigator.pushReplacement( _emailController.clear();
context, _passwordController.clear();
_nisnController.clear();
_nameController.clear();
validatorProvider.resetFields();
// Navigate to login screen
Navigator.of(dialogContext).pushAndRemoveUntil(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => SigninScreen(), builder: (context) => SigninScreen(),
), ),
(Route<dynamic> route) => false,
);
},
),
); );
}, },
); );
},
);
} else { } else {
// Show error message if (!context.mounted) return;
CustomSnackBar.show( CustomSnackBar.show(
context, context,
message: 'Registration failed', message: 'Registration failed. Please try again.',
isError: true, isError: true,
); );
} }
} catch (e) {
// Remove loading dialog if it's showing
if (Navigator.canPop(context)) {
Navigator.of(context).pop();
}
if (!context.mounted) return;
CustomSnackBar.show(
context,
message: 'An error occurred: ${e.toString()}',
isError: true,
);
} finally { } finally {
setState(() { setState(() {
isLoading = false; isLoading = false;