diff --git a/lib/core/di/cubit_registrations.dart b/lib/core/di/cubit_registrations.dart index 05bfd7d1..19a1962b 100644 --- a/lib/core/di/cubit_registrations.dart +++ b/lib/core/di/cubit_registrations.dart @@ -263,7 +263,7 @@ class CubitRegistrations { ), ); sl.registerFactory( - () => ConsultationsCubit(consultationsRepository: sl()), + () => ConsultationsCubit(consultationsRepository: sl(), chatRepository: sl()), ); sl.registerFactory( () => ConsultantCommentsCubit(diaryRepository: sl()), diff --git a/lib/features/consultations/presentation/cubit/consultations_cubit.dart b/lib/features/consultations/presentation/cubit/consultations_cubit.dart index 80f8b206..ea830c88 100644 --- a/lib/features/consultations/presentation/cubit/consultations_cubit.dart +++ b/lib/features/consultations/presentation/cubit/consultations_cubit.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:eatvio_app/core/enums/base_state.dart'; +import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart'; import 'package:eatvio_app/features/consultations/repositories/consultations_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -8,7 +9,8 @@ import 'package:eatvio_app/core/error_reporting/report_from.dart'; class ConsultationsCubit extends Cubit { final ConsultationsRepository consultationsRepository; - ConsultationsCubit({required this.consultationsRepository}) : super(ConsultationsState()); + final ChatRepository chatRepository; + ConsultationsCubit({required this.consultationsRepository, required this.chatRepository}) : super(ConsultationsState()); Future loadConsultations() async { try { @@ -20,4 +22,15 @@ class ConsultationsCubit extends Cubit { emit(state.copyWith(state: BaseState.error)); } } + + Future getChatIdByUserSlug(String slug) async { + try { + final chatIdOrError = await chatRepository.getChatByUserSlug(slug); + return chatIdOrError == '404' ? null : chatIdOrError; + } catch (e, st) { + await reportFrom(e, st, this); + log(e.toString(), stackTrace: st); + return null; + } + } } diff --git a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart index 7efa25f7..e579c9b3 100644 --- a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart +++ b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart @@ -3,14 +3,17 @@ import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; +import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; +import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart'; import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart'; import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart'; import 'package:eatvio_app/features/user_details/presentation/user_details_screen.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; class ConsultationCardWidget extends StatefulWidget { @@ -34,6 +37,7 @@ class _ConsultationCardWidgetState extends State { final List legendColors = const [Colors.white, Color(0xFFF0C497), Color(0xFFFF9500), Color(0xFF2F80ED), Color(0xFF4CD964)]; late final List<(int, DiaryModel)> givenDaysWithDayNumber; + bool _isOpeningChat = false; final List> legendStates = [ [DiaryStateEnum.init], [DiaryStateEnum.started, DiaryStateEnum.lastDayStarted], @@ -279,7 +283,7 @@ class _ConsultationCardWidgetState extends State { const SizedBox(width: 8), InkWell( borderRadius: BorderRadius.circular(20), - onTap: () {}, + onTap: _isOpeningChat ? null : _openChat, child: Ink( height: 52, width: 52, @@ -308,6 +312,18 @@ class _ConsultationCardWidgetState extends State { ); } + Future _openChat() async { + setState(() => _isOpeningChat = true); + final chatId = await context.read().getChatIdByUserSlug(widget.consultationsModel.userSlug); + if (!mounted) return; + setState(() => _isOpeningChat = false); + if (chatId == null) { + showErrorToast(S.of(context).msg_something_went_wrong); + return; + } + GoRouter.of(context).pushNamed('chatScreen', extra: {'uuid': chatId}); + } + Row _legendTitle(String text, Color color) { return Row( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/test/features/consultations/presentation/cubit/consultations_cubit_test.dart b/test/features/consultations/presentation/cubit/consultations_cubit_test.dart index 26ab5974..c9fe20b5 100644 --- a/test/features/consultations/presentation/cubit/consultations_cubit_test.dart +++ b/test/features/consultations/presentation/cubit/consultations_cubit_test.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/core/enums/base_state.dart'; +import 'package:eatvio_app/features/chat/repositories/chat_repository.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart'; import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart'; @@ -8,14 +9,21 @@ import 'package:mocktail/mocktail.dart'; class MockConsultationsRepository extends Mock implements ConsultationsRepository {} +class MockChatRepository extends Mock implements ChatRepository {} + void main() { group('ConsultationsCubit', () { late ConsultationsCubit cubit; late MockConsultationsRepository mockRepository; + late MockChatRepository mockChatRepository; setUp(() { mockRepository = MockConsultationsRepository(); - cubit = ConsultationsCubit(consultationsRepository: mockRepository); + mockChatRepository = MockChatRepository(); + cubit = ConsultationsCubit( + consultationsRepository: mockRepository, + chatRepository: mockChatRepository, + ); }); tearDown(() { @@ -47,5 +55,33 @@ void main() { expect(cubit.state.state, BaseState.error); }); + + test('getChatIdByUserSlug - возвращает id чата', () async { + when(() => mockChatRepository.getChatByUserSlug('john-doe')) + .thenAnswer((_) async => 'chat-uuid'); + + final chatId = await cubit.getChatIdByUserSlug('john-doe'); + + expect(chatId, 'chat-uuid'); + verify(() => mockChatRepository.getChatByUserSlug('john-doe')).called(1); + }); + + test('getChatIdByUserSlug - возвращает null при ошибке', () async { + when(() => mockChatRepository.getChatByUserSlug(any())) + .thenThrow(Exception('Error')); + + final chatId = await cubit.getChatIdByUserSlug('john-doe'); + + expect(chatId, isNull); + }); + + test('getChatIdByUserSlug - возвращает null когда чат не найден', () async { + when(() => mockChatRepository.getChatByUserSlug(any())) + .thenAnswer((_) async => '404'); + + final chatId = await cubit.getChatIdByUserSlug('john-doe'); + + expect(chatId, isNull); + }); }); } diff --git a/test/features/consultations/presentation/widgets/consultation_card_widget_test.dart b/test/features/consultations/presentation/widgets/consultation_card_widget_test.dart index c897d8e2..c170f224 100644 --- a/test/features/consultations/presentation/widgets/consultation_card_widget_test.dart +++ b/test/features/consultations/presentation/widgets/consultation_card_widget_test.dart @@ -3,16 +3,21 @@ import 'package:eatvio_app/core/app_initializer.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; +import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart'; import 'package:eatvio_app/features/consultations/presentation/widgets/consultation_card_widget.dart'; import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:go_router/go_router.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:network_image_mock/network_image_mock.dart'; +class MockConsultationsCubit extends Mock implements ConsultationsCubit {} + void main() { group('ConsultationCardWidget smoke', () { setUpAll(() async { @@ -64,7 +69,8 @@ void main() { personalDietId: null, ); - Widget wrap(Widget child) { + Widget wrap(Widget child, {ConsultationsCubit? cubit}) { + final consultationsCubit = cubit ?? MockConsultationsCubit(); return MaterialApp.router( theme: ThemeHelper().themeData(), localizationsDelegates: const [ @@ -77,7 +83,20 @@ void main() { routerConfig: GoRouter( initialLocation: '/', routes: [ - GoRoute(path: '/', builder: (context, state) => child), + GoRoute( + path: '/', + builder: (context, state) => BlocProvider.value( + value: consultationsCubit, + child: child, + ), + ), + GoRoute( + path: '/chat-screen', + name: 'chatScreen', + builder: (context, state) => Scaffold( + body: Text('chat:${(state.extra as Map)['uuid']}'), + ), + ), GoRoute( path: '/profile-screen', name: 'profileScreen', @@ -163,5 +182,30 @@ void main() { }); }, ); + + testWidgets('opens chat with consultation user when chat icon tapped', ( + tester, + ) async { + final cubit = MockConsultationsCubit(); + when(() => cubit.stream).thenAnswer((_) => const Stream.empty()); + when(() => cubit.getChatIdByUserSlug('john-doe')) + .thenAnswer((_) async => 'chat-uuid'); + + await mockNetworkImagesFor(() async { + await tester.pumpWidget( + wrap( + ConsultationCardWidget(consultationsModel: mockConsultation), + cubit: cubit, + ), + ); + await tester.pumpAndSettle(); + + await tester.tap(find.byIcon(Icons.chat_outlined)); + await tester.pumpAndSettle(); + + expect(find.text('chat:chat-uuid'), findsOneWidget); + verify(() => cubit.getChatIdByUserSlug('john-doe')).called(1); + }); + }); }); }