feat : refresh login
test: core
This commit is contained in:
@@ -2,9 +2,7 @@ import 'package:flutter/material.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';
|
||||||
|
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
import 'infrastructure/di/di.dart';
|
import 'infrastructure/di/di.dart';
|
||||||
import 'infrastructure/service/auth_service.dart';
|
import 'infrastructure/service/auth_service.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -18,58 +18,32 @@ class TokenStorageService extends GetxService {
|
|||||||
Rxn<Module> appModule = Rxn(null);
|
Rxn<Module> appModule = Rxn(null);
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
|
await Hive.initFlutter();
|
||||||
Hive.registerAdapters();
|
Hive.registerAdapters();
|
||||||
IsolatedHive.registerAdapters();
|
|
||||||
|
|
||||||
final String? encryptedKey = await _secureStorage.read(key: 'hive_enc_key');
|
final String? encryptedKey = await _secureStorage.read(key: 'hive_enc_key');
|
||||||
final encryptionKey =
|
final encryptionKey = encryptedKey != null ? base64Url.decode(encryptedKey) : Hive.generateSecureKey();
|
||||||
encryptedKey != null
|
|
||||||
? base64Url.decode(encryptedKey)
|
|
||||||
: Hive.generateSecureKey();
|
|
||||||
|
|
||||||
if (encryptedKey == null) {
|
if (encryptedKey == null) {
|
||||||
await _secureStorage.write(
|
await _secureStorage.write(key: 'hive_enc_key', value: base64UrlEncode(encryptionKey));
|
||||||
key: 'hive_enc_key',
|
|
||||||
value: base64UrlEncode(encryptionKey),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await _localStorage.init();
|
await _localStorage.init();
|
||||||
await _localStorage.openBox(
|
await _localStorage.openBox(_boxName, encryptionCipher: HiveAesCipher(encryptionKey));
|
||||||
_boxName,
|
|
||||||
encryptionCipher: HiveAesCipher(encryptionKey),
|
|
||||||
);
|
|
||||||
|
|
||||||
accessToken.value = _localStorage.read<String?>(
|
accessToken.value = _localStorage.read<String?>(boxName: _boxName, key: _accessTokenKey);
|
||||||
boxName: _boxName,
|
refreshToken.value = _localStorage.read<String?>(boxName: _boxName, key: _refreshTokenKey);
|
||||||
key: _accessTokenKey,
|
appModule.value = _localStorage.read<Module?>(boxName: _boxName, key: _moduleKey);
|
||||||
);
|
|
||||||
refreshToken.value = _localStorage.read<String?>(
|
|
||||||
boxName: _boxName,
|
|
||||||
key: _refreshTokenKey,
|
|
||||||
);
|
|
||||||
appModule.value = _localStorage.read<Module?>(
|
|
||||||
boxName: _boxName,
|
|
||||||
key: _moduleKey,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveAccessToken(String token) async {
|
Future<void> saveAccessToken(String token) async {
|
||||||
await _localStorage.save(
|
await _localStorage.save(boxName: _boxName, key: _accessTokenKey, value: token);
|
||||||
boxName: _boxName,
|
|
||||||
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(
|
await _localStorage.save(boxName: _boxName, key: _refreshTokenKey, value: token);
|
||||||
boxName: _boxName,
|
|
||||||
key: _refreshTokenKey,
|
|
||||||
value: token,
|
|
||||||
);
|
|
||||||
refreshToken.value = token;
|
refreshToken.value = token;
|
||||||
refreshToken.refresh();
|
refreshToken.refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
61
packages/auth/lib/data/utils/safe_call.dart
Normal file
61
packages/auth/lib/data/utils/safe_call.dart
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:rasadyar_auth/auth.dart';
|
||||||
|
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
||||||
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
|
import '../models/response/auth/auth_response_model.dart';
|
||||||
|
import '../services/token_storage_service.dart';
|
||||||
|
|
||||||
|
Future<void> safeCall<T>({
|
||||||
|
required AppAsyncCallback<T> call,
|
||||||
|
Function(T result)? onSuccess,
|
||||||
|
ErrorCallback? onError,
|
||||||
|
VoidCallback? onComplete,
|
||||||
|
bool showLoading = false,
|
||||||
|
bool showError = false,
|
||||||
|
bool showSuccess = false,
|
||||||
|
bool showToast = false,
|
||||||
|
bool showSnackBar = false,
|
||||||
|
Function()? onShowLoading,
|
||||||
|
Function()? onHideLoading,
|
||||||
|
Function()? onShowSuccessMessage,
|
||||||
|
Function()? onShowErrorMessage,
|
||||||
|
}) {
|
||||||
|
final authRepository = diAuth.get<AuthRepositoryImpl>();
|
||||||
|
TokenStorageService tokenStorageService = Get.find<TokenStorageService>();
|
||||||
|
|
||||||
|
return gSafeCall(
|
||||||
|
call: call,
|
||||||
|
onSuccess: onSuccess,
|
||||||
|
onError: onError,
|
||||||
|
onComplete: onComplete,
|
||||||
|
showLoading: showLoading,
|
||||||
|
showError: showError,
|
||||||
|
showSuccess: showSuccess,
|
||||||
|
showToast: showToast,
|
||||||
|
showSnackBar: showSnackBar,
|
||||||
|
onShowLoading: onShowLoading,
|
||||||
|
onHideLoading: onHideLoading,
|
||||||
|
onShowSuccessMessage: onShowSuccessMessage,
|
||||||
|
onShowErrorMessage: onShowErrorMessage,
|
||||||
|
retryOnAuthError: true,
|
||||||
|
onTokenRefresh: () {
|
||||||
|
var token = tokenStorageService.refreshToken.value;
|
||||||
|
authRepository
|
||||||
|
.loginWithRefreshToken(authRequest: {"refresh_token": token})
|
||||||
|
.then((value) async {
|
||||||
|
if (value is AuthResponseModel) {
|
||||||
|
await tokenStorageService.saveAccessToken(value.access!);
|
||||||
|
} else {
|
||||||
|
throw Exception("Failed to refresh token");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catchError((error) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
print('Error during token refresh: $error');
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,8 +7,9 @@ import 'package:rasadyar_auth/data/models/local/user_local/user_local_model.dart
|
|||||||
|
|
||||||
extension HiveRegistrar on HiveInterface {
|
extension HiveRegistrar on HiveInterface {
|
||||||
void registerAdapters() {
|
void registerAdapters() {
|
||||||
registerAdapter(ModuleAdapter());
|
|
||||||
registerAdapter(UserLocalModelAdapter());
|
registerAdapter(UserLocalModelAdapter());
|
||||||
|
registerAdapter(ModuleAdapter());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:rasadyar_auth/data/models/request/login_request/login_request_mo
|
|||||||
import 'package:rasadyar_auth/data/models/response/auth/auth_response_model.dart';
|
import 'package:rasadyar_auth/data/models/response/auth/auth_response_model.dart';
|
||||||
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.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/presentation/widget/captcha/logic.dart';
|
import 'package:rasadyar_auth/presentation/widget/captcha/logic.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
@@ -73,7 +74,6 @@ class AuthLogic extends GetxController {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
tokenStorageService.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -120,8 +120,6 @@ class AuthLogic extends GetxController {
|
|||||||
await tokenStorageService.saveModule(_module);
|
await tokenStorageService.saveModule(_module);
|
||||||
await tokenStorageService.saveRefreshToken(result?.refresh ?? '');
|
await tokenStorageService.saveRefreshToken(result?.refresh ?? '');
|
||||||
await tokenStorageService.saveAccessToken(result?.access ?? '');
|
await tokenStorageService.saveAccessToken(result?.access ?? '');
|
||||||
|
|
||||||
//Get.offAndToNamed(Routes.home);
|
|
||||||
},
|
},
|
||||||
onError: (error, stackTrace) {
|
onError: (error, stackTrace) {
|
||||||
if (error is DioException) {
|
if (error is DioException) {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:rasadyar_auth/data/di/auth_di.dart';
|
import 'package:rasadyar_auth/data/di/auth_di.dart';
|
||||||
import 'package:rasadyar_auth/data/models/response/captcha/captcha_response_model.dart';
|
import 'package:rasadyar_auth/data/models/response/captcha/captcha_response_model.dart';
|
||||||
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart';
|
||||||
|
import 'package:rasadyar_auth/data/utils/safe_call.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
class CaptchaWidgetLogic extends GetxController
|
class CaptchaWidgetLogic extends GetxController with StateMixin<CaptchaResponseModel> {
|
||||||
with StateMixin<CaptchaResponseModel> {
|
|
||||||
Rx<TextEditingController> textController = TextEditingController().obs;
|
Rx<TextEditingController> textController = TextEditingController().obs;
|
||||||
RxnString captchaKey = RxnString();
|
RxnString captchaKey = RxnString();
|
||||||
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||||
|
|||||||
1
packages/core/build/unit_test_assets/AssetManifest.json
Normal file
1
packages/core/build/unit_test_assets/AssetManifest.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_map/lib/assets/flutter_map_logo.png":["packages/flutter_map/lib/assets/flutter_map_logo.png"],"packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf":["packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"]}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
extension ColorUtils on Color {
|
extension ColorUtils on Color {
|
||||||
Color _darken([double amount = 0.1]) {
|
Color _darken([double amount = 0.1]) {
|
||||||
assert(amount >= 0 && amount <= 1, 'مقدار تیرگی باید بین 0 و 1 باشد');
|
assert(amount >= 0 && amount <= 1, 'Amount must be between 0 and 1');
|
||||||
final hslColor = HSLColor.fromColor(this);
|
final hslColor = HSLColor.fromColor(this);
|
||||||
final newLightness = (hslColor.lightness - amount).clamp(0.0, 1.0);
|
final newLightness = (hslColor.lightness - amount).clamp(0.0, 1.0);
|
||||||
final hslDarkerColor = hslColor.withLightness(newLightness);
|
final hslDarkerColor = hslColor.withLightness(newLightness);
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rasadyar_core/core.dart';
|
|
||||||
|
|
||||||
typedef AsyncCallback<T> = Future<T> Function();
|
import '../core.dart';
|
||||||
|
|
||||||
|
typedef AppAsyncCallback<T> = Future<T> Function();
|
||||||
typedef ErrorCallback = void Function(dynamic error, StackTrace? stackTrace);
|
typedef ErrorCallback = void Function(dynamic error, StackTrace? stackTrace);
|
||||||
typedef VoidCallback = void Function();
|
typedef VoidCallback = void Function();
|
||||||
|
|
||||||
// تعریف دقیق تابع safeCall
|
/// this is global safe call function
|
||||||
Future<void> safeCall<T>({
|
/// A utility function to safely cal l an asynchronous function with error
|
||||||
required AsyncCallback<T> call,
|
/// handling and optional loading, success, and error messages.
|
||||||
|
///
|
||||||
|
Future<void> gSafeCall<T>({
|
||||||
|
required AppAsyncCallback<T> call,
|
||||||
Function(T result)? onSuccess,
|
Function(T result)? onSuccess,
|
||||||
ErrorCallback? onError,
|
ErrorCallback? onError,
|
||||||
VoidCallback? onComplete,
|
VoidCallback? onComplete,
|
||||||
@@ -17,6 +20,8 @@ Future<void> safeCall<T>({
|
|||||||
bool showSuccess = false,
|
bool showSuccess = false,
|
||||||
bool showToast = false,
|
bool showToast = false,
|
||||||
bool showSnackBar = false,
|
bool showSnackBar = false,
|
||||||
|
bool retryOnAuthError = false,
|
||||||
|
Function()? onTokenRefresh,
|
||||||
Function()? onShowLoading,
|
Function()? onShowLoading,
|
||||||
Function()? onHideLoading,
|
Function()? onHideLoading,
|
||||||
Function()? onShowSuccessMessage,
|
Function()? onShowSuccessMessage,
|
||||||
@@ -34,18 +39,34 @@ Future<void> safeCall<T>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSuccess?.call(result);
|
onSuccess?.call(result);
|
||||||
|
|
||||||
|
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
if (showError) {
|
if (retryOnAuthError && isTokenExpiredError(error)) {
|
||||||
(onShowErrorMessage ?? _defaultShowErrorMessage)();
|
try {
|
||||||
|
await onTokenRefresh?.call();
|
||||||
|
final retryResult = await call();
|
||||||
|
if (showSuccess) {
|
||||||
|
(onShowSuccessMessage ?? _defaultShowSuccessMessage)();
|
||||||
|
}
|
||||||
|
onSuccess?.call(retryResult);
|
||||||
|
return;
|
||||||
|
} catch (retryError, retryStackTrace) {
|
||||||
|
if (showError) {
|
||||||
|
(onShowErrorMessage ?? _defaultShowErrorMessage)();
|
||||||
|
}
|
||||||
|
onError?.call(retryError, retryStackTrace);
|
||||||
|
if (kDebugMode) {
|
||||||
|
print('safeCall retry error: $retryError\n$retryStackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (showError) {
|
||||||
|
(onShowErrorMessage ?? _defaultShowErrorMessage)();
|
||||||
|
}
|
||||||
|
onError?.call(error, stackTrace);
|
||||||
|
if (kDebugMode) {
|
||||||
|
print('safeCall error: $error\n$stackTrace');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onError?.call(error, stackTrace);
|
|
||||||
if (kDebugMode) {
|
|
||||||
print('safeCall error: $error\n$stackTrace');
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
if (showLoading) {
|
if (showLoading) {
|
||||||
(onHideLoading ?? _defaultHideLoading)();
|
(onHideLoading ?? _defaultHideLoading)();
|
||||||
@@ -69,4 +90,8 @@ void _defaultShowSuccessMessage() {
|
|||||||
|
|
||||||
void _defaultShowErrorMessage() {
|
void _defaultShowErrorMessage() {
|
||||||
// پیادهسازی پیشفرض
|
// پیادهسازی پیشفرض
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isTokenExpiredError(dynamic error) {
|
||||||
|
return error is DioException && error.response?.statusCode == 401;
|
||||||
|
}
|
||||||
|
|||||||
112
packages/core/test/infrastructure/local/hive_local_storage.dart
Normal file
112
packages/core/test/infrastructure/local/hive_local_storage.dart
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
|
import 'i_local_storage.dart';
|
||||||
|
|
||||||
|
class HiveLocalStorage implements ILocalStorage {
|
||||||
|
HiveLocalStorage() {
|
||||||
|
Hive.initFlutter();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, Box> _boxes = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future init() async => await Hive.initFlutter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> openBox<T>(
|
||||||
|
String boxName, {
|
||||||
|
HiveCipher? encryptionCipher,
|
||||||
|
bool crashRecovery = true,
|
||||||
|
String? path,
|
||||||
|
Uint8List? bytes,
|
||||||
|
String? collection,
|
||||||
|
}) async {
|
||||||
|
if (!_boxes.containsKey(boxName)) {
|
||||||
|
final box = await Hive.openBox<T>(
|
||||||
|
boxName,
|
||||||
|
encryptionCipher: encryptionCipher,
|
||||||
|
crashRecovery: crashRecovery,
|
||||||
|
);
|
||||||
|
_boxes[boxName] = box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
T? read<T>({required String boxName, required String key}) {
|
||||||
|
try {
|
||||||
|
Box? box = getBox(boxName);
|
||||||
|
return box?.get(key) as T?;
|
||||||
|
} on Exception catch (e) {
|
||||||
|
eLog(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> add({required String boxName, required dynamic value}) async {
|
||||||
|
Box<dynamic>? box = getBox(boxName);
|
||||||
|
await box?.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> addAll({
|
||||||
|
required String boxName,
|
||||||
|
required Iterable values,
|
||||||
|
}) async {
|
||||||
|
Box<dynamic>? box = getBox(boxName);
|
||||||
|
await box?.addAll(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box<T>? getBox<T>(String boxName) {
|
||||||
|
final box = _boxes[boxName];
|
||||||
|
if (box is Box<T>) {
|
||||||
|
return box;
|
||||||
|
} else {
|
||||||
|
throw Exception('Box $boxName is not of exist');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> clear(String boxName) async {
|
||||||
|
await _boxes[boxName]?.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close(String boxName) async => await _boxes[boxName]?.close();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteValue({
|
||||||
|
required String boxName,
|
||||||
|
required String key,
|
||||||
|
}) async {
|
||||||
|
Box<dynamic>? box = getBox(boxName);
|
||||||
|
await box?.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> save({
|
||||||
|
required String boxName,
|
||||||
|
required String key,
|
||||||
|
required value,
|
||||||
|
}) async {
|
||||||
|
Box<dynamic>? box = getBox(boxName);
|
||||||
|
await box?.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveAll({required String boxName, required Map entries}) async {
|
||||||
|
Box<dynamic>? box = getBox(boxName);
|
||||||
|
await box?.putAll(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> saveAt({
|
||||||
|
required String boxName,
|
||||||
|
required int index,
|
||||||
|
required value,
|
||||||
|
}) async {
|
||||||
|
Box<dynamic>? box = getBox(boxName);
|
||||||
|
await box?.putAt(index, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
packages/core/test/infrastructure/local/i_local_storage.dart
Normal file
44
packages/core/test/infrastructure/local/i_local_storage.dart
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:hive_ce/hive.dart';
|
||||||
|
|
||||||
|
abstract class ILocalStorage<E> {
|
||||||
|
Future<void> init();
|
||||||
|
|
||||||
|
Future<void> openBox<T>(
|
||||||
|
String boxName, {
|
||||||
|
HiveCipher? encryptionCipher,
|
||||||
|
bool crashRecovery = true,
|
||||||
|
String? path,
|
||||||
|
Uint8List? bytes,
|
||||||
|
String? collection,
|
||||||
|
});
|
||||||
|
|
||||||
|
T? read<T>({required String boxName, required String key});
|
||||||
|
|
||||||
|
Future<void> deleteValue({required String boxName, required String key});
|
||||||
|
|
||||||
|
Future<void> add({required String boxName, required E value});
|
||||||
|
|
||||||
|
Future<void> addAll({required String boxName, required Iterable<E> values});
|
||||||
|
|
||||||
|
Future<void> clear(String boxName);
|
||||||
|
|
||||||
|
Future<void> close(String boxName);
|
||||||
|
|
||||||
|
Future<void> save({
|
||||||
|
required String boxName,
|
||||||
|
required String key,
|
||||||
|
required dynamic value,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> saveAt({
|
||||||
|
required String boxName,
|
||||||
|
required int index,
|
||||||
|
required dynamic value,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> saveAll({
|
||||||
|
required String boxName,
|
||||||
|
required Map<dynamic, E> entries,
|
||||||
|
});
|
||||||
|
}
|
||||||
23
packages/core/test/infrastructure/remote/dio_form_data.dart
Normal file
23
packages/core/test/infrastructure/remote/dio_form_data.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'interfaces/i_form_data.dart';
|
||||||
|
|
||||||
|
class DioFormData implements IFormData {
|
||||||
|
final FormData _formData = FormData();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addFile(String field, Uint8List bytes, String filename) {
|
||||||
|
_formData.files.add(MapEntry(
|
||||||
|
field,
|
||||||
|
MultipartFile.fromBytes(bytes, filename: filename),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addField(String key, String value) {
|
||||||
|
_formData.fields.add(MapEntry(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
FormData get raw => _formData;
|
||||||
|
}
|
||||||
20
packages/core/test/infrastructure/remote/dio_response.dart
Normal file
20
packages/core/test/infrastructure/remote/dio_response.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'interfaces/i_http_response.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
class DioResponse<T> implements IHttpResponse<T> {
|
||||||
|
final Response<dynamic> _response;
|
||||||
|
|
||||||
|
DioResponse(this._response);
|
||||||
|
|
||||||
|
@override
|
||||||
|
T? get data => _response.data;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get statusCode => _response.statusCode ?? 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic>? get headers => _response.headers.map;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isSuccessful => statusCode >= 200 && statusCode < 300;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
abstract class IFormData{
|
||||||
|
void addFile(String field, Uint8List bytes, String filename);
|
||||||
|
void addField(String key, String value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'i_form_data.dart';
|
||||||
|
import 'i_http_response.dart';
|
||||||
|
|
||||||
|
abstract class IHttpClient {
|
||||||
|
Future<void> init();
|
||||||
|
|
||||||
|
Future<IHttpResponse<T>> get<T>(
|
||||||
|
String path, {
|
||||||
|
Map<String, dynamic>? queryParameters,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Future<IHttpResponse<T>> post<T>(
|
||||||
|
String path, {
|
||||||
|
dynamic data,
|
||||||
|
Map<String, dynamic>? queryParameters,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<IHttpResponse<T>> put<T>(
|
||||||
|
String path, {
|
||||||
|
dynamic data,
|
||||||
|
Map<String, dynamic>? queryParameters,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<IHttpResponse<T>> delete<T>(
|
||||||
|
String path, {
|
||||||
|
dynamic data,
|
||||||
|
Map<String, dynamic>? queryParameters,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<IHttpResponse<T>> download<T>(
|
||||||
|
String url, {
|
||||||
|
ProgressCallback? onReceiveProgress,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<IHttpResponse<T>> upload<T>(
|
||||||
|
String path, {
|
||||||
|
required IFormData formData,
|
||||||
|
Map<String, String>? headers,
|
||||||
|
ProgressCallback? onSendProgress,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
abstract class IHttpResponse<T> {
|
||||||
|
T? get data;
|
||||||
|
int get statusCode;
|
||||||
|
Map<String, dynamic>? get headers;
|
||||||
|
bool get isSuccessful;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
abstract class IRemote<T>{
|
||||||
|
Future<T> init();
|
||||||
|
}
|
||||||
|
|
||||||
18
packages/core/test/injection/di_test.dart
Normal file
18
packages/core/test/injection/di_test.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
import 'package:rasadyar_core/core.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
setUp(() async {
|
||||||
|
await setupAllCoreProvider();
|
||||||
|
});
|
||||||
|
|
||||||
|
group('di', () {
|
||||||
|
test('is ready', () {
|
||||||
|
expect(diCore.isRegistered<Logger>(), isTrue);
|
||||||
|
expect(diCore.isRegistered<HiveLocalStorage>(), isTrue);
|
||||||
|
expect(diCore.get<Logger>(), isA<Logger>());
|
||||||
|
expect(diCore.get<HiveLocalStorage>(), isA<HiveLocalStorage>());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
43
packages/core/test/presentation/common/app_color_test.dart
Normal file
43
packages/core/test/presentation/common/app_color_test.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:rasadyar_core/presentation/common/app_color.dart';
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('AppColor', () {
|
||||||
|
test('blue colors', () {
|
||||||
|
expect(AppColor.blueLight, const Color(0xFFeaefff));
|
||||||
|
expect(AppColor.blueLightHover, const Color(0xFFe0e7ff));
|
||||||
|
expect(AppColor.blueLightActive, const Color(0xFFbecdff));
|
||||||
|
expect(AppColor.blueNormal, const Color(0xFF2d5fff));
|
||||||
|
expect(AppColor.blueNormalHover, const Color(0xFF2956e6));
|
||||||
|
expect(AppColor.blueNormalActive, const Color(0xFF244ccc));
|
||||||
|
expect(AppColor.blueDark, const Color(0xFF2247bf));
|
||||||
|
expect(AppColor.blueDarkHover, const Color(0xFF1b3999));
|
||||||
|
expect(AppColor.blueDarkActive, const Color(0xFF142b73));
|
||||||
|
expect(AppColor.blueDarker, const Color(0xFF102159));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('green colors', () {
|
||||||
|
expect(AppColor.greenLight, const Color(0xFFe6faf5));
|
||||||
|
expect(AppColor.greenLightHover, const Color(0xFFd9f7f0));
|
||||||
|
expect(AppColor.greenLightActive, const Color(0xFFb0efdf));
|
||||||
|
expect(AppColor.greenNormal, const Color(0xFF00cc99));
|
||||||
|
expect(AppColor.greenNormalHover, const Color(0xFF00b88a));
|
||||||
|
expect(AppColor.greenNormalActive, const Color(0xFF00a37a));
|
||||||
|
expect(AppColor.greenDark, const Color(0xFF009973));
|
||||||
|
expect(AppColor.greenDarkHover, const Color(0xFF007a5c));
|
||||||
|
expect(AppColor.greenDarkActive, const Color(0xFF005c45));
|
||||||
|
expect(AppColor.greenDarker, const Color(0xFF004736));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('category colors', () {
|
||||||
|
expect(AppColor.confirm, AppColor.greenNormalActive);
|
||||||
|
expect(AppColor.warning, AppColor.yellowNormal);
|
||||||
|
expect(AppColor.error, AppColor.redNormal);
|
||||||
|
expect(AppColor.info, AppColor.tealNormal);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
35
packages/core/test/presentation/utils/color_utils_test.dart
Normal file
35
packages/core/test/presentation/utils/color_utils_test.dart
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:rasadyar_core/presentation/utils/color_utils.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ColorUtils extension', () {
|
||||||
|
const baseColor = Color(0xFF42A5F5);
|
||||||
|
|
||||||
|
test('disabledColor returns color with alpha 38', () {
|
||||||
|
Color disabled = baseColor.disabledColor;
|
||||||
|
expect(disabled.a, 0.14901960784313725);
|
||||||
|
expect(disabled.r, baseColor.r);
|
||||||
|
expect(disabled.g, baseColor.g);
|
||||||
|
expect(disabled.b, baseColor.b);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hoverColor returns color darkened by 0.5', () {
|
||||||
|
Color hover = baseColor.hoverColor;
|
||||||
|
final expected =
|
||||||
|
HSLColor.fromColor(
|
||||||
|
baseColor,
|
||||||
|
).withLightness((HSLColor.fromColor(baseColor).lightness - 0.5).clamp(0.0, 1.0)).toColor();
|
||||||
|
expect(hover.g, expected.g);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('pressedColor returns color darkened by 0.1', () {
|
||||||
|
Color pressed = baseColor.pressedColor;
|
||||||
|
final expected =
|
||||||
|
HSLColor.fromColor(
|
||||||
|
baseColor,
|
||||||
|
).withLightness((HSLColor.fromColor(baseColor).lightness - 0.1).clamp(0.0, 1.0)).toColor();
|
||||||
|
expect(pressed.r, expected.r);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:rasadyar_core/presentation/utils/list_extensions.dart';
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
group('toggle test', (){
|
||||||
|
|
||||||
|
List<int> list = [1, 2, 3];
|
||||||
|
|
||||||
|
test('should remove item if it exists', () {
|
||||||
|
list.toggle(2);
|
||||||
|
expect(list, [1, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('should add item if it not exists', () {
|
||||||
|
list.toggle(10);
|
||||||
|
expect(list, [1, 3 , 10]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
group('insert to first item' ,(){
|
||||||
|
|
||||||
|
List<int> list = [1, 2, 3];
|
||||||
|
|
||||||
|
test('should insert item at start if it does not exist', () {
|
||||||
|
list.ensureContainsAtStart(0);
|
||||||
|
expect(list, [0, 1, 2, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not insert item at start if it already exists', () {
|
||||||
|
list.ensureContainsAtStart(2);
|
||||||
|
expect(list, [0, 1, 2, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
group('clear and add item' ,(){
|
||||||
|
|
||||||
|
List<int> list = [1, 2, 3];
|
||||||
|
|
||||||
|
test('must be clear and add item ', () {
|
||||||
|
list.resetWith(20);
|
||||||
|
expect(list, [20]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
name: rasadyar_app
|
name: rasadyar_app
|
||||||
description: "A new Flutter project."
|
description: "A new Flutter project."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.0.0+1
|
version: 1.2.0+2
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.2
|
sdk: ^3.7.2
|
||||||
|
|||||||
Reference in New Issue
Block a user