- Introduced a new `logSafe` function for consistent logging with sensitivity handling. - Replaced direct logger calls with `logSafe` in `api_service.dart`, `app_initializer.dart`, `auth_service.dart`, `permission_service.dart`, and `my_image_compressor.dart`. - Enhanced error handling and logging in various service methods to capture exceptions and provide more context. - Updated image compression logging to include quality and size metrics. - Improved app initialization logging to capture success and error states. - Ensured sensitive information is not logged directly.
292 lines
8.0 KiB
Dart
292 lines
8.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:marco/helpers/services/auth_service.dart';
|
|
import 'package:marco/helpers/services/storage/local_storage.dart';
|
|
import 'package:marco/helpers/widgets/my_form_validator.dart';
|
|
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
|
import 'package:marco/view/dashboard/dashboard_screen.dart';
|
|
import 'package:marco/helpers/services/app_logger.dart';
|
|
|
|
class MPINController extends GetxController {
|
|
final MyFormValidator basicValidator = MyFormValidator();
|
|
final isNewUser = false.obs;
|
|
final RxBool isLoading = false.obs;
|
|
final formKey = GlobalKey<FormState>();
|
|
|
|
final digitControllers = List.generate(6, (_) => TextEditingController());
|
|
final focusNodes = List.generate(6, (_) => FocusNode());
|
|
|
|
final retypeControllers = List.generate(6, (_) => TextEditingController());
|
|
final retypeFocusNodes = List.generate(6, (_) => FocusNode());
|
|
final RxInt failedAttempts = 0.obs;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
final bool hasMpin = LocalStorage.getIsMpin();
|
|
isNewUser.value = !hasMpin;
|
|
logSafe("onInit called. isNewUser: ${isNewUser.value}");
|
|
}
|
|
|
|
void onDigitChanged(String value, int index, {bool isRetype = false}) {
|
|
logSafe("onDigitChanged -> index: $index, value: $value, isRetype: $isRetype", sensitive: true);
|
|
final nodes = isRetype ? retypeFocusNodes : focusNodes;
|
|
if (value.isNotEmpty && index < 5) {
|
|
nodes[index + 1].requestFocus();
|
|
} else if (value.isEmpty && index > 0) {
|
|
nodes[index - 1].requestFocus();
|
|
}
|
|
}
|
|
|
|
Future<void> onSubmitMPIN() async {
|
|
logSafe("onSubmitMPIN triggered");
|
|
|
|
if (!formKey.currentState!.validate()) {
|
|
logSafe("Form validation failed", level: LogLevel.warning);
|
|
return;
|
|
}
|
|
|
|
final enteredMPIN = digitControllers.map((c) => c.text).join();
|
|
logSafe("Entered MPIN: $enteredMPIN", sensitive: true);
|
|
|
|
if (enteredMPIN.length < 6) {
|
|
_showError("Please enter all 6 digits.");
|
|
return;
|
|
}
|
|
|
|
if (isNewUser.value) {
|
|
final retypeMPIN = retypeControllers.map((c) => c.text).join();
|
|
logSafe("Retyped MPIN: $retypeMPIN", sensitive: true);
|
|
|
|
if (retypeMPIN.length < 6) {
|
|
_showError("Please enter all 6 digits in Retype MPIN.");
|
|
return;
|
|
}
|
|
|
|
if (enteredMPIN != retypeMPIN) {
|
|
_showError("MPIN and Retype MPIN do not match.");
|
|
clearFields();
|
|
clearRetypeFields();
|
|
return;
|
|
}
|
|
|
|
logSafe("MPINs matched. Proceeding to generate MPIN.");
|
|
final bool success = await generateMPIN(mpin: enteredMPIN);
|
|
|
|
if (success) {
|
|
logSafe("MPIN generation successful.");
|
|
showAppSnackbar(
|
|
title: "Success",
|
|
message: "MPIN generated successfully. Please login again.",
|
|
type: SnackbarType.success,
|
|
);
|
|
await LocalStorage.logout();
|
|
} else {
|
|
logSafe("MPIN generation failed.", level: LogLevel.warning);
|
|
clearFields();
|
|
clearRetypeFields();
|
|
}
|
|
} else {
|
|
logSafe("Existing user. Proceeding to verify MPIN.");
|
|
await verifyMPIN();
|
|
}
|
|
}
|
|
|
|
Future<void> onForgotMPIN() async {
|
|
logSafe("onForgotMPIN called");
|
|
isNewUser.value = true;
|
|
clearFields();
|
|
clearRetypeFields();
|
|
}
|
|
|
|
void switchToEnterMPIN() {
|
|
logSafe("switchToEnterMPIN called");
|
|
isNewUser.value = false;
|
|
clearFields();
|
|
clearRetypeFields();
|
|
}
|
|
|
|
void _showError(String message) {
|
|
logSafe("ERROR: $message", level: LogLevel.error);
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: message,
|
|
type: SnackbarType.error,
|
|
);
|
|
}
|
|
|
|
void _navigateToDashboard({String? message}) {
|
|
if (message != null) {
|
|
logSafe("Navigating to Dashboard with message: $message");
|
|
showAppSnackbar(
|
|
title: "Success",
|
|
message: message,
|
|
type: SnackbarType.success,
|
|
);
|
|
}
|
|
Get.offAll(() => const DashboardScreen());
|
|
}
|
|
|
|
void clearFields() {
|
|
logSafe("clearFields called");
|
|
for (final c in digitControllers) {
|
|
c.clear();
|
|
}
|
|
focusNodes.first.requestFocus();
|
|
}
|
|
|
|
void clearRetypeFields() {
|
|
logSafe("clearRetypeFields called");
|
|
for (final c in retypeControllers) {
|
|
c.clear();
|
|
}
|
|
retypeFocusNodes.first.requestFocus();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
logSafe("onClose called");
|
|
for (final controller in digitControllers) {
|
|
controller.dispose();
|
|
}
|
|
for (final node in focusNodes) {
|
|
node.dispose();
|
|
}
|
|
for (final controller in retypeControllers) {
|
|
controller.dispose();
|
|
}
|
|
for (final node in retypeFocusNodes) {
|
|
node.dispose();
|
|
}
|
|
super.onClose();
|
|
}
|
|
|
|
Future<bool> generateMPIN({
|
|
required String mpin,
|
|
}) async {
|
|
try {
|
|
isLoading.value = true;
|
|
logSafe("generateMPIN started");
|
|
|
|
final employeeInfo = LocalStorage.getEmployeeInfo();
|
|
final String? employeeId = employeeInfo?.id;
|
|
|
|
if (employeeId == null || employeeId.isEmpty) {
|
|
isLoading.value = false;
|
|
_showError("Missing employee ID.");
|
|
return false;
|
|
}
|
|
|
|
logSafe("Calling AuthService.generateMpin for employeeId: $employeeId", sensitive: true);
|
|
|
|
final response = await AuthService.generateMpin(
|
|
employeeId: employeeId,
|
|
mpin: mpin,
|
|
);
|
|
|
|
isLoading.value = false;
|
|
|
|
if (response == null) {
|
|
logSafe("MPIN generated successfully");
|
|
|
|
showAppSnackbar(
|
|
title: "Success",
|
|
message: "MPIN generated successfully. Please login again.",
|
|
type: SnackbarType.success,
|
|
);
|
|
|
|
await LocalStorage.logout();
|
|
|
|
return true;
|
|
} else {
|
|
logSafe("MPIN generation returned error: $response", level: LogLevel.warning);
|
|
showAppSnackbar(
|
|
title: "MPIN Generation Failed",
|
|
message: "Please check your inputs.",
|
|
type: SnackbarType.error,
|
|
);
|
|
basicValidator.addErrors(response);
|
|
basicValidator.validateForm();
|
|
basicValidator.clearErrors();
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
isLoading.value = false;
|
|
logSafe("Exception in generateMPIN", level: LogLevel.error, error: e);
|
|
_showError("Failed to generate MPIN.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<void> verifyMPIN() async {
|
|
logSafe("verifyMPIN triggered");
|
|
|
|
final enteredMPIN = digitControllers.map((c) => c.text).join();
|
|
logSafe("Entered MPIN: $enteredMPIN", sensitive: true);
|
|
|
|
if (enteredMPIN.length < 6) {
|
|
_showError("Please enter all 6 digits.");
|
|
return;
|
|
}
|
|
|
|
final mpinToken = await LocalStorage.getMpinToken();
|
|
|
|
if (mpinToken == null || mpinToken.isEmpty) {
|
|
_showError("Missing MPIN token. Please log in again.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
isLoading.value = true;
|
|
|
|
final response = await AuthService.verifyMpin(
|
|
mpin: enteredMPIN,
|
|
mpinToken: mpinToken,
|
|
);
|
|
|
|
isLoading.value = false;
|
|
|
|
if (response == null) {
|
|
logSafe("MPIN verified successfully");
|
|
await LocalStorage.setBool('mpin_verified', true);
|
|
|
|
showAppSnackbar(
|
|
title: "Success",
|
|
message: "MPIN Verified Successfully",
|
|
type: SnackbarType.success,
|
|
);
|
|
_navigateToDashboard();
|
|
} else {
|
|
final errorMessage = response["error"] ?? "Invalid MPIN";
|
|
logSafe("MPIN verification failed: $errorMessage", level: LogLevel.warning);
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: errorMessage,
|
|
type: SnackbarType.error,
|
|
);
|
|
clearFields();
|
|
onInvalidMPIN();
|
|
}
|
|
} catch (e) {
|
|
isLoading.value = false;
|
|
logSafe("Exception in verifyMPIN", level: LogLevel.error, error: e);
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: "Something went wrong. Please try again.",
|
|
type: SnackbarType.error,
|
|
);
|
|
}
|
|
}
|
|
|
|
void onInvalidMPIN() {
|
|
failedAttempts.value++;
|
|
if (failedAttempts.value >= 3) {
|
|
showAppSnackbar(
|
|
title: "Error",
|
|
message: "Too many failed attempts. Consider logging in again.",
|
|
type: SnackbarType.error,
|
|
);
|
|
}
|
|
}
|
|
}
|