Merge branch 'responsive-error-handling' into 'master'

Refactor: refactored code to remove print statements, updated widget...

See merge request profile-image/kedaireka/polinema-adapative-learning/mobile-adaptive-learning!26
This commit is contained in:
Naresh Pratista 2024-12-05 09:58:17 +00:00
commit 8e770d2e6e
29 changed files with 94 additions and 160 deletions

View File

@ -8,8 +8,8 @@ class DioClient {
DioClient() {
_dio.options.baseUrl = baseUrl;
_dio.options.connectTimeout = const Duration(seconds: 5);
_dio.options.receiveTimeout = const Duration(seconds: 10);
_dio.options.connectTimeout = const Duration(seconds: 10);
_dio.options.receiveTimeout = const Duration(seconds: 15);
}
Future<Response> refreshAccessToken(String refreshToken) async {

View File

@ -1,4 +1,3 @@
import 'package:dio/dio.dart';
import 'package:english_learning/core/services/dio_client.dart';
import 'package:english_learning/features/home/models/completed_topics_model.dart';
@ -8,38 +7,28 @@ class CompletedTopicsRepository {
CompletedTopicsRepository(this._dioClient);
Future<List<CompletedTopic>> getCompletedTopics(String token) async {
try {
final response = await _dioClient.getCompletedTopics(token);
final response = await _dioClient.getCompletedTopics(token);
// Tambahkan pengecekan status code dan payload
if (response.statusCode == 200) {
// Cek apakah payload null atau bukan list
if (response.data['payload'] == null) {
return []; // Kembalikan list kosong jika payload null
}
// Pastikan payload adalah list
final dynamic payloadData = response.data['payload'];
if (payloadData is List) {
return payloadData
.map((data) => CompletedTopic.fromJson(data))
.toList();
} else {
return []; // Kembalikan list kosong jika payload bukan list
}
} else {
// Tangani status code selain 200
return [];
// Tambahkan pengecekan status code dan payload
if (response.statusCode == 200) {
// Cek apakah payload null atau bukan list
if (response.data['payload'] == null) {
return []; // Kembalikan list kosong jika payload null
}
} on DioException catch (e) {
// Log error jika perlu
print('Network error: ${e.message}');
return []; // Kembalikan list kosong untuk error jaringan
} catch (e) {
// Log error tidak terduga
print('Unexpected error: $e');
return []; // Kembalikan list kosong untuk error lainnya
// Pastikan payload adalah list
final dynamic payloadData = response.data['payload'];
if (payloadData is List) {
return payloadData
.map((data) => CompletedTopic.fromJson(data))
.toList();
} else {
return []; // Kembalikan list kosong jika payload bukan list
}
} else {
// Tangani status code selain 200
return [];
}
}
}

View File

@ -28,7 +28,6 @@ class LevelRepository {
throw Exception('Failed to load levels: ${response.statusCode}');
}
} catch (e) {
print('Error in LevelRepository: $e');
rethrow;
}
}

View File

@ -15,7 +15,6 @@ class SectionRepository {
throw Exception('Failed to load sections: ${response.statusCode}');
}
} catch (e) {
print('Error in SectionRepository: $e');
rethrow;
}
}

View File

@ -15,7 +15,6 @@ class TopicRepository {
throw Exception('Failed to load topics: ${response.statusCode}');
}
} catch (e) {
print('Error in TopicRepository: $e');
rethrow;
}
}

View File

@ -47,7 +47,6 @@ class UserRepository {
}
return await dioClient.updateUserProfile(id, formData, token);
} catch (e) {
print('Update Profile error: $e');
rethrow;
}
}
@ -67,7 +66,6 @@ class UserRepository {
};
return await dioClient.updatePassword(id, data, token);
} catch (e) {
print('Update Password error: $e');
rethrow;
}
}

View File

@ -14,11 +14,11 @@ class ConfirmPasswordFieldWidget extends StatefulWidget {
});
@override
_ConfirmPasswordFieldWidgetState createState() =>
_ConfirmPasswordFieldWidgetState();
ConfirmPasswordFieldWidgetState createState() =>
ConfirmPasswordFieldWidgetState();
}
class _ConfirmPasswordFieldWidgetState
class ConfirmPasswordFieldWidgetState
extends State<ConfirmPasswordFieldWidget> {
late FocusNode _focusNode;

View File

@ -10,10 +10,10 @@ class EmailFieldWidget extends StatefulWidget {
{super.key, required this.labelText, required this.hintText});
@override
_EmailFieldWidgetState createState() => _EmailFieldWidgetState();
EmailFieldWidgetState createState() => EmailFieldWidgetState();
}
class _EmailFieldWidgetState extends State<EmailFieldWidget> {
class EmailFieldWidgetState extends State<EmailFieldWidget> {
late FocusNode _focusNode;
@override

View File

@ -13,10 +13,10 @@ class FullNameFieldWidget extends StatefulWidget {
});
@override
_FullNameFieldWidgetState createState() => _FullNameFieldWidgetState();
FullNameFieldWidgetState createState() => FullNameFieldWidgetState();
}
class _FullNameFieldWidgetState extends State<FullNameFieldWidget> {
class FullNameFieldWidgetState extends State<FullNameFieldWidget> {
late FocusNode _focusNode;
@override

View File

@ -13,10 +13,10 @@ class NISNFieldWidget extends StatefulWidget {
});
@override
_NISNFieldWidgetState createState() => _NISNFieldWidgetState();
NISNFieldWidgetState createState() => NISNFieldWidgetState();
}
class _NISNFieldWidgetState extends State<NISNFieldWidget> {
class NISNFieldWidgetState extends State<NISNFieldWidget> {
late FocusNode _focusNode;
@override

View File

@ -14,10 +14,10 @@ class PasswordFieldWidget extends StatefulWidget {
});
@override
_PasswordFieldWidgetState createState() => _PasswordFieldWidgetState();
PasswordFieldWidgetState createState() => PasswordFieldWidgetState();
}
class _PasswordFieldWidgetState extends State<PasswordFieldWidget> {
class PasswordFieldWidgetState extends State<PasswordFieldWidget> {
late FocusNode _focusNode;
@override

View File

@ -29,18 +29,14 @@ class UserProvider with ChangeNotifier {
}
Future<void> _loadLoginStatus() async {
try {
_jwtToken = await _userRepository.getToken();
if (_jwtToken != null) {
if (JwtDecoder.isExpired(_jwtToken!)) {
await logout();
} else {
_isLoggedIn = true;
await _loadUserData();
}
_jwtToken = await _userRepository.getToken();
if (_jwtToken != null) {
if (JwtDecoder.isExpired(_jwtToken!)) {
await logout();
} else {
_isLoggedIn = true;
await _loadUserData();
}
} catch (e) {
print('Error loading login status: $e');
}
notifyListeners();
}
@ -53,18 +49,14 @@ class UserProvider with ChangeNotifier {
}
Future<void> refreshUserData() async {
try {
if (_jwtToken != null) {
final response = await _userRepository.getMe(_jwtToken!);
if (response.statusCode == 200) {
_userData = response.data['payload'];
// Save all user data securely
await _userRepository.saveUserData(_userData!);
notifyListeners();
}
if (_jwtToken != null) {
final response = await _userRepository.getMe(_jwtToken!);
if (response.statusCode == 200) {
_userData = response.data['payload'];
// Save all user data securely
await _userRepository.saveUserData(_userData!);
notifyListeners();
}
} catch (e) {
print('Error refreshing user data: $e');
}
}
@ -96,13 +88,11 @@ class UserProvider with ChangeNotifier {
await refreshUserData();
return true;
} else if (response.statusCode == 403) {
print('User is not validated: ${response.data['message']}');
return false;
}
return false;
} catch (e) {
print('Login error: $e');
return false;
} finally {
setLoading(false);
@ -130,7 +120,6 @@ class UserProvider with ChangeNotifier {
}
return false;
} catch (e) {
print('Registration error: $e');
return false;
}
}
@ -151,7 +140,6 @@ class UserProvider with ChangeNotifier {
}
return false;
} catch (e) {
print('Logout error: $e');
_isLoggedIn = false;
_jwtToken = null;
_userData = null;
@ -168,15 +156,12 @@ class UserProvider with ChangeNotifier {
final response = await _userRepository.forgotPassword(email);
if (response.statusCode == 200) {
print("Password reset email sent successfully!");
return true;
} else if (response.statusCode == 404) {
print("Email is not registered!");
return false;
}
return false;
} catch (e) {
print('Forgot Password error: $e');
return false;
}
}
@ -205,7 +190,6 @@ class UserProvider with ChangeNotifier {
}
return false;
} catch (e) {
print('Error refreshing token: $e');
return false;
}
}
@ -225,7 +209,6 @@ class UserProvider with ChangeNotifier {
}
return null;
} catch (e) {
print('Error in getValidToken: $e');
return null;
}
}
@ -251,7 +234,6 @@ class UserProvider with ChangeNotifier {
}
}
} catch (e) {
print('Error updating user profile: $e');
rethrow;
}
}
@ -273,13 +255,11 @@ class UserProvider with ChangeNotifier {
token,
);
if (response.statusCode == 200) {
print("Password updated successfully!");
return true;
}
}
return false;
} catch (e) {
print('Error updating password: $e');
return false;
}
}
@ -290,13 +270,11 @@ class UserProvider with ChangeNotifier {
if (token != null) {
final response = await _userRepository.reportIssue(report, token);
if (response.statusCode == 201) {
print("Issue reported successfully!");
return true;
}
}
return false;
} catch (e) {
print('Error reporting issue: $e');
return false;
}
}

