diff --git a/lib/infrastructure/di/di.dart b/lib/infrastructure/di/di.dart index 1058c46..8ef306c 100644 --- a/lib/infrastructure/di/di.dart +++ b/lib/infrastructure/di/di.dart @@ -5,9 +5,17 @@ import 'package:rasadyar_core/core.dart'; final di = GetIt.instance; -Future setupInjection() async{ - await setupAuthDI(); +Future setupPreInjection() async{ await setupAllCoreProvider(); + await setupAuthDI(); + + + +} + +Future setupInjection() async{ + + await setupChickenDI(); } diff --git a/lib/infrastructure/service/app_navigation_observer.dart b/lib/infrastructure/service/app_navigation_observer.dart new file mode 100644 index 0000000..5d4a8dc --- /dev/null +++ b/lib/infrastructure/service/app_navigation_observer.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_app/infrastructure/di/di.dart'; +import 'package:rasadyar_chicken/chicken.dart'; +import 'package:rasadyar_core/core.dart'; + +class CustomNavigationObserver extends NavigatorObserver { + bool _isWorkDone = false; + + void setInjectionDone() { + _isWorkDone = true; + } + + @override + void didPush(Route route, Route? previousRoute) async { + super.didPush(route, previousRoute); + final routeName = route.settings.name; + if (!_isWorkDone && routeName == ChickenRoutes.init) { + _isWorkDone = true; + await setupInjection(); + } + } + + + @override + void didReplace({Route? newRoute, Route? oldRoute}) { + super.didReplace(newRoute: newRoute, oldRoute: oldRoute); + + } + + @override + void didPop(Route route, Route? previousRoute) { + super.didPop(route, previousRoute); + + } + + @override + void didRemove(Route route, Route? previousRoute) { + super.didRemove(route, previousRoute); + } +} diff --git a/lib/main.dart b/lib/main.dart index 543d9ad..b693b95 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:rasadyar_app/infrastructure/service/app_navigation_observer.dart'; import 'package:rasadyar_app/presentation/routes/app_pages.dart'; import 'package:rasadyar_auth/auth.dart'; import 'package:rasadyar_auth/data/services/token_storage_service.dart'; @@ -9,14 +10,15 @@ import 'infrastructure/service/auth_service.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - await setupInjection(); + await setupPreInjection(); + Get.put(TokenStorageService()); var tokenService = Get.find(); await tokenService.init(); Get.put(AuthMiddleware()); Get.put(AuthService()); - runApp(MyApp()); + // runApp(DevicePreview(builder: (context) => ForDevicePreview(),)); } @@ -59,6 +61,7 @@ class MyApp extends StatelessWidget { getPages: AppPages.pages, locale: const Locale("fa", "IR"), supportedLocales: const [Locale("fa", "IR")], + navigatorObservers: [CustomNavigationObserver()], localizationsDelegates: [ PersianMaterialLocalizations.delegate, PersianCupertinoLocalizations.delegate, diff --git a/lib/presentation/routes/app_pages.dart b/lib/presentation/routes/app_pages.dart index 2800452..049b90d 100644 --- a/lib/presentation/routes/app_pages.dart +++ b/lib/presentation/routes/app_pages.dart @@ -34,7 +34,6 @@ sealed class AppPages { } String getTargetPage(Module? value) { - eLog('getTargetPage: $value'); switch (value) { case Module.inspection: return InspectionRoutes.inspection; diff --git a/packages/auth/lib/data/common/dio_manager.dart b/packages/auth/lib/data/common/dio_manager.dart index 2c920a0..bbc17b5 100644 --- a/packages/auth/lib/data/common/dio_manager.dart +++ b/packages/auth/lib/data/common/dio_manager.dart @@ -4,15 +4,21 @@ import 'package:rasadyar_core/core.dart'; import '../di/auth_di.dart'; import 'constant.dart'; +/* class DioRemoteManager { DioRemote? _currentClient; ApiEnvironment? _currentEnv; - Future setEnvironment([ - ApiEnvironment env = ApiEnvironment.dam, - ]) async { + Future setEnvironment([ApiEnvironment env = ApiEnvironment.dam]) async { if (_currentEnv != env) { - _currentClient = DioRemote(baseUrl: env.baseUrl); + _currentClient = DioRemote( + baseUrl: env.baseUrl, + interceptors: AppInterceptor( + refreshTokenCallback: () async{ + return null; + }, + ), + ); await _currentClient?.init(); _currentEnv = env; } @@ -39,7 +45,6 @@ Future switchAuthEnvironment(ApiEnvironment env) async { await diAuth.unregister(); } - diAuth.registerLazySingleton( - () => AuthRepositoryImpl(dioRemote), - ); + diAuth.registerLazySingleton(() => AuthRepositoryImpl(dioRemote)); } +*/ diff --git a/packages/auth/lib/data/di/auth_di.dart b/packages/auth/lib/data/di/auth_di.dart index 48554de..3cb2747 100644 --- a/packages/auth/lib/data/di/auth_di.dart +++ b/packages/auth/lib/data/di/auth_di.dart @@ -9,7 +9,7 @@ import '../common/dio_manager.dart'; GetIt diAuth = GetIt.instance; Future setupAuthDI() async { - diAuth.registerLazySingleton(() => DioRemoteManager()); + diAuth.registerLazySingleton( () => AppInterceptor( refreshTokenCallback: () async { @@ -19,7 +19,9 @@ Future setupAuthDI() async { final refreshToken = tokenService.refreshToken.value; if (refreshToken == null) return null; - final result = await authRepo.loginWithRefreshToken(authRequest: {"refresh_token": refreshToken}); + final result = await authRepo.loginWithRefreshToken( + authRequest: {"refresh_token": refreshToken}, + ); if (result is AuthResponseModel) { await tokenService.saveAccessToken(result.access!); @@ -27,10 +29,18 @@ Future setupAuthDI() async { } return null; }, + saveTokenCallback: (String newToken) async { + // + }, + clearTokenCallback: () async { + //await tokenService.clearTokens(); // حذف همه توکن‌ها + }, ), ); - diAuth.registerLazySingleton(() => DioRemote(interceptors: [diAuth.get()])); + diAuth.registerLazySingleton( + () => DioRemote(interceptors: diAuth.get()), + ); final dioRemote = diAuth.get(); await dioRemote.init(); @@ -40,10 +50,13 @@ Future setupAuthDI() async { Future newSetupAuthDI(String newUrl) async { diAuth.registerLazySingleton( - () => DioRemote(baseUrl: newUrl, interceptors: [diAuth.get()]), + () => DioRemote(baseUrl: newUrl, interceptors: diAuth.get()), instanceName: 'newRemote', ); final dioRemote = diAuth.get(instanceName: 'newRemote'); await dioRemote.init(); - diAuth.registerSingleton(AuthRepositoryImpl(dioRemote), instanceName: 'newUrl'); + diAuth.registerSingleton( + AuthRepositoryImpl(dioRemote), + instanceName: 'newUrl', + ); } diff --git a/packages/auth/lib/data/services/token_storage_service.dart b/packages/auth/lib/data/services/token_storage_service.dart index 816a5b6..d81c591 100644 --- a/packages/auth/lib/data/services/token_storage_service.dart +++ b/packages/auth/lib/data/services/token_storage_service.dart @@ -5,7 +5,8 @@ import 'package:rasadyar_auth/hive_registrar.g.dart'; import 'package:rasadyar_core/core.dart'; class TokenStorageService extends GetxService { - static const String _boxName = 'secureBox'; + static const String _tokenBoxName = 'TokenBox'; + static const String _appBoxName = 'AppBox'; static const String _accessTokenKey = 'accessToken'; static const String _refreshTokenKey = 'refreshToken'; static const String _baseUrlKey = 'baseUrl'; @@ -17,7 +18,7 @@ class TokenStorageService extends GetxService { RxnString accessToken = RxnString(); RxnString refreshToken = RxnString(); - RxnString baseurl= RxnString(); + RxnString baseurl = RxnString(); Rxn appModule = Rxn(null); Future init() async { @@ -25,54 +26,61 @@ class TokenStorageService extends GetxService { Hive.registerAdapters(); final String? encryptedKey = await _secureStorage.read(key: 'hive_enc_key'); - final encryptionKey = encryptedKey != null ? base64Url.decode(encryptedKey) : Hive.generateSecureKey(); + final encryptionKey = encryptedKey != null + ? base64Url.decode(encryptedKey) + : Hive.generateSecureKey(); if (encryptedKey == null) { await _secureStorage.write(key: 'hive_enc_key', value: base64UrlEncode(encryptionKey)); } await _localStorage.init(); - await _localStorage.openBox(_boxName, encryptionCipher: HiveAesCipher(encryptionKey)); + await _localStorage.openBox(_tokenBoxName, encryptionCipher: HiveAesCipher(encryptionKey)); + await _localStorage.openBox(_appBoxName); - accessToken.value = _localStorage.read(boxName: _boxName, key: _accessTokenKey); - refreshToken.value = _localStorage.read(boxName: _boxName, key: _refreshTokenKey); - appModule.value = _localStorage.read(boxName: _boxName, key: _moduleKey); - baseurl.value = _localStorage.read(boxName: _boxName, key: _baseUrlKey); + accessToken.value = _localStorage.read(boxName: _tokenBoxName, key: _accessTokenKey); + refreshToken.value = _localStorage.read(boxName: _tokenBoxName, key: _refreshTokenKey); + appModule.value = _localStorage.read(boxName: _appBoxName, key: _moduleKey); + baseurl.value = _localStorage.read(boxName: _appBoxName, key: _baseUrlKey); } Future saveAccessToken(String token) async { - await _localStorage.save(boxName: _boxName, key: _accessTokenKey, value: token); + await _localStorage.save(boxName: _tokenBoxName, key: _accessTokenKey, value: token); accessToken.value = token; accessToken.refresh(); } Future saveRefreshToken(String token) async { - await _localStorage.save(boxName: _boxName, key: _refreshTokenKey, value: token); + await _localStorage.save(boxName: _tokenBoxName, key: _refreshTokenKey, value: token); refreshToken.value = token; refreshToken.refresh(); } Future saveModule(Module input) async { - await _localStorage.save(boxName: _boxName, key: _moduleKey, value: input); + await _localStorage.save(boxName: _tokenBoxName, key: _moduleKey, value: input); appModule.value = input; appModule.refresh(); } Future deleteTokens() async { - await _localStorage.clear(_boxName); + await _localStorage.clear(_tokenBoxName); accessToken.value = null; refreshToken.value = null; } - Future saveBaseUrl(String url) async { - await _localStorage.save(boxName: _boxName, key: _baseUrlKey, value: url); + await _localStorage.save(boxName: _appBoxName, key: _baseUrlKey, value: url); + baseurl.value = url; + baseurl.refresh(); + } + + void getBaseUrl() { + var url = _localStorage.read(boxName: _appBoxName, key: _baseUrlKey); baseurl.value = url; baseurl.refresh(); } Future saveApiKey(String key) async { - await _localStorage.save(boxName: _boxName, key: _apiKey, value: key); - + await _localStorage.save(boxName: _tokenBoxName, key: _apiKey, value: key); } } diff --git a/packages/auth/lib/data/utils/safe_call.dart b/packages/auth/lib/data/utils/safe_call.dart index edc659f..f850990 100644 --- a/packages/auth/lib/data/utils/safe_call.dart +++ b/packages/auth/lib/data/utils/safe_call.dart @@ -1,6 +1,6 @@ import 'package:rasadyar_core/core.dart'; -Future safeCall({ +Future safeCall({ required AppAsyncCallback call, Function(T result)? onSuccess, ErrorCallback? onError, @@ -12,10 +12,8 @@ Future safeCall({ bool showSnackBar = false, Function()? onShowLoading, Function()? onHideLoading, - Function()? onShowSuccessMessage, - Function()? onShowErrorMessage, }) { - return gSafeCall( + return gSafeCall( call: call, onSuccess: onSuccess, onError: onError, @@ -23,12 +21,7 @@ Future safeCall({ showLoading: showLoading, showError: showError, showSuccess: showSuccess, - showToast: showToast, - showSnackBar: showSnackBar, onShowLoading: onShowLoading, onHideLoading: onHideLoading, - onShowSuccessMessage: onShowSuccessMessage, - onShowErrorMessage: onShowErrorMessage, ); -} - +} \ No newline at end of file diff --git a/packages/auth/lib/presentation/pages/auth/logic.dart b/packages/auth/lib/presentation/pages/auth/logic.dart index 0c6093a..05acde5 100644 --- a/packages/auth/lib/presentation/pages/auth/logic.dart +++ b/packages/auth/lib/presentation/pages/auth/logic.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_auth/auth.dart'; import 'package:rasadyar_auth/data/common/dio_error_handler.dart'; import 'package:rasadyar_auth/data/models/request/login_request/login_request_model.dart'; -import 'package:rasadyar_auth/data/models/response/auth/auth_response_model.dart'; import 'package:rasadyar_auth/data/models/response/user_info/user_info_model.dart'; import 'package:rasadyar_auth/data/models/response/user_profile_model/user_profile_model.dart'; import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart'; @@ -28,8 +27,7 @@ class AuthLogic extends GetxController { Rx> formKeySentOtp = GlobalKey().obs; Rx phoneNumberController = TextEditingController().obs; Rx passwordController = TextEditingController().obs; - Rx phoneOtpNumberController = - TextEditingController().obs; + Rx phoneOtpNumberController = TextEditingController().obs; Rx otpCodeController = TextEditingController().obs; var captchaController = Get.find(); @@ -73,15 +71,11 @@ class AuthLogic extends GetxController { return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; } - @override - void onInit() { - super.onInit(); - } @override void onReady() { super.onReady(); - iLog('module111 : ${_module.toString()}'); + iLog('module selected : ${_module.toString()}'); } @override @@ -91,8 +85,7 @@ class AuthLogic extends GetxController { } bool _isFormValid() { - final isCaptchaValid = - captchaController.formKey.currentState?.validate() ?? false; + final isCaptchaValid = captchaController.formKey.currentState?.validate() ?? false; final isFormValid = formKey.currentState?.validate() ?? false; return isCaptchaValid && isFormValid; } @@ -179,22 +172,4 @@ class AuthLogic extends GetxController { ); isLoading.value = false; } - - GetSnackBar _errorSnackBar(String message) { - return GetSnackBar( - titleText: Text( - 'خطا', - style: AppFonts.yekan14.copyWith(color: Colors.white), - ), - messageText: Text( - message, - style: AppFonts.yekan12.copyWith(color: Colors.white), - ), - backgroundColor: AppColor.error, - margin: EdgeInsets.symmetric(horizontal: 12, vertical: 8), - borderRadius: 12, - duration: Duration(milliseconds: 3500), - snackPosition: SnackPosition.TOP, - ); - } } diff --git a/packages/chicken/lib/data/common/dio_manager.dart b/packages/chicken/lib/data/common/dio_manager.dart index e6d2946..3b1a566 100644 --- a/packages/chicken/lib/data/common/dio_manager.dart +++ b/packages/chicken/lib/data/common/dio_manager.dart @@ -4,7 +4,7 @@ import 'package:rasadyar_core/core.dart'; import '../di/chicken_di.dart'; import 'constant.dart'; -class DioRemoteManager { +/*class DioRemoteManager { DioRemote? _currentClient; ApiEnvironment? _currentEnv; @@ -28,6 +28,6 @@ class DioRemoteManager { } ApiEnvironment? get currentEnv => _currentEnv; -} +}*/ diff --git a/packages/chicken/lib/data/di/chicken_di.dart b/packages/chicken/lib/data/di/chicken_di.dart index a233521..eca66b7 100644 --- a/packages/chicken/lib/data/di/chicken_di.dart +++ b/packages/chicken/lib/data/di/chicken_di.dart @@ -1,4 +1,3 @@ -import 'package:rasadyar_auth/data/di/auth_di.dart'; import 'package:rasadyar_auth/data/services/token_storage_service.dart'; import 'package:rasadyar_chicken/data/repositories/chicken_repository_imp.dart'; import 'package:rasadyar_core/core.dart'; @@ -8,10 +7,30 @@ GetIt diChicken = GetIt.instance; Future setupChickenDI() async { var tokenService = Get.find(); - diAuth.registerLazySingleton(() => DioRemote(baseUrl: tokenService.baseurl.value)); - final dioRemote = diAuth.get(); + diChicken.registerLazySingleton( + () => AppInterceptor( + refreshTokenCallback: () async {}, + saveTokenCallback: (String newToken) async { + await tokenService.saveAccessToken(newToken); // ذخیره توکن جدید + }, + clearTokenCallback: () async { + await tokenService.deleteTokens(); + }, + ), + instanceName: 'chickenInterceptor', + ); + + tokenService.getBaseUrl(); + diChicken.registerLazySingleton(() { + return DioRemote( + baseUrl: tokenService.baseurl.value, + interceptors: diChicken.get(instanceName: 'chickenInterceptor'), + ); + }, instanceName: 'chickenDioRemote'); + + final dioRemote = diChicken.get(instanceName: 'chickenDioRemote'); await dioRemote.init(); - diAuth.registerLazySingleton(() => ChickenRepositoryImpl(dioRemote)); + diChicken.registerLazySingleton(() => ChickenRepositoryImpl(dioRemote)); diChicken.registerSingleton(ImagePicker()); } diff --git a/packages/chicken/lib/data/repositories/chicken_repository.dart b/packages/chicken/lib/data/repositories/chicken_repository.dart index 6ab177b..e85f4a9 100644 --- a/packages/chicken/lib/data/repositories/chicken_repository.dart +++ b/packages/chicken/lib/data/repositories/chicken_repository.dart @@ -24,7 +24,7 @@ import 'package:rasadyar_core/core.dart'; import '../models/request/create_steward_free_bar/create_steward_free_bar.dart'; abstract class ChickenRepository { - Future?> getInventory({required String token}); + Future?> getInventory({required String token, CancelToken? cancelToken}); Future getKillHouseDistributionInfo({required String token}); @@ -117,7 +117,7 @@ abstract class ChickenRepository { required OutProvinceCarcassesBuyer body, }); - Future?> getProvince(); + Future?> getProvince({CancelToken? cancelToken}); Future?> getCity({required String provinceName}); diff --git a/packages/chicken/lib/data/repositories/chicken_repository_imp.dart b/packages/chicken/lib/data/repositories/chicken_repository_imp.dart index 86eecf1..6067433 100644 --- a/packages/chicken/lib/data/repositories/chicken_repository_imp.dart +++ b/packages/chicken/lib/data/repositories/chicken_repository_imp.dart @@ -30,10 +30,13 @@ class ChickenRepositoryImpl implements ChickenRepository { ChickenRepositoryImpl(this._httpClient); @override - Future?> getInventory({required String token}) async { + Future?> getInventory({required String token, CancelToken? cancelToken}) async { + + eLog(_httpClient.baseUrl); var res = await _httpClient.get( '/roles-products/?role=Steward', headers: {'Authorization': 'Bearer $token'}, + fromJsonList: (json) => (json).map((item) => InventoryModel.fromJson(item as Map)).toList(), ); @@ -290,7 +293,7 @@ class ChickenRepositoryImpl implements ChickenRepository { } @override - Future?> getProvince() async { + Future?> getProvince({CancelToken? cancelToken}) async { var res = await _httpClient.get( '/iran_province/', fromJsonList: (json) => diff --git a/packages/chicken/lib/presentation/pages/home/logic.dart b/packages/chicken/lib/presentation/pages/home/logic.dart index d544079..16a3954 100644 --- a/packages/chicken/lib/presentation/pages/home/logic.dart +++ b/packages/chicken/lib/presentation/pages/home/logic.dart @@ -9,20 +9,19 @@ import 'package:rasadyar_core/core.dart'; class HomeLogic extends GetxController { RootLogic rootLogic = Get.find(); RxnInt totalWeightTodayBars = RxnInt(); - Rxn inventoryModel = Rxn(); Rxn killHouseDistributionInfo = Rxn(); RxBool isExpanded = false.obs; @override - void onInit() { - super.onInit(); + void onReady() { + super.onReady(); getTodayBars(); - getInventory(); getDistributionInformation(); } + Future getTodayBars() async { await safeCall( call: () async => await rootLogic.chickenRepository.getGeneralBarInformation( @@ -30,51 +29,22 @@ class HomeLogic extends GetxController { queryParameters: buildQueryParams(fromDate: DateTime.now(), toDate: DateTime.now()), ), onSuccess: (result) { + iLog(result); if (result != null) { totalWeightTodayBars.value = result.totalBarsWeight?.toInt(); } }, onError: (error, stackTrace) { - switch (error.response?.statusCode) { - case 401: - errorHandler(error); - break; - case 403: - errorHandler(error); - break; - default: - errorHandler(error); - } - }, - ); - } - Future getInventory() async { - await safeCall?>( - call: () async => await rootLogic.chickenRepository.getInventory(token: rootLogic.tokenService.accessToken.value!), - onSuccess: (result) { - if (result != null) { - inventoryModel.value = result.first; - } - }, - onError: (error, stackTrace) { - switch (error.response?.statusCode) { - case 401: - errorHandler(error); - break; - case 403: - errorHandler(error); - break; - default: - errorHandler(error); - } }, ); } Future getDistributionInformation() async { await safeCall( - call: () async => await rootLogic.chickenRepository.getKillHouseDistributionInfo(token: rootLogic.tokenService.accessToken.value!), + call: () async => await rootLogic.chickenRepository.getKillHouseDistributionInfo( + token: rootLogic.tokenService.accessToken.value!, + ), onSuccess: (result) { if (result != null) { killHouseDistributionInfo.value = result; @@ -85,19 +55,5 @@ class HomeLogic extends GetxController { } - void errorHandler(DioException error) { - handleGeneric(error, () { - rootLogic.tokenService.deleteTokens(); - }); - } - @override - void onReady() { - super.onReady(); - } - - @override - void onClose() { - super.onClose(); - } } diff --git a/packages/chicken/lib/presentation/pages/home/view.dart b/packages/chicken/lib/presentation/pages/home/view.dart index 11e40f6..bd9f07a 100644 --- a/packages/chicken/lib/presentation/pages/home/view.dart +++ b/packages/chicken/lib/presentation/pages/home/view.dart @@ -309,7 +309,7 @@ class HomePage extends GetView { ), ], ); - }, controller.inventoryModel), + }, controller.rootLogic.inventoryModel), ); } @@ -345,7 +345,7 @@ class HomePage extends GetView { ], ), ); - }, controller.inventoryModel); + }, controller.rootLogic.inventoryModel); } Widget _todayShipmentWidget() { diff --git a/packages/chicken/lib/presentation/pages/root/logic.dart b/packages/chicken/lib/presentation/pages/root/logic.dart index 5d89ac1..4d020da 100644 --- a/packages/chicken/lib/presentation/pages/root/logic.dart +++ b/packages/chicken/lib/presentation/pages/root/logic.dart @@ -1,7 +1,10 @@ +import 'dart:async'; + import 'package:flutter/material.dart' show Colors; import 'package:flutter/widgets.dart'; import 'package:rasadyar_auth/data/services/token_storage_service.dart'; import 'package:rasadyar_auth/data/utils/safe_call.dart'; +import 'package:rasadyar_chicken/data/di/chicken_di.dart'; import 'package:rasadyar_chicken/data/models/response/inventory/inventory_model.dart'; import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart'; import 'package:rasadyar_chicken/data/repositories/chicken_repository.dart'; @@ -39,19 +42,39 @@ class RootLogic extends GetxController { Rxn inventoryModel = Rxn(); RxList provinces = [].obs; + // Cancel tokens for API calls + CancelToken? _inventoryCancelToken; + CancelToken? _provincesCancelToken; + @override void onInit() { super.onInit(); - dioRemote = DioRemote(baseUrl: tokenService.baseurl.value); - dioRemote.init(); - chickenRepository = ChickenRepositoryImpl(dioRemote); - getProvinces(); + chickenRepository = diChicken.get(); - getInventory(); //getKillHouseDistributionInfo(); } + @override + void onReady() { + super.onReady(); + // Only call these methods if they haven't been called before + if (provinces.isEmpty) { + getProvinces(); + } + if (inventoryModel.value == null) { + getInventory(); + } + } + + @override + void onClose() { + // Cancel any ongoing requests when controller is disposed + _inventoryCancelToken?.cancel(); + _provincesCancelToken?.cancel(); + super.onClose(); + } + void toggleExpanded(int index) { if (inventoryExpandedList.keys.contains(index)) { inventoryExpandedList.remove(index); @@ -61,24 +84,24 @@ class RootLogic extends GetxController { } Future getInventory() async { + // Cancel previous request if still running + _inventoryCancelToken?.cancel(); + _inventoryCancelToken = CancelToken(); + await safeCall?>( - call: () async => - await chickenRepository.getInventory(token: tokenService.accessToken.value!), + call: () async => await chickenRepository.getInventory( + token: tokenService.accessToken.value!, + cancelToken: _inventoryCancelToken, + ), onSuccess: (result) { if (result != null) { inventoryModel.value = result.first; } }, onError: (error, stackTrace) { - switch (error.response?.statusCode) { - case 401: - errorHandler(error); - break; - case 403: - errorHandler(error); - break; - default: - errorHandler(error); + if (error is DioException && error.type == DioExceptionType.cancel) { + // Request was cancelled, ignore the error + return; } }, ); @@ -95,20 +118,22 @@ class RootLogic extends GetxController { } Future getProvinces() async { + // Cancel previous request if still running + _provincesCancelToken?.cancel(); + _provincesCancelToken = CancelToken(); + try { - final res = await chickenRepository.getProvince(); + final res = await chickenRepository.getProvince(cancelToken: _provincesCancelToken); if (res != null) { provinces.clear(); provinces.value = res; } } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) { + // Request was cancelled, ignore the error + return; + } provinces.clear(); } } - - void errorHandler(DioException error) { - handleGeneric(error, () { - tokenService.deleteTokens(); - }); - } } diff --git a/packages/chicken/lib/presentation/pages/sale/logic.dart b/packages/chicken/lib/presentation/pages/sale/logic.dart index 458c1ee..234d362 100644 --- a/packages/chicken/lib/presentation/pages/sale/logic.dart +++ b/packages/chicken/lib/presentation/pages/sale/logic.dart @@ -8,14 +8,12 @@ import 'package:rasadyar_chicken/presentation/pages/root/logic.dart'; import 'package:rasadyar_core/core.dart'; class SaleLogic extends GetxController { - Rxn?> allocatedMadeModel = - Rxn?>(); + Rxn?> allocatedMadeModel = Rxn?>(); RxList rolesProductsModel = RxList(); RxList guildsModel = [].obs; - Rxn stewardFreeDashboard = - Rxn(); + Rxn stewardFreeDashboard = Rxn(); RootLogic rootLogic = Get.find(); @@ -25,6 +23,11 @@ class SaleLogic extends GetxController { void onInit() { super.onInit(); routesName = [...rootLogic.routesName, 'فروش'].toList(); + } + + @override + void onReady() { + super.onReady(); getStewardDashBord(); getRolesProducts(); } @@ -33,12 +36,7 @@ class SaleLogic extends GetxController { safeCall( call: () async => await rootLogic.chickenRepository.getAllocatedMade( token: rootLogic.tokenService.accessToken.value!, - queryParameters: buildQueryParams( - page: 1, - pageSize: 20, - search: 'filter', - role: 'Steward', - ), + queryParameters: buildQueryParams(page: 1, pageSize: 20, search: 'filter', role: 'Steward'), ), onSuccess: (result) { if (result != null) { @@ -81,8 +79,7 @@ class SaleLogic extends GetxController { safeCall( call: () async => await rootLogic.chickenRepository.confirmAllAllocation( token: rootLogic.tokenService.accessToken.value!, - allocationTokens: - allocatedMadeModel.value?.map((e) => e.key!).toList() ?? [], + allocationTokens: allocatedMadeModel.value?.map((e) => e.key!).toList() ?? [], ), onSuccess: (result) { getAllocatedMade(); diff --git a/packages/chicken/lib/presentation/routes/pages.dart b/packages/chicken/lib/presentation/routes/pages.dart index 15d9d88..6323aa9 100644 --- a/packages/chicken/lib/presentation/routes/pages.dart +++ b/packages/chicken/lib/presentation/routes/pages.dart @@ -1,11 +1,7 @@ import 'package:rasadyar_auth/auth.dart'; import 'package:rasadyar_chicken/presentation/pages/buy/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/buy/view.dart'; -import 'package:rasadyar_chicken/presentation/pages/buy_in_province/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/buy_in_province/view.dart'; -import 'package:rasadyar_chicken/presentation/pages/buy_in_province_all/logic.dart'; -import 'package:rasadyar_chicken/presentation/pages/buy_in_province_waiting/logic.dart'; -import 'package:rasadyar_chicken/presentation/pages/buy_out_of_province/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/buy_out_of_province/view.dart'; import 'package:rasadyar_chicken/presentation/pages/home/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/home/view.dart'; @@ -14,18 +10,13 @@ import 'package:rasadyar_chicken/presentation/pages/root/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/root/view.dart'; import 'package:rasadyar_chicken/presentation/pages/sale/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/sale/view.dart'; -import 'package:rasadyar_chicken/presentation/pages/sales_in_province/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/sales_in_province/view.dart'; -import 'package:rasadyar_chicken/presentation/pages/sales_out_of_province/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/sales_out_of_province/view.dart'; import 'package:rasadyar_chicken/presentation/routes/routes.dart'; import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart'; import 'package:rasadyar_chicken/presentation/widget/search/logic.dart'; import 'package:rasadyar_core/core.dart'; -import '../pages/sales_out_of_province_buyers/logic.dart'; -import '../pages/sales_out_of_province_sales_list/logic.dart'; - sealed class ChickenPages { ChickenPages._(); @@ -35,12 +26,12 @@ sealed class ChickenPages { page: () => RootPage(), middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { - Get.put(RootLogic()); + Get.lazyPut(() => RootLogic()); + Get.lazyPut(() => BaseLogic()); Get.lazyPut(() => HomeLogic()); Get.lazyPut(() => BuyLogic()); Get.lazyPut(() => SaleLogic()); Get.lazyPut(() => ProfileLogic()); - Get.lazyPut(() => BaseLogic()); }), ), @@ -50,8 +41,7 @@ sealed class ChickenPages { middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.put(HomeLogic()); - Get.put(RootLogic()); - Get.lazyPut(() => BaseLogic()); + //Get.lazyPut(() => BaseLogic()); }), ), @@ -61,7 +51,7 @@ sealed class ChickenPages { middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.lazyPut(() => EnteringTheWarehouseLogic()); - Get.lazyPut(() => RootLogic()); + // RootLogic already registered in root page binding }), ),*/ @@ -72,9 +62,9 @@ sealed class ChickenPages { middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.lazyPut(() => SaleLogic()); - Get.lazyPut(() => BaseLogic()); - Get.lazyPut(() => SalesOutOfProvinceLogic()); - Get.lazyPut(() => RootLogic()); + //Get.lazyPut(() => BaseLogic()); + //Get.lazyPut(() => SalesOutOfProvinceLogic()); + //Get.lazyPut(() => RootLogic()); }), ), GetPage( @@ -82,13 +72,10 @@ sealed class ChickenPages { page: () => SalesOutOfProvincePage(), middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { - Get.lazyPut(() => RootLogic()); - Get.lazyPut(() => BaseLogic()); Get.lazyPut(() => SearchLogic()); - Get.lazyPut(() => SalesOutOfProvinceLogic()); - Get.lazyPut(() => SalesOutOfProvinceBuyersLogic()); - Get.lazyPut(() => SalesOutOfProvinceSalesListLogic()); - + // Get.lazyPut(() => SalesOutOfProvinceLogic()); + // Get.lazyPut(() => SalesOutOfProvinceBuyersLogic()); + // Get.lazyPut(() => SalesOutOfProvinceSalesListLogic()); }), ), GetPage( @@ -97,8 +84,7 @@ sealed class ChickenPages { middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.lazyPut(() => BaseLogic()); - Get.lazyPut(() => SalesInProvinceLogic()); - Get.lazyPut(() => RootLogic()); + // Get.lazyPut(() => SalesInProvinceLogic()); Get.lazyPut(() => SearchLogic()); }), ), @@ -111,7 +97,6 @@ sealed class ChickenPages { binding: BindingsBuilder(() { Get.lazyPut(() => BaseLogic()); Get.lazyPut(() => BuyLogic()); - Get.lazyPut(() => RootLogic()); }), ), @@ -122,8 +107,7 @@ sealed class ChickenPages { binding: BindingsBuilder(() { Get.lazyPut(() => BaseLogic()); Get.lazyPut(() => SearchLogic()); - Get.lazyPut(() => BuyOutOfProvinceLogic()); - Get.lazyPut(() => RootLogic()); + // Get.lazyPut(() => BuyOutOfProvinceLogic()); }), ), @@ -134,10 +118,9 @@ sealed class ChickenPages { binding: BindingsBuilder(() { Get.lazyPut(() => BaseLogic()); Get.lazyPut(() => SearchLogic()); - Get.lazyPut(() => BuyInProvinceLogic()); - Get.lazyPut(() => BuyInProvinceWaitingLogic()); - Get.lazyPut(() => BuyInProvinceAllLogic()); - Get.lazyPut(() => RootLogic()); + // Get.lazyPut(() => BuyInProvinceLogic()); + // Get.lazyPut(() => BuyInProvinceWaitingLogic()); + // Get.lazyPut(() => BuyInProvinceAllLogic()); }), ), ]; diff --git a/packages/chicken/lib/presentation/routes/routes.dart b/packages/chicken/lib/presentation/routes/routes.dart index d91ae8d..ce08250 100644 --- a/packages/chicken/lib/presentation/routes/routes.dart +++ b/packages/chicken/lib/presentation/routes/routes.dart @@ -1,8 +1,8 @@ sealed class ChickenRoutes { ChickenRoutes._(); - static const _base = '/init'; - static const init = '$_base/root'; + static const _base = '/chicken'; + static const init = '$_base/'; static const home = '$_base/home'; static const buy = '$_base/buy'; static const sale = '$_base/sale'; diff --git a/packages/core/lib/infrastructure/remote/app_interceptor.dart b/packages/core/lib/infrastructure/remote/app_interceptor.dart index 725889c..602d34f 100644 --- a/packages/core/lib/infrastructure/remote/app_interceptor.dart +++ b/packages/core/lib/infrastructure/remote/app_interceptor.dart @@ -1,62 +1,101 @@ import 'dart:async'; + import '../../core.dart'; typedef RefreshTokenCallback = Future Function(); +typedef SaveTokenCallback = Future Function(String token); +typedef ClearTokenCallback = Future Function(); + class AppInterceptor extends Interceptor { - final RefreshTokenCallback refreshTokenCallback; - Completer? _refreshCompleter; + final RefreshTokenCallback? refreshTokenCallback; + final SaveTokenCallback saveTokenCallback; + final ClearTokenCallback clearTokenCallback; + late final Dio dio; + static Completer? _refreshCompleter; + static bool _isRefreshing = false; - AppInterceptor({required this.refreshTokenCallback}); + AppInterceptor({ + required this.saveTokenCallback, + required this.clearTokenCallback, + this.refreshTokenCallback, + }); + @override + Future onRequest(RequestOptions options, RequestInterceptorHandler handler) async { + if (_isRefreshing && _refreshCompleter != null) { + try { + final newToken = await _refreshCompleter!.future; + if (newToken != null && newToken.isNotEmpty) { + options.headers['Authorization'] = 'Bearer $newToken'; + } + } catch (_) { + handler.reject(DioException(requestOptions: options, type: DioExceptionType.cancel)); + return; + } + } + handler.next(options); + } @override Future onError(DioException err, ErrorInterceptorHandler handler) async { - if (err.response?.statusCode == 401 && !ApiHandler.isRefreshing) { - ApiHandler.cancelAllRequests("Token expired - refreshing"); - - if (_refreshCompleter == null) { - _refreshCompleter = Completer(); - ApiHandler.isRefreshing = true; - - try { - final newToken = await refreshTokenCallback(); - if (newToken == null) throw Exception("Refresh failed"); - - ApiHandler.reset(); - _refreshCompleter?.complete(newToken); - } catch (e) { - _refreshCompleter?.completeError(e); - if (!ApiHandler.isRedirecting) { - ApiHandler.isRedirecting = true; - ApiHandler.cancelAllRequests("Cancel All Requests - Unauthorized"); - if (Get.currentRoute != '/Auth') { - Get.offAllNamed('/Auth'); - } - } - } finally { - ApiHandler.isRefreshing = false; - _refreshCompleter = null; - } + if (err.response?.statusCode == 401) { + final retryResult = await _handleUnauthorizedError(err); + if (retryResult != null) { + handler.resolve(retryResult); + return; } + } + handler.next(err); + } + Future _handleUnauthorizedError(DioException err) async { + if (_isRefreshing && _refreshCompleter != null) { try { final newToken = await _refreshCompleter!.future; - if (newToken != null) { - final opts = err.requestOptions; - opts.headers['Authorization'] = 'Bearer $newToken'; - - final dio = Dio(); - final cloneReq = await dio.fetch(opts); - handler.resolve(cloneReq); - return; - } + return newToken != null ? await _retryRequest(err.requestOptions, newToken) : null; } catch (_) { - handler.reject(err); + return null; } - } else if (err.type == DioExceptionType.cancel) { - handler.next(err); - } else { - handler.next(err); + } + + _isRefreshing = true; + _refreshCompleter = Completer(); + + try { + final newToken = await refreshTokenCallback!(); + + if (!_refreshCompleter!.isCompleted) _refreshCompleter!.complete(newToken); + + if (newToken != null) { + await saveTokenCallback(newToken); // ✅ ذخیره توکن جدید + return await _retryRequest(err.requestOptions, newToken); + } else { + await clearTokenCallback(); // ✅ پاک‌کردن توکن‌های قبلی + return null; + } + } catch (e) { + if (!_refreshCompleter!.isCompleted) _refreshCompleter!.completeError(e); + await clearTokenCallback(); // ✅ پاک‌کردن توکن در صورت خطا + _handleRefreshFailure(); + return null; + } finally { + _isRefreshing = false; + _refreshCompleter = null; } } + + Future _retryRequest(RequestOptions options, String token) async { + final newOptions = options.copyWith(); + newOptions.headers['Authorization'] = 'Bearer $token'; + return dio.fetch(newOptions); + } + + void _handleRefreshFailure() { + ApiHandler.cancelAllRequests("Token refresh failed"); + Future.delayed(const Duration(milliseconds: 100), () { + if (Get.currentRoute != '/Auth') { + Get.offAllNamed('/Auth'); + } + }); + } } \ No newline at end of file diff --git a/packages/core/lib/infrastructure/remote/dio_remote.dart b/packages/core/lib/infrastructure/remote/dio_remote.dart index e87c10f..96ba77a 100644 --- a/packages/core/lib/infrastructure/remote/dio_remote.dart +++ b/packages/core/lib/infrastructure/remote/dio_remote.dart @@ -1,45 +1,45 @@ import 'package:flutter/foundation.dart'; import 'package:rasadyar_core/core.dart'; - class DioRemote implements IHttpClient { String? baseUrl; - late final Dio _dio; - final List interceptors; + late Dio dio; + final AppInterceptor interceptors; - DioRemote({this.baseUrl, this.interceptors = const []}); + + DioRemote({this.baseUrl, required this.interceptors}); @override Future init() async { - final dio = Dio(BaseOptions(baseUrl: baseUrl ?? '')); - dio.interceptors.addAll(interceptors); + dio = Dio(BaseOptions(baseUrl: baseUrl ?? '')); + dio.interceptors.add(interceptors); if (kDebugMode) { dio.interceptors.add( PrettyDioLogger( requestHeader: true, responseHeader: true, requestBody: true, + responseBody: true, ), ); } - _dio = dio; } @override Future> get( - String path, { - Map? queryParameters, - Map? headers, - ProgressCallback? onReceiveProgress, - T Function(Map json)? fromJson, - T Function(List json)? fromJsonList, - }) async { - final response = await _dio.get( + String path, { + Map? queryParameters, + Map? headers, + ProgressCallback? onReceiveProgress, + T Function(Map json)? fromJson, + T Function(List json)? fromJsonList, + }) async { + final response = await dio.get( path, queryParameters: queryParameters, options: Options(headers: headers), onReceiveProgress: onReceiveProgress, - cancelToken: ApiHandler.globalCancelToken + cancelToken: ApiHandler.globalCancelToken, ); if (fromJsonList != null && response.data is List) { response.data = fromJsonList(response.data); @@ -62,21 +62,19 @@ class DioRemote implements IHttpClient { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final response = await _dio.post( + final response = await dio.post( path, data: data, queryParameters: queryParameters, options: Options(headers: headers), onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, - cancelToken: ApiHandler.globalCancelToken + cancelToken: ApiHandler.globalCancelToken, ); if (fromJson != null) { final rawData = response.data; - final parsedData = rawData is Map - ? fromJson(rawData) - : null; + final parsedData = rawData is Map ? fromJson(rawData) : null; response.data = parsedData; return DioResponse(response); } @@ -93,14 +91,14 @@ class DioRemote implements IHttpClient { ProgressCallback? onSendProgress, ProgressCallback? onReceiveProgress, }) async { - final response = await _dio.put( + final response = await dio.put( path, data: data, queryParameters: queryParameters, options: Options(headers: headers), onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, - cancelToken: ApiHandler.globalCancelToken + cancelToken: ApiHandler.globalCancelToken, ); return DioResponse(response); } @@ -112,12 +110,12 @@ class DioRemote implements IHttpClient { Map? queryParameters, Map? headers, }) async { - final response = await _dio.delete( + final response = await dio.delete( path, data: data, queryParameters: queryParameters, options: Options(headers: headers), - cancelToken: ApiHandler.globalCancelToken + cancelToken: ApiHandler.globalCancelToken, ); return DioResponse(response); } @@ -127,11 +125,11 @@ class DioRemote implements IHttpClient { String url, { ProgressCallback? onReceiveProgress, }) async { - final response = await _dio.get( + final response = await dio.get( url, options: Options(responseType: ResponseType.bytes), onReceiveProgress: onReceiveProgress, - cancelToken: ApiHandler.globalCancelToken + cancelToken: ApiHandler.globalCancelToken, ); return DioResponse(response); } @@ -143,12 +141,12 @@ class DioRemote implements IHttpClient { Map? headers, ProgressCallback? onSendProgress, }) async { - final response = await _dio.post( + final response = await dio.post( path, data: (formData as DioFormData).raw, options: Options(headers: headers, contentType: 'multipart/form-data'), onSendProgress: onSendProgress, - cancelToken: ApiHandler.globalCancelToken + cancelToken: ApiHandler.globalCancelToken, ); return DioResponse(response); } diff --git a/packages/core/lib/injection/di.dart b/packages/core/lib/injection/di.dart index 8e1c1b2..9df3a5f 100644 --- a/packages/core/lib/injection/di.dart +++ b/packages/core/lib/injection/di.dart @@ -17,6 +17,7 @@ Future _setUpLogger() async{ Future _setupLocalStorage() async { diCore.registerSingleton(HiveLocalStorage()); + print('====> HiveLocalStorage registered'); } diff --git a/packages/core/lib/utils/network/safe_call_utils.dart b/packages/core/lib/utils/network/safe_call_utils.dart index fca9f61..19e354a 100644 --- a/packages/core/lib/utils/network/safe_call_utils.dart +++ b/packages/core/lib/utils/network/safe_call_utils.dart @@ -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 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 = Future 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 gSafeCall({ +Future gSafeCall({ required AppAsyncCallback 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, + ); } diff --git a/pubspec.lock b/pubspec.lock index 5c08254..1bba7de 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1162,7 +1162,7 @@ packages: path: "packages/chicken" relative: true source: path - version: "1.0.0" + version: "1.0.1" rasadyar_core: dependency: "direct main" description: