feat : change app inspector and exception handling
This commit is contained in:
@@ -5,9 +5,17 @@ import 'package:rasadyar_core/core.dart';
|
|||||||
|
|
||||||
final di = GetIt.instance;
|
final di = GetIt.instance;
|
||||||
|
|
||||||
Future<void> setupInjection() async{
|
Future<void> setupPreInjection() async{
|
||||||
await setupAuthDI();
|
|
||||||
await setupAllCoreProvider();
|
await setupAllCoreProvider();
|
||||||
|
await setupAuthDI();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setupInjection() async{
|
||||||
|
|
||||||
|
await setupChickenDI();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
40
lib/infrastructure/service/app_navigation_observer.dart
Normal file
40
lib/infrastructure/service/app_navigation_observer.dart
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
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_app/presentation/routes/app_pages.dart';
|
||||||
import 'package:rasadyar_auth/auth.dart';
|
import 'package:rasadyar_auth/auth.dart';
|
||||||
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
|
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
|
||||||
@@ -9,14 +10,15 @@ import 'infrastructure/service/auth_service.dart';
|
|||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await setupInjection();
|
await setupPreInjection();
|
||||||
|
|
||||||
Get.put(TokenStorageService());
|
Get.put(TokenStorageService());
|
||||||
var tokenService = Get.find<TokenStorageService>();
|
var tokenService = Get.find<TokenStorageService>();
|
||||||
await tokenService.init();
|
await tokenService.init();
|
||||||
Get.put(AuthMiddleware());
|
Get.put(AuthMiddleware());
|
||||||
Get.put(AuthService());
|
Get.put(AuthService());
|
||||||
|
|
||||||
runApp(MyApp());
|
runApp(MyApp());
|
||||||
|
|
||||||
// runApp(DevicePreview(builder: (context) => ForDevicePreview(),));
|
// runApp(DevicePreview(builder: (context) => ForDevicePreview(),));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ class MyApp extends StatelessWidget {
|
|||||||
getPages: AppPages.pages,
|
getPages: AppPages.pages,
|
||||||
locale: const Locale("fa", "IR"),
|
locale: const Locale("fa", "IR"),
|
||||||
supportedLocales: const [Locale("fa", "IR")],
|
supportedLocales: const [Locale("fa", "IR")],
|
||||||
|
navigatorObservers: [CustomNavigationObserver()],
|
||||||
localizationsDelegates: [
|
localizationsDelegates: [
|
||||||
PersianMaterialLocalizations.delegate,
|
PersianMaterialLocalizations.delegate,
|
||||||
PersianCupertinoLocalizations.delegate,
|
PersianCupertinoLocalizations.delegate,
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ sealed class AppPages {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getTargetPage(Module? value) {
|
String getTargetPage(Module? value) {
|
||||||
eLog('getTargetPage: $value');
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case Module.inspection:
|
case Module.inspection:
|
||||||
return InspectionRoutes.inspection;
|
return InspectionRoutes.inspection;
|
||||||
|
|||||||
@@ -4,15 +4,21 @@ import 'package:rasadyar_core/core.dart';
|
|||||||
import '../di/auth_di.dart';
|
import '../di/auth_di.dart';
|
||||||
import 'constant.dart';
|
import 'constant.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
class DioRemoteManager {
|
class DioRemoteManager {
|
||||||
DioRemote? _currentClient;
|
DioRemote? _currentClient;
|
||||||
ApiEnvironment? _currentEnv;
|
ApiEnvironment? _currentEnv;
|
||||||
|
|
||||||
Future<DioRemote> setEnvironment([
|
Future<DioRemote> setEnvironment([ApiEnvironment env = ApiEnvironment.dam]) async {
|
||||||
ApiEnvironment env = ApiEnvironment.dam,
|
|
||||||
]) async {
|
|
||||||
if (_currentEnv != env) {
|
if (_currentEnv != env) {
|
||||||
_currentClient = DioRemote(baseUrl: env.baseUrl);
|
_currentClient = DioRemote(
|
||||||
|
baseUrl: env.baseUrl,
|
||||||
|
interceptors: AppInterceptor(
|
||||||
|
refreshTokenCallback: () async{
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
await _currentClient?.init();
|
await _currentClient?.init();
|
||||||
_currentEnv = env;
|
_currentEnv = env;
|
||||||
}
|
}
|
||||||
@@ -39,7 +45,6 @@ Future<void> switchAuthEnvironment(ApiEnvironment env) async {
|
|||||||
await diAuth.unregister<AuthRepositoryImpl>();
|
await diAuth.unregister<AuthRepositoryImpl>();
|
||||||
}
|
}
|
||||||
|
|
||||||
diAuth.registerLazySingleton<AuthRepositoryImpl>(
|
diAuth.registerLazySingleton<AuthRepositoryImpl>(() => AuthRepositoryImpl(dioRemote));
|
||||||
() => AuthRepositoryImpl(dioRemote),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import '../common/dio_manager.dart';
|
|||||||
GetIt diAuth = GetIt.instance;
|
GetIt diAuth = GetIt.instance;
|
||||||
|
|
||||||
Future<void> setupAuthDI() async {
|
Future<void> setupAuthDI() async {
|
||||||
diAuth.registerLazySingleton(() => DioRemoteManager());
|
|
||||||
diAuth.registerLazySingleton<AppInterceptor>(
|
diAuth.registerLazySingleton<AppInterceptor>(
|
||||||
() => AppInterceptor(
|
() => AppInterceptor(
|
||||||
refreshTokenCallback: () async {
|
refreshTokenCallback: () async {
|
||||||
@@ -19,7 +19,9 @@ Future<void> setupAuthDI() async {
|
|||||||
final refreshToken = tokenService.refreshToken.value;
|
final refreshToken = tokenService.refreshToken.value;
|
||||||
if (refreshToken == null) return null;
|
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) {
|
if (result is AuthResponseModel) {
|
||||||
await tokenService.saveAccessToken(result.access!);
|
await tokenService.saveAccessToken(result.access!);
|
||||||
@@ -27,10 +29,18 @@ Future<void> setupAuthDI() async {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
saveTokenCallback: (String newToken) async {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
clearTokenCallback: () async {
|
||||||
|
//await tokenService.clearTokens(); // حذف همه توکنها
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
diAuth.registerLazySingleton<DioRemote>(() => DioRemote(interceptors: [diAuth.get<AppInterceptor>()]));
|
diAuth.registerLazySingleton<DioRemote>(
|
||||||
|
() => DioRemote(interceptors: diAuth.get<AppInterceptor>()),
|
||||||
|
);
|
||||||
|
|
||||||
final dioRemote = diAuth.get<DioRemote>();
|
final dioRemote = diAuth.get<DioRemote>();
|
||||||
await dioRemote.init();
|
await dioRemote.init();
|
||||||
@@ -40,10 +50,13 @@ Future<void> setupAuthDI() async {
|
|||||||
|
|
||||||
Future<void> newSetupAuthDI(String newUrl) async {
|
Future<void> newSetupAuthDI(String newUrl) async {
|
||||||
diAuth.registerLazySingleton<DioRemote>(
|
diAuth.registerLazySingleton<DioRemote>(
|
||||||
() => DioRemote(baseUrl: newUrl, interceptors: [diAuth.get<AppInterceptor>()]),
|
() => DioRemote(baseUrl: newUrl, interceptors: diAuth.get<AppInterceptor>()),
|
||||||
instanceName: 'newRemote',
|
instanceName: 'newRemote',
|
||||||
);
|
);
|
||||||
final dioRemote = diAuth.get<DioRemote>(instanceName: 'newRemote');
|
final dioRemote = diAuth.get<DioRemote>(instanceName: 'newRemote');
|
||||||
await dioRemote.init();
|
await dioRemote.init();
|
||||||
diAuth.registerSingleton<AuthRepositoryImpl>(AuthRepositoryImpl(dioRemote), instanceName: 'newUrl');
|
diAuth.registerSingleton<AuthRepositoryImpl>(
|
||||||
|
AuthRepositoryImpl(dioRemote),
|
||||||
|
instanceName: 'newUrl',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import 'package:rasadyar_auth/hive_registrar.g.dart';
|
|||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
class TokenStorageService extends GetxService {
|
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 _accessTokenKey = 'accessToken';
|
||||||
static const String _refreshTokenKey = 'refreshToken';
|
static const String _refreshTokenKey = 'refreshToken';
|
||||||
static const String _baseUrlKey = 'baseUrl';
|
static const String _baseUrlKey = 'baseUrl';
|
||||||
@@ -17,7 +18,7 @@ class TokenStorageService extends GetxService {
|
|||||||
|
|
||||||
RxnString accessToken = RxnString();
|
RxnString accessToken = RxnString();
|
||||||
RxnString refreshToken = RxnString();
|
RxnString refreshToken = RxnString();
|
||||||
RxnString baseurl= RxnString();
|
RxnString baseurl = RxnString();
|
||||||
Rxn<Module> appModule = Rxn(null);
|
Rxn<Module> appModule = Rxn(null);
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
@@ -25,54 +26,61 @@ class TokenStorageService extends GetxService {
|
|||||||
Hive.registerAdapters();
|
Hive.registerAdapters();
|
||||||
|
|
||||||
final String? encryptedKey = await _secureStorage.read(key: 'hive_enc_key');
|
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) {
|
if (encryptedKey == null) {
|
||||||
await _secureStorage.write(key: 'hive_enc_key', value: base64UrlEncode(encryptionKey));
|
await _secureStorage.write(key: 'hive_enc_key', value: base64UrlEncode(encryptionKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
await _localStorage.init();
|
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<String?>(boxName: _boxName, key: _accessTokenKey);
|
accessToken.value = _localStorage.read<String?>(boxName: _tokenBoxName, key: _accessTokenKey);
|
||||||
refreshToken.value = _localStorage.read<String?>(boxName: _boxName, key: _refreshTokenKey);
|
refreshToken.value = _localStorage.read<String?>(boxName: _tokenBoxName, key: _refreshTokenKey);
|
||||||
appModule.value = _localStorage.read<Module?>(boxName: _boxName, key: _moduleKey);
|
appModule.value = _localStorage.read<Module?>(boxName: _appBoxName, key: _moduleKey);
|
||||||
baseurl.value = _localStorage.read<String?>(boxName: _boxName, key: _baseUrlKey);
|
baseurl.value = _localStorage.read<String?>(boxName: _appBoxName, key: _baseUrlKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveAccessToken(String token) async {
|
Future<void> 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.value = token;
|
||||||
accessToken.refresh();
|
accessToken.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveRefreshToken(String token) async {
|
Future<void> 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.value = token;
|
||||||
refreshToken.refresh();
|
refreshToken.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveModule(Module input) async {
|
Future<void> 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.value = input;
|
||||||
appModule.refresh();
|
appModule.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteTokens() async {
|
Future<void> deleteTokens() async {
|
||||||
await _localStorage.clear(_boxName);
|
await _localStorage.clear(_tokenBoxName);
|
||||||
accessToken.value = null;
|
accessToken.value = null;
|
||||||
refreshToken.value = null;
|
refreshToken.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> saveBaseUrl(String url) async {
|
Future<void> 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.value = url;
|
||||||
baseurl.refresh();
|
baseurl.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveApiKey(String key) async {
|
Future<void> saveApiKey(String key) async {
|
||||||
await _localStorage.save(boxName: _boxName, key: _apiKey, value: key);
|
await _localStorage.save(boxName: _tokenBoxName, key: _apiKey, value: key);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
Future<void> safeCall<T>({
|
Future<T?> safeCall<T>({
|
||||||
required AppAsyncCallback<T> call,
|
required AppAsyncCallback<T> call,
|
||||||
Function(T result)? onSuccess,
|
Function(T result)? onSuccess,
|
||||||
ErrorCallback? onError,
|
ErrorCallback? onError,
|
||||||
@@ -12,10 +12,8 @@ Future<void> safeCall<T>({
|
|||||||
bool showSnackBar = false,
|
bool showSnackBar = false,
|
||||||
Function()? onShowLoading,
|
Function()? onShowLoading,
|
||||||
Function()? onHideLoading,
|
Function()? onHideLoading,
|
||||||
Function()? onShowSuccessMessage,
|
|
||||||
Function()? onShowErrorMessage,
|
|
||||||
}) {
|
}) {
|
||||||
return gSafeCall(
|
return gSafeCall<T>(
|
||||||
call: call,
|
call: call,
|
||||||
onSuccess: onSuccess,
|
onSuccess: onSuccess,
|
||||||
onError: onError,
|
onError: onError,
|
||||||
@@ -23,12 +21,7 @@ Future<void> safeCall<T>({
|
|||||||
showLoading: showLoading,
|
showLoading: showLoading,
|
||||||
showError: showError,
|
showError: showError,
|
||||||
showSuccess: showSuccess,
|
showSuccess: showSuccess,
|
||||||
showToast: showToast,
|
|
||||||
showSnackBar: showSnackBar,
|
|
||||||
onShowLoading: onShowLoading,
|
onShowLoading: onShowLoading,
|
||||||
onHideLoading: onHideLoading,
|
onHideLoading: onHideLoading,
|
||||||
onShowSuccessMessage: onShowSuccessMessage,
|
|
||||||
onShowErrorMessage: onShowErrorMessage,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:rasadyar_auth/auth.dart';
|
import 'package:rasadyar_auth/auth.dart';
|
||||||
import 'package:rasadyar_auth/data/common/dio_error_handler.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/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_info/user_info_model.dart';
|
||||||
import 'package:rasadyar_auth/data/models/response/user_profile_model/user_profile_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';
|
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
||||||
@@ -28,8 +27,7 @@ class AuthLogic extends GetxController {
|
|||||||
Rx<GlobalKey<FormState>> formKeySentOtp = GlobalKey<FormState>().obs;
|
Rx<GlobalKey<FormState>> formKeySentOtp = GlobalKey<FormState>().obs;
|
||||||
Rx<TextEditingController> phoneNumberController = TextEditingController().obs;
|
Rx<TextEditingController> phoneNumberController = TextEditingController().obs;
|
||||||
Rx<TextEditingController> passwordController = TextEditingController().obs;
|
Rx<TextEditingController> passwordController = TextEditingController().obs;
|
||||||
Rx<TextEditingController> phoneOtpNumberController =
|
Rx<TextEditingController> phoneOtpNumberController = TextEditingController().obs;
|
||||||
TextEditingController().obs;
|
|
||||||
Rx<TextEditingController> otpCodeController = TextEditingController().obs;
|
Rx<TextEditingController> otpCodeController = TextEditingController().obs;
|
||||||
|
|
||||||
var captchaController = Get.find<CaptchaWidgetLogic>();
|
var captchaController = Get.find<CaptchaWidgetLogic>();
|
||||||
@@ -73,15 +71,11 @@ class AuthLogic extends GetxController {
|
|||||||
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void onInit() {
|
|
||||||
super.onInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onReady() {
|
void onReady() {
|
||||||
super.onReady();
|
super.onReady();
|
||||||
iLog('module111 : ${_module.toString()}');
|
iLog('module selected : ${_module.toString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -91,8 +85,7 @@ class AuthLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _isFormValid() {
|
bool _isFormValid() {
|
||||||
final isCaptchaValid =
|
final isCaptchaValid = captchaController.formKey.currentState?.validate() ?? false;
|
||||||
captchaController.formKey.currentState?.validate() ?? false;
|
|
||||||
final isFormValid = formKey.currentState?.validate() ?? false;
|
final isFormValid = formKey.currentState?.validate() ?? false;
|
||||||
return isCaptchaValid && isFormValid;
|
return isCaptchaValid && isFormValid;
|
||||||
}
|
}
|
||||||
@@ -179,22 +172,4 @@ class AuthLogic extends GetxController {
|
|||||||
);
|
);
|
||||||
isLoading.value = false;
|
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:rasadyar_core/core.dart';
|
|||||||
import '../di/chicken_di.dart';
|
import '../di/chicken_di.dart';
|
||||||
import 'constant.dart';
|
import 'constant.dart';
|
||||||
|
|
||||||
class DioRemoteManager {
|
/*class DioRemoteManager {
|
||||||
DioRemote? _currentClient;
|
DioRemote? _currentClient;
|
||||||
ApiEnvironment? _currentEnv;
|
ApiEnvironment? _currentEnv;
|
||||||
|
|
||||||
@@ -28,6 +28,6 @@ class DioRemoteManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ApiEnvironment? get currentEnv => _currentEnv;
|
ApiEnvironment? get currentEnv => _currentEnv;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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_auth/data/services/token_storage_service.dart';
|
||||||
import 'package:rasadyar_chicken/data/repositories/chicken_repository_imp.dart';
|
import 'package:rasadyar_chicken/data/repositories/chicken_repository_imp.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
@@ -8,10 +7,30 @@ GetIt diChicken = GetIt.instance;
|
|||||||
Future<void> setupChickenDI() async {
|
Future<void> setupChickenDI() async {
|
||||||
var tokenService = Get.find<TokenStorageService>();
|
var tokenService = Get.find<TokenStorageService>();
|
||||||
|
|
||||||
diAuth.registerLazySingleton<DioRemote>(() => DioRemote(baseUrl: tokenService.baseurl.value));
|
diChicken.registerLazySingleton<AppInterceptor>(
|
||||||
final dioRemote = diAuth.get<DioRemote>();
|
() => AppInterceptor(
|
||||||
|
refreshTokenCallback: () async {},
|
||||||
|
saveTokenCallback: (String newToken) async {
|
||||||
|
await tokenService.saveAccessToken(newToken); // ذخیره توکن جدید
|
||||||
|
},
|
||||||
|
clearTokenCallback: () async {
|
||||||
|
await tokenService.deleteTokens();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
instanceName: 'chickenInterceptor',
|
||||||
|
);
|
||||||
|
|
||||||
|
tokenService.getBaseUrl();
|
||||||
|
diChicken.registerLazySingleton<DioRemote>(() {
|
||||||
|
return DioRemote(
|
||||||
|
baseUrl: tokenService.baseurl.value,
|
||||||
|
interceptors: diChicken.get<AppInterceptor>(instanceName: 'chickenInterceptor'),
|
||||||
|
);
|
||||||
|
}, instanceName: 'chickenDioRemote');
|
||||||
|
|
||||||
|
final dioRemote = diChicken.get<DioRemote>(instanceName: 'chickenDioRemote');
|
||||||
await dioRemote.init();
|
await dioRemote.init();
|
||||||
diAuth.registerLazySingleton<ChickenRepositoryImpl>(() => ChickenRepositoryImpl(dioRemote));
|
diChicken.registerLazySingleton<ChickenRepositoryImpl>(() => ChickenRepositoryImpl(dioRemote));
|
||||||
|
|
||||||
diChicken.registerSingleton(ImagePicker());
|
diChicken.registerSingleton(ImagePicker());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import 'package:rasadyar_core/core.dart';
|
|||||||
import '../models/request/create_steward_free_bar/create_steward_free_bar.dart';
|
import '../models/request/create_steward_free_bar/create_steward_free_bar.dart';
|
||||||
|
|
||||||
abstract class ChickenRepository {
|
abstract class ChickenRepository {
|
||||||
Future<List<InventoryModel>?> getInventory({required String token});
|
Future<List<InventoryModel>?> getInventory({required String token, CancelToken? cancelToken});
|
||||||
|
|
||||||
Future<KillHouseDistributionInfo?> getKillHouseDistributionInfo({required String token});
|
Future<KillHouseDistributionInfo?> getKillHouseDistributionInfo({required String token});
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ abstract class ChickenRepository {
|
|||||||
required OutProvinceCarcassesBuyer body,
|
required OutProvinceCarcassesBuyer body,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<List<IranProvinceCityModel>?> getProvince();
|
Future<List<IranProvinceCityModel>?> getProvince({CancelToken? cancelToken});
|
||||||
|
|
||||||
Future<List<IranProvinceCityModel>?> getCity({required String provinceName});
|
Future<List<IranProvinceCityModel>?> getCity({required String provinceName});
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,13 @@ class ChickenRepositoryImpl implements ChickenRepository {
|
|||||||
ChickenRepositoryImpl(this._httpClient);
|
ChickenRepositoryImpl(this._httpClient);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<InventoryModel>?> getInventory({required String token}) async {
|
Future<List<InventoryModel>?> getInventory({required String token, CancelToken? cancelToken}) async {
|
||||||
|
|
||||||
|
eLog(_httpClient.baseUrl);
|
||||||
var res = await _httpClient.get(
|
var res = await _httpClient.get(
|
||||||
'/roles-products/?role=Steward',
|
'/roles-products/?role=Steward',
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
|
|
||||||
fromJsonList: (json) =>
|
fromJsonList: (json) =>
|
||||||
(json).map((item) => InventoryModel.fromJson(item as Map<String, dynamic>)).toList(),
|
(json).map((item) => InventoryModel.fromJson(item as Map<String, dynamic>)).toList(),
|
||||||
);
|
);
|
||||||
@@ -290,7 +293,7 @@ class ChickenRepositoryImpl implements ChickenRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<IranProvinceCityModel>?> getProvince() async {
|
Future<List<IranProvinceCityModel>?> getProvince({CancelToken? cancelToken}) async {
|
||||||
var res = await _httpClient.get(
|
var res = await _httpClient.get(
|
||||||
'/iran_province/',
|
'/iran_province/',
|
||||||
fromJsonList: (json) =>
|
fromJsonList: (json) =>
|
||||||
|
|||||||
@@ -9,20 +9,19 @@ import 'package:rasadyar_core/core.dart';
|
|||||||
class HomeLogic extends GetxController {
|
class HomeLogic extends GetxController {
|
||||||
RootLogic rootLogic = Get.find<RootLogic>();
|
RootLogic rootLogic = Get.find<RootLogic>();
|
||||||
RxnInt totalWeightTodayBars = RxnInt();
|
RxnInt totalWeightTodayBars = RxnInt();
|
||||||
Rxn<InventoryModel> inventoryModel = Rxn<InventoryModel>();
|
|
||||||
Rxn<KillHouseDistributionInfo> killHouseDistributionInfo = Rxn<KillHouseDistributionInfo>();
|
Rxn<KillHouseDistributionInfo> killHouseDistributionInfo = Rxn<KillHouseDistributionInfo>();
|
||||||
|
|
||||||
RxBool isExpanded = false.obs;
|
RxBool isExpanded = false.obs;
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onReady() {
|
||||||
super.onInit();
|
super.onReady();
|
||||||
getTodayBars();
|
getTodayBars();
|
||||||
getInventory();
|
|
||||||
getDistributionInformation();
|
getDistributionInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> getTodayBars() async {
|
Future<void> getTodayBars() async {
|
||||||
await safeCall<BarInformation?>(
|
await safeCall<BarInformation?>(
|
||||||
call: () async => await rootLogic.chickenRepository.getGeneralBarInformation(
|
call: () async => await rootLogic.chickenRepository.getGeneralBarInformation(
|
||||||
@@ -30,51 +29,22 @@ class HomeLogic extends GetxController {
|
|||||||
queryParameters: buildQueryParams(fromDate: DateTime.now(), toDate: DateTime.now()),
|
queryParameters: buildQueryParams(fromDate: DateTime.now(), toDate: DateTime.now()),
|
||||||
),
|
),
|
||||||
onSuccess: (result) {
|
onSuccess: (result) {
|
||||||
|
iLog(result);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
totalWeightTodayBars.value = result.totalBarsWeight?.toInt();
|
totalWeightTodayBars.value = result.totalBarsWeight?.toInt();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error, stackTrace) {
|
onError: (error, stackTrace) {
|
||||||
switch (error.response?.statusCode) {
|
|
||||||
case 401:
|
|
||||||
errorHandler(error);
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
errorHandler(error);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errorHandler(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> getInventory() async {
|
|
||||||
await safeCall<List<InventoryModel>?>(
|
|
||||||
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<void> getDistributionInformation() async {
|
Future<void> getDistributionInformation() async {
|
||||||
await safeCall<KillHouseDistributionInfo?>(
|
await safeCall<KillHouseDistributionInfo?>(
|
||||||
call: () async => await rootLogic.chickenRepository.getKillHouseDistributionInfo(token: rootLogic.tokenService.accessToken.value!),
|
call: () async => await rootLogic.chickenRepository.getKillHouseDistributionInfo(
|
||||||
|
token: rootLogic.tokenService.accessToken.value!,
|
||||||
|
),
|
||||||
onSuccess: (result) {
|
onSuccess: (result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
killHouseDistributionInfo.value = result;
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ class HomePage extends GetView<HomeLogic> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}, controller.inventoryModel),
|
}, controller.rootLogic.inventoryModel),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ class HomePage extends GetView<HomeLogic> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}, controller.inventoryModel);
|
}, controller.rootLogic.inventoryModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _todayShipmentWidget() {
|
Widget _todayShipmentWidget() {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart' show Colors;
|
import 'package:flutter/material.dart' show Colors;
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
|
import 'package:rasadyar_auth/data/services/token_storage_service.dart';
|
||||||
import 'package:rasadyar_auth/data/utils/safe_call.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/inventory/inventory_model.dart';
|
||||||
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_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';
|
import 'package:rasadyar_chicken/data/repositories/chicken_repository.dart';
|
||||||
@@ -39,19 +42,39 @@ class RootLogic extends GetxController {
|
|||||||
Rxn<InventoryModel> inventoryModel = Rxn<InventoryModel>();
|
Rxn<InventoryModel> inventoryModel = Rxn<InventoryModel>();
|
||||||
RxList<IranProvinceCityModel> provinces = <IranProvinceCityModel>[].obs;
|
RxList<IranProvinceCityModel> provinces = <IranProvinceCityModel>[].obs;
|
||||||
|
|
||||||
|
// Cancel tokens for API calls
|
||||||
|
CancelToken? _inventoryCancelToken;
|
||||||
|
CancelToken? _provincesCancelToken;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
dioRemote = DioRemote(baseUrl: tokenService.baseurl.value);
|
|
||||||
dioRemote.init();
|
|
||||||
chickenRepository = ChickenRepositoryImpl(dioRemote);
|
|
||||||
|
|
||||||
getProvinces();
|
chickenRepository = diChicken.get<ChickenRepositoryImpl>();
|
||||||
|
|
||||||
getInventory();
|
|
||||||
//getKillHouseDistributionInfo();
|
//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) {
|
void toggleExpanded(int index) {
|
||||||
if (inventoryExpandedList.keys.contains(index)) {
|
if (inventoryExpandedList.keys.contains(index)) {
|
||||||
inventoryExpandedList.remove(index);
|
inventoryExpandedList.remove(index);
|
||||||
@@ -61,24 +84,24 @@ class RootLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getInventory() async {
|
Future<void> getInventory() async {
|
||||||
|
// Cancel previous request if still running
|
||||||
|
_inventoryCancelToken?.cancel();
|
||||||
|
_inventoryCancelToken = CancelToken();
|
||||||
|
|
||||||
await safeCall<List<InventoryModel>?>(
|
await safeCall<List<InventoryModel>?>(
|
||||||
call: () async =>
|
call: () async => await chickenRepository.getInventory(
|
||||||
await chickenRepository.getInventory(token: tokenService.accessToken.value!),
|
token: tokenService.accessToken.value!,
|
||||||
|
cancelToken: _inventoryCancelToken,
|
||||||
|
),
|
||||||
onSuccess: (result) {
|
onSuccess: (result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
inventoryModel.value = result.first;
|
inventoryModel.value = result.first;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error, stackTrace) {
|
onError: (error, stackTrace) {
|
||||||
switch (error.response?.statusCode) {
|
if (error is DioException && error.type == DioExceptionType.cancel) {
|
||||||
case 401:
|
// Request was cancelled, ignore the error
|
||||||
errorHandler(error);
|
return;
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
errorHandler(error);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errorHandler(error);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -95,20 +118,22 @@ class RootLogic extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getProvinces() async {
|
Future<void> getProvinces() async {
|
||||||
|
// Cancel previous request if still running
|
||||||
|
_provincesCancelToken?.cancel();
|
||||||
|
_provincesCancelToken = CancelToken();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final res = await chickenRepository.getProvince();
|
final res = await chickenRepository.getProvince(cancelToken: _provincesCancelToken);
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
provinces.clear();
|
provinces.clear();
|
||||||
provinces.value = res;
|
provinces.value = res;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (e is DioException && e.type == DioExceptionType.cancel) {
|
||||||
|
// Request was cancelled, ignore the error
|
||||||
|
return;
|
||||||
|
}
|
||||||
provinces.clear();
|
provinces.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void errorHandler(DioException error) {
|
|
||||||
handleGeneric(error, () {
|
|
||||||
tokenService.deleteTokens();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,12 @@ import 'package:rasadyar_chicken/presentation/pages/root/logic.dart';
|
|||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
class SaleLogic extends GetxController {
|
class SaleLogic extends GetxController {
|
||||||
Rxn<List<AllocatedMadeModel>?> allocatedMadeModel =
|
Rxn<List<AllocatedMadeModel>?> allocatedMadeModel = Rxn<List<AllocatedMadeModel>?>();
|
||||||
Rxn<List<AllocatedMadeModel>?>();
|
|
||||||
RxList<ProductModel> rolesProductsModel = RxList<ProductModel>();
|
RxList<ProductModel> rolesProductsModel = RxList<ProductModel>();
|
||||||
|
|
||||||
RxList<GuildModel> guildsModel = <GuildModel>[].obs;
|
RxList<GuildModel> guildsModel = <GuildModel>[].obs;
|
||||||
|
|
||||||
Rxn<StewardFreeBarDashboard> stewardFreeDashboard =
|
Rxn<StewardFreeBarDashboard> stewardFreeDashboard = Rxn<StewardFreeBarDashboard>();
|
||||||
Rxn<StewardFreeBarDashboard>();
|
|
||||||
|
|
||||||
RootLogic rootLogic = Get.find<RootLogic>();
|
RootLogic rootLogic = Get.find<RootLogic>();
|
||||||
|
|
||||||
@@ -25,6 +23,11 @@ class SaleLogic extends GetxController {
|
|||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
routesName = [...rootLogic.routesName, 'فروش'].toList();
|
routesName = [...rootLogic.routesName, 'فروش'].toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
super.onReady();
|
||||||
getStewardDashBord();
|
getStewardDashBord();
|
||||||
getRolesProducts();
|
getRolesProducts();
|
||||||
}
|
}
|
||||||
@@ -33,12 +36,7 @@ class SaleLogic extends GetxController {
|
|||||||
safeCall(
|
safeCall(
|
||||||
call: () async => await rootLogic.chickenRepository.getAllocatedMade(
|
call: () async => await rootLogic.chickenRepository.getAllocatedMade(
|
||||||
token: rootLogic.tokenService.accessToken.value!,
|
token: rootLogic.tokenService.accessToken.value!,
|
||||||
queryParameters: buildQueryParams(
|
queryParameters: buildQueryParams(page: 1, pageSize: 20, search: 'filter', role: 'Steward'),
|
||||||
page: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
search: 'filter',
|
|
||||||
role: 'Steward',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onSuccess: (result) {
|
onSuccess: (result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
@@ -81,8 +79,7 @@ class SaleLogic extends GetxController {
|
|||||||
safeCall(
|
safeCall(
|
||||||
call: () async => await rootLogic.chickenRepository.confirmAllAllocation(
|
call: () async => await rootLogic.chickenRepository.confirmAllAllocation(
|
||||||
token: rootLogic.tokenService.accessToken.value!,
|
token: rootLogic.tokenService.accessToken.value!,
|
||||||
allocationTokens:
|
allocationTokens: allocatedMadeModel.value?.map((e) => e.key!).toList() ?? [],
|
||||||
allocatedMadeModel.value?.map((e) => e.key!).toList() ?? [],
|
|
||||||
),
|
),
|
||||||
onSuccess: (result) {
|
onSuccess: (result) {
|
||||||
getAllocatedMade();
|
getAllocatedMade();
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import 'package:rasadyar_auth/auth.dart';
|
import 'package:rasadyar_auth/auth.dart';
|
||||||
import 'package:rasadyar_chicken/presentation/pages/buy/logic.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/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/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/buy_out_of_province/view.dart';
|
||||||
import 'package:rasadyar_chicken/presentation/pages/home/logic.dart';
|
import 'package:rasadyar_chicken/presentation/pages/home/logic.dart';
|
||||||
import 'package:rasadyar_chicken/presentation/pages/home/view.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/root/view.dart';
|
||||||
import 'package:rasadyar_chicken/presentation/pages/sale/logic.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/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_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/pages/sales_out_of_province/view.dart';
|
||||||
import 'package:rasadyar_chicken/presentation/routes/routes.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/base_page/logic.dart';
|
||||||
import 'package:rasadyar_chicken/presentation/widget/search/logic.dart';
|
import 'package:rasadyar_chicken/presentation/widget/search/logic.dart';
|
||||||
import 'package:rasadyar_core/core.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 {
|
sealed class ChickenPages {
|
||||||
ChickenPages._();
|
ChickenPages._();
|
||||||
|
|
||||||
@@ -35,12 +26,12 @@ sealed class ChickenPages {
|
|||||||
page: () => RootPage(),
|
page: () => RootPage(),
|
||||||
middlewares: [AuthMiddleware()],
|
middlewares: [AuthMiddleware()],
|
||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.put(RootLogic());
|
Get.lazyPut(() => RootLogic());
|
||||||
|
Get.lazyPut(() => BaseLogic());
|
||||||
Get.lazyPut(() => HomeLogic());
|
Get.lazyPut(() => HomeLogic());
|
||||||
Get.lazyPut(() => BuyLogic());
|
Get.lazyPut(() => BuyLogic());
|
||||||
Get.lazyPut(() => SaleLogic());
|
Get.lazyPut(() => SaleLogic());
|
||||||
Get.lazyPut(() => ProfileLogic());
|
Get.lazyPut(() => ProfileLogic());
|
||||||
Get.lazyPut(() => BaseLogic());
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -50,8 +41,7 @@ sealed class ChickenPages {
|
|||||||
middlewares: [AuthMiddleware()],
|
middlewares: [AuthMiddleware()],
|
||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.put(HomeLogic());
|
Get.put(HomeLogic());
|
||||||
Get.put(RootLogic());
|
//Get.lazyPut(() => BaseLogic());
|
||||||
Get.lazyPut(() => BaseLogic());
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -61,7 +51,7 @@ sealed class ChickenPages {
|
|||||||
middlewares: [AuthMiddleware()],
|
middlewares: [AuthMiddleware()],
|
||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.lazyPut(() => EnteringTheWarehouseLogic());
|
Get.lazyPut(() => EnteringTheWarehouseLogic());
|
||||||
Get.lazyPut(() => RootLogic());
|
// RootLogic already registered in root page binding
|
||||||
}),
|
}),
|
||||||
),*/
|
),*/
|
||||||
|
|
||||||
@@ -72,9 +62,9 @@ sealed class ChickenPages {
|
|||||||
middlewares: [AuthMiddleware()],
|
middlewares: [AuthMiddleware()],
|
||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.lazyPut(() => SaleLogic());
|
Get.lazyPut(() => SaleLogic());
|
||||||
Get.lazyPut(() => BaseLogic());
|
//Get.lazyPut(() => BaseLogic());
|
||||||
Get.lazyPut(() => SalesOutOfProvinceLogic());
|
//Get.lazyPut(() => SalesOutOfProvinceLogic());
|
||||||
Get.lazyPut(() => RootLogic());
|
//Get.lazyPut(() => RootLogic());
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
@@ -82,13 +72,10 @@ sealed class ChickenPages {
|
|||||||
page: () => SalesOutOfProvincePage(),
|
page: () => SalesOutOfProvincePage(),
|
||||||
middlewares: [AuthMiddleware()],
|
middlewares: [AuthMiddleware()],
|
||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.lazyPut(() => RootLogic());
|
|
||||||
Get.lazyPut(() => BaseLogic());
|
|
||||||
Get.lazyPut(() => SearchLogic());
|
Get.lazyPut(() => SearchLogic());
|
||||||
Get.lazyPut(() => SalesOutOfProvinceLogic());
|
// Get.lazyPut(() => SalesOutOfProvinceLogic());
|
||||||
Get.lazyPut(() => SalesOutOfProvinceBuyersLogic());
|
// Get.lazyPut(() => SalesOutOfProvinceBuyersLogic());
|
||||||
Get.lazyPut(() => SalesOutOfProvinceSalesListLogic());
|
// Get.lazyPut(() => SalesOutOfProvinceSalesListLogic());
|
||||||
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
GetPage(
|
GetPage(
|
||||||
@@ -97,8 +84,7 @@ sealed class ChickenPages {
|
|||||||
middlewares: [AuthMiddleware()],
|
middlewares: [AuthMiddleware()],
|
||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.lazyPut(() => BaseLogic());
|
Get.lazyPut(() => BaseLogic());
|
||||||
Get.lazyPut(() => SalesInProvinceLogic());
|
// Get.lazyPut(() => SalesInProvinceLogic());
|
||||||
Get.lazyPut(() => RootLogic());
|
|
||||||
Get.lazyPut(() => SearchLogic());
|
Get.lazyPut(() => SearchLogic());
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -111,7 +97,6 @@ sealed class ChickenPages {
|
|||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.lazyPut(() => BaseLogic());
|
Get.lazyPut(() => BaseLogic());
|
||||||
Get.lazyPut(() => BuyLogic());
|
Get.lazyPut(() => BuyLogic());
|
||||||
Get.lazyPut(() => RootLogic());
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -122,8 +107,7 @@ sealed class ChickenPages {
|
|||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.lazyPut(() => BaseLogic());
|
Get.lazyPut(() => BaseLogic());
|
||||||
Get.lazyPut(() => SearchLogic());
|
Get.lazyPut(() => SearchLogic());
|
||||||
Get.lazyPut(() => BuyOutOfProvinceLogic());
|
// Get.lazyPut(() => BuyOutOfProvinceLogic());
|
||||||
Get.lazyPut(() => RootLogic());
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -134,10 +118,9 @@ sealed class ChickenPages {
|
|||||||
binding: BindingsBuilder(() {
|
binding: BindingsBuilder(() {
|
||||||
Get.lazyPut(() => BaseLogic());
|
Get.lazyPut(() => BaseLogic());
|
||||||
Get.lazyPut(() => SearchLogic());
|
Get.lazyPut(() => SearchLogic());
|
||||||
Get.lazyPut(() => BuyInProvinceLogic());
|
// Get.lazyPut(() => BuyInProvinceLogic());
|
||||||
Get.lazyPut(() => BuyInProvinceWaitingLogic());
|
// Get.lazyPut(() => BuyInProvinceWaitingLogic());
|
||||||
Get.lazyPut(() => BuyInProvinceAllLogic());
|
// Get.lazyPut(() => BuyInProvinceAllLogic());
|
||||||
Get.lazyPut(() => RootLogic());
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
sealed class ChickenRoutes {
|
sealed class ChickenRoutes {
|
||||||
ChickenRoutes._();
|
ChickenRoutes._();
|
||||||
|
|
||||||
static const _base = '/init';
|
static const _base = '/chicken';
|
||||||
static const init = '$_base/root';
|
static const init = '$_base/';
|
||||||
static const home = '$_base/home';
|
static const home = '$_base/home';
|
||||||
static const buy = '$_base/buy';
|
static const buy = '$_base/buy';
|
||||||
static const sale = '$_base/sale';
|
static const sale = '$_base/sale';
|
||||||
|
|||||||
@@ -1,62 +1,101 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import '../../core.dart';
|
import '../../core.dart';
|
||||||
|
|
||||||
typedef RefreshTokenCallback = Future<String?> Function();
|
typedef RefreshTokenCallback = Future<String?> Function();
|
||||||
|
typedef SaveTokenCallback = Future<void> Function(String token);
|
||||||
|
typedef ClearTokenCallback = Future<void> Function();
|
||||||
|
|
||||||
class AppInterceptor extends Interceptor {
|
class AppInterceptor extends Interceptor {
|
||||||
final RefreshTokenCallback refreshTokenCallback;
|
final RefreshTokenCallback? refreshTokenCallback;
|
||||||
Completer<String?>? _refreshCompleter;
|
final SaveTokenCallback saveTokenCallback;
|
||||||
|
final ClearTokenCallback clearTokenCallback;
|
||||||
|
late final Dio dio;
|
||||||
|
static Completer<String?>? _refreshCompleter;
|
||||||
|
static bool _isRefreshing = false;
|
||||||
|
|
||||||
AppInterceptor({required this.refreshTokenCallback});
|
AppInterceptor({
|
||||||
|
required this.saveTokenCallback,
|
||||||
|
required this.clearTokenCallback,
|
||||||
|
this.refreshTokenCallback,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> 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
|
@override
|
||||||
Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
|
Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||||
if (err.response?.statusCode == 401 && !ApiHandler.isRefreshing) {
|
if (err.response?.statusCode == 401) {
|
||||||
ApiHandler.cancelAllRequests("Token expired - refreshing");
|
final retryResult = await _handleUnauthorizedError(err);
|
||||||
|
if (retryResult != null) {
|
||||||
if (_refreshCompleter == null) {
|
handler.resolve(retryResult);
|
||||||
_refreshCompleter = Completer<String?>();
|
return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
handler.next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response?> _handleUnauthorizedError(DioException err) async {
|
||||||
|
if (_isRefreshing && _refreshCompleter != null) {
|
||||||
try {
|
try {
|
||||||
final newToken = await _refreshCompleter!.future;
|
final newToken = await _refreshCompleter!.future;
|
||||||
if (newToken != null) {
|
return newToken != null ? await _retryRequest(err.requestOptions, newToken) : null;
|
||||||
final opts = err.requestOptions;
|
|
||||||
opts.headers['Authorization'] = 'Bearer $newToken';
|
|
||||||
|
|
||||||
final dio = Dio();
|
|
||||||
final cloneReq = await dio.fetch(opts);
|
|
||||||
handler.resolve(cloneReq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
handler.reject(err);
|
return null;
|
||||||
}
|
}
|
||||||
} else if (err.type == DioExceptionType.cancel) {
|
}
|
||||||
handler.next(err);
|
|
||||||
} else {
|
_isRefreshing = true;
|
||||||
handler.next(err);
|
_refreshCompleter = Completer<String?>();
|
||||||
|
|
||||||
|
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<Response> _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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,45 +1,45 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
|
|
||||||
class DioRemote implements IHttpClient {
|
class DioRemote implements IHttpClient {
|
||||||
String? baseUrl;
|
String? baseUrl;
|
||||||
late final Dio _dio;
|
late Dio dio;
|
||||||
final List<Interceptor> interceptors;
|
final AppInterceptor interceptors;
|
||||||
|
|
||||||
DioRemote({this.baseUrl, this.interceptors = const []});
|
|
||||||
|
DioRemote({this.baseUrl, required this.interceptors});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
final dio = Dio(BaseOptions(baseUrl: baseUrl ?? ''));
|
dio = Dio(BaseOptions(baseUrl: baseUrl ?? ''));
|
||||||
dio.interceptors.addAll(interceptors);
|
dio.interceptors.add(interceptors);
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
dio.interceptors.add(
|
dio.interceptors.add(
|
||||||
PrettyDioLogger(
|
PrettyDioLogger(
|
||||||
requestHeader: true,
|
requestHeader: true,
|
||||||
responseHeader: true,
|
responseHeader: true,
|
||||||
requestBody: true,
|
requestBody: true,
|
||||||
|
responseBody: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_dio = dio;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<DioResponse<T>> get<T>(
|
Future<DioResponse<T>> get<T>(
|
||||||
String path, {
|
String path, {
|
||||||
Map<String, dynamic>? queryParameters,
|
Map<String, dynamic>? queryParameters,
|
||||||
Map<String, String>? headers,
|
Map<String, String>? headers,
|
||||||
ProgressCallback? onReceiveProgress,
|
ProgressCallback? onReceiveProgress,
|
||||||
T Function(Map<String, dynamic> json)? fromJson,
|
T Function(Map<String, dynamic> json)? fromJson,
|
||||||
T Function(List<dynamic> json)? fromJsonList,
|
T Function(List<dynamic> json)? fromJsonList,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _dio.get(
|
final response = await dio.get(
|
||||||
path,
|
path,
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
options: Options(headers: headers),
|
options: Options(headers: headers),
|
||||||
onReceiveProgress: onReceiveProgress,
|
onReceiveProgress: onReceiveProgress,
|
||||||
cancelToken: ApiHandler.globalCancelToken
|
cancelToken: ApiHandler.globalCancelToken,
|
||||||
);
|
);
|
||||||
if (fromJsonList != null && response.data is List) {
|
if (fromJsonList != null && response.data is List) {
|
||||||
response.data = fromJsonList(response.data);
|
response.data = fromJsonList(response.data);
|
||||||
@@ -62,21 +62,19 @@ class DioRemote implements IHttpClient {
|
|||||||
ProgressCallback? onSendProgress,
|
ProgressCallback? onSendProgress,
|
||||||
ProgressCallback? onReceiveProgress,
|
ProgressCallback? onReceiveProgress,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _dio.post(
|
final response = await dio.post(
|
||||||
path,
|
path,
|
||||||
data: data,
|
data: data,
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
options: Options(headers: headers),
|
options: Options(headers: headers),
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
onReceiveProgress: onReceiveProgress,
|
onReceiveProgress: onReceiveProgress,
|
||||||
cancelToken: ApiHandler.globalCancelToken
|
cancelToken: ApiHandler.globalCancelToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (fromJson != null) {
|
if (fromJson != null) {
|
||||||
final rawData = response.data;
|
final rawData = response.data;
|
||||||
final parsedData = rawData is Map<String, dynamic>
|
final parsedData = rawData is Map<String, dynamic> ? fromJson(rawData) : null;
|
||||||
? fromJson(rawData)
|
|
||||||
: null;
|
|
||||||
response.data = parsedData;
|
response.data = parsedData;
|
||||||
return DioResponse<T>(response);
|
return DioResponse<T>(response);
|
||||||
}
|
}
|
||||||
@@ -93,14 +91,14 @@ class DioRemote implements IHttpClient {
|
|||||||
ProgressCallback? onSendProgress,
|
ProgressCallback? onSendProgress,
|
||||||
ProgressCallback? onReceiveProgress,
|
ProgressCallback? onReceiveProgress,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _dio.put(
|
final response = await dio.put(
|
||||||
path,
|
path,
|
||||||
data: data,
|
data: data,
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
options: Options(headers: headers),
|
options: Options(headers: headers),
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
onReceiveProgress: onReceiveProgress,
|
onReceiveProgress: onReceiveProgress,
|
||||||
cancelToken: ApiHandler.globalCancelToken
|
cancelToken: ApiHandler.globalCancelToken,
|
||||||
);
|
);
|
||||||
return DioResponse<T>(response);
|
return DioResponse<T>(response);
|
||||||
}
|
}
|
||||||
@@ -112,12 +110,12 @@ class DioRemote implements IHttpClient {
|
|||||||
Map<String, dynamic>? queryParameters,
|
Map<String, dynamic>? queryParameters,
|
||||||
Map<String, String>? headers,
|
Map<String, String>? headers,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _dio.delete<T>(
|
final response = await dio.delete<T>(
|
||||||
path,
|
path,
|
||||||
data: data,
|
data: data,
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
options: Options(headers: headers),
|
options: Options(headers: headers),
|
||||||
cancelToken: ApiHandler.globalCancelToken
|
cancelToken: ApiHandler.globalCancelToken,
|
||||||
);
|
);
|
||||||
return DioResponse<T>(response);
|
return DioResponse<T>(response);
|
||||||
}
|
}
|
||||||
@@ -127,11 +125,11 @@ class DioRemote implements IHttpClient {
|
|||||||
String url, {
|
String url, {
|
||||||
ProgressCallback? onReceiveProgress,
|
ProgressCallback? onReceiveProgress,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _dio.get<Uint8List>(
|
final response = await dio.get<Uint8List>(
|
||||||
url,
|
url,
|
||||||
options: Options(responseType: ResponseType.bytes),
|
options: Options(responseType: ResponseType.bytes),
|
||||||
onReceiveProgress: onReceiveProgress,
|
onReceiveProgress: onReceiveProgress,
|
||||||
cancelToken: ApiHandler.globalCancelToken
|
cancelToken: ApiHandler.globalCancelToken,
|
||||||
);
|
);
|
||||||
return DioResponse(response);
|
return DioResponse(response);
|
||||||
}
|
}
|
||||||
@@ -143,12 +141,12 @@ class DioRemote implements IHttpClient {
|
|||||||
Map<String, String>? headers,
|
Map<String, String>? headers,
|
||||||
ProgressCallback? onSendProgress,
|
ProgressCallback? onSendProgress,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _dio.post(
|
final response = await dio.post(
|
||||||
path,
|
path,
|
||||||
data: (formData as DioFormData).raw,
|
data: (formData as DioFormData).raw,
|
||||||
options: Options(headers: headers, contentType: 'multipart/form-data'),
|
options: Options(headers: headers, contentType: 'multipart/form-data'),
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
cancelToken: ApiHandler.globalCancelToken
|
cancelToken: ApiHandler.globalCancelToken,
|
||||||
);
|
);
|
||||||
return DioResponse<T>(response);
|
return DioResponse<T>(response);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Future<void> _setUpLogger() async{
|
|||||||
|
|
||||||
Future<void> _setupLocalStorage() async {
|
Future<void> _setupLocalStorage() async {
|
||||||
diCore.registerSingleton<HiveLocalStorage>(HiveLocalStorage());
|
diCore.registerSingleton<HiveLocalStorage>(HiveLocalStorage());
|
||||||
|
print('====> HiveLocalStorage registered');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,84 +1,169 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../core.dart';
|
import '../../core.dart';
|
||||||
|
|
||||||
class ApiHandler {
|
class ApiHandler {
|
||||||
static bool _isRefreshing = false;
|
static CancelToken _globalCancelToken = CancelToken();
|
||||||
static bool _isRedirecting = false;
|
|
||||||
static CancelToken globalCancelToken = CancelToken();
|
static CancelToken get globalCancelToken => _globalCancelToken;
|
||||||
|
|
||||||
static Future<void> reset() async {
|
static Future<void> reset() async {
|
||||||
_isRefreshing = false;
|
_globalCancelToken = CancelToken();
|
||||||
_isRedirecting = false;
|
|
||||||
globalCancelToken = CancelToken();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void cancelAllRequests(String reason) {
|
static void cancelAllRequests(String reason) {
|
||||||
if (!globalCancelToken.isCancelled) {
|
if (!_globalCancelToken.isCancelled) {
|
||||||
globalCancelToken.cancel(reason);
|
_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 AppAsyncCallback<T> = Future<T> Function();
|
||||||
typedef ErrorCallback = Function(dynamic error, StackTrace? stackTrace);
|
typedef ErrorCallback = Function(dynamic error, StackTrace? stackTrace);
|
||||||
typedef VoidCallback = void Function();
|
typedef VoidCallback = void Function();
|
||||||
|
|
||||||
/// this is global safe call function
|
Future<T?> gSafeCall<T>({
|
||||||
/// A utility function to safely cal l an asynchronous function with error
|
|
||||||
/// handling and optional loading, success, and error messages.
|
|
||||||
Future<void> gSafeCall<T>({
|
|
||||||
required AppAsyncCallback<T> call,
|
required AppAsyncCallback<T> call,
|
||||||
Function(T result)? onSuccess,
|
Function(T result)? onSuccess,
|
||||||
ErrorCallback? onError,
|
ErrorCallback? onError,
|
||||||
VoidCallback? onComplete,
|
VoidCallback? onComplete,
|
||||||
bool showLoading = false,
|
bool showLoading = false,
|
||||||
bool showError = false,
|
bool showError = true,
|
||||||
bool showSuccess = false,
|
bool showSuccess = false,
|
||||||
bool showToast = false,
|
|
||||||
bool showSnackBar = false,
|
|
||||||
Function()? onShowLoading,
|
Function()? onShowLoading,
|
||||||
Function()? onHideLoading,
|
Function()? onHideLoading,
|
||||||
Function()? onShowSuccessMessage,
|
Function(String message)? onShowErrorMessage,
|
||||||
Function()? onShowErrorMessage,
|
Function(String message)? onShowSuccessMessage,
|
||||||
|
int maxRetries = 0,
|
||||||
|
Duration retryDelay = const Duration(seconds: 1),
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
int retryCount = 0;
|
||||||
if (showLoading) (onShowLoading ?? _defaultShowLoading)();
|
|
||||||
final result = await call();
|
while (retryCount <= maxRetries) {
|
||||||
if (showSuccess) (onShowSuccessMessage ?? _defaultShowSuccessMessage)();
|
try {
|
||||||
onSuccess?.call(result);
|
if (showLoading && retryCount == 0) {
|
||||||
} catch (error, stackTrace) {
|
(onShowLoading ?? _defaultShowLoading)();
|
||||||
if (showError) (onShowErrorMessage ?? _defaultShowErrorMessage)();
|
}
|
||||||
onError?.call(error, stackTrace);
|
|
||||||
} finally {
|
final result = await call();
|
||||||
if (showLoading) (onHideLoading ?? _defaultHideLoading)();
|
|
||||||
onComplete?.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() {
|
void _defaultShowLoading() {
|
||||||
// پیادهسازی پیشفرض
|
// نمایش loading
|
||||||
|
Get.dialog(
|
||||||
|
Center(child: CircularProgressIndicator()),
|
||||||
|
barrierDismissible: false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _defaultHideLoading() {
|
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() {
|
void _defaultShowErrorMessage(String message) {
|
||||||
// پیادهسازی پیشفرض
|
Get.snackbar(
|
||||||
}
|
'خطا',
|
||||||
|
message,
|
||||||
bool isTokenExpiredError(dynamic error) {
|
snackPosition: SnackPosition.TOP,
|
||||||
return error is DioException && error.response?.statusCode == 401;
|
backgroundColor: Colors.red,
|
||||||
|
colorText: Colors.white,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1162,7 +1162,7 @@ packages:
|
|||||||
path: "packages/chicken"
|
path: "packages/chicken"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "1.0.0"
|
version: "1.0.1"
|
||||||
rasadyar_core:
|
rasadyar_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user