View File

@ -43,7 +43,7 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SigninScreen(),
builder: (context) => const SigninScreen(),
),
);
},
@ -145,7 +145,7 @@ class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SigninScreen()),
builder: (context) => const SigninScreen()),
);
},
child: Text(

View File

@ -117,6 +117,9 @@ class _SigninScreenState extends State<SigninScreen> {
@override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final screenHeight = mediaQuery.size.height;
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
@ -133,7 +136,7 @@ class _SigninScreenState extends State<SigninScreen> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 24),
SizedBox(height: screenHeight * 0.02),
Hero(
tag: 'welcome_text',
child: Material(
@ -155,6 +158,7 @@ class _SigninScreenState extends State<SigninScreen> {
),
),
const SizedBox(height: 26),
// SizedBox(height: screenHeight * 0.05),
Center(
child: Hero(
tag: 'login_illustration',
@ -165,6 +169,7 @@ class _SigninScreenState extends State<SigninScreen> {
),
),
const SizedBox(height: 30),
// SizedBox(height: screenHeight * 0.05),
CustomFieldWidget(
fieldName: 'login',
controller: _loginController,
@ -314,7 +319,7 @@ class _SigninScreenState extends State<SigninScreen> {
PageRouteBuilder(
pageBuilder: (context, animation,
secondaryAnimation) =>
SignupScreen(),
const SignupScreen(),
transitionsBuilder: (context, animation,
secondaryAnimation, child) {
return FadeTransition(
@ -341,7 +346,7 @@ class _SigninScreenState extends State<SigninScreen> {
),
],
),
const SizedBox(height: 16),
SizedBox(height: screenHeight * 0.02),
],
);
},

View File

@ -8,10 +8,10 @@ class LoginPasswordField extends StatefulWidget {
const LoginPasswordField({super.key});
@override
_LoginPasswordFieldState createState() => _LoginPasswordFieldState();
LoginPasswordFieldState createState() => LoginPasswordFieldState();
}
class _LoginPasswordFieldState extends State<LoginPasswordField> {
class LoginPasswordFieldState extends State<LoginPasswordField> {
late FocusNode _focusNode;
@override

View File

@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SignupScreen extends StatefulWidget {
SignupScreen({super.key});
const SignupScreen({super.key});
@override
State<SignupScreen> createState() => _SignupScreenState();
@ -88,7 +88,7 @@ class _SignupScreenState extends State<SignupScreen> {
// Navigate to login screen
Navigator.of(dialogContext).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => SigninScreen(),
builder: (context) => const SigninScreen(),
),
(Route<dynamic> route) => false,
);
@ -288,7 +288,7 @@ class _SignupScreenState extends State<SignupScreen> {
}
},
),
const SizedBox(height: 8),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -303,7 +303,7 @@ class _SignupScreenState extends State<SignupScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SigninScreen()),
builder: (context) => const SigninScreen()),
);
},
child: Text(

View File

@ -37,7 +37,7 @@ class _HistoryScreenState extends State<HistoryScreen> {
try {
await historyProvider.fetchLearningHistory(userProvider.jwtToken!);
} catch (e) {
print('Error initializing history: $e');
rethrow;
} finally {
setState(() {
_isInitialLoading = false;
@ -109,7 +109,6 @@ class _HistoryScreenState extends State<HistoryScreen> {
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 30.0,
),
child: Column(
children: [

View File

@ -8,7 +8,7 @@ class NoProgressCard extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
decoration: const BoxDecoration(
color: AppColors.whiteColor,
),
);

View File

@ -181,7 +181,6 @@ class ExerciseProvider extends ChangeNotifier {
void _selectRightOption(int exerciseIndex, String option) {
if (_activeLeftOption == null) {
print("Please select a left option first.");
return;
}
@ -250,30 +249,23 @@ class ExerciseProvider extends ChangeNotifier {
void goToExercise(int index) {
if (index >= 0 && index < _exercises.length) {
_currentExerciseIndex = index;
print('Going to exercise: $_currentExerciseIndex'); // Debug print
notifyListeners();
}
}
void nextQuestion() {
print('Current index before next: $_currentExerciseIndex'); // Debug print
if (_currentExerciseIndex <
(_exercises.isNotEmpty
? _exercises.length - 1
: _reviewExercises.length - 1)) {
_currentExerciseIndex++;
print('Moving to next question: $_currentExerciseIndex'); // Debug print
notifyListeners();
}
}
void previousQuestion() {
print(
'Current index before previous: $_currentExerciseIndex'); // Debug print
if (_currentExerciseIndex > 0) {
_currentExerciseIndex--;
print(
'Moving to previous question: $_currentExerciseIndex'); // Debug print
notifyListeners();
}
}
@ -334,7 +326,7 @@ class ExerciseProvider extends ChangeNotifier {
_answers = List.generate(_exercises.length, (index) => '');
initializeAnswers();
} catch (e) {
print('Error fetching exercises: $e');
rethrow;
} finally {
_isLoading = false;
notifyListeners();
@ -383,7 +375,6 @@ class ExerciseProvider extends ChangeNotifier {
}
}
} catch (e) {
print('Error fetching review exercises: $e');
rethrow;
} finally {
_isLoading = false;
@ -480,12 +471,9 @@ class ExerciseProvider extends ChangeNotifier {
'No answers to submit. Please answer at least one question.');
}
print('Submitting answers to repository');
final result = await _repository.submitAnswersAndGetScore(
answersToSubmit, _studentLearningId!, token);
print('Repository response: $result');
return {
'CURRENT_LEVEL_NAME': result['CURRENT_LEVEL_NAME'],
'NEXT_LEARNING_NAME': result['NEXT_LEARNING_NAME'],
@ -493,7 +481,6 @@ class ExerciseProvider extends ChangeNotifier {
'IS_PASS': result['IS_PASS'],
};
} catch (e) {
print('Error in submitAnswersAndGetScore: $e');
rethrow;
}
}
@ -507,13 +494,10 @@ class ExerciseProvider extends ChangeNotifier {
if (_studentLearningId == null) {
throw Exception('Student Learning ID is not set');
}
print('Submitting feedback for stdLearningId: $_studentLearningId');
final result = await _repository.submitFeedback(
_studentLearningId!, feedback, token);
print('Feedback submitted successfully');
return result;
} catch (e) {
print('Error submitting feedback: $e');
rethrow;
}
}

View File

@ -37,7 +37,6 @@ class ExerciseNavigator extends StatelessWidget {
Future<void> submitAnswers() async {
try {
final result = await exerciseProvider.submitAnswersAndGetScore();
print('Submit result: $result');
if (context.mounted) {
Navigator.of(context).pushReplacement(
@ -55,7 +54,6 @@ class ExerciseNavigator extends StatelessWidget {
);
}
} catch (e) {
print('Error submitting answers: $e');
if (context.mounted) {
CustomSnackBar.show(
context,

View File

@ -363,7 +363,7 @@ class _MatchingPairsQuestionState extends State<MatchingPairsQuestion> {
],
),
);
}).toList(),
}),
],
);
}

View File

@ -80,10 +80,7 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
});
try {
final exerciseProvider =
Provider.of<ExerciseProvider>(context, listen: false);
final result =
await exerciseProvider.submitFeedback(_controller.text.trim());
Provider.of<ExerciseProvider>(context, listen: false);
if (!mounted) return;
@ -91,27 +88,19 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
_isLoading = false;
});
if (result != null) {
await showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) => PopScope(
canPop: false,
child: FeedbackDialog(
onSubmit: () {
Navigator.pop(dialogContext);
_navigateToLevelList();
},
),
await showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext dialogContext) => PopScope(
canPop: false,
child: FeedbackDialog(
onSubmit: () {
Navigator.pop(dialogContext);
_navigateToLevelList();
},
),
);
} else {
CustomSnackBar.show(
context,
message: 'Failed to submit feedback. Please try again.',
isError: true,
);
}
),
);
} catch (e) {
if (!mounted) return;

View File

@ -77,7 +77,6 @@ class VideoPlayerWidgetState extends State<VideoPlayerWidget> {
}
return null;
} catch (e) {
print('Error extracting YouTube ID: $e');
return null;
}
}

