Xử lý login
This commit is contained in:
parent
9c67d123c8
commit
5d86cfa542
@ -1,5 +1,10 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/core/constants/index.dart';
|
||||||
|
import 'package:baseproject/features/presentation/app/view/app.dart';
|
||||||
|
import 'package:baseproject/features/route/route_goto.dart';
|
||||||
|
import 'package:baseproject/features/usecases/index.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
@ -8,8 +13,8 @@ class CustomInterceptor extends InterceptorsWrapper {
|
|||||||
@override
|
@override
|
||||||
// ignore: avoid_void_async
|
// ignore: avoid_void_async
|
||||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
|
void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
|
||||||
// final String token = LocalStoreManager.getString(UserSettings.tokenUser);
|
final String token = LocalStoreManager.getString(StorageKey.tokenUser);
|
||||||
// if (token.isNotEmpty) options.headers["Authorization"] = "Bearer $token";
|
if (token.isNotEmpty) options.headers["Authorization"] = "Bearer $token";
|
||||||
|
|
||||||
final String method = options.method.toLowerCase();
|
final String method = options.method.toLowerCase();
|
||||||
if (method == 'get' || method == 'put') {
|
if (method == 'get' || method == 'put') {
|
||||||
@ -44,35 +49,42 @@ class CustomInterceptor extends InterceptorsWrapper {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
onError(DioError err, ErrorInterceptorHandler handler) async {
|
onError(DioError err, ErrorInterceptorHandler handler) async {
|
||||||
if (retryCount >= 3) {
|
if (retryCount < 3) {
|
||||||
return;
|
await Future.delayed(Duration(milliseconds: retryCount * 500));
|
||||||
}
|
if ((err.response?.statusCode == 403 || err.response?.statusCode == 401)) {
|
||||||
if (err.response?.statusCode == 403 || err.response?.statusCode == 401) {
|
retryCount++;
|
||||||
retryCount++;
|
final Dio dio = GetIt.I();
|
||||||
final Dio dio = GetIt.I();
|
final RequestOptions options = err.requestOptions;
|
||||||
dio.lock();
|
dio.lock();
|
||||||
dio.interceptors.requestLock.lock();
|
dio.interceptors.requestLock.lock();
|
||||||
dio.interceptors.responseLock.lock();
|
dio.interceptors.responseLock.lock();
|
||||||
|
|
||||||
//Refresh token
|
// Refresh token
|
||||||
// final CoreUserRepository sessionRepository = GetIt.I();
|
final UserUseCases sessionRepository = GetIt.I();
|
||||||
// final Token? token = await sessionRepository.refreshToken(
|
final bool token = await sessionRepository.tryRefreshToken();
|
||||||
// clientId: UserSettings.oidcClientId, refreshToken: LocalStoreManager.getString(UserSettings.refreshToken));
|
if (!token) {
|
||||||
// if (token == null) {
|
dio.unlock();
|
||||||
// // final AuthenticateApp authenticateApp = GetIt.I();
|
dio.interceptors.requestLock.unlock();
|
||||||
// // await authenticateApp.authenticate(UserSettings.oidcClientId, <String>["profile", "email", "offline_access"]);
|
dio.interceptors.responseLock.unlock();
|
||||||
// await Navigator.pushNamedAndRemoveUntil(
|
// ignore: use_build_context_synchronously
|
||||||
// navigatorKey!.currentState!.context, vhs3LoginUser, (Route<dynamic> route) => false);
|
gotoLogin(navigatorKey.currentState!.context);
|
||||||
// } else {
|
} else {
|
||||||
// dio.unlock();
|
dio.unlock();
|
||||||
// dio.interceptors.requestLock.unlock();
|
dio.interceptors.requestLock.unlock();
|
||||||
// dio.interceptors.responseLock.unlock();
|
dio.interceptors.responseLock.unlock();
|
||||||
// options.headers = <String, dynamic>{
|
options.headers = <String, dynamic>{
|
||||||
// "Content-type": "application/json",
|
"Content-type": "application/json",
|
||||||
// "Authorization": "Bearer ${LocalStoreManager.getString(UserSettings.tokenUser)}"
|
"Authorization": "Bearer ${LocalStoreManager.getString(StorageKey.tokenUser)}"
|
||||||
// };
|
};
|
||||||
// await dio.fetch<dynamic>(options);
|
|
||||||
// }
|
try {
|
||||||
|
final response = await dio.fetch<dynamic>(options);
|
||||||
|
return handler.resolve(response);
|
||||||
|
} catch (e) {
|
||||||
|
return handler.next(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final dynamic errorData = err.response?.data;
|
final dynamic errorData = err.response?.data;
|
||||||
|
|||||||
@ -8,10 +8,12 @@
|
|||||||
import 'package:get_it/get_it.dart' as _i1;
|
import 'package:get_it/get_it.dart' as _i1;
|
||||||
import 'package:injectable/injectable.dart' as _i2;
|
import 'package:injectable/injectable.dart' as _i2;
|
||||||
|
|
||||||
import '../../features/presentation/account/bloc/login_bloc.dart' as _i5;
|
import '../../features/presentation/account/bloc/login_bloc.dart' as _i7;
|
||||||
import '../../features/repositories/hra_repository.dart' as _i4;
|
import '../../features/presentation/app/bloc/user_bloc.dart' as _i3;
|
||||||
import '../../features/usecases/user_use_cases.dart'
|
import '../../features/repositories/hra_repository.dart' as _i6;
|
||||||
as _i3; // ignore_for_file: unnecessary_lambdas
|
import '../../features/usecases/index.dart' as _i4;
|
||||||
|
import '../../features/usecases/user/user_use_cases.dart'
|
||||||
|
as _i5; // ignore_for_file: unnecessary_lambdas
|
||||||
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
/// initializes the registration of provided dependencies inside of [GetIt]
|
/// initializes the registration of provided dependencies inside of [GetIt]
|
||||||
@ -25,8 +27,9 @@ _i1.GetIt $initGetIt(
|
|||||||
environment,
|
environment,
|
||||||
environmentFilter,
|
environmentFilter,
|
||||||
);
|
);
|
||||||
gh.lazySingleton<_i3.UserUseCases>(
|
gh.factory<_i3.UserBloc>(() => _i3.UserBloc(get<_i4.UserUseCases>()));
|
||||||
() => _i3.UserUseCases(get<_i4.HraRepository>()));
|
gh.lazySingleton<_i5.UserUseCases>(
|
||||||
gh.factory<_i5.LoginBloc>(() => _i5.LoginBloc(get<_i3.UserUseCases>()));
|
() => _i5.UserUseCases(get<_i6.HraRepository>()));
|
||||||
|
gh.factory<_i7.LoginBloc>(() => _i7.LoginBloc(get<_i5.UserUseCases>()));
|
||||||
return get;
|
return get;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import 'package:baseproject/core/common/custom_interceptor.dart';
|
|||||||
import 'package:baseproject/core/components/alice.dart';
|
import 'package:baseproject/core/components/alice.dart';
|
||||||
import 'package:baseproject/core/constants/index.dart';
|
import 'package:baseproject/core/constants/index.dart';
|
||||||
import 'package:baseproject/features/presentation/app/view/app.dart';
|
import 'package:baseproject/features/presentation/app/view/app.dart';
|
||||||
import 'package:baseproject/features/repositories/account_repository.dart';
|
|
||||||
import 'package:baseproject/features/repositories/hra_repository.dart';
|
import 'package:baseproject/features/repositories/hra_repository.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|||||||
@ -17,4 +17,6 @@ export 'custom_pull_to_refresh.dart';
|
|||||||
export 'date/date_time_picker.dart';
|
export 'date/date_time_picker.dart';
|
||||||
export 'alice.dart';
|
export 'alice.dart';
|
||||||
export 'tab/custom_tab.dart';
|
export 'tab/custom_tab.dart';
|
||||||
export 'switch/custom_switch_list_tile.dart';
|
export 'switch/custom_switch_list_tile.dart';
|
||||||
|
export 'text_field/custom_textfield.dart';
|
||||||
|
export 'text_field/text_field_password.dart';
|
||||||
|
|||||||
225
lib/core/components/text_field/custom_textfield.dart
Normal file
225
lib/core/components/text_field/custom_textfield.dart
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import 'package:baseproject/core/components/form/form_builder_field.dart';
|
||||||
|
import 'package:baseproject/core/components/form/form_control.dart';
|
||||||
|
import 'package:baseproject/core/theme/custom_color.dart';
|
||||||
|
import 'package:baseproject/core/theme/text_style.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
typedef ItemToString<T> = String Function(T item);
|
||||||
|
typedef ItemFromString<T> = T? Function(String string);
|
||||||
|
|
||||||
|
class CustomTextField<T> extends FormBuilderField<T> {
|
||||||
|
final FormFieldValidator<T?>? validator;
|
||||||
|
final FormFieldSetter<T?>? onSaved;
|
||||||
|
final TextEditingController? controller;
|
||||||
|
final T? initialValue;
|
||||||
|
final bool autofocus;
|
||||||
|
final int? maxLines;
|
||||||
|
final int? maxLength;
|
||||||
|
final bool enabled;
|
||||||
|
final ItemFromString<T?>? itemFromString;
|
||||||
|
final ItemToString<T?>? itemToString;
|
||||||
|
final bool isLabelTop;
|
||||||
|
final String labelText;
|
||||||
|
final TextStyle? labelTextStyle;
|
||||||
|
final GestureTapCallback? onTap;
|
||||||
|
final int? minLines;
|
||||||
|
final double borderRadius;
|
||||||
|
final InputDecoration decoration;
|
||||||
|
final bool enableSuggestions;
|
||||||
|
final TextInputType? textInputType;
|
||||||
|
final TextStyle? style;
|
||||||
|
final TextAlign textAlign;
|
||||||
|
final bool readOnly;
|
||||||
|
final ValueChanged<String>? onFieldSubmitted;
|
||||||
|
List<TextInputFormatter>? inputFormatters;
|
||||||
|
final TextInputAction? textInputAction;
|
||||||
|
final bool obscureText;
|
||||||
|
final bool autoCorrect;
|
||||||
|
final TextCapitalization textCapitalization;
|
||||||
|
final bool isShowTextRequire;
|
||||||
|
final Color? cursorColor;
|
||||||
|
final Color? borderColor;
|
||||||
|
|
||||||
|
CustomTextField({
|
||||||
|
Key? key,
|
||||||
|
String name = 'textField',
|
||||||
|
this.validator,
|
||||||
|
this.onSaved,
|
||||||
|
ValueChanged<T?>? onChanged,
|
||||||
|
this.initialValue,
|
||||||
|
this.autofocus = false,
|
||||||
|
this.maxLength,
|
||||||
|
this.enabled = true,
|
||||||
|
this.itemFromString,
|
||||||
|
this.itemToString,
|
||||||
|
this.isLabelTop = false,
|
||||||
|
this.labelText = "",
|
||||||
|
this.labelTextStyle,
|
||||||
|
//TextFormField
|
||||||
|
this.controller,
|
||||||
|
AutovalidateMode autovalidateMode = AutovalidateMode.disabled,
|
||||||
|
this.decoration = const InputDecoration(
|
||||||
|
//border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
this.maxLines,
|
||||||
|
this.minLines,
|
||||||
|
this.borderRadius = 16,
|
||||||
|
this.onTap,
|
||||||
|
this.enableSuggestions = false,
|
||||||
|
this.textInputType,
|
||||||
|
this.style,
|
||||||
|
this.textAlign = TextAlign.start,
|
||||||
|
this.readOnly = false,
|
||||||
|
this.inputFormatters,
|
||||||
|
FocusNode? focusNode,
|
||||||
|
this.onFieldSubmitted,
|
||||||
|
this.textInputAction,
|
||||||
|
this.obscureText = false,
|
||||||
|
this.autoCorrect = false,
|
||||||
|
this.textCapitalization = TextCapitalization.sentences,
|
||||||
|
this.isShowTextRequire = false,
|
||||||
|
this.cursorColor,
|
||||||
|
this.borderColor,
|
||||||
|
}) :
|
||||||
|
//: controller = controller ?? TextEditingController(text: _toString<T>(initialValue, itemToString)),
|
||||||
|
// ??
|
||||||
|
// InputDecoration(
|
||||||
|
// border: OutlineInputBorder(borderRadius: BorderRadius.circular(borderRadius)),
|
||||||
|
// focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(borderRadius)),
|
||||||
|
// // enabledBorder: InputBorder.none,
|
||||||
|
// // errorBorder: InputBorder.none,
|
||||||
|
// // disabledBorder: InputBorder.none,
|
||||||
|
// ),
|
||||||
|
super(
|
||||||
|
name: name,
|
||||||
|
key: key,
|
||||||
|
autovalidateMode: autovalidateMode,
|
||||||
|
onChanged: onChanged,
|
||||||
|
focusNode: focusNode,
|
||||||
|
initialValue: initialValue,
|
||||||
|
decoration: decoration,
|
||||||
|
builder: (FormFieldState<T?> field) {
|
||||||
|
final _TextFieldState<T> state = field as _TextFieldState<T>;
|
||||||
|
//return state.build(state.context);
|
||||||
|
if (isLabelTop) return state._buildFieldSet();
|
||||||
|
|
||||||
|
return state._buildTextFormField();
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_TextFieldState<T> createState() => _TextFieldState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TextFieldState<T> extends FormBuilderFieldState<CustomTextField<T>, T> {
|
||||||
|
late TextEditingController textController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
textController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
textController = widget.controller ?? TextEditingController();
|
||||||
|
textController.text = initialText;
|
||||||
|
// widget.decoration = widget.decoration.copyWith(
|
||||||
|
// border: OutlineInputBorder(
|
||||||
|
// borderRadius: BorderRadius.all(
|
||||||
|
// Radius.circular(widget.borderRadius),
|
||||||
|
// )),
|
||||||
|
// enabledBorder: OutlineInputBorder(
|
||||||
|
// borderRadius: BorderRadius.all(
|
||||||
|
// Radius.circular(widget.borderRadius),
|
||||||
|
// )),
|
||||||
|
//);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// if (widget.isLabelTop)
|
||||||
|
// return _buildFieldSet();
|
||||||
|
// else
|
||||||
|
// return _buildTextFormField();
|
||||||
|
// }
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
super.reset();
|
||||||
|
textController.text = initialText;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get initialText => widget.itemToString?.call(initialValue) ?? initialValue?.toString() ?? '';
|
||||||
|
|
||||||
|
Widget _buildTextFormField() {
|
||||||
|
return TextFormField(
|
||||||
|
autocorrect: widget.autoCorrect,
|
||||||
|
onFieldSubmitted: widget.onFieldSubmitted,
|
||||||
|
textInputAction: widget.textInputAction,
|
||||||
|
focusNode: effectiveFocusNode,
|
||||||
|
enableSuggestions: true,
|
||||||
|
|
||||||
|
//widget.enableSuggestions,
|
||||||
|
textCapitalization: widget.textCapitalization,
|
||||||
|
controller: textController,
|
||||||
|
decoration: widget.decoration.copyWith(
|
||||||
|
labelText: !widget.isLabelTop ? widget.labelText : (widget.decoration.labelText),
|
||||||
|
labelStyle: !widget.isLabelTop ? labelTextStyle : null,
|
||||||
|
// fillColor: widget.fillColor ?? CustomColor.bgGrayLight,
|
||||||
|
// filled: true,
|
||||||
|
),
|
||||||
|
|
||||||
|
keyboardType: widget.textInputType,
|
||||||
|
style: widget.style,
|
||||||
|
textAlign: widget.textAlign,
|
||||||
|
autofocus: widget.autofocus,
|
||||||
|
obscureText: widget.obscureText,
|
||||||
|
|
||||||
|
//autocorrect: widget.autocorrect,
|
||||||
|
//maxLengthEnforcement: widget.maxLengthEnforcement,
|
||||||
|
maxLines: widget.maxLines,
|
||||||
|
maxLength: widget.maxLength,
|
||||||
|
//scrollPadding: widget.scrollPadding,
|
||||||
|
//textCapitalization: widget.textCapitalization,
|
||||||
|
inputFormatters: widget.inputFormatters,
|
||||||
|
minLines: widget.minLines,
|
||||||
|
enabled: widget.enabled,
|
||||||
|
readOnly: widget.readOnly,
|
||||||
|
autovalidateMode: widget.autovalidateMode,
|
||||||
|
cursorColor: widget.cursorColor,
|
||||||
|
|
||||||
|
//initialValue: widget.controller == null ? initialText : null,
|
||||||
|
onChanged: (String value) {
|
||||||
|
didChange(_toObject<T>(value, widget.itemFromString));
|
||||||
|
},
|
||||||
|
validator: (String? value) {
|
||||||
|
if (widget.validator != null) {
|
||||||
|
return widget.validator!(_toObject<T>(value, widget.itemFromString));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTap: widget.onTap,
|
||||||
|
onSaved: (String? value) {
|
||||||
|
if (widget.onSaved != null) {
|
||||||
|
return widget.onSaved!(_toObject<T>(value, widget.itemFromString));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle get labelTextStyle => textStyleBodySmall.copyWith(color: CustomColor.textGray);
|
||||||
|
|
||||||
|
Widget _buildFieldSet() {
|
||||||
|
return FormControl(
|
||||||
|
child: _buildTextFormField(),
|
||||||
|
isShowTextRequire: widget.isShowTextRequire,
|
||||||
|
labelText: widget.labelText,
|
||||||
|
labelTextStyle: widget.labelTextStyle,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _toString<T>(T? value, ItemToString<T?>? fn) => (fn == null ? value?.toString() : fn(value)) ?? '';
|
||||||
|
|
||||||
|
T? _toObject<T>(String? s, ItemFromString<T?>? fn) => fn == null ? s as T : fn(s ?? '');
|
||||||
56
lib/core/components/text_field/text_field_password.dart
Normal file
56
lib/core/components/text_field/text_field_password.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import 'package:baseproject/core/components/index.dart';
|
||||||
|
import 'package:baseproject/core/theme/form_theme.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class TextFieldPassword extends StatefulWidget {
|
||||||
|
const TextFieldPassword({
|
||||||
|
Key? key,
|
||||||
|
this.labelText = "",
|
||||||
|
this.name = "TextFieldPassword",
|
||||||
|
this.validator,
|
||||||
|
this.onChanged,
|
||||||
|
}) : super(key: key);
|
||||||
|
final String labelText;
|
||||||
|
final String name;
|
||||||
|
final FormFieldValidator<String?>? validator;
|
||||||
|
final ValueChanged<String?>? onChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TextFieldPassword> createState() => _TextFieldPasswordState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TextFieldPasswordState extends State<TextFieldPassword> {
|
||||||
|
late bool _passwordVisible;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_passwordVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CustomTextField(
|
||||||
|
textCapitalization: TextCapitalization.none,
|
||||||
|
isShowTextRequire: true,
|
||||||
|
isLabelTop: true,
|
||||||
|
name: widget.name,
|
||||||
|
validator: widget.validator,
|
||||||
|
labelText: widget.labelText,
|
||||||
|
obscureText: !_passwordVisible,
|
||||||
|
maxLines: 1,
|
||||||
|
onChanged: widget.onChanged,
|
||||||
|
decoration: FormTheme.getInputDecoration().copyWith(
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
_passwordVisible ? Icons.visibility : Icons.visibility_off,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_passwordVisible = !_passwordVisible;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,6 @@
|
|||||||
class StorageKey {
|
class StorageKey {
|
||||||
static const String libraryKeywordHistoryKey = 'libraryKeywordHistoryKey';
|
static const String libraryKeywordHistoryKey = 'libraryKeywordHistoryKey';
|
||||||
|
static const String userInfo = 'USER_INFO';
|
||||||
|
static const String tokenUser = 'TOKEN_USER';
|
||||||
|
static const String refreshToken = 'REFRESH_TOKEN';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
import 'package:baseproject/core/common/bloc/bloc_index.dart';
|
import 'package:baseproject/core/common/bloc/bloc_index.dart';
|
||||||
import 'package:baseproject/core/common/index.dart';
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/features/presentation/app/bloc/user_bloc.dart';
|
||||||
import 'package:baseproject/features/repositories/hra_repository.dart';
|
import 'package:baseproject/features/repositories/hra_repository.dart';
|
||||||
import 'package:baseproject/features/usecases/user_use_cases.dart';
|
import 'package:baseproject/features/route/route_goto.dart';
|
||||||
|
import 'package:baseproject/features/usecases/user/user_use_cases.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class LoginViewModel {
|
class LoginViewModel {
|
||||||
LoginViewModel({
|
LoginViewModel({
|
||||||
this.isLoading = false,
|
this.isLoading = false,
|
||||||
this.errorMessage,
|
|
||||||
this.loginResponse,
|
this.loginResponse,
|
||||||
this.captcha,
|
this.captcha,
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final String? errorMessage;
|
|
||||||
final LoginResponseDto? loginResponse;
|
final LoginResponseDto? loginResponse;
|
||||||
final DNTCaptchaApiResponse? captcha;
|
final DNTCaptchaApiResponse? captcha;
|
||||||
|
|
||||||
@ -24,7 +25,6 @@ class LoginViewModel {
|
|||||||
}) {
|
}) {
|
||||||
return LoginViewModel(
|
return LoginViewModel(
|
||||||
isLoading: isLoading ?? this.isLoading,
|
isLoading: isLoading ?? this.isLoading,
|
||||||
errorMessage: errorMessage,
|
|
||||||
loginResponse: loginResponse ?? this.loginResponse,
|
loginResponse: loginResponse ?? this.loginResponse,
|
||||||
captcha: captcha ?? this.captcha,
|
captcha: captcha ?? this.captcha,
|
||||||
);
|
);
|
||||||
@ -33,7 +33,7 @@ class LoginViewModel {
|
|||||||
|
|
||||||
class LoginBloc extends BaseCubit<BaseStateBloc<LoginViewModel>> {
|
class LoginBloc extends BaseCubit<BaseStateBloc<LoginViewModel>> {
|
||||||
LoginBloc(this._userUseCases) : super(InitState<LoginViewModel>(LoginViewModel())) {
|
LoginBloc(this._userUseCases) : super(InitState<LoginViewModel>(LoginViewModel())) {
|
||||||
loadCaptcha();
|
// loadCaptcha();
|
||||||
}
|
}
|
||||||
|
|
||||||
final UserUseCases _userUseCases;
|
final UserUseCases _userUseCases;
|
||||||
@ -61,7 +61,7 @@ class LoginBloc extends BaseCubit<BaseStateBloc<LoginViewModel>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> login(LoginDto request) async {
|
Future<void> login(LoginDto request, BuildContext context) async {
|
||||||
final currentModel = state.model;
|
final currentModel = state.model;
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
@ -71,30 +71,22 @@ class LoginBloc extends BaseCubit<BaseStateBloc<LoginViewModel>> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final result = await _userUseCases.loginAccount(request);
|
final result = await _userUseCases.loginAccount(request);
|
||||||
|
emit(
|
||||||
|
LoadedState<LoginViewModel>(
|
||||||
|
currentModel.copyWith(
|
||||||
|
isLoading: false,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
result.fold(
|
result.fold(
|
||||||
(error) {
|
(error) {
|
||||||
showErrorMessage(error);
|
showErrorMessage(error);
|
||||||
emit(
|
// loadCaptcha();
|
||||||
ErrorState<LoginViewModel>(
|
|
||||||
currentModel.copyWith(
|
|
||||||
isLoading: false,
|
|
||||||
errorMessage: error,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
loadCaptcha();
|
|
||||||
},
|
},
|
||||||
(response) {
|
(response) {
|
||||||
emit(
|
BlocProvider.of<UserBloc>(context).updateUserInfo(response.userInfo ?? UserInfoDto());
|
||||||
LoadedState<LoginViewModel>(
|
gotoHome(context);
|
||||||
currentModel.copyWith(
|
|
||||||
isLoading: false,
|
|
||||||
errorMessage: null,
|
|
||||||
loginResponse: response,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'package:baseproject/core/common/index.dart';
|
|||||||
import 'package:baseproject/core/components/index.dart';
|
import 'package:baseproject/core/components/index.dart';
|
||||||
import 'package:baseproject/features/presentation/account/bloc/login_bloc.dart';
|
import 'package:baseproject/features/presentation/account/bloc/login_bloc.dart';
|
||||||
import 'package:baseproject/features/repositories/hra_repository.dart';
|
import 'package:baseproject/features/repositories/hra_repository.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
dto.captchaToken = captcha?.dntCaptchaTokenValue;
|
dto.captchaToken = captcha?.dntCaptchaTokenValue;
|
||||||
dto.captchaInputText = "999999";
|
dto.captchaInputText = "999999";
|
||||||
|
|
||||||
_loginBloc.login(dto);
|
_loginBloc.login(dto, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -51,128 +52,40 @@ class _LoginScreenState extends State<LoginScreen> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: FormBuilder(
|
child: FormBuilder(
|
||||||
|
initialValue: kDebugMode
|
||||||
|
? {
|
||||||
|
'userName': 'quylx',
|
||||||
|
'password': 'BearCMS0011002848238master',
|
||||||
|
}
|
||||||
|
: {},
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
child: BlocBuilder<LoginBloc, BaseStateBloc<LoginViewModel>>(
|
child: BlocBuilder<LoginBloc, BaseStateBloc<LoginViewModel>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final vm = state.model;
|
final vm = state.model;
|
||||||
final isLoading = state is LoadingState<LoginViewModel> || vm.isLoading;
|
final isLoading = state is LoadingState<LoginViewModel> || vm.isLoading;
|
||||||
final error = vm.errorMessage;
|
|
||||||
final captcha = vm.captcha;
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (error != null)
|
CustomTextField(
|
||||||
Padding(
|
name: 'userName',
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
|
||||||
child: Text(
|
|
||||||
error,
|
|
||||||
style: const TextStyle(color: Colors.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FormControl(
|
|
||||||
labelText: 'Tên đăng nhập',
|
labelText: 'Tên đăng nhập',
|
||||||
|
isLabelTop: true,
|
||||||
isShowTextRequire: true,
|
isShowTextRequire: true,
|
||||||
child: TextFormField(
|
validator: FormBuilderValidators.required<String>(
|
||||||
decoration: const InputDecoration(
|
context,
|
||||||
border: OutlineInputBorder(),
|
|
||||||
hintText: 'Nhập tên đăng nhập',
|
|
||||||
),
|
|
||||||
validator: FormBuilderValidators.required<String>(
|
|
||||||
context,
|
|
||||||
),
|
|
||||||
onSaved: (value) {
|
|
||||||
_formKey.currentState?.setInternalFieldValue(
|
|
||||||
'userName',
|
|
||||||
value,
|
|
||||||
isUpdateState: false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
ConstantWidget.heightSpace16,
|
||||||
FormControl(
|
TextFieldPassword(
|
||||||
|
name: 'password',
|
||||||
labelText: 'Mật khẩu',
|
labelText: 'Mật khẩu',
|
||||||
isShowTextRequire: true,
|
validator: FormBuilderValidators.required<String>(
|
||||||
child: TextFormField(
|
context,
|
||||||
obscureText: true,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
hintText: 'Nhập mật khẩu',
|
|
||||||
),
|
|
||||||
validator: FormBuilderValidators.required<String>(
|
|
||||||
context,
|
|
||||||
),
|
|
||||||
onSaved: (value) {
|
|
||||||
_formKey.currentState?.setInternalFieldValue(
|
|
||||||
'password',
|
|
||||||
value,
|
|
||||||
isUpdateState: false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// const SizedBox(height: 8),
|
ConstantWidget.heightSpace24,
|
||||||
// if (captcha != null) ...[
|
|
||||||
// const SizedBox(height: 16),
|
|
||||||
// GestureDetector(
|
|
||||||
// onTap: () {
|
|
||||||
// _loginBloc.loadCaptcha();
|
|
||||||
// },
|
|
||||||
// child: Column(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
// children: [
|
|
||||||
// const Text('Captcha'),
|
|
||||||
// const SizedBox(height: 8),
|
|
||||||
// Image.network(
|
|
||||||
// captcha.dntCaptchaImgUrl ?? '',
|
|
||||||
// height: 60,
|
|
||||||
// errorBuilder: (_, __, ___) => Text(
|
|
||||||
// captcha.dntCaptchaImgUrl ?? '',
|
|
||||||
// style: const TextStyle(color: Colors.blue),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(height: 8),
|
|
||||||
// FormControl(
|
|
||||||
// labelText: 'Nhập mã captcha',
|
|
||||||
// isShowTextRequire: true,
|
|
||||||
// child: TextFormField(
|
|
||||||
// decoration: const InputDecoration(
|
|
||||||
// border: OutlineInputBorder(),
|
|
||||||
// hintText: 'Nhập mã captcha',
|
|
||||||
// ),
|
|
||||||
// validator: FormBuilderValidators.required<String>(
|
|
||||||
// context,
|
|
||||||
// ),
|
|
||||||
// onSaved: (value) {
|
|
||||||
// _formKey.currentState?.setInternalFieldValue(
|
|
||||||
// 'captchaInput',
|
|
||||||
// value,
|
|
||||||
// isUpdateState: false,
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Checkbox(
|
|
||||||
value: _rememberMe,
|
|
||||||
onChanged: (v) {
|
|
||||||
setState(() {
|
|
||||||
_rememberMe = v ?? false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const Text('Ghi nhớ đăng nhập'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 48,
|
height: 48,
|
||||||
child: ConstantWidget.buildPrimaryButton(
|
child: ConstantWidget.buildPrimaryButton(
|
||||||
|
|||||||
30
lib/features/presentation/app/bloc/user_bloc.dart
Normal file
30
lib/features/presentation/app/bloc/user_bloc.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository.dart';
|
||||||
|
import 'package:baseproject/features/usecases/index.dart';
|
||||||
|
import 'package:baseproject/features/usecases/user/user_use_cases.dart';
|
||||||
|
|
||||||
|
class UserBloc extends BaseCubit<BaseStateBloc<UserInfoDto?>> {
|
||||||
|
UserBloc(this._userUseCases) : super(InitState<UserInfoDto?>(null));
|
||||||
|
|
||||||
|
final UserUseCases _userUseCases;
|
||||||
|
|
||||||
|
Future<bool> getUserInfo() async {
|
||||||
|
final resultRefreshToken = await _userUseCases.refreshToken();
|
||||||
|
if (resultRefreshToken.isRight()) {
|
||||||
|
final resultUserInfo = await _userUseCases.getUserInfoFromApi();
|
||||||
|
emit(LoadedState<UserInfoDto>(resultUserInfo.fold((l) => UserInfoDto(), (r) => r)));
|
||||||
|
} else {
|
||||||
|
emit(LoadedState<UserInfoDto?>(null));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUserInfo(UserInfoDto userInfo) {
|
||||||
|
emit(LoadedState<UserInfoDto>(userInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
emit(LoadedState<UserInfoDto?>(null));
|
||||||
|
_userUseCases.clearData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
import 'package:baseproject/core/components/alice.dart';
|
import 'package:baseproject/core/components/alice.dart';
|
||||||
import 'package:baseproject/core/language/app_localizations.dart';
|
import 'package:baseproject/core/language/app_localizations.dart';
|
||||||
import 'package:baseproject/core/theme/custom_theme.dart';
|
import 'package:baseproject/core/theme/custom_theme.dart';
|
||||||
|
import 'package:baseproject/features/presentation/app/bloc/user_bloc.dart';
|
||||||
import 'package:baseproject/features/route/route_const.dart';
|
import 'package:baseproject/features/route/route_const.dart';
|
||||||
import 'package:baseproject/features/route/route_generator.dart';
|
import 'package:baseproject/features/route/route_generator.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -18,55 +20,59 @@ class App extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AppState extends State<App> {
|
class _AppState extends State<App> {
|
||||||
|
final UserBloc _userBloc = getItSuper<UserBloc>();
|
||||||
String _getLanguage() {
|
String _getLanguage() {
|
||||||
return 'vi';
|
return 'vi';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return BlocProvider(
|
||||||
// navigatorObservers: [CustomNavigatorObserver()],
|
create: (context) => _userBloc,
|
||||||
debugShowCheckedModeBanner: false,
|
child: MaterialApp(
|
||||||
theme: getTheme(context, true),
|
// navigatorObservers: [CustomNavigatorObserver()],
|
||||||
// navigatorKey: navigatorKey,
|
debugShowCheckedModeBanner: false,
|
||||||
locale: Locale(_getLanguage()),
|
theme: getTheme(context, true),
|
||||||
supportedLocales: AppLocalizations.locales,
|
// navigatorKey: navigatorKey,
|
||||||
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
|
locale: Locale(_getLanguage()),
|
||||||
AppLocalizations.delegate,
|
supportedLocales: AppLocalizations.locales,
|
||||||
GlobalMaterialLocalizations.delegate,
|
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
|
||||||
GlobalWidgetsLocalizations.delegate,
|
AppLocalizations.delegate,
|
||||||
GlobalCupertinoLocalizations.delegate
|
GlobalMaterialLocalizations.delegate,
|
||||||
],
|
GlobalWidgetsLocalizations.delegate,
|
||||||
navigatorKey: navigatorKey,
|
GlobalCupertinoLocalizations.delegate
|
||||||
localeResolutionCallback: AppLocalizations.localeResolutionCallback,
|
],
|
||||||
initialRoute: appInitRouteName,
|
navigatorKey: navigatorKey,
|
||||||
onGenerateRoute: RouteGenerator.generatorRoute,
|
localeResolutionCallback: AppLocalizations.localeResolutionCallback,
|
||||||
builder: EasyLoading.init(builder: (BuildContext context, Widget? child) {
|
initialRoute: appInitRouteName,
|
||||||
EasyLoading.instance.userInteractions = false;
|
onGenerateRoute: RouteGenerator.generatorRoute,
|
||||||
return Container(
|
builder: EasyLoading.init(builder: (BuildContext context, Widget? child) {
|
||||||
child: kDebugMode
|
EasyLoading.instance.userInteractions = false;
|
||||||
? Stack(
|
return Container(
|
||||||
children: <Widget>[
|
child: kDebugMode
|
||||||
child!,
|
? Stack(
|
||||||
Positioned(
|
children: <Widget>[
|
||||||
bottom: 10,
|
child!,
|
||||||
right: 10,
|
Positioned(
|
||||||
child: Container(
|
bottom: 10,
|
||||||
width: 30,
|
right: 10,
|
||||||
height: 30,
|
child: Container(
|
||||||
child: FloatingActionButton(
|
width: 30,
|
||||||
onPressed: () {
|
height: 30,
|
||||||
CustomAlice.showScreen();
|
child: FloatingActionButton(
|
||||||
},
|
onPressed: () {
|
||||||
backgroundColor: Colors.red,
|
CustomAlice.showScreen();
|
||||||
child: const Text("A"),
|
},
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
child: const Text("A"),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
)
|
||||||
)
|
: child!,
|
||||||
: child!,
|
);
|
||||||
);
|
})),
|
||||||
}));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
lib/features/presentation/app/view/init_screen.dart
Normal file
38
lib/features/presentation/app/view/init_screen.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/features/presentation/app/bloc/user_bloc.dart';
|
||||||
|
import 'package:baseproject/features/route/route_goto.dart';
|
||||||
|
import 'package:baseproject/features/usecases/index.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class InitScreen extends StatefulWidget {
|
||||||
|
const InitScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InitScreen> createState() => _InitScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InitScreenState extends State<InitScreen> {
|
||||||
|
UserBloc get _userBloc => BlocProvider.of<UserBloc>(context);
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initData() {
|
||||||
|
_userBloc.getUserInfo().then((value) {
|
||||||
|
if (value) {
|
||||||
|
gotoHome(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,8 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
import 'package:baseproject/core/components/constants_widget.dart';
|
import 'package:baseproject/core/components/constants_widget.dart';
|
||||||
import 'package:baseproject/core/language/app_localizations.dart';
|
import 'package:baseproject/core/language/app_localizations.dart';
|
||||||
|
import 'package:baseproject/features/model/index.dart';
|
||||||
|
import 'package:baseproject/features/presentation/app/bloc/user_bloc.dart';
|
||||||
import 'package:baseproject/features/route/route_goto.dart';
|
import 'package:baseproject/features/route/route_goto.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -15,18 +18,39 @@ class _HomeState extends State<Home> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: BlocBuilder<UserBloc, BaseStateBloc<UserInfoDto?>>(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
builder: (context, state) {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
final userInfo = state.model;
|
||||||
children: [
|
if (userInfo != null) {
|
||||||
Text(AppLocalizations.of(context)!.translate("first_string")),
|
return Column(
|
||||||
ConstantWidget.buildPrimaryButton(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
onPressed: () {
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
gotoLogin(context);
|
children: [
|
||||||
},
|
Text("Chào ${userInfo.fullName ?? ''}"),
|
||||||
text: 'Đăng nhập',
|
ConstantWidget.buildPrimaryButton(
|
||||||
),
|
onPressed: () {
|
||||||
],
|
BlocProvider.of<UserBloc>(context).logout();
|
||||||
|
},
|
||||||
|
text: 'Đăng xuất',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(context)!.translate("first_string")),
|
||||||
|
ConstantWidget.buildPrimaryButton(
|
||||||
|
onPressed: () {
|
||||||
|
gotoLogin(context);
|
||||||
|
},
|
||||||
|
text: 'Đăng nhập',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -69,7 +69,8 @@ abstract class HraRepository {
|
|||||||
|
|
||||||
///
|
///
|
||||||
@POST('/api/v1/account/refresh-token')
|
@POST('/api/v1/account/refresh-token')
|
||||||
Future<RefreshTokenResponseDtoApiResponse> accountRefreshToken();
|
Future<RefreshTokenResponseDtoApiResponse> accountRefreshToken(
|
||||||
|
@Body() RefreshTokenRequestDto body);
|
||||||
|
|
||||||
///
|
///
|
||||||
@POST('/api/v1/account/login-with-2fa')
|
@POST('/api/v1/account/login-with-2fa')
|
||||||
|
|||||||
@ -254,11 +254,12 @@ class _HraRepository implements HraRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<RefreshTokenResponseDtoApiResponse> accountRefreshToken() async {
|
Future<RefreshTokenResponseDtoApiResponse> accountRefreshToken(body) async {
|
||||||
const _extra = <String, dynamic>{};
|
const _extra = <String, dynamic>{};
|
||||||
final queryParameters = <String, dynamic>{};
|
final queryParameters = <String, dynamic>{};
|
||||||
final _headers = <String, dynamic>{};
|
final _headers = <String, dynamic>{};
|
||||||
final _data = <String, dynamic>{};
|
final _data = <String, dynamic>{};
|
||||||
|
_data.addAll(body.toJson());
|
||||||
final _result = await _dio.fetch<Map<String, dynamic>>(
|
final _result = await _dio.fetch<Map<String, dynamic>>(
|
||||||
_setStreamType<RefreshTokenResponseDtoApiResponse>(Options(
|
_setStreamType<RefreshTokenResponseDtoApiResponse>(Options(
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@ -12641,6 +12641,22 @@ class RefreshTokenEntity {
|
|||||||
Map<String, dynamic> toJson() => _$RefreshTokenEntityToJson(this);
|
Map<String, dynamic> toJson() => _$RefreshTokenEntityToJson(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class RefreshTokenRequestDto {
|
||||||
|
RefreshTokenRequestDto({
|
||||||
|
this.refreshToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory RefreshTokenRequestDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$RefreshTokenRequestDtoFromJson(json);
|
||||||
|
|
||||||
|
@JsonKey(name: 'refreshToken', includeIfNull: true)
|
||||||
|
String? refreshToken;
|
||||||
|
static const fromJsonFactory = _$RefreshTokenRequestDtoFromJson;
|
||||||
|
static const toJsonFactory = _$RefreshTokenRequestDtoToJson;
|
||||||
|
Map<String, dynamic> toJson() => _$RefreshTokenRequestDtoToJson(this);
|
||||||
|
}
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
class RefreshTokenResponseDto {
|
class RefreshTokenResponseDto {
|
||||||
RefreshTokenResponseDto({
|
RefreshTokenResponseDto({
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,3 @@
|
|||||||
const String appInitRouteName = '/app_init';
|
const String appInitRouteName = '/app_init';
|
||||||
const String loginRouteName = '/login';
|
const String loginRouteName = '/login';
|
||||||
|
const String homeApp = '/home_app';
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:baseproject/features/presentation/account/login_screen.dart';
|
import 'package:baseproject/features/presentation/account/login_screen.dart';
|
||||||
|
import 'package:baseproject/features/presentation/app/view/init_screen.dart';
|
||||||
import 'package:baseproject/features/presentation/home/view/home.dart';
|
import 'package:baseproject/features/presentation/home/view/home.dart';
|
||||||
import 'package:baseproject/features/route/route_const.dart';
|
import 'package:baseproject/features/route/route_const.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -9,6 +10,8 @@ class RouteGenerator {
|
|||||||
// tracking vào màn
|
// tracking vào màn
|
||||||
switch (setting.name) {
|
switch (setting.name) {
|
||||||
case appInitRouteName:
|
case appInitRouteName:
|
||||||
|
return MaterialPageRoute<void>(settings: setting, builder: (_) => const InitScreen());
|
||||||
|
case homeApp:
|
||||||
return MaterialPageRoute<void>(settings: setting, builder: (_) => const Home());
|
return MaterialPageRoute<void>(settings: setting, builder: (_) => const Home());
|
||||||
case loginRouteName:
|
case loginRouteName:
|
||||||
return MaterialPageRoute<void>(settings: setting, builder: (_) => const LoginScreen());
|
return MaterialPageRoute<void>(settings: setting, builder: (_) => const LoginScreen());
|
||||||
|
|||||||
@ -4,3 +4,11 @@ import 'route_const.dart';
|
|||||||
void gotoLogin(BuildContext context) {
|
void gotoLogin(BuildContext context) {
|
||||||
Navigator.pushNamed(context, loginRouteName);
|
Navigator.pushNamed(context, loginRouteName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gotoHome(BuildContext context) {
|
||||||
|
try {
|
||||||
|
Navigator.pushNamedAndRemoveUntil(context, homeApp, (Route<dynamic> route) => false);
|
||||||
|
} catch (e) {
|
||||||
|
Navigator.pushReplacementNamed(context, homeApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
1
lib/features/usecases/index.dart
Normal file
1
lib/features/usecases/index.dart
Normal file
@ -0,0 +1 @@
|
|||||||
|
export 'user/user_use_cases.dart';
|
||||||
115
lib/features/usecases/user/user_use_cases.dart
Normal file
115
lib/features/usecases/user/user_use_cases.dart
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import 'package:baseproject/core/common/index.dart';
|
||||||
|
import 'package:baseproject/core/constants/index.dart';
|
||||||
|
import 'package:baseproject/core/extension/string_extension.dart';
|
||||||
|
import 'package:baseproject/features/repositories/hra_repository.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:injectable/injectable.dart';
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
|
class UserUseCases {
|
||||||
|
final HraRepository _hraRepository;
|
||||||
|
|
||||||
|
UserUseCases(this._hraRepository);
|
||||||
|
|
||||||
|
Future<Either<String, LoginResponseDto>> loginAccount(LoginDto request) async {
|
||||||
|
try {
|
||||||
|
final result = await _hraRepository.accountLoginMobile(request);
|
||||||
|
|
||||||
|
if (result.data == null || result.success == false) {
|
||||||
|
return Left<String, LoginResponseDto>(result.message ?? 'Login failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
await saveToken(result.data?.token ?? '', refreshToken: result.data?.refreshToken ?? '');
|
||||||
|
await saveUserInfo(result.data?.userInfo);
|
||||||
|
|
||||||
|
return Right<String, LoginResponseDto>(result.data!);
|
||||||
|
} catch (ex) {
|
||||||
|
return Left<String, LoginResponseDto>(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveToken(String token, {String? refreshToken}) async {
|
||||||
|
await LocalStoreManager.setString(StorageKey.tokenUser, token);
|
||||||
|
if (refreshToken != null) {
|
||||||
|
await LocalStoreManager.setString(StorageKey.refreshToken, refreshToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserInfo(UserInfoDto? userInfo) async {
|
||||||
|
if (userInfo != null) {
|
||||||
|
await LocalStoreManager.setObject(StorageKey.userInfo, userInfo.toJson());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInfoDto? getUserInfo() {
|
||||||
|
final Map<String, dynamic>? temp = LocalStoreManager.getObject(StorageKey.userInfo);
|
||||||
|
if (temp != null) return UserInfoDto.fromJson(temp);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCurrentUserId() {
|
||||||
|
return getUserInfo()?.id?.toInt() ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearData() async {
|
||||||
|
await LocalStoreManager.remove(StorageKey.refreshToken);
|
||||||
|
await LocalStoreManager.remove(StorageKey.tokenUser);
|
||||||
|
await LocalStoreManager.remove(StorageKey.userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<String, DNTCaptchaApiResponse>> getCaptcha() async {
|
||||||
|
try {
|
||||||
|
final result = await _hraRepository.accountCaptcha();
|
||||||
|
|
||||||
|
return Right<String, DNTCaptchaApiResponse>(result);
|
||||||
|
} catch (ex) {
|
||||||
|
return Left<String, DNTCaptchaApiResponse>(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<String, bool>> refreshToken() async {
|
||||||
|
final refreshToken = LocalStoreManager.getString(StorageKey.refreshToken);
|
||||||
|
if (refreshToken.isNullOrEmpty) {
|
||||||
|
return const Left<String, bool>('Refresh token not found');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final result = await _hraRepository.accountRefreshToken(
|
||||||
|
RefreshTokenRequestDto(refreshToken: LocalStoreManager.getString(StorageKey.refreshToken)));
|
||||||
|
if (result.data != null) {
|
||||||
|
await saveToken(result.data?.token ?? '');
|
||||||
|
}
|
||||||
|
return Right<String, bool>(result.success ?? false);
|
||||||
|
} catch (ex) {
|
||||||
|
await clearData();
|
||||||
|
return Left<String, bool>(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Either<String, UserInfoDto>> getUserInfoFromApi() async {
|
||||||
|
try {
|
||||||
|
final result = await _hraRepository.accountUserInfo();
|
||||||
|
await saveUserInfo(result);
|
||||||
|
return Right<String, UserInfoDto>(result);
|
||||||
|
} catch (ex) {
|
||||||
|
await clearData();
|
||||||
|
return Left<String, UserInfoDto>(ex.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> tryRefreshToken() async {
|
||||||
|
try {
|
||||||
|
RefreshTokenRequestDto requestModel =
|
||||||
|
RefreshTokenRequestDto(refreshToken: LocalStoreManager.getString(StorageKey.refreshToken));
|
||||||
|
final RefreshTokenResponseDtoApiResponse? token =
|
||||||
|
await HraRepository(Dio(), baseUrl: ApiPath.hra).accountRefreshToken(requestModel);
|
||||||
|
if (token != null) {
|
||||||
|
await saveToken(token.data?.token ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return token != null;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,34 +0,0 @@
|
|||||||
import 'package:baseproject/features/repositories/hra_repository.dart';
|
|
||||||
import 'package:dartz/dartz.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
|
|
||||||
@lazySingleton
|
|
||||||
class UserUseCases {
|
|
||||||
final HraRepository _hraRepository;
|
|
||||||
|
|
||||||
UserUseCases(this._hraRepository);
|
|
||||||
|
|
||||||
Future<Either<String, LoginResponseDto>> loginAccount(LoginDto request) async {
|
|
||||||
try {
|
|
||||||
final result = await _hraRepository.accountLoginMobile(request);
|
|
||||||
|
|
||||||
if (result.data == null) {
|
|
||||||
return Left<String, LoginResponseDto>(result.message ?? 'Login failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return Right<String, LoginResponseDto>(result.data!);
|
|
||||||
} catch (ex) {
|
|
||||||
return Left<String, LoginResponseDto>(ex.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Either<String, DNTCaptchaApiResponse>> getCaptcha() async {
|
|
||||||
try {
|
|
||||||
final result = await _hraRepository.accountCaptcha();
|
|
||||||
|
|
||||||
return Right<String, DNTCaptchaApiResponse>(result);
|
|
||||||
} catch (ex) {
|
|
||||||
return Left<String, DNTCaptchaApiResponse>(ex.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user