diff --git a/lib/app/features/contact/data/repositories/contact_repository_impl.dart b/lib/app/features/contact/data/repositories/contact_repository_impl.dart index 3dfba863..99be92a9 100644 --- a/lib/app/features/contact/data/repositories/contact_repository_impl.dart +++ b/lib/app/features/contact/data/repositories/contact_repository_impl.dart @@ -18,17 +18,23 @@ class ContactRepositoryImpl implements ContactRepository { required ContactModel contact, }) async { try { - final response = await _httpClient.post( + final Response response = await _httpClient.post( ConstantsAPI.apiSendMail, data: ContactUser.toJson(contact), ); - // return response.data; - return Success(ContactAnswer.fromJson(response.data)); + return Success( + ContactAnswer.fromResponse( + contact: contact, + response: response, + ), + ); } catch (e, s) { - log('Error: $e', stackTrace: s); + log('[Error]: ContactRepositoryImpl.sendMail', error: e, stackTrace: s); - return const Failure(ContactFailedResult.unknown); + return const Failure( + ContactFailedResult.unknown, + ); } } } diff --git a/lib/app/features/contact/domain/models/contact_answer.dart b/lib/app/features/contact/domain/models/contact_answer.dart index 760a24a8..5833be33 100644 --- a/lib/app/features/contact/domain/models/contact_answer.dart +++ b/lib/app/features/contact/domain/models/contact_answer.dart @@ -1,14 +1,18 @@ +import 'package:dio/dio.dart'; import 'package:site/app/features/contact/domain/models/contact_model.dart'; class ContactAnswer extends ContactModel { - factory ContactAnswer.fromJson(Map json) { + factory ContactAnswer.fromResponse({ + required ContactModel contact, + required Response response, + }) { return ContactAnswer( - name: json['sender_name'], - email: json['source_email'], - message: json['template_body'], - subject: json['template_subject'], - response: json['response'], - status: json['status'], + name: contact.name, + email: contact.email, + message: contact.message, + subject: contact.subject, + status: response.statusMessage ?? '', + response: '${response.statusCode}', ); } @@ -17,10 +21,10 @@ class ContactAnswer extends ContactModel { required super.email, required super.message, required super.subject, - required this.response, required this.status, + required this.response, }); - final String response; final String status; + final String response; } diff --git a/lib/app/features/contact/presentation/cubit/contact_cubit.dart b/lib/app/features/contact/presentation/cubit/contact_cubit.dart index 76277755..cd02b866 100644 --- a/lib/app/features/contact/presentation/cubit/contact_cubit.dart +++ b/lib/app/features/contact/presentation/cubit/contact_cubit.dart @@ -1,19 +1,22 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:site/app/core/result/result.dart'; import 'package:site/app/features/contact/contact.dart'; part 'contact_state.dart'; class ContactCubit extends Cubit { ContactCubit({ - required ContactRepositoryImpl contactRepository, + required ContactRepository contactRepository, }) : _contactRepository = contactRepository, super( const ContactInitial(), ); - final ContactRepositoryImpl _contactRepository; + final ContactRepository _contactRepository; Future sendMail({ required ContactModel contact, @@ -21,16 +24,31 @@ class ContactCubit extends Cubit { emit(const ContactLoading()); try { - await _contactRepository.sendMail( - contact: contact, - ); - emit( - ContactSuccess( - contact: contact, - message: 'Email sent successfully', - ), - ); - } catch (e) { + final result = await _contactRepository.sendMail(contact: contact); + + switch (result) { + case Success(object: final contactAnswer): + emit( + ContactSuccess( + contact: contactAnswer, + message: 'Email sent successfully', + ), + ); + case Failure(error: final contactFailedResult): + emit( + ContactError( + contact: contact, + message: switch (contactFailedResult) { + ContactFailedResult.unauthorized => 'Unauthorized', + ContactFailedResult.tooManyRequests => + 'Too many requests, try again later', + ContactFailedResult.unknown => 'Error sending email', + }, + ), + ); + } + } catch (e, s) { + log('[Error]: ContactCubit.sendMail', error: e, stackTrace: s); emit( ContactError( contact: contact, @@ -42,8 +60,8 @@ class ContactCubit extends Cubit { } -// class ContactController extends ChangeNotifier { -// ContactController({ +// class ContactCubit extends ChangeNotifier { +// ContactCubit({ // ContactRepositoryImpl? contactRepository, // }) : _contactRepository = contactRepository ?? getIt(); diff --git a/lib/app/features/contact/presentation/presentation.dart b/lib/app/features/contact/presentation/presentation.dart index b944e927..ac29eaf8 100644 --- a/lib/app/features/contact/presentation/presentation.dart +++ b/lib/app/features/contact/presentation/presentation.dart @@ -1,2 +1,2 @@ -export 'controller/controller.dart'; export 'widgets/widgets.dart'; +export 'cubit/contact_cubit.dart'; \ No newline at end of file diff --git a/lib/app/features/contact/presentation/widgets/form/custom_form.dart b/lib/app/features/contact/presentation/widgets/form/custom_form.dart index 32c733a0..6ade631f 100644 --- a/lib/app/features/contact/presentation/widgets/form/custom_form.dart +++ b/lib/app/features/contact/presentation/widgets/form/custom_form.dart @@ -16,9 +16,9 @@ class CustomForm extends StatelessWidget { required this.subjectController, required this.messageController, required this.onPressed, - ContactController? contactController, - }) : _contactController = contactController ?? - ContactController( + ContactCubit? contactCubit, + }) : _contactCubit = contactCubit ?? + ContactCubit( contactRepository: ContactRepositoryImpl( httpClient: getIt(), ), @@ -30,8 +30,7 @@ class CustomForm extends StatelessWidget { final TextEditingController subjectController; final TextEditingController messageController; final VoidCallback onPressed; - - final ContactController? _contactController; + final ContactCubit? _contactCubit; @override Widget build(BuildContext context) { @@ -77,9 +76,21 @@ class CustomForm extends StatelessWidget { Center( child: AppTextButton( text: AppTexts.get(context).sendEmailUpper, - onPressed: onPressed, + + /// TODO: + // onPressed: onPressed, + onPressed: () { + _contactCubit?.sendMail( + contact: ContactUser( + name: 'felipe sales', + email: 'soufeliposales@gmail.com', + message: 'teste de mensagem', + subject: 'vamos testar, mais uma vez', + ), + ); + }, onLongPress: () { - _contactController?.sendMail( + _contactCubit?.sendMail( contact: ContactUser( name: 'felipe sales', email: 'soufeliposales@gmail.com', diff --git a/lib/app/features/contact/presentation/widgets/ui/contact_widget.dart b/lib/app/features/contact/presentation/widgets/ui/contact_widget.dart index 36b271c4..73713892 100644 --- a/lib/app/features/contact/presentation/widgets/ui/contact_widget.dart +++ b/lib/app/features/contact/presentation/widgets/ui/contact_widget.dart @@ -1,26 +1,25 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:site/app/core/injections/injections.dart'; -import 'package:site/app/core/l10n/l10n.dart'; import 'package:site/app/core/responsive/responsive.dart'; import 'package:site/app/core/shared/app_keys.dart'; -import 'package:site/app/core/tokens/tokens.dart'; import 'package:site/app/features/contact/contact.dart'; import 'package:site/app/widgets/snack_bars/snack_bars.dart'; -// import 'package:site/data/services/recaptcha/recaptcha.dart'; class ContactWidget extends StatelessWidget { ContactWidget({ super.key, - ContactController? contactController, - }) : _contactController = contactController ?? - ContactController( - contactRepository: ContactRepositoryImpl( - httpClient: getIt(), - ), - ); + ContactCubit? contactCubit, + }) : _contactCubit = contactCubit ?? _cubit; - final ContactController? _contactController; + final ContactCubit? _contactCubit; + + static final _cubit = ContactCubit( + contactRepository: ContactRepositoryImpl( + httpClient: getIt(), + ), + ); @override Widget build(BuildContext context) { @@ -29,56 +28,46 @@ class ContactWidget extends StatelessWidget { final emailController = TextEditingController(); final messageController = TextEditingController(); final subjectController = TextEditingController(); - final form = CustomForm( - formKey: formKey, - nameController: nameController, - emailController: emailController, - subjectController: subjectController, - messageController: messageController, - onPressed: () async { - if (formKey.currentState?.validate() ?? false) { - // final isNotABot = await RecaptchaService.isNotABot(); - - // if (isNotABot) { - if (true) { - if (context.mounted) { - appShowSnackBar( - context, - text: AppTexts.get(context).emailSendedWithSuccess, - icon: Icons.check, - color: AppColors.primaryDark, - width: 300, - ); - } - - _contactController?.sendMail( - contact: ContactUser( - name: nameController.text, - email: emailController.text.trim(), - message: messageController.text, - subject: subjectController.text, - ), - ); - for (var controller in [ - nameController, - emailController, - messageController, - subjectController, - ]) { - controller.clear(); - } - } else if (context.mounted) { - appShowSnackBar( - context, - text: AppTexts.get(context).emailNotSended, - icon: Icons.error, - color: AppColors.red, - width: 300, - ); + final form = BlocProvider( + create: (context) => _contactCubit ?? _cubit, + child: BlocConsumer( + listener: (context, state) { + if ([ContactSuccess, ContactError].contains(state.runtimeType)) { + appShowSnackBarFromContact(context, state); } - } - }, + }, + builder: (context, state) { + return CustomForm( + formKey: formKey, + nameController: nameController, + emailController: emailController, + subjectController: subjectController, + messageController: messageController, + onPressed: () { + if (formKey.currentState?.validate() ?? false) { + _contactCubit?.sendMail( + contact: ContactUser( + name: nameController.text, + email: emailController.text, + subject: subjectController.text, + message: messageController.text, + ), + ); + + for (var controller in [ + nameController, + emailController, + messageController, + subjectController, + ]) { + controller.clear(); + } + } + }, + ); + }, + ), ); return LayoutBuilder( diff --git a/lib/app/features/home/home_page.dart b/lib/app/features/home/home_page.dart index cd8763fd..751310d4 100644 --- a/lib/app/features/home/home_page.dart +++ b/lib/app/features/home/home_page.dart @@ -41,17 +41,18 @@ class _HomePageState extends State { void initState() { super.initState(); items = [ - Presentation(itemScrollController), - const Projects(), - const Experience(), - const Social(), + /// TODO: ContactWidget( - contactController: ContactController( + contactCubit: ContactCubit( contactRepository: ContactRepositoryImpl( httpClient: widget._httpClient, ), ), ), + Presentation(itemScrollController), + const Projects(), + const Experience(), + const Social(), const CustomFooter(), ]; } diff --git a/lib/app/widgets/snack_bars/app_show_snack_bar.dart b/lib/app/widgets/snack_bars/app_show_snack_bar.dart index 07c7e7d5..4f1988e7 100644 --- a/lib/app/widgets/snack_bars/app_show_snack_bar.dart +++ b/lib/app/widgets/snack_bars/app_show_snack_bar.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:auto_size_text/auto_size_text.dart'; +import 'package:site/app/core/l10n/localizations/localizations.dart'; import 'package:site/app/core/tokens/tokens.dart'; +import 'package:site/app/features/contact/presentation/presentation.dart'; ScaffoldFeatureController appShowSnackBar( BuildContext context, { @@ -44,3 +46,28 @@ ScaffoldFeatureController appShowSnackBar( ), ); } + +ScaffoldFeatureController appShowSnackBarFromContact( + BuildContext context, + ContactState state, +) { + return appShowSnackBar( + context, + width: 300, + text: switch (state) { + ContactSuccess() => AppTexts.get(context).emailSendedWithSuccess, + ContactError() => AppTexts.get(context).emailNotSended, + _ => '', + }, + icon: switch (state) { + ContactSuccess() => Icons.check, + ContactError() => Icons.error, + _ => Icons.error, + }, + color: switch (state) { + ContactSuccess() => AppColors.primaryDark, + ContactError() => AppColors.red, + _ => AppColors.red, + }, + ); +} diff --git a/test/app/features/contact/presentation/cubit/contact_cubit_test.dart b/test/app/features/contact/presentation/cubit/contact_cubit_test.dart new file mode 100644 index 00000000..55f33b9f --- /dev/null +++ b/test/app/features/contact/presentation/cubit/contact_cubit_test.dart @@ -0,0 +1,9 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// TODO: + +void main() { + testWidgets('contact cubit ...', (tester) async { + // TODO: Implement test + }); +} diff --git a/test/utils/mocks/mock_contact_cubit.dart b/test/utils/mocks/mock_contact_cubit.dart new file mode 100644 index 00000000..98e226e3 --- /dev/null +++ b/test/utils/mocks/mock_contact_cubit.dart @@ -0,0 +1,5 @@ +import 'package:mocktail/mocktail.dart'; + +import 'package:site/app/features/contact/contact.dart'; + +class MockContactCubit extends Mock implements ContactCubit {} diff --git a/test/utils/mocks/mocks.dart b/test/utils/mocks/mocks.dart index b68f7754..160c14f0 100644 --- a/test/utils/mocks/mocks.dart +++ b/test/utils/mocks/mocks.dart @@ -1,3 +1,3 @@ -export 'mock_contact_controller.dart'; +export 'mock_contact_cubit.dart'; export 'mock_firebase_remote_config.dart'; export 'mock_http_client.dart';