View File

@ -1,4 +1,5 @@
import 'package:english_learning/core/services/constants.dart';
import 'package:english_learning/core/widgets/custom_snackbar.dart';
import 'package:english_learning/core/widgets/loading/shimmer_loading_widget.dart';
import 'package:english_learning/features/auth/provider/user_provider.dart';
import 'package:english_learning/features/learning/modules/level/screens/level_list_screen.dart';
@ -46,12 +47,15 @@ class _TopicsListScreenState extends State<TopicsListScreen> {
try {
await Provider.of<TopicProvider>(context, listen: false)
.fetchTopics(widget.sectionId, token);
print('Topics fetched successfully');
} catch (e) {
print('Error fetching topics: $e');
rethrow;
}
} else {
print('No valid token found. User might need to log in.');
CustomSnackBar.show(
context,
message: 'No valid token found. User might need to log in.',
isError: true,
);
}
}

View File

@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart';
class SectionProvider extends ChangeNotifier {
final SectionRepository _repository = SectionRepository();
List<Section> _sections = [];
bool _isLoading = false;
final bool _isLoading = false;
dynamic _error;
List<Section> get sections => _sections;

View File

@ -40,7 +40,7 @@ class _LearningScreenState extends State<LearningScreen>
await sectionProvider.fetchSections(token);
}
} catch (e) {
print('Error initializing sections: $e');
rethrow;
} finally {
setState(() {
_isInitialLoading = false;

View File

@ -117,7 +117,7 @@ class _OnBoardingScreenState extends State<OnBoardingScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SignupScreen()),
builder: (context) => const SignupScreen()),
);
},
),

View File

@ -114,7 +114,8 @@ class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => SigninScreen()),
builder: (context) =>
const SigninScreen()),
(route) => false,
);
},

View File

@ -23,14 +23,6 @@ class SettingsScreen extends StatefulWidget {
}
class _SettingsScreenState extends State<SettingsScreen> {
// @override
// void initState() {
// super.initState();
// WidgetsBinding.instance.addPostFrameCallback((_) {
// Provider.of<UserProvider>(context, listen: false).refreshUserData();
// });
// }
@override
Widget build(BuildContext context) {
return Scaffold(
@ -186,7 +178,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
if (success) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => SigninScreen()),
builder: (context) =>
const SigninScreen()),
(Route<dynamic> route) => false,
);
} else {