feat : new injection logic

test : some file :)
chore : upgrade android gradle
This commit is contained in:
2025-08-19 11:22:34 +03:30
parent 9b04c0374b
commit 7c3c1280b2
47 changed files with 1139 additions and 377 deletions

View File

@@ -2,6 +2,7 @@ library;
export 'package:android_intent_plus/android_intent.dart';
export 'package:android_intent_plus/flag.dart';
export 'package:connectivity_plus/connectivity_plus.dart';
export 'package:device_info_plus/device_info_plus.dart';
export 'package:dio/dio.dart';
//other packages

View File

@@ -0,0 +1,24 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart';
class NetworkStatus {
NetworkStatus._();
static final NetworkStatus _instance = NetworkStatus._();
factory NetworkStatus() => _instance;
final Connectivity _connectivity = Connectivity();
RxBool isConnected = false.obs;
void startListening() {
_connectivity.onConnectivityChanged.listen((result) {
isConnected.value = !result.contains(ConnectivityResult.none);
});
_connectivity.checkConnectivity().then((result) {
isConnected.value = !result.contains(ConnectivityResult.none);
});
}
}

View File

@@ -0,0 +1,168 @@
import 'dart:async';
import '../../core.dart';
/// Callback to refresh the authentication token.
/// Typically used to request a new token from the server.
typedef RefreshTokenCallback = Future<String?> Function();
/// Callback to save a new authentication token.
typedef SaveTokenCallback = Future<void> Function(String token);
/// Callback to clear the authentication token, e.g., on logout or failure.
typedef ClearTokenCallback = Future<void> Function();
/// Callback invoked when token refresh fails.
/// Typically used to redirect the user to login or show a logout message.
typedef OnRefreshFailedCallback = Future<void> Function();
/// Represents a queued request waiting for token refresh.
class QueuedRequest {
/// The original request options.
final RequestOptions options;
/// Completer used to complete the response once the request is retried.
final Completer<Response> completer;
/// Constructs a queued request.
QueuedRequest(this.options, this.completer);
}
/// An interceptor for automatic token management and refresh handling.
///
/// Features:
/// - Queues requests while a token refresh is in progress.
/// - Saves and clears tokens via provided callbacks.
/// - Calls [OnRefreshFailedCallback] if token refresh fails.
class AppInterceptorN extends Interceptor {
/// Callback to refresh the authentication token.
final RefreshTokenCallback? refreshTokenCallback;
/// Callback to save the new token.
final SaveTokenCallback saveTokenCallback;
/// Callback to clear the token.
final ClearTokenCallback clearTokenCallback;
/// Callback executed when token refresh fails.
final OnRefreshFailedCallback onRefreshFailed;
/// Optional additional arguments for authentication.
final dynamic authArguments;
/// The Dio instance used to send requests.
final Dio dio;
/// Maximum number of retry attempts for failed requests.
final int maxRetries;
/// Whether a token refresh is currently in progress.
bool _isRefreshing = false;
/// Queue of requests waiting for a new token.
final List<QueuedRequest> _queue = [];
/// Current token in use.
String? _currentToken;
/// Constructs the interceptor.
AppInterceptorN({
required this.dio,
required this.saveTokenCallback,
required this.clearTokenCallback,
required this.onRefreshFailed,
this.refreshTokenCallback,
this.authArguments,
this.maxRetries = 3,
});
/// Called before sending a request.
/// If a token refresh is in progress, the request is added to the queue.
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
if (_isRefreshing) {
final completer = Completer<Response>();
_queue.add(QueuedRequest(options, completer));
return handler.resolve(await completer.future);
}
handler.next(options);
}
/// Called when an error occurs during a request.
///
/// - If the error is a 401 (unauthorized) and retry count is below `maxRetries`,
/// the token is refreshed and queued requests are retried.
/// - If the token refresh fails, all queued requests are cancelled and
/// [onRefreshFailed] is executed.
@override
Future<void> onError(DioException err, ErrorInterceptorHandler handler) async {
int currentRetry = err.requestOptions.extra['retryCount'] ?? 0;
if (err.response?.statusCode == 401 &&
err.type != DioExceptionType.cancel &&
currentRetry < maxRetries) {
final completer = Completer<Response>();
final updatedOptions = err.requestOptions.copyWith(
extra: {...err.requestOptions.extra, 'retryCount': currentRetry + 1},
);
_queue.add(QueuedRequest(updatedOptions, completer));
if (!_isRefreshing) {
_isRefreshing = true;
try {
final newToken = await refreshTokenCallback?.call();
if (newToken != null && newToken.isNotEmpty) {
_currentToken = newToken;
await saveTokenCallback(newToken);
for (var req in _queue) {
final newOptions = req.options.copyWith(
headers: {...req.options.headers, 'Authorization': 'Bearer $newToken'},
);
dio
.fetch(newOptions)
.then(req.completer.complete)
.catchError(req.completer.completeError);
}
} else {
await clearTokenCallback();
await _handleRefreshFailure();
for (var req in _queue) {
req.completer.completeError(
DioException(requestOptions: req.options, type: DioExceptionType.cancel),
);
}
}
} catch (e) {
await clearTokenCallback();
await _handleRefreshFailure();
for (var req in _queue) {
req.completer.completeError(e);
}
} finally {
_queue.clear();
_isRefreshing = false;
}
}
return handler.resolve(await completer.future);
}
handler.next(err);
}
/// Handles token refresh failure:
/// - Cancels all ongoing requests via [ApiHandler].
/// - Executes external [onRefreshFailed] callback.
Future<void> _handleRefreshFailure() async {
ApiHandler.cancelAllRequests("Token refresh failed");
await onRefreshFailed.call();
}
@visibleForTesting
set isRefreshingForTest(bool value) => _isRefreshing = value;
@visibleForTesting
List<QueuedRequest> get queue => _queue;
}

View File

@@ -1,15 +1,15 @@
import 'package:get_it/get_it.dart';
import 'package:logger/logger.dart';
import 'package:rasadyar_core/data/services/auth_middelware.dart';
import 'package:rasadyar_core/data/services/network_status.dart';
import 'package:rasadyar_core/infrastructure/local/hive_local_storage.dart';
final diCore = GetIt.instance;
Future<void> setupAllCoreProvider() async {
await _setUpLogger();
await _setupLocalStorage();
await _setupRemote();
diCore.registerSingleton(NetworkStatus()..startListening());
await diCore.allReady();
}
@@ -23,4 +23,4 @@ Future<void> _setupLocalStorage() async {
Future<void> _setupRemote() async {
// diCore.registerSingleton<HiveLocalStorage>(HiveLocalStorage());
}
}

View File

@@ -1,16 +1,21 @@
import 'package:flutter/material.dart';
import '../../core.dart';
/// Handles global API requests management with CancelToken.
class ApiHandler {
// Global CancelToken for all requests.
static CancelToken _globalCancelToken = CancelToken();
/// Returns the current global CancelToken.
static CancelToken get globalCancelToken => _globalCancelToken;
/// Resets the global CancelToken to a new one.
static Future<void> reset() async {
_globalCancelToken = CancelToken();
}
/// Cancels all ongoing requests and resets the CancelToken.
/// [reason] is optional text explaining why requests are canceled.
static void cancelAllRequests(String reason) {
if (!_globalCancelToken.isCancelled) {
_globalCancelToken.cancel(reason);