From 7a544a832a1b45d65691afdd6f0d72fca8844397 Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 13:20:33 -0300 Subject: [PATCH 01/11] refactor: remove block and add change notifier with command handler --- .../repositories/auth_repository_impl.dart | 4 +- .../auth_repository_interface.dart | 4 +- .../auth/domain/usecases/login_usecase.dart | 2 +- .../auth/domain/usecases/sign_up_usecase.dart | 2 +- .../auth/presentation/bloc/auth_bloc.dart | 82 -------------- .../auth/presentation/bloc/auth_event.dart | 30 ----- .../auth/presentation/bloc/auth_state.dart | 58 ---------- .../auth/presentation/pages/login_page.dart | 72 +++++++----- .../presentation/pages/register_page.dart | 84 ++++++++------ .../viewmodels/auth_viewmodel.dart | 77 +++++++++++++ .../donate/presentation/bloc/donate_bloc.dart | 13 --- .../presentation/bloc/donate_event.dart | 8 -- .../presentation/bloc/donate_state.dart | 9 -- .../repositories/pet_repository_impl.dart | 2 +- .../pet_repository_interface.dart | 2 +- .../home/domain/usecases/get_pet_usecase.dart | 2 +- .../home/presentation/bloc/home_bloc.dart | 31 ----- .../home/presentation/bloc/home_event.dart | 19 ---- .../home/presentation/bloc/home_state.dart | 39 ------- .../home/presentation/pages/filters_page.dart | 83 +++++++++----- .../viewmodels/home_viewmodel.dart | 35 ++++++ .../presentation/bloc/message_bloc.dart | 13 --- .../presentation/bloc/message_event.dart | 8 -- .../presentation/bloc/message_state.dart | 9 -- lib/src/app_widget.dart | 20 +--- lib/src/core/DI/dependency_injector.dart | 9 +- lib/src/core/command/command.dart | 106 ++++++++++++++++++ lib/src/core/typedefs/types.dart | 2 +- lib/src/core/usecase/usecase_interface.dart | 2 +- pubspec.lock | 2 +- pubspec.yaml | 1 - 31 files changed, 381 insertions(+), 449 deletions(-) delete mode 100644 lib/src/app/features/auth/presentation/bloc/auth_bloc.dart delete mode 100644 lib/src/app/features/auth/presentation/bloc/auth_event.dart delete mode 100644 lib/src/app/features/auth/presentation/bloc/auth_state.dart create mode 100644 lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart delete mode 100644 lib/src/app/features/donate/presentation/bloc/donate_bloc.dart delete mode 100644 lib/src/app/features/donate/presentation/bloc/donate_event.dart delete mode 100644 lib/src/app/features/donate/presentation/bloc/donate_state.dart delete mode 100644 lib/src/app/features/home/presentation/bloc/home_bloc.dart delete mode 100644 lib/src/app/features/home/presentation/bloc/home_event.dart delete mode 100644 lib/src/app/features/home/presentation/bloc/home_state.dart create mode 100644 lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart delete mode 100644 lib/src/app/features/message/presentation/bloc/message_bloc.dart delete mode 100644 lib/src/app/features/message/presentation/bloc/message_event.dart delete mode 100644 lib/src/app/features/message/presentation/bloc/message_state.dart create mode 100644 lib/src/core/command/command.dart diff --git a/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart b/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart index 00209e3..c04a8ed 100644 --- a/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart +++ b/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart @@ -25,7 +25,7 @@ class AuthRepositoryImpl implements IAuthRepository { }) : _authRemoteDatasource = authRemoteDatasource; @override - Future>> login( + Output> login( LoginParams params, ) async { try { @@ -69,7 +69,7 @@ class AuthRepositoryImpl implements IAuthRepository { } @override - Future>> signUp(RegisterParams params) async { + Output> signUp(RegisterParams params) async { try { final response = await _authRemoteDatasource.register(RegisterModel( photoUrl: 'url', diff --git a/lib/src/app/features/auth/domain/repositories/auth_repository_interface.dart b/lib/src/app/features/auth/domain/repositories/auth_repository_interface.dart index 9421be7..7fab8e4 100644 --- a/lib/src/app/features/auth/domain/repositories/auth_repository_interface.dart +++ b/lib/src/app/features/auth/domain/repositories/auth_repository_interface.dart @@ -6,6 +6,6 @@ import '../entities/auth_entity.dart'; import '../entities/user_entity.dart'; abstract interface class IAuthRepository { - Future>> login(LoginParams params); - Future>> signUp(RegisterParams params); + Output> login(LoginParams params); + Output> signUp(RegisterParams params); } diff --git a/lib/src/app/features/auth/domain/usecases/login_usecase.dart b/lib/src/app/features/auth/domain/usecases/login_usecase.dart index dbf7005..72a729b 100644 --- a/lib/src/app/features/auth/domain/usecases/login_usecase.dart +++ b/lib/src/app/features/auth/domain/usecases/login_usecase.dart @@ -19,7 +19,7 @@ class LoginUsecase implements UseCase, LoginParams> { _sessionService = sessionService; @override - Future>> call(LoginParams params) async { + Output> call(LoginParams params) async { final result = await _authRepository.login(params); final appResponse = result.getOrNull(); diff --git a/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart b/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart index 7acd4ba..39d9d17 100644 --- a/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart +++ b/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart @@ -14,7 +14,7 @@ class SignUpUsecase }) : _authRepository = authRepository; @override - Future>> call(RegisterParams params) async { + Output> call(RegisterParams params) async { return (await _authRepository.signUp(params)); } } diff --git a/lib/src/app/features/auth/presentation/bloc/auth_bloc.dart b/lib/src/app/features/auth/presentation/bloc/auth_bloc.dart deleted file mode 100644 index be6ecd5..0000000 --- a/lib/src/app/features/auth/presentation/bloc/auth_bloc.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:result_dart/result_dart.dart'; - -import '../../../../../core/client_http/app_response.dart'; -import '../../../../../core/errors/base_exception.dart'; -import '../../../../../core/extensions/lucid_validator_extensions.dart'; -import '../../domain/dtos/login_params.dart'; -import '../../domain/dtos/register_params.dart'; -import '../../domain/entities/auth_entity.dart'; -import '../../domain/usecases/login_usecase.dart'; -import '../../domain/usecases/sign_up_usecase.dart'; -import '../../domain/validators/login_params_validator.dart'; -import '../../domain/validators/register_params_validator.dart'; - -part 'auth_event.dart'; -part 'auth_state.dart'; - -class AuthBloc extends Bloc { - final SignUpUsecase _signUpUsecase; - final LoginUsecase _loginUsecase; - AuthBloc({ - required SignUpUsecase signUpUsecase, - required LoginUsecase loginUsecase, - }) : _signUpUsecase = signUpUsecase, - _loginUsecase = loginUsecase, - super(AuthInitial()) { - on((event, emit) async { - emit(SignUpAuthLoading()); - - final validator = RegisterParamsValidator(); - - final newState = await validator - - /// valida o registerParams - .validateResult(event.registerParams) - - /// converte em um async Result: Future> - .toAsyncResult() - - /// Executa o usecase de Sign Up - .flatMap(_signUpUsecase.call) - - /// transforma o retorno do Result em [LoginParams] - .pure( - LoginParams( - email: event.registerParams.email, - password: event.registerParams.password, - ), - ) - - /// Executa o usecase de Login - .flatMap(_loginUsecase.call) - - /// converte para o estado apropriado - .fold(SignUpAuthSuccess.new, SignUpAuthFailure.new); - - emit(newState); - }); - - on((event, emit) async { - emit(LoginAuthLoading()); - final validator = LoginParamsValidator(); - - final newState = await validator - - /// valida o loginParams - .validateResult(event.loginParams) - - /// converte em um async Result: Future> - .toAsyncResult() - - /// executa o usecase - .flatMap(_loginUsecase.call) - - /// converte para o estado apropriado - .fold(LoginAuthSuccess.new, LoginAuthFailure.new); - - emit(newState); - }); - } -} diff --git a/lib/src/app/features/auth/presentation/bloc/auth_event.dart b/lib/src/app/features/auth/presentation/bloc/auth_event.dart deleted file mode 100644 index 1c9365f..0000000 --- a/lib/src/app/features/auth/presentation/bloc/auth_event.dart +++ /dev/null @@ -1,30 +0,0 @@ -part of 'auth_bloc.dart'; - -abstract class AuthEvent extends Equatable { - const AuthEvent(); - - @override - List get props => []; -} - -class SignUpAuthEvent extends AuthEvent { - final RegisterParams registerParams; - - const SignUpAuthEvent({ - required this.registerParams, - }); - - @override - List get props => [registerParams]; -} - -class LoginAuthEvent extends AuthEvent { - final LoginParams loginParams; - - const LoginAuthEvent({ - required this.loginParams, - }); - - @override - List get props => [loginParams]; -} diff --git a/lib/src/app/features/auth/presentation/bloc/auth_state.dart b/lib/src/app/features/auth/presentation/bloc/auth_state.dart deleted file mode 100644 index f296b3b..0000000 --- a/lib/src/app/features/auth/presentation/bloc/auth_state.dart +++ /dev/null @@ -1,58 +0,0 @@ -part of 'auth_bloc.dart'; - -abstract class AuthState extends Equatable { - const AuthState(); - - @override - List get props => []; -} - -class AuthInitial extends AuthState {} - -class SignUpAuthLoading extends AuthState {} - -class SignUpAuthSuccess extends AuthState { - final AppResponse data; - - const SignUpAuthSuccess( - this.data, - ); - - @override - List get props => [data]; -} - -class SignUpAuthFailure extends AuthState { - final BaseException exception; - - const SignUpAuthFailure( - this.exception, - ); - - String get message => exception.message; - - @override - List get props => [exception]; -} - -class LoginAuthLoading extends AuthState {} - -class LoginAuthSuccess extends AuthState { - final AppResponse data; - - const LoginAuthSuccess(this.data); - - @override - List get props => [data]; -} - -class LoginAuthFailure extends AuthState { - final BaseException exception; - - const LoginAuthFailure(this.exception); - - String get message => exception.message; - - @override - List get props => [exception]; -} diff --git a/lib/src/app/features/auth/presentation/pages/login_page.dart b/lib/src/app/features/auth/presentation/pages/login_page.dart index a2e5fae..167d7a0 100644 --- a/lib/src/app/features/auth/presentation/pages/login_page.dart +++ b/lib/src/app/features/auth/presentation/pages/login_page.dart @@ -1,7 +1,6 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; @@ -9,7 +8,7 @@ import 'package:go_router/go_router.dart'; import '../../../../../core/utils/show_snack_bar.dart'; import '../../domain/dtos/login_params.dart'; import '../../domain/validators/login_params_validator.dart'; -import '../bloc/auth_bloc.dart'; +import '../viewmodels/auth_viewmodel.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -21,14 +20,47 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { final _loginParams = LoginParams.empty(); final _validator = LoginParamsValidator(); - final formKey = GlobalKey(); + final authViewModel = GetIt.I.get(); + + @override + void initState() { + super.initState(); + authViewModel.login.addListener(listener); + } + + listener() { + if (authViewModel.login.completed) { + formKey.currentState!.reset(); + authViewModel.login.result?.fold( + (appResponse) => showMessageSnackBar( + context, + appResponse.message, + icon: Icons.check, + color: AppColors.secondaryColor, + iconColor: AppColors.whiteColor, + ), + (exception) => showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ), + ); + } + } + + @override + void dispose() { + authViewModel.login.removeListener(listener); + super.dispose(); + } @override Widget build(BuildContext context) { // final size = MediaQuery.sizeOf(context); final theme = Theme.of(context); - final authBloc = GetIt.I.get(); return Scaffold( appBar: AppBar( @@ -67,39 +99,17 @@ class _LoginPageState extends State { autovalidateMode: AutovalidateMode.onUserInteraction, ), const Gap(25), - BlocConsumer( - listener: (context, state) { - if (state is LoginAuthSuccess) { - formKey.currentState!.reset(); - showMessageSnackBar( - context, - state.data.message, - icon: Icons.check, - color: AppColors.secondaryColor, - iconColor: AppColors.whiteColor, - ); - } - if (state is LoginAuthFailure) { - showMessageSnackBar( - context, - state.message, - icon: Icons.error, - iconColor: AppColors.whiteColor, - color: AppColors.primaryColor, - ); - } - }, - builder: (context, state) { - if (state is LoginAuthLoading) { + ListenableBuilder( + listenable: authViewModel.login, + builder: (context, child) { + if (authViewModel.login.running) { return const Center(child: CircularProgressIndicator()); } return PrimaryButtonDs( title: 'Login', onPressed: () { if (formKey.currentState!.validate()) { - authBloc.add( - LoginAuthEvent(loginParams: _loginParams), - ); + authViewModel.login.execute(_loginParams); } }, ); diff --git a/lib/src/app/features/auth/presentation/pages/register_page.dart b/lib/src/app/features/auth/presentation/pages/register_page.dart index b3e8beb..9003f31 100644 --- a/lib/src/app/features/auth/presentation/pages/register_page.dart +++ b/lib/src/app/features/auth/presentation/pages/register_page.dart @@ -1,6 +1,5 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_masked_text2/flutter_masked_text2.dart'; import 'package:gap/gap.dart'; import 'package:get_it/get_it.dart'; @@ -10,7 +9,7 @@ import '../../../../../core/utils/show_snack_bar.dart'; import '../../../../../routes.dart'; import '../../domain/dtos/register_params.dart'; import '../../domain/validators/register_params_validator.dart'; -import '../bloc/auth_bloc.dart'; +import '../viewmodels/auth_viewmodel.dart'; class RegisterPage extends StatefulWidget { const RegisterPage({super.key}); @@ -29,11 +28,53 @@ class _RegisterPageState extends State { final formKey = GlobalKey(); + final authViewmodel = GetIt.I.get(); + + @override + void initState() { + super.initState(); + authViewmodel.signUp.addListener(listener); + } + + listener() { + if (authViewmodel.signUp.completed) { + final appResponse = authViewmodel.signUp.result?.getOrNull(); + if (appResponse != null) { + showMessageSnackBar( + context, + appResponse.message, + icon: Icons.check, + iconColor: AppColors.whiteColor, + color: AppColors.secondaryColor, + ); + formKey.currentState!.reset(); + + router.go('/auth/welcome'); + } + } + + final exception = authViewmodel.signUp.result?.exceptionOrNull(); + if (exception != null) { + showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ); + } + } + + @override + void dispose() { + authViewmodel.signUp.removeListener(listener); + super.dispose(); + } + @override Widget build(BuildContext context) { final size = MediaQuery.sizeOf(context); final theme = Theme.of(context); - final authBloc = GetIt.I.get(); return Scaffold( appBar: AppBar( leading: IconButton( @@ -152,43 +193,14 @@ class _RegisterPageState extends State { const Gap(40), Align( alignment: Alignment.center, - child: BlocConsumer( - listener: (context, state) { - if (state is SignUpAuthSuccess) { - showMessageSnackBar( - context, - state.data.message, - icon: Icons.check, - iconColor: AppColors.whiteColor, - color: AppColors.secondaryColor, - ); - formKey.currentState!.reset(); - - router.go('/auth/welcome'); - } - if (state is SignUpAuthFailure) { - showMessageSnackBar( - context, - state.message, - icon: Icons.error, - iconColor: AppColors.whiteColor, - color: AppColors.primaryColor, - ); - } - }, - builder: (context, state) { - if (state is SignUpAuthLoading) { - return const Center( - child: CircularProgressIndicator(), - ); - } + child: ListenableBuilder( + listenable: authViewmodel.signUp, + builder: (context, _) { return PrimaryButtonDs( title: 'Cadastrar', onPressed: () { if (formKey.currentState!.validate()) { - authBloc.add( - SignUpAuthEvent(registerParams: _registerParams), - ); + authViewmodel.signUp.execute(_registerParams); } }, ); diff --git a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart new file mode 100644 index 0000000..aa12385 --- /dev/null +++ b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart @@ -0,0 +1,77 @@ +import 'package:result_dart/result_dart.dart'; + +import '../../../../../core/client_http/app_response.dart'; +import '../../../../../core/command/command.dart'; +import '../../../../../core/extensions/lucid_validator_extensions.dart'; +import '../../../../../core/typedefs/types.dart'; +import '../../domain/dtos/login_params.dart'; +import '../../domain/dtos/register_params.dart'; +import '../../domain/entities/auth_entity.dart'; +import '../../domain/usecases/login_usecase.dart'; +import '../../domain/usecases/sign_up_usecase.dart'; +import '../../domain/validators/login_params_validator.dart'; +import '../../domain/validators/register_params_validator.dart'; + +class AuthViewmodel { + AuthViewmodel({ + required SignUpUsecase signUpUsecase, + required LoginUsecase loginUsecase, + }) : _signUpUsecase = signUpUsecase, + _loginUsecase = loginUsecase, + super() { + signUp = Command1(_signUpAuth); + login = Command1(_loginAuth); + } + + late final Command1, RegisterParams> signUp; + late final Command1, LoginParams> login; + + late final SignUpUsecase _signUpUsecase; + late final LoginUsecase _loginUsecase; + + Output> _signUpAuth( + RegisterParams registerParams) async { + final validator = RegisterParamsValidator(); + + final result = await validator + + /// valida o registerParams + .validateResult(registerParams) + + /// converte em um async Result: Future> + .toAsyncResult() + + /// Executa o usecase de Sign Up + .flatMap(_signUpUsecase.call) + + /// transforma o retorno do Result em [LoginParams] + .pure( + LoginParams( + email: registerParams.email, + password: registerParams.password, + ), + ) + + /// Executa o usecase de Login + .flatMap(_loginUsecase.call); + + return result; + } + + Output> _loginAuth(LoginParams loginParams) async { + final validator = LoginParamsValidator(); + + final result = await validator + + /// valida o loginParams + .validateResult(loginParams) + + /// converte em um async Result: Future> + .toAsyncResult() + + /// executa o usecase + .flatMap(_loginUsecase.call); + + return result; + } +} diff --git a/lib/src/app/features/donate/presentation/bloc/donate_bloc.dart b/lib/src/app/features/donate/presentation/bloc/donate_bloc.dart deleted file mode 100644 index ee2fc1a..0000000 --- a/lib/src/app/features/donate/presentation/bloc/donate_bloc.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; - -part 'donate_event.dart'; -part 'donate_state.dart'; - -class DonateBloc extends Bloc { - DonateBloc() : super(DonateInitial()) { - on((event, emit) { - // TODO: implement event handler - }); - } -} diff --git a/lib/src/app/features/donate/presentation/bloc/donate_event.dart b/lib/src/app/features/donate/presentation/bloc/donate_event.dart deleted file mode 100644 index 4aa7e57..0000000 --- a/lib/src/app/features/donate/presentation/bloc/donate_event.dart +++ /dev/null @@ -1,8 +0,0 @@ -part of 'donate_bloc.dart'; - -abstract class DonateEvent extends Equatable { - const DonateEvent(); - - @override - List get props => []; -} diff --git a/lib/src/app/features/donate/presentation/bloc/donate_state.dart b/lib/src/app/features/donate/presentation/bloc/donate_state.dart deleted file mode 100644 index f7cb731..0000000 --- a/lib/src/app/features/donate/presentation/bloc/donate_state.dart +++ /dev/null @@ -1,9 +0,0 @@ -part of 'donate_bloc.dart'; - -abstract class DonateState extends Equatable { - const DonateState(); - - @override - List get props => []; -} -class DonateInitial extends DonateState {} diff --git a/lib/src/app/features/home/data/repositories/pet_repository_impl.dart b/lib/src/app/features/home/data/repositories/pet_repository_impl.dart index 96f03ac..007d3f6 100644 --- a/lib/src/app/features/home/data/repositories/pet_repository_impl.dart +++ b/lib/src/app/features/home/data/repositories/pet_repository_impl.dart @@ -17,7 +17,7 @@ class PetRepositoryImpl implements IPetRepository { }); @override - Future>>> getPets({ + Output>> getPets({ String? type, String? gender, String? size, diff --git a/lib/src/app/features/home/domain/repositories/pet_repository_interface.dart b/lib/src/app/features/home/domain/repositories/pet_repository_interface.dart index 799b141..fc07557 100644 --- a/lib/src/app/features/home/domain/repositories/pet_repository_interface.dart +++ b/lib/src/app/features/home/domain/repositories/pet_repository_interface.dart @@ -3,7 +3,7 @@ import '../../../../../core/typedefs/types.dart'; import '../entities/pet_entity.dart'; abstract interface class IPetRepository { - Future>>> getPets({ + Output>> getPets({ String? type, String? gender, String? size, diff --git a/lib/src/app/features/home/domain/usecases/get_pet_usecase.dart b/lib/src/app/features/home/domain/usecases/get_pet_usecase.dart index 7338023..c374af0 100644 --- a/lib/src/app/features/home/domain/usecases/get_pet_usecase.dart +++ b/lib/src/app/features/home/domain/usecases/get_pet_usecase.dart @@ -14,7 +14,7 @@ class GetPetUsecase }) : _petRepository = petRepository; @override - Future>>> call(GetPetsParams params) { + Output>> call(GetPetsParams params) { return _petRepository.getPets( size: params.size, type: params.type, diff --git a/lib/src/app/features/home/presentation/bloc/home_bloc.dart b/lib/src/app/features/home/presentation/bloc/home_bloc.dart deleted file mode 100644 index d00de48..0000000 --- a/lib/src/app/features/home/presentation/bloc/home_bloc.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; - -import '../../../../../core/client_http/app_response.dart'; -import '../../../../../core/errors/errors.dart'; -import '../../domain/dtos/get_pets_params.dart'; -import '../../domain/entities/pet_entity.dart'; -import '../../domain/usecases/get_pet_usecase.dart'; - -part 'home_event.dart'; -part 'home_state.dart'; - -class HomeBloc extends Bloc { - final GetPetUsecase _getPetUsecase; - - HomeBloc({ - required GetPetUsecase getPetUsecase, - }) : _getPetUsecase = getPetUsecase, - super(HomeInitial()) { - on((event, emit) async { - emit(GetPetsLoading()); //!STATE de loading - - final result = await _getPetUsecase(event.getPetsParams); - result.fold((success) { - emit(GetPetsSuccess(success)); - }, (failure) { - emit(GetPetsFailure(failure)); - }); - }); - } -} diff --git a/lib/src/app/features/home/presentation/bloc/home_event.dart b/lib/src/app/features/home/presentation/bloc/home_event.dart deleted file mode 100644 index b8c47b0..0000000 --- a/lib/src/app/features/home/presentation/bloc/home_event.dart +++ /dev/null @@ -1,19 +0,0 @@ -part of 'home_bloc.dart'; - -abstract class HomeEvent extends Equatable { - const HomeEvent(); - - @override - List get props => []; -} - -class GetPetsEvent extends HomeEvent { - final GetPetsParams getPetsParams; - - const GetPetsEvent({ - required this.getPetsParams, - }); - - @override - List get props => [getPetsParams]; -} diff --git a/lib/src/app/features/home/presentation/bloc/home_state.dart b/lib/src/app/features/home/presentation/bloc/home_state.dart deleted file mode 100644 index 8e559cb..0000000 --- a/lib/src/app/features/home/presentation/bloc/home_state.dart +++ /dev/null @@ -1,39 +0,0 @@ -part of 'home_bloc.dart'; - -abstract class HomeState extends Equatable { - const HomeState(); - - @override - List get props => []; -} - -class HomeInitial extends HomeState {} - -//?Loading -class GetPetsLoading extends HomeState {} - -//?Success -class GetPetsSuccess extends HomeState { - final AppResponse> data; - - const GetPetsSuccess( - this.data, - ); - - @override - List get props => [data]; -} - -//?Failure -class GetPetsFailure extends HomeState { - final BaseException exception; - - const GetPetsFailure( - this.exception, - ); - - String get message => exception.message; - - @override - List get props => [exception]; -} diff --git a/lib/src/app/features/home/presentation/pages/filters_page.dart b/lib/src/app/features/home/presentation/pages/filters_page.dart index 410e133..ad4082e 100644 --- a/lib/src/app/features/home/presentation/pages/filters_page.dart +++ b/lib/src/app/features/home/presentation/pages/filters_page.dart @@ -1,13 +1,12 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; import '../../../../../core/utils/show_snack_bar.dart'; import '../../domain/dtos/get_pets_params.dart'; -import '../bloc/home_bloc.dart'; +import '../viewmodels/home_viewmodel.dart'; class FiltersPage extends StatefulWidget { const FiltersPage({super.key}); @@ -17,10 +16,36 @@ class FiltersPage extends StatefulWidget { } class _FiltersPageState extends State { - final homeBloc = - GetIt.I.get(); //! Controller que funciona por eventos + final homeViewModel = GetIt.I.get(); final petsParams = GetPetsParams.empty(); + @override + void initState() { + super.initState(); + homeViewModel.getPet.addListener(listener); + } + + listener() { + if (homeViewModel.getPet.completed) { + final exception = homeViewModel.getPet.result?.getOrNull(); + if (exception != null) { + showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ); + } + } + } + + @override + void dispose() { + homeViewModel.getPet.removeListener(listener); + super.dispose(); + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -84,33 +109,31 @@ class _FiltersPageState extends State { const Gap(24), const AgeSlider(), const Gap(48), - BlocConsumer( - listener: (context, state) { - //! Reação - if (state is GetPetsSuccess) { - print('${state.data.data.toString()}'); - } - if (state is GetPetsFailure) { - showMessageSnackBar( - context, - state.message, - icon: Icons.error, - iconColor: AppColors.whiteColor, - color: AppColors.primaryColor, + ListenableBuilder( + listenable: homeViewModel, + builder: (context, _) { + return ListView.builder( + itemCount: homeViewModel.pets.length, + itemBuilder: (context, index) { + return const SizedBox(); + }, ); - } - }, - builder: (context, state) { - return PrimaryButtonDs( - width: double.maxFinite, - title: 'Adote o seu pet', - onPressed: () { - homeBloc.add(GetPetsEvent( - getPetsParams: petsParams)); //! Adição de evento - }, - ); - }, - ), + }), + ListenableBuilder( + listenable: homeViewModel.getPet, + builder: (context, _) { + if (homeViewModel.getPet.running) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return PrimaryButtonDs( + width: double.maxFinite, + title: 'Adote o seu pet', + onPressed: () { + homeViewModel.getPet.execute(petsParams); + }); + }), ], ), ), diff --git a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart new file mode 100644 index 0000000..3fe22db --- /dev/null +++ b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart @@ -0,0 +1,35 @@ +import 'package:flutter/cupertino.dart'; +import 'package:result_dart/result_dart.dart'; + +import '../../../../../core/client_http/app_response.dart'; +import '../../../../../core/command/command.dart'; +import '../../../../../core/typedefs/types.dart'; +import '../../domain/dtos/get_pets_params.dart'; +import '../../domain/entities/pet_entity.dart'; +import '../../domain/usecases/get_pet_usecase.dart'; + +class HomeViewmodel extends ChangeNotifier { + HomeViewmodel({ + required GetPetUsecase getPetUsecase, + }) : _getPetUsecase = getPetUsecase, + super() { + getPet = Command1(_getPet); + } + + late final Command1>, GetPetsParams> getPet; + + late final GetPetUsecase _getPetUsecase; + + late final List _pets; + + List get pets => List.unmodifiable(_pets); + + Output>> _getPet(GetPetsParams params) async { + return _getPetUsecase(params).onSuccess((appResponse) { + if (appResponse.data != null) { + _pets = appResponse.data!; + notifyListeners(); + } + }); + } +} diff --git a/lib/src/app/features/message/presentation/bloc/message_bloc.dart b/lib/src/app/features/message/presentation/bloc/message_bloc.dart deleted file mode 100644 index c930134..0000000 --- a/lib/src/app/features/message/presentation/bloc/message_bloc.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; - -part 'message_event.dart'; -part 'message_state.dart'; - -class MessageBloc extends Bloc { - MessageBloc() : super(MessageInitial()) { - on((event, emit) { - // TODO: implement event handler - }); - } -} diff --git a/lib/src/app/features/message/presentation/bloc/message_event.dart b/lib/src/app/features/message/presentation/bloc/message_event.dart deleted file mode 100644 index 7e1e511..0000000 --- a/lib/src/app/features/message/presentation/bloc/message_event.dart +++ /dev/null @@ -1,8 +0,0 @@ -part of 'message_bloc.dart'; - -abstract class MessageEvent extends Equatable { - const MessageEvent(); - - @override - List get props => []; -} diff --git a/lib/src/app/features/message/presentation/bloc/message_state.dart b/lib/src/app/features/message/presentation/bloc/message_state.dart deleted file mode 100644 index 1d5280e..0000000 --- a/lib/src/app/features/message/presentation/bloc/message_state.dart +++ /dev/null @@ -1,9 +0,0 @@ -part of 'message_bloc.dart'; - -abstract class MessageState extends Equatable { - const MessageState(); - - @override - List get props => []; -} -class MessageInitial extends MessageState {} diff --git a/lib/src/app_widget.dart b/lib/src/app_widget.dart index 6dba9b3..f2712d7 100644 --- a/lib/src/app_widget.dart +++ b/lib/src/app_widget.dart @@ -1,10 +1,6 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'app/features/auth/presentation/bloc/auth_bloc.dart'; -import 'app/features/home/presentation/bloc/home_bloc.dart'; -import 'core/DI/dependency_injector.dart'; import 'routes.dart'; class AppWidget extends StatelessWidget { @@ -12,17 +8,11 @@ class AppWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider(create: (context) => injector()), - BlocProvider(create: (context) => injector()), - ], - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Adote um Pet', - theme: AppTheme.theme, - routerConfig: router, - ), + return MaterialApp.router( + debugShowCheckedModeBanner: false, + title: 'Adote um Pet', + theme: AppTheme.theme, + routerConfig: router, ); } } diff --git a/lib/src/core/DI/dependency_injector.dart b/lib/src/core/DI/dependency_injector.dart index 4369735..431875d 100644 --- a/lib/src/core/DI/dependency_injector.dart +++ b/lib/src/core/DI/dependency_injector.dart @@ -6,13 +6,12 @@ import '../../app/features/auth/domain/repositories/auth_repository_interface.da import '../../app/features/auth/domain/usecases/login_usecase.dart'; import '../../app/features/auth/domain/usecases/sign_up_usecase.dart'; import '../../app/features/auth/infrastructure/interceptor/auth_interceptor.dart'; -import '../../app/features/auth/presentation/bloc/auth_bloc.dart'; -import '../../app/features/auth/presentation/controller/session_controller.dart'; +import '../../app/features/auth/presentation/viewmodels/auth_viewmodel.dart'; import '../../app/features/home/data/datasources/pet_remote_datasource.dart'; import '../../app/features/home/data/repositories/pet_repository_impl.dart'; import '../../app/features/home/domain/repositories/pet_repository_interface.dart'; import '../../app/features/home/domain/usecases/get_pet_usecase.dart'; -import '../../app/features/home/presentation/bloc/home_bloc.dart'; +import '../../app/features/home/presentation/viewmodels/home_viewmodel.dart'; import '../cache/shared_preferences/shared_preferences_impl.dart'; import '../client_http/client_http.dart'; import '../client_http/dio/rest_client_dio_impl.dart'; @@ -67,7 +66,7 @@ void setupDependencyInjector({bool loggerAPI = false}) { () => PetRepositoryImpl(datasource: injector()), ); injector.registerLazySingleton( - () => AuthBloc( + () => AuthViewmodel( signUpUsecase: SignUpUsecase( authRepository: injector(), ), @@ -78,7 +77,7 @@ void setupDependencyInjector({bool loggerAPI = false}) { ), ); injector.registerLazySingleton( - () => HomeBloc( + () => HomeViewmodel( getPetUsecase: GetPetUsecase( petRepository: injector(), ), diff --git a/lib/src/core/command/command.dart b/lib/src/core/command/command.dart new file mode 100644 index 0000000..9e643d6 --- /dev/null +++ b/lib/src/core/command/command.dart @@ -0,0 +1,106 @@ +// Copyright 2024 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:result_dart/result_dart.dart'; + +import '../errors/base_exception.dart'; +import '../typedefs/types.dart'; + +/// Defines a command action that returns a [Result] of type [T]. +/// Used by [Command0] for actions without arguments. +typedef CommandAction0 = Output Function(); + +/// Defines a command action that returns a [Result] of type [T]. +/// Takes an argument of type [A]. +/// Used by [Command1] for actions with one argument. +typedef CommandAction1 = Output Function(A); + +/// Facilitates interaction with a view model. +/// +/// Encapsulates an action, +/// exposes its running and error states, +/// and ensures that it can't be launched again until it finishes. +/// +/// Use [Command0] for actions without arguments. +/// Use [Command1] for actions with one argument. +/// +/// Actions must return a [Result] of type [T]. +/// +/// Consume the action result by listening to changes, +/// then call to [clearResult] when the state is consumed. +abstract class Command extends ChangeNotifier { + bool _running = false; + + /// Whether the action is running. + bool get running => _running; + + Result? _result; + + /// Whether the action completed with an error. + bool get error => _result is Failure; + + /// Whether the action completed successfully. + bool get completed => _result is Success; + + /// The result of the most recent action. + /// + /// Returns `null` if the action is running or completed with an error. + Result? get result => _result; + + /// Clears the most recent action's result. + void clearResult() { + _result = null; + notifyListeners(); + } + + /// Execute the provided [action], notifying listeners and + /// setting the running and result states as necessary. + Future _execute(CommandAction0 action) async { + // Ensure the action can't launch multiple times. + // e.g. avoid multiple taps on button + if (_running) return; + + // Notify listeners. + // e.g. button shows loading state + _running = true; + _result = null; + notifyListeners(); + + try { + _result = await action(); + } finally { + _running = false; + notifyListeners(); + } + } +} + +/// A [Command] that accepts no arguments. +final class Command0 extends Command { + /// Creates a [Command0] with the provided [CommandAction0]. + Command0(this._action); + + final CommandAction0 _action; + + /// Executes the action. + Future execute() async { + await _execute(() => _action()); + } +} + +/// A [Command] that accepts one argument. +final class Command1 extends Command { + /// Creates a [Command1] with the provided [CommandAction1]. + Command1(this._action); + + final CommandAction1 _action; + + /// Executes the action with the specified [argument]. + Future execute(A argument) async { + await _execute(() => _action(argument)); + } +} diff --git a/lib/src/core/typedefs/types.dart b/lib/src/core/typedefs/types.dart index ccfb733..c221597 100644 --- a/lib/src/core/typedefs/types.dart +++ b/lib/src/core/typedefs/types.dart @@ -2,4 +2,4 @@ import 'package:result_dart/result_dart.dart'; import '../errors/base_exception.dart'; -typedef Output = Result; +typedef Output = AsyncResult; diff --git a/lib/src/core/usecase/usecase_interface.dart b/lib/src/core/usecase/usecase_interface.dart index 85ddab8..8010e0a 100644 --- a/lib/src/core/usecase/usecase_interface.dart +++ b/lib/src/core/usecase/usecase_interface.dart @@ -1,5 +1,5 @@ import '../typedefs/types.dart'; abstract interface class UseCase { - Future> call(Params params); + Output call(Params params); } diff --git a/pubspec.lock b/pubspec.lock index f519827..b3a5baa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -10,7 +10,7 @@ packages: source: hosted version: "2.11.0" bloc: - dependency: "direct main" + dependency: transitive description: name: bloc sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" diff --git a/pubspec.yaml b/pubspec.yaml index c6fe877..60ec8ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,7 +22,6 @@ dependencies: flutter_dotenv: ^5.2.1 shared_preferences: ^2.3.3 get_it: ^8.0.2 - bloc: ^8.1.4 logger: ^2.5.0 lucid_validation: ^1.1.0 result_dart: ^1.1.1 From 1b1b1b1236470430ebe431a43a17f763f966f785 Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 14:08:57 -0300 Subject: [PATCH 02/11] refactor: rename --- .../auth/domain/usecases/login_usecase.dart | 17 +++++- .../auth/domain/usecases/sign_up_usecase.dart | 17 +++++- .../auth/presentation/pages/login_page.dart | 14 ++--- .../presentation/pages/register_page.dart | 34 ++++++------ .../viewmodels/auth_viewmodel.dart | 54 ++++--------------- .../home/presentation/pages/filters_page.dart | 53 +++++++++--------- .../viewmodels/home_viewmodel.dart | 5 +- lib/src/core/command/command.dart | 2 +- 8 files changed, 96 insertions(+), 100 deletions(-) diff --git a/lib/src/app/features/auth/domain/usecases/login_usecase.dart b/lib/src/app/features/auth/domain/usecases/login_usecase.dart index 72a729b..1dc99ec 100644 --- a/lib/src/app/features/auth/domain/usecases/login_usecase.dart +++ b/lib/src/app/features/auth/domain/usecases/login_usecase.dart @@ -1,12 +1,16 @@ import 'dart:developer'; +import 'package:result_dart/result_dart.dart'; + import '../../../../../core/client_http/app_response.dart'; +import '../../../../../core/extensions/lucid_validator_extensions.dart'; import '../../../../../core/services/session_service.dart'; import '../../../../../core/typedefs/types.dart'; import '../../../../../core/usecase/usecase_interface.dart'; import '../dtos/login_params.dart'; import '../entities/auth_entity.dart'; import '../repositories/auth_repository_interface.dart'; +import '../validators/login_params_validator.dart'; class LoginUsecase implements UseCase, LoginParams> { final IAuthRepository _authRepository; @@ -20,7 +24,18 @@ class LoginUsecase implements UseCase, LoginParams> { @override Output> call(LoginParams params) async { - final result = await _authRepository.login(params); + final validator = LoginParamsValidator(); + + final result = await validator + + /// valida o loginParams + .validateResult(params) + + /// converte em um async Result: Future> + .toAsyncResult() + + /// executa o respository + .flatMap(_authRepository.login); final appResponse = result.getOrNull(); diff --git a/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart b/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart index 39d9d17..9b622bb 100644 --- a/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart +++ b/lib/src/app/features/auth/domain/usecases/sign_up_usecase.dart @@ -1,9 +1,13 @@ +import 'package:result_dart/result_dart.dart'; + import '../../../../../core/client_http/app_response.dart'; +import '../../../../../core/extensions/lucid_validator_extensions.dart'; import '../../../../../core/typedefs/types.dart'; import '../../../../../core/usecase/usecase_interface.dart'; import '../dtos/register_params.dart'; import '../entities/user_entity.dart'; import '../repositories/auth_repository_interface.dart'; +import '../validators/register_params_validator.dart'; class SignUpUsecase implements UseCase, RegisterParams> { @@ -15,6 +19,17 @@ class SignUpUsecase @override Output> call(RegisterParams params) async { - return (await _authRepository.signUp(params)); + final validator = RegisterParamsValidator(); + + return await validator + + /// valida o registerParams + .validateResult(params) + + /// converte em um async Result: Future> + .toAsyncResult() + + /// Executa o repository de Sign Up + .flatMap(_authRepository.signUp); } } diff --git a/lib/src/app/features/auth/presentation/pages/login_page.dart b/lib/src/app/features/auth/presentation/pages/login_page.dart index 167d7a0..acb7799 100644 --- a/lib/src/app/features/auth/presentation/pages/login_page.dart +++ b/lib/src/app/features/auth/presentation/pages/login_page.dart @@ -26,13 +26,13 @@ class _LoginPageState extends State { @override void initState() { super.initState(); - authViewModel.login.addListener(listener); + authViewModel.loginAction.addListener(listener); } listener() { - if (authViewModel.login.completed) { + if (authViewModel.loginAction.success) { formKey.currentState!.reset(); - authViewModel.login.result?.fold( + authViewModel.loginAction.result?.fold( (appResponse) => showMessageSnackBar( context, appResponse.message, @@ -53,7 +53,7 @@ class _LoginPageState extends State { @override void dispose() { - authViewModel.login.removeListener(listener); + authViewModel.loginAction.removeListener(listener); super.dispose(); } @@ -100,16 +100,16 @@ class _LoginPageState extends State { ), const Gap(25), ListenableBuilder( - listenable: authViewModel.login, + listenable: authViewModel.loginAction, builder: (context, child) { - if (authViewModel.login.running) { + if (authViewModel.loginAction.running) { return const Center(child: CircularProgressIndicator()); } return PrimaryButtonDs( title: 'Login', onPressed: () { if (formKey.currentState!.validate()) { - authViewModel.login.execute(_loginParams); + authViewModel.loginAction.execute(_loginParams); } }, ); diff --git a/lib/src/app/features/auth/presentation/pages/register_page.dart b/lib/src/app/features/auth/presentation/pages/register_page.dart index 9003f31..281d619 100644 --- a/lib/src/app/features/auth/presentation/pages/register_page.dart +++ b/lib/src/app/features/auth/presentation/pages/register_page.dart @@ -33,27 +33,25 @@ class _RegisterPageState extends State { @override void initState() { super.initState(); - authViewmodel.signUp.addListener(listener); + authViewmodel.signUpAction.addListener(listener); } listener() { - if (authViewmodel.signUp.completed) { - final appResponse = authViewmodel.signUp.result?.getOrNull(); - if (appResponse != null) { - showMessageSnackBar( - context, - appResponse.message, - icon: Icons.check, - iconColor: AppColors.whiteColor, - color: AppColors.secondaryColor, - ); - formKey.currentState!.reset(); + final appResponse = authViewmodel.signUpAction.result?.getOrNull(); + if (appResponse != null) { + showMessageSnackBar( + context, + appResponse.message, + icon: Icons.check, + iconColor: AppColors.whiteColor, + color: AppColors.secondaryColor, + ); + formKey.currentState!.reset(); - router.go('/auth/welcome'); - } + router.go('/auth/welcome'); } - final exception = authViewmodel.signUp.result?.exceptionOrNull(); + final exception = authViewmodel.signUpAction.result?.exceptionOrNull(); if (exception != null) { showMessageSnackBar( context, @@ -67,7 +65,7 @@ class _RegisterPageState extends State { @override void dispose() { - authViewmodel.signUp.removeListener(listener); + authViewmodel.signUpAction.removeListener(listener); super.dispose(); } @@ -194,13 +192,13 @@ class _RegisterPageState extends State { Align( alignment: Alignment.center, child: ListenableBuilder( - listenable: authViewmodel.signUp, + listenable: authViewmodel.signUpAction, builder: (context, _) { return PrimaryButtonDs( title: 'Cadastrar', onPressed: () { if (formKey.currentState!.validate()) { - authViewmodel.signUp.execute(_registerParams); + authViewmodel.signUpAction.execute(_registerParams); } }, ); diff --git a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart index aa12385..a53c378 100644 --- a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart +++ b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart @@ -2,15 +2,12 @@ import 'package:result_dart/result_dart.dart'; import '../../../../../core/client_http/app_response.dart'; import '../../../../../core/command/command.dart'; -import '../../../../../core/extensions/lucid_validator_extensions.dart'; import '../../../../../core/typedefs/types.dart'; import '../../domain/dtos/login_params.dart'; import '../../domain/dtos/register_params.dart'; import '../../domain/entities/auth_entity.dart'; import '../../domain/usecases/login_usecase.dart'; import '../../domain/usecases/sign_up_usecase.dart'; -import '../../domain/validators/login_params_validator.dart'; -import '../../domain/validators/register_params_validator.dart'; class AuthViewmodel { AuthViewmodel({ @@ -19,38 +16,20 @@ class AuthViewmodel { }) : _signUpUsecase = signUpUsecase, _loginUsecase = loginUsecase, super() { - signUp = Command1(_signUpAuth); - login = Command1(_loginAuth); + signUpAction = Command1(_signUpAuth); + loginAction = Command1(_loginUsecase.call); } - late final Command1, RegisterParams> signUp; - late final Command1, LoginParams> login; + late final Command1, RegisterParams> signUpAction; + late final Command1, LoginParams> loginAction; late final SignUpUsecase _signUpUsecase; late final LoginUsecase _loginUsecase; Output> _signUpAuth( RegisterParams registerParams) async { - final validator = RegisterParamsValidator(); - - final result = await validator - - /// valida o registerParams - .validateResult(registerParams) - - /// converte em um async Result: Future> - .toAsyncResult() - - /// Executa o usecase de Sign Up - .flatMap(_signUpUsecase.call) - - /// transforma o retorno do Result em [LoginParams] - .pure( - LoginParams( - email: registerParams.email, - password: registerParams.password, - ), - ) + final result = await _signUpUsecase(registerParams) + .pure(convertToLoginParams(registerParams)) /// Executa o usecase de Login .flatMap(_loginUsecase.call); @@ -58,20 +37,9 @@ class AuthViewmodel { return result; } - Output> _loginAuth(LoginParams loginParams) async { - final validator = LoginParamsValidator(); - - final result = await validator - - /// valida o loginParams - .validateResult(loginParams) - - /// converte em um async Result: Future> - .toAsyncResult() - - /// executa o usecase - .flatMap(_loginUsecase.call); - - return result; - } + LoginParams convertToLoginParams(RegisterParams registerParams) => + LoginParams( + email: registerParams.email, + password: registerParams.password, + ); } diff --git a/lib/src/app/features/home/presentation/pages/filters_page.dart b/lib/src/app/features/home/presentation/pages/filters_page.dart index ad4082e..6cf5a67 100644 --- a/lib/src/app/features/home/presentation/pages/filters_page.dart +++ b/lib/src/app/features/home/presentation/pages/filters_page.dart @@ -22,27 +22,25 @@ class _FiltersPageState extends State { @override void initState() { super.initState(); - homeViewModel.getPet.addListener(listener); + homeViewModel.getPetAction.addListener(listener); } listener() { - if (homeViewModel.getPet.completed) { - final exception = homeViewModel.getPet.result?.getOrNull(); - if (exception != null) { - showMessageSnackBar( - context, - exception.message, - icon: Icons.error, - iconColor: AppColors.whiteColor, - color: AppColors.primaryColor, - ); - } + final exception = homeViewModel.getPetAction.result?.exceptionOrNull(); + if (exception != null) { + showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ); } } @override void dispose() { - homeViewModel.getPet.removeListener(listener); + homeViewModel.getPetAction.removeListener(listener); super.dispose(); } @@ -120,20 +118,21 @@ class _FiltersPageState extends State { ); }), ListenableBuilder( - listenable: homeViewModel.getPet, - builder: (context, _) { - if (homeViewModel.getPet.running) { - return const Center( - child: CircularProgressIndicator(), - ); - } - return PrimaryButtonDs( - width: double.maxFinite, - title: 'Adote o seu pet', - onPressed: () { - homeViewModel.getPet.execute(petsParams); - }); - }), + listenable: homeViewModel.getPetAction, + builder: (context, _) { + if (homeViewModel.getPetAction.running) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return PrimaryButtonDs( + width: double.maxFinite, + title: 'Adote o seu pet', + onPressed: () { + homeViewModel.getPetAction.execute(petsParams); + }); + }, + ), ], ), ), diff --git a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart index 3fe22db..2575222 100644 --- a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart +++ b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart @@ -13,10 +13,10 @@ class HomeViewmodel extends ChangeNotifier { required GetPetUsecase getPetUsecase, }) : _getPetUsecase = getPetUsecase, super() { - getPet = Command1(_getPet); + getPetAction = Command1(_getPet); } - late final Command1>, GetPetsParams> getPet; + late final Command1>, GetPetsParams> getPetAction; late final GetPetUsecase _getPetUsecase; @@ -33,3 +33,4 @@ class HomeViewmodel extends ChangeNotifier { }); } } + diff --git a/lib/src/core/command/command.dart b/lib/src/core/command/command.dart index 9e643d6..800e18f 100644 --- a/lib/src/core/command/command.dart +++ b/lib/src/core/command/command.dart @@ -44,7 +44,7 @@ abstract class Command extends ChangeNotifier { bool get error => _result is Failure; /// Whether the action completed successfully. - bool get completed => _result is Success; + bool get success => _result is Success; /// The result of the most recent action. /// From aa6641a92ab9f3a7cd69fb6fbfb40bb6416a57b7 Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 14:12:11 -0300 Subject: [PATCH 03/11] refactor: remove unused variable --- .../features/auth/presentation/viewmodels/auth_viewmodel.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart index a53c378..151650c 100644 --- a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart +++ b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart @@ -28,13 +28,11 @@ class AuthViewmodel { Output> _signUpAuth( RegisterParams registerParams) async { - final result = await _signUpUsecase(registerParams) + return _signUpUsecase(registerParams) .pure(convertToLoginParams(registerParams)) /// Executa o usecase de Login .flatMap(_loginUsecase.call); - - return result; } LoginParams convertToLoginParams(RegisterParams registerParams) => From ac065a78e43a805d6f2d57004b7d65d453b9dd29 Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 15:15:07 -0300 Subject: [PATCH 04/11] refactor: listeners --- .../auth/presentation/pages/login_page.dart | 29 ++++++------ .../presentation/pages/register_page.dart | 46 +++++++++---------- .../home/presentation/pages/filters_page.dart | 5 +- 3 files changed, 39 insertions(+), 41 deletions(-) diff --git a/lib/src/app/features/auth/presentation/pages/login_page.dart b/lib/src/app/features/auth/presentation/pages/login_page.dart index acb7799..6c14551 100644 --- a/lib/src/app/features/auth/presentation/pages/login_page.dart +++ b/lib/src/app/features/auth/presentation/pages/login_page.dart @@ -30,25 +30,26 @@ class _LoginPageState extends State { } listener() { - if (authViewModel.loginAction.success) { - formKey.currentState!.reset(); - authViewModel.loginAction.result?.fold( - (appResponse) => showMessageSnackBar( + authViewModel.loginAction.result?.fold( + (appResponse) { + formKey.currentState!.reset(); + + showMessageSnackBar( context, appResponse.message, icon: Icons.check, color: AppColors.secondaryColor, iconColor: AppColors.whiteColor, - ), - (exception) => showMessageSnackBar( - context, - exception.message, - icon: Icons.error, - iconColor: AppColors.whiteColor, - color: AppColors.primaryColor, - ), - ); - } + ); + }, + (exception) => showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ), + ); } @override diff --git a/lib/src/app/features/auth/presentation/pages/register_page.dart b/lib/src/app/features/auth/presentation/pages/register_page.dart index 281d619..cf7d279 100644 --- a/lib/src/app/features/auth/presentation/pages/register_page.dart +++ b/lib/src/app/features/auth/presentation/pages/register_page.dart @@ -37,30 +37,28 @@ class _RegisterPageState extends State { } listener() { - final appResponse = authViewmodel.signUpAction.result?.getOrNull(); - if (appResponse != null) { - showMessageSnackBar( - context, - appResponse.message, - icon: Icons.check, - iconColor: AppColors.whiteColor, - color: AppColors.secondaryColor, - ); - formKey.currentState!.reset(); - - router.go('/auth/welcome'); - } - - final exception = authViewmodel.signUpAction.result?.exceptionOrNull(); - if (exception != null) { - showMessageSnackBar( - context, - exception.message, - icon: Icons.error, - iconColor: AppColors.whiteColor, - color: AppColors.primaryColor, - ); - } + authViewmodel.signUpAction.result?.fold( + (appResponse) { + formKey.currentState!.reset(); + showMessageSnackBar( + context, + appResponse.message, + icon: Icons.check, + iconColor: AppColors.whiteColor, + color: AppColors.secondaryColor, + ); + router.go('/auth/welcome'); + }, + (exception) { + showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ); + }, + ); } @override diff --git a/lib/src/app/features/home/presentation/pages/filters_page.dart b/lib/src/app/features/home/presentation/pages/filters_page.dart index 6cf5a67..bdb3029 100644 --- a/lib/src/app/features/home/presentation/pages/filters_page.dart +++ b/lib/src/app/features/home/presentation/pages/filters_page.dart @@ -26,8 +26,7 @@ class _FiltersPageState extends State { } listener() { - final exception = homeViewModel.getPetAction.result?.exceptionOrNull(); - if (exception != null) { + homeViewModel.getPetAction.result?.onFailure((exception) { showMessageSnackBar( context, exception.message, @@ -35,7 +34,7 @@ class _FiltersPageState extends State { iconColor: AppColors.whiteColor, color: AppColors.primaryColor, ); - } + }); } @override From 6e01f0ad6cc43a526a901a6c9290cc88e174f9ec Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 15:18:13 -0300 Subject: [PATCH 05/11] refactor: rename actions to commands --- .../features/auth/presentation/pages/login_page.dart | 12 ++++++------ .../auth/presentation/pages/register_page.dart | 10 +++++----- .../auth/presentation/viewmodels/auth_viewmodel.dart | 8 ++++---- .../home/presentation/pages/filters_page.dart | 10 +++++----- .../home/presentation/viewmodels/home_viewmodel.dart | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/src/app/features/auth/presentation/pages/login_page.dart b/lib/src/app/features/auth/presentation/pages/login_page.dart index 6c14551..24198b6 100644 --- a/lib/src/app/features/auth/presentation/pages/login_page.dart +++ b/lib/src/app/features/auth/presentation/pages/login_page.dart @@ -26,11 +26,11 @@ class _LoginPageState extends State { @override void initState() { super.initState(); - authViewModel.loginAction.addListener(listener); + authViewModel.loginCommand.addListener(listener); } listener() { - authViewModel.loginAction.result?.fold( + authViewModel.loginCommand.result?.fold( (appResponse) { formKey.currentState!.reset(); @@ -54,7 +54,7 @@ class _LoginPageState extends State { @override void dispose() { - authViewModel.loginAction.removeListener(listener); + authViewModel.loginCommand.removeListener(listener); super.dispose(); } @@ -101,16 +101,16 @@ class _LoginPageState extends State { ), const Gap(25), ListenableBuilder( - listenable: authViewModel.loginAction, + listenable: authViewModel.loginCommand, builder: (context, child) { - if (authViewModel.loginAction.running) { + if (authViewModel.loginCommand.running) { return const Center(child: CircularProgressIndicator()); } return PrimaryButtonDs( title: 'Login', onPressed: () { if (formKey.currentState!.validate()) { - authViewModel.loginAction.execute(_loginParams); + authViewModel.loginCommand.execute(_loginParams); } }, ); diff --git a/lib/src/app/features/auth/presentation/pages/register_page.dart b/lib/src/app/features/auth/presentation/pages/register_page.dart index cf7d279..e04cb5c 100644 --- a/lib/src/app/features/auth/presentation/pages/register_page.dart +++ b/lib/src/app/features/auth/presentation/pages/register_page.dart @@ -33,11 +33,11 @@ class _RegisterPageState extends State { @override void initState() { super.initState(); - authViewmodel.signUpAction.addListener(listener); + authViewmodel.signUpCommand.addListener(listener); } listener() { - authViewmodel.signUpAction.result?.fold( + authViewmodel.signUpCommand.result?.fold( (appResponse) { formKey.currentState!.reset(); showMessageSnackBar( @@ -63,7 +63,7 @@ class _RegisterPageState extends State { @override void dispose() { - authViewmodel.signUpAction.removeListener(listener); + authViewmodel.signUpCommand.removeListener(listener); super.dispose(); } @@ -190,13 +190,13 @@ class _RegisterPageState extends State { Align( alignment: Alignment.center, child: ListenableBuilder( - listenable: authViewmodel.signUpAction, + listenable: authViewmodel.signUpCommand, builder: (context, _) { return PrimaryButtonDs( title: 'Cadastrar', onPressed: () { if (formKey.currentState!.validate()) { - authViewmodel.signUpAction.execute(_registerParams); + authViewmodel.signUpCommand.execute(_registerParams); } }, ); diff --git a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart index 151650c..98d9047 100644 --- a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart +++ b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart @@ -16,12 +16,12 @@ class AuthViewmodel { }) : _signUpUsecase = signUpUsecase, _loginUsecase = loginUsecase, super() { - signUpAction = Command1(_signUpAuth); - loginAction = Command1(_loginUsecase.call); + signUpCommand = Command1(_signUpAuth); + loginCommand = Command1(_loginUsecase.call); } - late final Command1, RegisterParams> signUpAction; - late final Command1, LoginParams> loginAction; + late final Command1, RegisterParams> signUpCommand; + late final Command1, LoginParams> loginCommand; late final SignUpUsecase _signUpUsecase; late final LoginUsecase _loginUsecase; diff --git a/lib/src/app/features/home/presentation/pages/filters_page.dart b/lib/src/app/features/home/presentation/pages/filters_page.dart index bdb3029..c4494de 100644 --- a/lib/src/app/features/home/presentation/pages/filters_page.dart +++ b/lib/src/app/features/home/presentation/pages/filters_page.dart @@ -22,11 +22,11 @@ class _FiltersPageState extends State { @override void initState() { super.initState(); - homeViewModel.getPetAction.addListener(listener); + homeViewModel.getPetCommand.addListener(listener); } listener() { - homeViewModel.getPetAction.result?.onFailure((exception) { + homeViewModel.getPetCommand.result?.onFailure((exception) { showMessageSnackBar( context, exception.message, @@ -117,9 +117,9 @@ class _FiltersPageState extends State { ); }), ListenableBuilder( - listenable: homeViewModel.getPetAction, + listenable: homeViewModel.getPetCommand, builder: (context, _) { - if (homeViewModel.getPetAction.running) { + if (homeViewModel.getPetCommand.running) { return const Center( child: CircularProgressIndicator(), ); @@ -128,7 +128,7 @@ class _FiltersPageState extends State { width: double.maxFinite, title: 'Adote o seu pet', onPressed: () { - homeViewModel.getPetAction.execute(petsParams); + homeViewModel.getPetCommand.execute(petsParams); }); }, ), diff --git a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart index 2575222..6f4beb8 100644 --- a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart +++ b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart @@ -13,10 +13,10 @@ class HomeViewmodel extends ChangeNotifier { required GetPetUsecase getPetUsecase, }) : _getPetUsecase = getPetUsecase, super() { - getPetAction = Command1(_getPet); + getPetCommand = Command1(_getPet); } - late final Command1>, GetPetsParams> getPetAction; + late final Command1>, GetPetsParams> getPetCommand; late final GetPetUsecase _getPetUsecase; From 22ed0c7b1a5c983678ff84188d4657b7411a119b Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 15:18:26 -0300 Subject: [PATCH 06/11] refactor: rename actions to commands --- lib/src/app/features/home/presentation/pages/filters_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/app/features/home/presentation/pages/filters_page.dart b/lib/src/app/features/home/presentation/pages/filters_page.dart index c4494de..24f8ea3 100644 --- a/lib/src/app/features/home/presentation/pages/filters_page.dart +++ b/lib/src/app/features/home/presentation/pages/filters_page.dart @@ -39,7 +39,7 @@ class _FiltersPageState extends State { @override void dispose() { - homeViewModel.getPetAction.removeListener(listener); + homeViewModel.getPetCommand.removeListener(listener); super.dispose(); } From bbc03f2df7ec2b443ae7abb49756c8a3cd42f064 Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 15:40:34 -0300 Subject: [PATCH 07/11] refactor: repository --- .../repositories/auth_repository_impl.dart | 49 +++++++------------ .../auth/presentation/pages/login_page.dart | 19 ++++--- .../presentation/pages/register_page.dart | 2 + .../repositories/pet_repository_impl.dart | 49 +++++++++---------- .../home/presentation/pages/filters_page.dart | 1 + 5 files changed, 58 insertions(+), 62 deletions(-) diff --git a/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart b/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart index c04a8ed..17425e3 100644 --- a/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart +++ b/lib/src/app/features/auth/data/repositories/auth_repository_impl.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:result_dart/result_dart.dart'; import '../../../../../core/client_http/app_response.dart'; +import '../../../../../core/client_http/client_http.dart'; import '../../../../../core/errors/errors.dart'; import '../../../../../core/errors/unauthorized_exception.dart'; import '../../../../../core/typedefs/types.dart'; @@ -35,33 +36,23 @@ class AuthRepositoryImpl implements IAuthRepository { log('Response: ${response.data}'); - if (response.statusCode == 200) { - final appResponse = AppResponse.fromJson( - response.data, - (dynamic json) => AuthModel.fromMap(json as Map), - ); - - return Success(appResponse); - } - - if (response.statusCode == 401) { - return Failure( - UnauthorizedException( - message: response.data['message'], - ), - ); - } + final appResponse = AppResponse.fromJson( + response.data, + (dynamic json) => AuthModel.fromMap(json as Map), + ); + return Success(appResponse); + } on RestClientException catch (e) { return Failure( ServerException( - message: 'Error', - error: response.data['message'], + message: e.data['message'], + error: e.toString(), ), ); } catch (e) { return Failure( ServerException( - message: 'Error', + message: 'Unexpected error', error: e.toString(), ), ); @@ -83,25 +74,23 @@ class AuthRepositoryImpl implements IAuthRepository { password: params.password, )); - if (response.statusCode == 201) { - final appResponse = AppResponse.fromJson( - response.data, - (dynamic json) => UserModel.fromMap(json as Map), - ); - - return Success(appResponse); - } + final appResponse = AppResponse.fromJson( + response.data, + (dynamic json) => UserModel.fromMap(json as Map), + ); + return Success(appResponse); + } on RestClientException catch (e) { return Failure( ServerException( - message: 'Error', - error: response.data['message'], + message: e.data['message'], + error: e.toString(), ), ); } catch (e) { return Failure( ServerException( - message: 'Error', + message: 'Unexpected error', error: e.toString(), ), ); diff --git a/lib/src/app/features/auth/presentation/pages/login_page.dart b/lib/src/app/features/auth/presentation/pages/login_page.dart index 24198b6..7d893f1 100644 --- a/lib/src/app/features/auth/presentation/pages/login_page.dart +++ b/lib/src/app/features/auth/presentation/pages/login_page.dart @@ -32,6 +32,7 @@ class _LoginPageState extends State { listener() { authViewModel.loginCommand.result?.fold( (appResponse) { + authViewModel.loginCommand.clearResult(); formKey.currentState!.reset(); showMessageSnackBar( @@ -42,13 +43,17 @@ class _LoginPageState extends State { iconColor: AppColors.whiteColor, ); }, - (exception) => showMessageSnackBar( - context, - exception.message, - icon: Icons.error, - iconColor: AppColors.whiteColor, - color: AppColors.primaryColor, - ), + (exception) { + authViewModel.loginCommand.clearResult(); + + showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ); + }, ); } diff --git a/lib/src/app/features/auth/presentation/pages/register_page.dart b/lib/src/app/features/auth/presentation/pages/register_page.dart index e04cb5c..afe908e 100644 --- a/lib/src/app/features/auth/presentation/pages/register_page.dart +++ b/lib/src/app/features/auth/presentation/pages/register_page.dart @@ -39,6 +39,7 @@ class _RegisterPageState extends State { listener() { authViewmodel.signUpCommand.result?.fold( (appResponse) { + authViewmodel.signUpCommand.clearResult(); formKey.currentState!.reset(); showMessageSnackBar( context, @@ -50,6 +51,7 @@ class _RegisterPageState extends State { router.go('/auth/welcome'); }, (exception) { + authViewmodel.signUpCommand.clearResult(); showMessageSnackBar( context, exception.message, diff --git a/lib/src/app/features/home/data/repositories/pet_repository_impl.dart b/lib/src/app/features/home/data/repositories/pet_repository_impl.dart index 007d3f6..b3c6675 100644 --- a/lib/src/app/features/home/data/repositories/pet_repository_impl.dart +++ b/lib/src/app/features/home/data/repositories/pet_repository_impl.dart @@ -1,8 +1,8 @@ import 'package:result_dart/result_dart.dart'; import '../../../../../core/client_http/app_response.dart'; +import '../../../../../core/client_http/client_http.dart'; import '../../../../../core/errors/errors.dart'; -import '../../../../../core/errors/unauthorized_exception.dart'; import '../../../../../core/typedefs/types.dart'; import '../../domain/entities/pet_entity.dart'; import '../../domain/repositories/pet_repository_interface.dart'; @@ -24,32 +24,31 @@ class PetRepositoryImpl implements IPetRepository { }) async { try { final result = await datasource.getPets(); - if (result.statusCode == 200) { - final appResponse = AppResponse>.fromJson( - result.data, - (json) { - return (json as List).map((json) { - return PetModel.fromMap( - json as Map, - ); - }).toList(); - }, - ); - return Success(appResponse); - } - - if (result.statusCode == 401) { - return Failure( - UnauthorizedException( - message: result.data['message'], - ), - ); - } - + final appResponse = AppResponse>.fromJson( + result.data, + (json) { + return (json as List).map((json) { + return PetModel.fromMap( + json as Map, + ); + }).toList(); + }, + ); + return Success(appResponse); + } on RestClientException catch (e) { return Failure( - ServerException(message: 'Error', error: result.data['message'])); + ServerException( + message: e.data['message'], + error: e.toString(), + ), + ); } catch (e) { - return Failure(ServerException(message: 'Error', error: e.toString())); + return Failure( + ServerException( + message: 'Unexpected error', + error: e.toString(), + ), + ); } } } diff --git a/lib/src/app/features/home/presentation/pages/filters_page.dart b/lib/src/app/features/home/presentation/pages/filters_page.dart index 24f8ea3..bd79ddd 100644 --- a/lib/src/app/features/home/presentation/pages/filters_page.dart +++ b/lib/src/app/features/home/presentation/pages/filters_page.dart @@ -27,6 +27,7 @@ class _FiltersPageState extends State { listener() { homeViewModel.getPetCommand.result?.onFailure((exception) { + homeViewModel.getPetCommand.clearResult(); showMessageSnackBar( context, exception.message, From 1dcb974eca8349cd16d9000511350a2b32c38e7f Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 16:07:55 -0300 Subject: [PATCH 08/11] feature: implement logout --- design_system/lib/src/drawer/drawer_ds.dart | 2 +- .../auth/domain/usecases/logout_usecase.dart | 25 +++++++++++ .../auth/presentation/pages/login_page.dart | 3 ++ .../home/presentation/pages/home_page.dart | 44 ++++++++++++++++++- .../viewmodels/home_viewmodel.dart | 10 ++++- lib/src/core/DI/dependency_injector.dart | 6 ++- lib/src/core/services/session_service.dart | 4 ++ 7 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 lib/src/app/features/auth/domain/usecases/logout_usecase.dart diff --git a/design_system/lib/src/drawer/drawer_ds.dart b/design_system/lib/src/drawer/drawer_ds.dart index 843b4d1..f030768 100644 --- a/design_system/lib/src/drawer/drawer_ds.dart +++ b/design_system/lib/src/drawer/drawer_ds.dart @@ -87,7 +87,7 @@ class CustomDrawerDS extends StatelessWidget { ), DrawerItemDS( title: 'Sair', - onTap: onAccountTap ?? () {}, + onTap: onLogoutTap ?? () {}, showLeading: false, ), ], diff --git a/lib/src/app/features/auth/domain/usecases/logout_usecase.dart b/lib/src/app/features/auth/domain/usecases/logout_usecase.dart new file mode 100644 index 0000000..76eff59 --- /dev/null +++ b/lib/src/app/features/auth/domain/usecases/logout_usecase.dart @@ -0,0 +1,25 @@ +import 'dart:developer'; + +import 'package:result_dart/result_dart.dart'; + +import '../../../../../core/errors/default_exception.dart'; +import '../../../../../core/services/session_service.dart'; +import '../../../../../core/typedefs/types.dart'; +import '../../../../../core/usecase/usecase_interface.dart'; + +class LogoutUsecase implements UseCase { + final SessionService _sessionService; + + LogoutUsecase({required SessionService sessionService}) + : _sessionService = sessionService; + + @override + Output call([_]) async { + final removed = await _sessionService.removeToken(); + if (removed) { + log('Remove Token'); + return const Success(unit); + } + return const Failure(DefaultException(message: 'Not remove token')); + } +} diff --git a/lib/src/app/features/auth/presentation/pages/login_page.dart b/lib/src/app/features/auth/presentation/pages/login_page.dart index 7d893f1..f4ff85d 100644 --- a/lib/src/app/features/auth/presentation/pages/login_page.dart +++ b/lib/src/app/features/auth/presentation/pages/login_page.dart @@ -6,6 +6,7 @@ import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; import '../../../../../core/utils/show_snack_bar.dart'; +import '../../../../../routes.dart'; import '../../domain/dtos/login_params.dart'; import '../../domain/validators/login_params_validator.dart'; import '../viewmodels/auth_viewmodel.dart'; @@ -42,6 +43,8 @@ class _LoginPageState extends State { color: AppColors.secondaryColor, iconColor: AppColors.whiteColor, ); + + router.go('/home'); }, (exception) { authViewModel.loginCommand.clearResult(); diff --git a/lib/src/app/features/home/presentation/pages/home_page.dart b/lib/src/app/features/home/presentation/pages/home_page.dart index 7be3002..6407b61 100644 --- a/lib/src/app/features/home/presentation/pages/home_page.dart +++ b/lib/src/app/features/home/presentation/pages/home_page.dart @@ -1,10 +1,51 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:get_it/get_it.dart'; -class HomePage extends StatelessWidget { +import '../../../../../core/utils/show_snack_bar.dart'; +import '../../../../../routes.dart'; +import '../viewmodels/home_viewmodel.dart'; + +class HomePage extends StatefulWidget { const HomePage({super.key}); + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + final homeViewModel = GetIt.I.get(); + + @override + void initState() { + super.initState(); + homeViewModel.logoutCommand.addListener(listener); + } + + listener() { + homeViewModel.logoutCommand.result?.fold( + (unit) { + router.go('/auth/login'); + }, + (exception) { + showMessageSnackBar( + context, + exception.message, + icon: Icons.error, + iconColor: AppColors.whiteColor, + color: AppColors.primaryColor, + ); + }, + ); + } + + @override + void dispose() { + homeViewModel.logoutCommand.removeListener(listener); + super.dispose(); + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -14,6 +55,7 @@ class HomePage extends StatelessWidget { userName: 'Beth Almeida', userLocation: 'Sao Paulo - SP', userImage: image.image, + onLogoutTap: () => homeViewModel.logoutCommand.execute(), ), appBar: AppBar( title: Row( diff --git a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart index 6f4beb8..02c7103 100644 --- a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart +++ b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart @@ -4,6 +4,7 @@ import 'package:result_dart/result_dart.dart'; import '../../../../../core/client_http/app_response.dart'; import '../../../../../core/command/command.dart'; import '../../../../../core/typedefs/types.dart'; +import '../../../auth/domain/usecases/logout_usecase.dart'; import '../../domain/dtos/get_pets_params.dart'; import '../../domain/entities/pet_entity.dart'; import '../../domain/usecases/get_pet_usecase.dart'; @@ -11,14 +12,20 @@ import '../../domain/usecases/get_pet_usecase.dart'; class HomeViewmodel extends ChangeNotifier { HomeViewmodel({ required GetPetUsecase getPetUsecase, + required LogoutUsecase logoutUsecase, }) : _getPetUsecase = getPetUsecase, + _logoutUsecase = logoutUsecase, super() { getPetCommand = Command1(_getPet); + logoutCommand = Command0(_logoutUsecase.call); } - late final Command1>, GetPetsParams> getPetCommand; + late final Command1>, GetPetsParams> + getPetCommand; + late final Command0 logoutCommand; late final GetPetUsecase _getPetUsecase; + late final LogoutUsecase _logoutUsecase; late final List _pets; @@ -33,4 +40,3 @@ class HomeViewmodel extends ChangeNotifier { }); } } - diff --git a/lib/src/core/DI/dependency_injector.dart b/lib/src/core/DI/dependency_injector.dart index 431875d..d0e838c 100644 --- a/lib/src/core/DI/dependency_injector.dart +++ b/lib/src/core/DI/dependency_injector.dart @@ -4,6 +4,7 @@ import '../../app/features/auth/data/datasources/auth_remote_datasource.dart'; import '../../app/features/auth/data/repositories/auth_repository_impl.dart'; import '../../app/features/auth/domain/repositories/auth_repository_interface.dart'; import '../../app/features/auth/domain/usecases/login_usecase.dart'; +import '../../app/features/auth/domain/usecases/logout_usecase.dart'; import '../../app/features/auth/domain/usecases/sign_up_usecase.dart'; import '../../app/features/auth/infrastructure/interceptor/auth_interceptor.dart'; import '../../app/features/auth/presentation/viewmodels/auth_viewmodel.dart'; @@ -59,7 +60,7 @@ void setupDependencyInjector({bool loggerAPI = false}) { ); injector.registerFactory( () => PetRemoteDatasource( - restClient: injector(), + restClient: injector(), ), ); injector.registerFactory( @@ -81,6 +82,9 @@ void setupDependencyInjector({bool loggerAPI = false}) { getPetUsecase: GetPetUsecase( petRepository: injector(), ), + logoutUsecase: LogoutUsecase( + sessionService: injector(), + ), ), ); } diff --git a/lib/src/core/services/session_service.dart b/lib/src/core/services/session_service.dart index 99ae5d2..a045324 100644 --- a/lib/src/core/services/session_service.dart +++ b/lib/src/core/services/session_service.dart @@ -25,4 +25,8 @@ class SessionService { return response as String; } + + Future removeToken() async { + return await _sharedPreferences.removeData('token'); + } } \ No newline at end of file From 4f33f4f756b2ebb12f9740b5b0b9c0ae9d84bc1c Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 16:32:07 -0300 Subject: [PATCH 09/11] refactor: _signUpAuth method --- .../auth/presentation/viewmodels/auth_viewmodel.dart | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart index 98d9047..33d3d8f 100644 --- a/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart +++ b/lib/src/app/features/auth/presentation/viewmodels/auth_viewmodel.dart @@ -26,14 +26,10 @@ class AuthViewmodel { late final SignUpUsecase _signUpUsecase; late final LoginUsecase _loginUsecase; - Output> _signUpAuth( - RegisterParams registerParams) async { - return _signUpUsecase(registerParams) - .pure(convertToLoginParams(registerParams)) - - /// Executa o usecase de Login - .flatMap(_loginUsecase.call); - } + Output> _signUpAuth(RegisterParams registerParams) => + _signUpUsecase(registerParams) // Execute o signUpUsecase + .pure(convertToLoginParams(registerParams)) + .flatMap(_loginUsecase.call); // Execute o loginUsecase LoginParams convertToLoginParams(RegisterParams registerParams) => LoginParams( From cb061eeb760a9b97fdd8faf571473b3f70b39125 Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 17:08:08 -0300 Subject: [PATCH 10/11] refactor: add UnmodifiableListView --- .../features/home/presentation/viewmodels/home_viewmodel.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart index 02c7103..66b77a7 100644 --- a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart +++ b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart @@ -1,3 +1,5 @@ +import 'dart:collection'; + import 'package:flutter/cupertino.dart'; import 'package:result_dart/result_dart.dart'; @@ -29,7 +31,7 @@ class HomeViewmodel extends ChangeNotifier { late final List _pets; - List get pets => List.unmodifiable(_pets); + List get pets => UnmodifiableListView(_pets); Output>> _getPet(GetPetsParams params) async { return _getPetUsecase(params).onSuccess((appResponse) { From ed2f4a62d768893f74489760ebc287544feab098 Mon Sep 17 00:00:00 2001 From: Wellington Santos Date: Tue, 10 Dec 2024 17:09:10 -0300 Subject: [PATCH 11/11] refactor: remove final late --- .../features/home/presentation/viewmodels/home_viewmodel.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart index 66b77a7..e05ae33 100644 --- a/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart +++ b/lib/src/app/features/home/presentation/viewmodels/home_viewmodel.dart @@ -29,8 +29,7 @@ class HomeViewmodel extends ChangeNotifier { late final GetPetUsecase _getPetUsecase; late final LogoutUsecase _logoutUsecase; - late final List _pets; - + List _pets = []; List get pets => UnmodifiableListView(_pets); Output>> _getPet(GetPetsParams params) async {