feat : change app inspector and exception handling
This commit is contained in:
@@ -1,84 +1,169 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core.dart';
|
||||
|
||||
class ApiHandler {
|
||||
static bool _isRefreshing = false;
|
||||
static bool _isRedirecting = false;
|
||||
static CancelToken globalCancelToken = CancelToken();
|
||||
static CancelToken _globalCancelToken = CancelToken();
|
||||
|
||||
static CancelToken get globalCancelToken => _globalCancelToken;
|
||||
|
||||
static Future<void> reset() async {
|
||||
_isRefreshing = false;
|
||||
_isRedirecting = false;
|
||||
globalCancelToken = CancelToken();
|
||||
_globalCancelToken = CancelToken();
|
||||
}
|
||||
|
||||
|
||||
static void cancelAllRequests(String reason) {
|
||||
if (!globalCancelToken.isCancelled) {
|
||||
globalCancelToken.cancel(reason);
|
||||
if (!_globalCancelToken.isCancelled) {
|
||||
_globalCancelToken.cancel(reason);
|
||||
}
|
||||
globalCancelToken = CancelToken();
|
||||
reset();
|
||||
}
|
||||
|
||||
static bool get isRefreshing => _isRefreshing;
|
||||
static set isRefreshing(bool val) => _isRefreshing = val;
|
||||
|
||||
static bool get isRedirecting => _isRedirecting;
|
||||
static set isRedirecting(bool val) => _isRedirecting = val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef AppAsyncCallback<T> = Future<T> Function();
|
||||
typedef ErrorCallback = Function(dynamic error, StackTrace? stackTrace);
|
||||
typedef VoidCallback = void Function();
|
||||
|
||||
/// this is global safe call function
|
||||
/// A utility function to safely cal l an asynchronous function with error
|
||||
/// handling and optional loading, success, and error messages.
|
||||
Future<void> gSafeCall<T>({
|
||||
Future<T?> gSafeCall<T>({
|
||||
required AppAsyncCallback<T> call,
|
||||
Function(T result)? onSuccess,
|
||||
ErrorCallback? onError,
|
||||
VoidCallback? onComplete,
|
||||
bool showLoading = false,
|
||||
bool showError = false,
|
||||
bool showError = true,
|
||||
bool showSuccess = false,
|
||||
bool showToast = false,
|
||||
bool showSnackBar = false,
|
||||
Function()? onShowLoading,
|
||||
Function()? onHideLoading,
|
||||
Function()? onShowSuccessMessage,
|
||||
Function()? onShowErrorMessage,
|
||||
Function(String message)? onShowErrorMessage,
|
||||
Function(String message)? onShowSuccessMessage,
|
||||
int maxRetries = 0,
|
||||
Duration retryDelay = const Duration(seconds: 1),
|
||||
}) async {
|
||||
try {
|
||||
if (showLoading) (onShowLoading ?? _defaultShowLoading)();
|
||||
final result = await call();
|
||||
if (showSuccess) (onShowSuccessMessage ?? _defaultShowSuccessMessage)();
|
||||
onSuccess?.call(result);
|
||||
} catch (error, stackTrace) {
|
||||
if (showError) (onShowErrorMessage ?? _defaultShowErrorMessage)();
|
||||
onError?.call(error, stackTrace);
|
||||
} finally {
|
||||
if (showLoading) (onHideLoading ?? _defaultHideLoading)();
|
||||
onComplete?.call();
|
||||
int retryCount = 0;
|
||||
|
||||
while (retryCount <= maxRetries) {
|
||||
try {
|
||||
if (showLoading && retryCount == 0) {
|
||||
(onShowLoading ?? _defaultShowLoading)();
|
||||
}
|
||||
|
||||
final result = await call();
|
||||
|
||||
if (showSuccess) {
|
||||
(onShowSuccessMessage ?? _defaultShowSuccessMessage)('عملیات با موفقیت انجام شد');
|
||||
}
|
||||
|
||||
onSuccess?.call(result);
|
||||
return result;
|
||||
|
||||
} catch (error, stackTrace) {
|
||||
retryCount++;
|
||||
|
||||
|
||||
if (error is DioException && error.response?.statusCode == 401) {
|
||||
if (showError) {
|
||||
(onShowErrorMessage ?? _defaultShowErrorMessage)('خطا در احراز هویت');
|
||||
}
|
||||
onError?.call(error, stackTrace);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (retryCount > maxRetries || !_isRetryableError(error)) {
|
||||
if (showError) {
|
||||
final message = _getErrorMessage(error);
|
||||
(onShowErrorMessage ?? _defaultShowErrorMessage)(message);
|
||||
}
|
||||
onError?.call(error, stackTrace);
|
||||
return null;
|
||||
}
|
||||
|
||||
// صبر قبل از retry
|
||||
if (retryCount <= maxRetries) {
|
||||
await Future.delayed(retryDelay);
|
||||
}
|
||||
} finally {
|
||||
if (showLoading && retryCount > maxRetries) {
|
||||
(onHideLoading ?? _defaultHideLoading)();
|
||||
}
|
||||
if (retryCount > maxRetries) {
|
||||
onComplete?.call();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isRetryableError(dynamic error) {
|
||||
if (error is DioException) {
|
||||
// خطاهای قابل retry
|
||||
return error.type == DioExceptionType.connectionTimeout ||
|
||||
error.type == DioExceptionType.receiveTimeout ||
|
||||
error.type == DioExceptionType.sendTimeout ||
|
||||
(error.response?.statusCode != null &&
|
||||
error.response!.statusCode! >= 500);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String _getErrorMessage(dynamic error) {
|
||||
if (error is DioException) {
|
||||
switch (error.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
return 'خطا در اتصال - زمان اتصال تمام شد';
|
||||
case DioExceptionType.receiveTimeout:
|
||||
return 'خطا در دریافت پاسخ';
|
||||
case DioExceptionType.sendTimeout:
|
||||
return 'خطا در ارسال درخواست';
|
||||
case DioExceptionType.badCertificate:
|
||||
return 'خطا در گواهی امنیتی';
|
||||
case DioExceptionType.connectionError:
|
||||
return 'خطا در اتصال به سرور';
|
||||
case DioExceptionType.unknown:
|
||||
return 'خطای نامشخص';
|
||||
default:
|
||||
if (error.response?.statusCode != null) {
|
||||
return 'خطا: ${error.response!.statusCode}';
|
||||
}
|
||||
return 'خطای نامشخص';
|
||||
}
|
||||
}
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
void _defaultShowLoading() {
|
||||
// پیادهسازی پیشفرض
|
||||
// نمایش loading
|
||||
Get.dialog(
|
||||
Center(child: CircularProgressIndicator()),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
}
|
||||
|
||||
void _defaultHideLoading() {
|
||||
// پیادهسازی پیشفرض
|
||||
// مخفی کردن loading
|
||||
if (Get.isDialogOpen == true) {
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
|
||||
void _defaultShowSuccessMessage() {
|
||||
// پیادهسازی پیشفرض
|
||||
void _defaultShowSuccessMessage(String message) {
|
||||
Get.snackbar(
|
||||
'موفقیت',
|
||||
message,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
|
||||
void _defaultShowErrorMessage() {
|
||||
// پیادهسازی پیشفرض
|
||||
}
|
||||
|
||||
bool isTokenExpiredError(dynamic error) {
|
||||
return error is DioException && error.response?.statusCode == 401;
|
||||
void _defaultShowErrorMessage(String message) {
|
||||
Get.snackbar(
|
||||
'خطا',
|
||||
message,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user