From c540cbe6dfe53fff9b672191e12e4b6bddc56d7a Mon Sep 17 00:00:00 2001 From: misha Date: Wed, 18 Feb 2026 19:45:32 +0300 Subject: [PATCH] feat(notifications): added navigation from different types of notifications --- lib/core/routing/main_navigation_routes.dart | 35 +- lib/core/routing/other_routes.dart | 5 +- .../widgets/bottom_nav_scope.dart | 38 + .../widgets/bottom_navigation_bar_widget.dart | 25 + .../chat/presentation/pages/chats_screen.dart | 15 +- .../widgets/consultation_card_widget.dart | 6 +- .../diary/presentation/bloc/diary_cubit.dart | 8 +- .../presentation/pages/diary_screen.dart | 24 +- .../presentation/widgets/calendar_widget.dart | 20 +- .../models/notification_model.dart | 688 ++++++++++++++---- .../widgets/notification_widget.dart | 597 +++++++-------- .../bloc/sub_cubit/sub_cubit.dart | 18 +- .../presentation/pages/subs_screen.dart | 2 +- 13 files changed, 974 insertions(+), 507 deletions(-) create mode 100644 lib/features/bottom_navigation_bar/presentation/widgets/bottom_nav_scope.dart diff --git a/lib/core/routing/main_navigation_routes.dart b/lib/core/routing/main_navigation_routes.dart index 88a8db04..f7756fbd 100644 --- a/lib/core/routing/main_navigation_routes.dart +++ b/lib/core/routing/main_navigation_routes.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/core/routing/route_constants.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart'; +import 'package:eatvio_app/features/bottom_navigation_bar/presentation/widgets/bottom_nav_scope.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart'; import 'package:eatvio_app/features/chat/presentation/pages/chats_screen.dart'; import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart'; @@ -31,8 +32,8 @@ class MainNavigationRoutes { profileAvatarCubit.getMyProfile(); }); } - - return BottomNavigationBarWidget(child: child); + + return _MainShellWithNavScope(child: child); }, routes: [ GoRoute( @@ -58,7 +59,7 @@ class MainNavigationRoutes { GoRoute( path: RouteConstants.diaryScreen, name: 'diaryScreen', - pageBuilder: (context, state) => const NoTransitionPage(child: DiaryScreen()), + pageBuilder: (context, state) => NoTransitionPage(child: DiaryScreen(date: state.extra as DateTime?)), ), GoRoute( path: ConsultationsScreen.path, @@ -68,7 +69,10 @@ class MainNavigationRoutes { GoRoute( path: RouteConstants.chatSelectScreen, name: 'chatSelectScreen', - pageBuilder: (context, state) => const NoTransitionPage(child: ChatsScreen()), + pageBuilder: (context, state) { + final initialSearchQuery = state.extra is String ? state.extra as String : null; + return NoTransitionPage(child: ChatsScreen(initialSearchQuery: initialSearchQuery)); + }, ), GoRoute( path: RouteConstants.notificationsScreen, @@ -94,3 +98,26 @@ class MainNavigationRoutes { ]; } } + +/// Wraps the shell with [BottomNavScope] so descendants (e.g. notifications) +/// can switch tabs via [BottomNavController.switchToRoute] instead of pushing. +class _MainShellWithNavScope extends StatefulWidget { + const _MainShellWithNavScope({required this.child}); + + final Widget child; + + @override + State<_MainShellWithNavScope> createState() => _MainShellWithNavScopeState(); +} + +class _MainShellWithNavScopeState extends State<_MainShellWithNavScope> { + final BottomNavController _navController = BottomNavController(); + + @override + Widget build(BuildContext context) { + return BottomNavScope( + controller: _navController, + child: BottomNavigationBarWidget(child: widget.child), + ); + } +} diff --git a/lib/core/routing/other_routes.dart b/lib/core/routing/other_routes.dart index 0e30336e..a04d2bee 100644 --- a/lib/core/routing/other_routes.dart +++ b/lib/core/routing/other_routes.dart @@ -195,7 +195,7 @@ class OtherRoutes { GoRoute( path: '${RouteConstants.subsScreen}/:slug', name: 'subsScreen', - builder: (context, state) => SubsScreen(slug: state.pathParameters['slug']!, subType: state.extra as SubType), + builder: (context, state) => SubsScreen(slug: state.pathParameters['slug'], subType: state.extra as SubType), ), // Chat routes @@ -276,8 +276,7 @@ class OtherRoutes { builder: (context, state) { final data = state.extra as Map; final diaryId = data['diaryId'] as int; - final currentPlanDay = data['currentDiaryDay'] as int; - return UserDiaryScreen(diaryId: diaryId, currentPlanDay: currentPlanDay); + return UserDiaryScreen(diaryId: diaryId); }, ), GoRoute( diff --git a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_nav_scope.dart b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_nav_scope.dart new file mode 100644 index 00000000..53ea8741 --- /dev/null +++ b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_nav_scope.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class BottomNavController { + BottomNavController(); + + void Function(String path, Object? extra)? _onSwitchToRoute; + + void register(void Function(String path, Object? extra) callback) { + _onSwitchToRoute = callback; + } + + void switchToRoute(String path, [Object? extra]) { + _onSwitchToRoute?.call(path, extra); + } +} + +class BottomNavScope extends InheritedWidget { + const BottomNavScope({ + required this.controller, + required super.child, + super.key, + }); + + final BottomNavController controller; + + static BottomNavController? maybeOf(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType()?.controller; + } + + static BottomNavController of(BuildContext context) { + final controller = maybeOf(context); + assert(controller != null, 'No BottomNavScope found in context'); + return controller!; + } + + @override + bool updateShouldNotify(BottomNavScope oldWidget) => controller != oldWidget.controller; +} diff --git a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart index efac96bf..cd534644 100644 --- a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart +++ b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart @@ -8,6 +8,7 @@ import 'package:eatvio_app/features/notifications/presentation/notifications_cub import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart'; +import 'package:eatvio_app/features/bottom_navigation_bar/presentation/widgets/bottom_nav_scope.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_state.dart'; import 'package:eatvio_app/generated/l10n.dart'; @@ -66,6 +67,24 @@ class _BottomNavigationBarWidgetState extends State { } } + /// Switch to the tab that corresponds to [path], optionally with [extra] + /// (e.g. date for diary). If [path] is not in the current tab list, falls back to push. + void _switchToRoute(String path, [Object? extra]) { + if (pages == null) return; + final index = pages!.indexOf(path); + if (index >= 0 && index != currentBottomNavBarIndex) { + setState(() { + currentBottomNavBarIndex = index; + }); + GoRouter.of(context).replace(path, extra: extra); + } else if (index >= 0) { + // Same tab but with new extra (e.g. diary with date) + GoRouter.of(context).replace(path, extra: extra); + } else { + GoRouter.of(context).push(path, extra: extra); + } + } + static const Color _devAccentColor = Color(0xFFF59E0B); // amber-500 Widget _buildLogoIcon() { @@ -121,6 +140,12 @@ class _BottomNavigationBarWidgetState extends State { // Инициализируем pages на основе profileModel pages ??= _getPages(isConsultant); + // Expose tab switching to descendants (e.g. notification widget) + final controller = BottomNavScope.maybeOf(context); + if (controller != null) { + controller.register(_switchToRoute); + } + return Scaffold( body: widget.child, bottomNavigationBar: BottomNavigationBar( diff --git a/lib/features/chat/presentation/pages/chats_screen.dart b/lib/features/chat/presentation/pages/chats_screen.dart index 539e971c..993bcfd3 100644 --- a/lib/features/chat/presentation/pages/chats_screen.dart +++ b/lib/features/chat/presentation/pages/chats_screen.dart @@ -11,7 +11,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class ChatsScreen extends StatefulWidget { - const ChatsScreen({super.key}); + const ChatsScreen({super.key, this.initialSearchQuery}); + + /// When set (e.g. from notification tap), the search field is pre-filled + /// and chat list is filtered by this query. + final String? initialSearchQuery; @override State createState() => _ChatsScreenState(); @@ -23,12 +27,17 @@ class _ChatsScreenState extends State { @override void initState() { super.initState(); - // Инициализация ChatsCubit при первом открытии экрана + final query = widget.initialSearchQuery?.trim(); + if (query != null && query.isNotEmpty) { + searchController.text = query; + } WidgetsBinding.instance.addPostFrameCallback((_) { final cubit = context.read(); final state = cubit.state; if (state.state == BaseState.initial) { - cubit.getChatRoomsWithLoading(); + cubit.getChatRoomsWithLoading(query: query?.isNotEmpty == true ? query : null); + } else if (query != null && query.isNotEmpty) { + cubit.getChatRooms(query: query); } }); } diff --git a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart index b1529280..7efa25f7 100644 --- a/lib/features/consultations/presentation/widgets/consultation_card_widget.dart +++ b/lib/features/consultations/presentation/widgets/consultation_card_widget.dart @@ -172,8 +172,8 @@ class _ConsultationCardWidgetState extends State { ); return; } - GoRouter.of(context).push(UserDiaryScreen.path, extra: {'diaryId': day.$2.id, 'currentDiaryDay': index + 1}); - }, + GoRouter.of(context).push(UserDiaryScreen.path, extra: {'diaryId': day.$2.id,}); + }, borderRadius: BorderRadius.circular(100), child: Ink( decoration: BoxDecoration( @@ -255,7 +255,7 @@ class _ConsultationCardWidgetState extends State { .where((e) => e.$2.state == DiaryStateEnum.finished || e.$2.state == DiaryStateEnum.lastDayFinished) .lastOrNull; if (lastActiveDay != null) { - GoRouter.of(context).push(UserDiaryScreen.path, extra: {'diaryId': lastActiveDay.$2.id, 'currentDiaryDay': lastActiveDay.$1}); + GoRouter.of(context).push(UserDiaryScreen.path, extra: {'diaryId': lastActiveDay.$2.id,}); return; } showErrorDialog( diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index 0bbb4f82..93e24045 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -48,6 +48,11 @@ class DiaryCubit extends Cubit { String comment = ''; + void setInitialDate(DateTime? date) { + if (date == null) return; + selectedDay = date; + } + Future setSelectedDay(DateTime day) async { final prevDay = selectedDay; final prevMeal = selectedMeal; @@ -63,7 +68,7 @@ class DiaryCubit extends Cubit { return canSelect; } - Future fetchDiaryV2({bool needLoading = false, int? diaryId}) async { + Future fetchDiaryV2({bool needLoading = false, int? diaryId, DateTime? date}) async { final currentState = state; try { if (needLoading) { @@ -77,6 +82,7 @@ class DiaryCubit extends Cubit { return true; } } + final diaryDay = await getDiary(); if (diaryDay == null) { emit(const DiaryState.error(errorMessage: 'Diary not found')); diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart index c7f832d4..07042f16 100644 --- a/lib/features/diary/presentation/pages/diary_screen.dart +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -24,31 +24,29 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class DiaryScreen extends StatelessWidget { - const DiaryScreen({super.key}); - + const DiaryScreen({super.key, required this.date}); + final DateTime? date; @override Widget build(BuildContext context) { - return const DiaryScreenContent(); + return DiaryScreenContent(date: date); } } class UserDiaryScreen extends StatelessWidget { static const path = '/user-diary-screen'; static const name = 'userDiaryScreen'; - const UserDiaryScreen({super.key, required this.diaryId, required this.currentPlanDay}); + const UserDiaryScreen({super.key, required this.diaryId}); final int diaryId; - final int currentPlanDay; @override Widget build(BuildContext context) { - return DiaryScreenContent(diaryId: diaryId, currentPlanDay: currentPlanDay); + return DiaryScreenContent(diaryId: diaryId,); } } class DiaryScreenContent extends StatefulWidget { - const DiaryScreenContent({super.key, this.diaryId, this.currentPlanDay}); + const DiaryScreenContent({super.key, this.diaryId,this.date}); final int? diaryId; - final int? currentPlanDay; - + final DateTime? date; @override State createState() => _DiaryScreenContentState(); } @@ -61,7 +59,11 @@ class _DiaryScreenContentState extends State { final s = S.of(context); return MultiBlocProvider( providers: [ - BlocProvider(create: (BuildContext context) => sl()..fetchDiaryV2(needLoading: true, diaryId: widget.diaryId)), + BlocProvider( + create: (BuildContext context) => sl() + ..setInitialDate(widget.date) + ..fetchDiaryV2(needLoading: true, diaryId: widget.diaryId), + ), BlocProvider(create: (context) => sl()), ], child: BlocBuilder( @@ -91,7 +93,7 @@ class _DiaryScreenContentState extends State { physics: const BouncingScrollPhysics(), child: Column( children: [ - if (widget.diaryId == null) const CalendarWidget(), + if (widget.diaryId == null) CalendarWidget(date: widget.date), if (state is DiaryStateLoading) ...[const SizedBox(height: 60), const Loader()], if (state is DiaryStateLoaded) WakeupAndRetireWidget( diff --git a/lib/features/diary/presentation/widgets/calendar_widget.dart b/lib/features/diary/presentation/widgets/calendar_widget.dart index cce80a55..bcf1ab09 100644 --- a/lib/features/diary/presentation/widgets/calendar_widget.dart +++ b/lib/features/diary/presentation/widgets/calendar_widget.dart @@ -8,7 +8,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:table_calendar/table_calendar.dart'; class CalendarWidget extends StatefulWidget { - const CalendarWidget({super.key}); + const CalendarWidget({super.key, this.date}); + final DateTime? date; @override State createState() => _CalendarWidgetState(); @@ -18,6 +19,23 @@ class _CalendarWidgetState extends State { CalendarFormat _calendarFormat = CalendarFormat.week; DateTime _focusedDay = DateTime.now(); DateTime _selectedDay = DateTime.now(); + @override + void initState() { + super.initState(); + if (widget.date != null) { + _selectedDay = widget.date!; + _focusedDay = widget.date!; + } + } + + @override + void didUpdateWidget(CalendarWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.date != null && widget.date != oldWidget.date) { + _selectedDay = widget.date!; + _focusedDay = widget.date!; + } + } @override Widget build(BuildContext context) { diff --git a/lib/features/notifications/models/notification_model.dart b/lib/features/notifications/models/notification_model.dart index 3375a312..5715fb90 100644 --- a/lib/features/notifications/models/notification_model.dart +++ b/lib/features/notifications/models/notification_model.dart @@ -10,16 +10,24 @@ class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationMod @override final bool isRead; final String planName; + final int planId; final String customerName; + final int userId; + final String userPhotoPath; final String chatId; + final String chatUrl; @override final DateTime createdAt; PlanSubscriptionStartedToAuthorNotification({ required this.type, required this.planName, + required this.planId, required this.customerName, + required this.userId, + required this.userPhotoPath, required this.chatId, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -27,9 +35,13 @@ class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationMod factory PlanSubscriptionStartedToAuthorNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return PlanSubscriptionStartedToAuthorNotification( type: type, - planName: json['plan_name'], - customerName: json['user_name'], - chatId: json['chat_id'], + planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + customerName: json['user_name'] ?? '', + userId: json['user_id'] ?? 0, + userPhotoPath: json['user_photo_path'] ?? '', + chatId: json['chat_id']?.toString() ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -42,14 +54,22 @@ class PlanSubscriptionCanceledToAuthorNotification implements BaseNotificationMo @override final bool isRead; final String planName; + final int planId; final String customerName; + final int userId; + final String userPhotoPath; + final String chatUrl; @override final DateTime createdAt; PlanSubscriptionCanceledToAuthorNotification({ required this.type, required this.planName, + required this.planId, required this.customerName, + required this.userId, + required this.userPhotoPath, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -57,8 +77,12 @@ class PlanSubscriptionCanceledToAuthorNotification implements BaseNotificationMo factory PlanSubscriptionCanceledToAuthorNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return PlanSubscriptionCanceledToAuthorNotification( type: type, - planName: json['plan_name'], - customerName: json['user_name'], + planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + customerName: json['user_name'] ?? '', + userId: json['user_id'] ?? 0, + userPhotoPath: json['user_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -72,16 +96,28 @@ class ChosenPlanStartedToClientNotification implements BaseNotificationModel { final bool isRead; final String planName; final int planId; + final String consultantName; + final String peerProfileUrl; @override final DateTime createdAt; - ChosenPlanStartedToClientNotification({required this.type, required this.planName, required this.planId, required this.isRead, required this.createdAt}); + ChosenPlanStartedToClientNotification({ + required this.type, + required this.planName, + required this.planId, + required this.consultantName, + required this.peerProfileUrl, + required this.isRead, + required this.createdAt, + }); factory ChosenPlanStartedToClientNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanStartedToClientNotification( type: type, - planName: json['plan_name'], - planId: json['plan_id'], + planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + consultantName: json['consultant_name'] ?? '', + peerProfileUrl: json['peer_profile_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -94,15 +130,26 @@ class ChosenPlanTomorrowReminderToClientNotification implements BaseNotification @override final bool isRead; final String planName; + final String consultantName; + final int chatId; @override final DateTime createdAt; - ChosenPlanTomorrowReminderToClientNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); + ChosenPlanTomorrowReminderToClientNotification({ + required this.type, + required this.planName, + required this.consultantName, + required this.chatId, + required this.isRead, + required this.createdAt, + }); factory ChosenPlanTomorrowReminderToClientNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanTomorrowReminderToClientNotification( type: type, - planName: json['plan_name'], + planName: json['plan_name'] ?? '', + consultantName: json['consultant_name'] ?? '', + chatId: json['chat_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -114,15 +161,25 @@ class DiaryReminderNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; + final int? diaryId; + final String? message; @override final DateTime createdAt; - DiaryReminderNotification({required this.type, required this.isRead, required this.createdAt}); + DiaryReminderNotification({ + required this.type, + required this.isRead, + this.diaryId, + this.message, + required this.createdAt, + }); factory DiaryReminderNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return DiaryReminderNotification( type: type, isRead: isRead ?? false, + diaryId: json['diary_id'], + message: json['message'], createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); } @@ -133,16 +190,36 @@ class ConsultantLeftCommentNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; + final int chosenPlanId; + final int diaryId; final DateTime date; + final String planName; + final String userName; + final String consultantName; @override final DateTime createdAt; - ConsultantLeftCommentNotification({required this.type, required this.date, required this.isRead, required this.createdAt}); + ConsultantLeftCommentNotification({ + required this.type, + required this.chosenPlanId, + required this.diaryId, + required this.date, + required this.planName, + required this.userName, + required this.consultantName, + required this.isRead, + required this.createdAt, + }); factory ConsultantLeftCommentNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ConsultantLeftCommentNotification( type: type, + chosenPlanId: json['chosen_plan_id'] ?? 0, + diaryId: json['diary_id'] ?? 0, date: parseDate(json['diary_date'] ?? DateTime.now().toIso8601String()), + planName: json['plan_name'] ?? '', + userName: json['user_name'] ?? '', + consultantName: json['consultant_name'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -174,14 +251,34 @@ class ChosenPlanFinishedToClientNotification implements BaseNotificationModel { @override final bool isRead; final String planName; + final int chosenPlanId; + final String consultantName; + final String consultantPhotoPath; + final String consultantProfileUrl; + final int consultantId; @override final DateTime createdAt; - ChosenPlanFinishedToClientNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); + ChosenPlanFinishedToClientNotification({ + required this.type, + required this.planName, + required this.chosenPlanId, + required this.consultantName, + required this.consultantPhotoPath, + required this.consultantProfileUrl, + required this.consultantId, + required this.isRead, + required this.createdAt, + }); factory ChosenPlanFinishedToClientNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanFinishedToClientNotification( type: type, - planName: json['plan_name'], + planName: json['plan_name'] ?? '', + chosenPlanId: json['chosen_plan_id'] ?? 0, + consultantName: json['consultant_name'] ?? '', + consultantPhotoPath: json['consultant_photo_path'] ?? '', + consultantProfileUrl: json['consultant_profile_url'] ?? '', + consultantId: json['consultant_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -194,14 +291,34 @@ class PlanSubscriptionCanceledToCustomerNotification implements BaseNotification @override final bool isRead; final String planName; + final int planId; + final String authorName; + final int authorId; + final String authorPhotoPath; + final String chatUrl; @override final DateTime createdAt; - PlanSubscriptionCanceledToCustomerNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); + PlanSubscriptionCanceledToCustomerNotification({ + required this.type, + required this.planName, + required this.planId, + required this.authorName, + required this.authorId, + required this.authorPhotoPath, + required this.chatUrl, + required this.isRead, + required this.createdAt, + }); factory PlanSubscriptionCanceledToCustomerNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return PlanSubscriptionCanceledToCustomerNotification( type: type, - planName: json['plan_name'], + planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + authorName: json['author_name'] ?? '', + authorId: json['author_id'] ?? 0, + authorPhotoPath: json['author_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -217,6 +334,8 @@ class ChosenPlanStartedToConsultantNotification implements BaseNotificationModel final String customerName; final int planId; final int customerId; + final String peerProfileUrl; + final String consultationUrl; @override final DateTime createdAt; ChosenPlanStartedToConsultantNotification({ @@ -226,6 +345,8 @@ class ChosenPlanStartedToConsultantNotification implements BaseNotificationModel required this.customerName, required this.planId, required this.customerId, + required this.peerProfileUrl, + required this.consultationUrl, required this.createdAt, }); @@ -233,10 +354,12 @@ class ChosenPlanStartedToConsultantNotification implements BaseNotificationModel return ChosenPlanStartedToConsultantNotification( type: type, isRead: isRead ?? false, - planName: json['plan_name'], - customerName: json['client_name'], + planName: json['plan_name'] ?? '', + customerName: json['client_name'] ?? '', planId: json['plan_id'] ?? 0, customerId: json['client_id'] ?? 0, + peerProfileUrl: json['peer_profile_url'] ?? '', + consultationUrl: json['consultation_url'] ?? '', createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); } @@ -249,12 +372,14 @@ class ChosenPlanTomorrowReminderToConsultantNotification implements BaseNotifica final bool isRead; final String planName; final String customerName; + final int chatId; @override final DateTime createdAt; ChosenPlanTomorrowReminderToConsultantNotification({ required this.type, required this.planName, required this.customerName, + required this.chatId, required this.isRead, required this.createdAt, }); @@ -262,8 +387,9 @@ class ChosenPlanTomorrowReminderToConsultantNotification implements BaseNotifica factory ChosenPlanTomorrowReminderToConsultantNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanTomorrowReminderToConsultantNotification( type: type, - planName: json['plan_name'], - customerName: json['client_name'], + planName: json['plan_name'] ?? '', + customerName: json['client_name'] ?? '', + chatId: json['chat_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -276,14 +402,22 @@ class PlanSubscriptionPaidToAuthorNotification implements BaseNotificationModel @override final bool isRead; final String planName; + final int planId; final String customerName; + final int userId; + final String userPhotoPath; + final String chatUrl; @override final DateTime createdAt; PlanSubscriptionPaidToAuthorNotification({ required this.type, required this.isRead, required this.planName, + required this.planId, required this.customerName, + required this.userId, + required this.userPhotoPath, + required this.chatUrl, required this.createdAt, }); @@ -291,8 +425,12 @@ class PlanSubscriptionPaidToAuthorNotification implements BaseNotificationModel return PlanSubscriptionPaidToAuthorNotification( type: type, isRead: isRead ?? false, - planName: json['plan_name'], - customerName: json['user_name'], + planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + customerName: json['user_name'] ?? '', + userId: json['user_id'] ?? 0, + userPhotoPath: json['user_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); } @@ -306,6 +444,9 @@ class ChosenPlanFinishedToConsultantNotification implements BaseNotificationMode final String planName; final String customerName; final int planId; + final int chosenPlanId; + final String clientPhotoPath; + final int clientId; @override final DateTime createdAt; ChosenPlanFinishedToConsultantNotification({ @@ -313,6 +454,9 @@ class ChosenPlanFinishedToConsultantNotification implements BaseNotificationMode required this.planName, required this.customerName, required this.planId, + required this.chosenPlanId, + required this.clientPhotoPath, + required this.clientId, required this.isRead, required this.createdAt, }); @@ -320,9 +464,12 @@ class ChosenPlanFinishedToConsultantNotification implements BaseNotificationMode factory ChosenPlanFinishedToConsultantNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanFinishedToConsultantNotification( type: type, - planName: json['plan_name'], - customerName: json['client_name'], - planId: json['chosen_plan_id'], + planName: json['plan_name'] ?? '', + customerName: json['client_name'] ?? '', + planId: json['plan_id'] ?? 0, + chosenPlanId: json['chosen_plan_id'] ?? 0, + clientPhotoPath: json['client_photo_path'] ?? '', + clientId: json['client_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -334,15 +481,31 @@ class ChosenPlanLastDayFinishedForUser implements BaseNotificationModel { final NotificationType type; @override final bool isRead; + final String planName; + final int chosenPlanId; + final String complaintUrl; + final String chatUrl; @override final DateTime createdAt; - ChosenPlanLastDayFinishedForUser({required this.type, required this.isRead, required this.createdAt}); + ChosenPlanLastDayFinishedForUser({ + required this.type, + required this.isRead, + required this.planName, + required this.chosenPlanId, + required this.complaintUrl, + required this.chatUrl, + required this.createdAt, + }); factory ChosenPlanLastDayFinishedForUser.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanLastDayFinishedForUser( type: type, isRead: isRead ?? false, + planName: json['plan_name'] ?? '', + chosenPlanId: json['chosen_plan_id'] ?? 0, + complaintUrl: json['complaintUrl'] ?? json['complaint_url'] ?? '', + chatUrl: json['chatUrl'] ?? json['chat_url'] ?? '', createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); } @@ -373,10 +536,10 @@ class ChosenPlanConsultantLastDayStarted implements BaseNotificationModel { factory ChosenPlanConsultantLastDayStarted.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanConsultantLastDayStarted( type: type, - planName: json['plan_name'], - planId: json['plan_id'], - chatId: json['chat_id'], - chatUrl: json['chat_url'], + planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + chatId: json['chat_id']?.toString() ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -391,6 +554,7 @@ class ComplaintToModerationForUser implements BaseNotificationModel { final String message; final int entityId; final String entityType; + final String entityName; @override final DateTime createdAt; @@ -399,6 +563,7 @@ class ComplaintToModerationForUser implements BaseNotificationModel { required this.message, required this.entityId, required this.entityType, + required this.entityName, required this.isRead, required this.createdAt, }); @@ -406,9 +571,10 @@ class ComplaintToModerationForUser implements BaseNotificationModel { factory ComplaintToModerationForUser.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ComplaintToModerationForUser( type: type, - message: json['message'], - entityId: json['entity_id'], - entityType: json['entity_type'], + message: json['message'] ?? '', + entityId: json['entity_id'] ?? 0, + entityType: json['entity_type'] ?? '', + entityName: json['entity_name'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -860,15 +1026,17 @@ class DietCancelledNotification implements BaseNotificationModel { @override final bool isRead; final String dietName; + final int dietId; @override final DateTime createdAt; - DietCancelledNotification({required this.type, required this.dietName, required this.isRead, required this.createdAt}); + DietCancelledNotification({required this.type, required this.dietName, required this.dietId, required this.isRead, required this.createdAt}); factory DietCancelledNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return DietCancelledNotification( type: type, dietName: json['diet_name'] ?? '', + dietId: json['diet_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -881,15 +1049,17 @@ class DietStartedNotification implements BaseNotificationModel { @override final bool isRead; final String dietName; + final int dietId; @override final DateTime createdAt; - DietStartedNotification({required this.type, required this.dietName, required this.isRead, required this.createdAt}); + DietStartedNotification({required this.type, required this.dietName, required this.dietId, required this.isRead, required this.createdAt}); factory DietStartedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return DietStartedNotification( type: type, dietName: json['diet_name'] ?? '', + dietId: json['diet_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -902,15 +1072,17 @@ class DietExpiredNotification implements BaseNotificationModel { @override final bool isRead; final String dietName; + final int dietId; @override final DateTime createdAt; - DietExpiredNotification({required this.type, required this.dietName, required this.isRead, required this.createdAt}); + DietExpiredNotification({required this.type, required this.dietName, required this.dietId, required this.isRead, required this.createdAt}); factory DietExpiredNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return DietExpiredNotification( type: type, dietName: json['diet_name'] ?? '', + dietId: json['diet_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -923,16 +1095,25 @@ class DietWillStartedInNotification implements BaseNotificationModel { @override final bool isRead; final String dietName; + final int dietId; final String startDate; @override final DateTime createdAt; - DietWillStartedInNotification({required this.type, required this.dietName, required this.startDate, required this.isRead, required this.createdAt}); + DietWillStartedInNotification({ + required this.type, + required this.dietName, + required this.dietId, + required this.startDate, + required this.isRead, + required this.createdAt, + }); factory DietWillStartedInNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return DietWillStartedInNotification( type: type, dietName: json['diet_name'] ?? '', + dietId: json['diet_id'] ?? 0, startDate: json['start_date'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), @@ -946,16 +1127,25 @@ class DietCanNotBeStartedNotification implements BaseNotificationModel { @override final bool isRead; final String dietName; + final int dietId; final String reason; @override final DateTime createdAt; - DietCanNotBeStartedNotification({required this.type, required this.dietName, required this.reason, required this.isRead, required this.createdAt}); + DietCanNotBeStartedNotification({ + required this.type, + required this.dietName, + required this.dietId, + required this.reason, + required this.isRead, + required this.createdAt, + }); factory DietCanNotBeStartedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return DietCanNotBeStartedNotification( type: type, dietName: json['diet_name'] ?? '', + dietId: json['diet_id'] ?? 0, reason: json['reason'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), @@ -968,18 +1158,27 @@ class PersonalDietCreationNeededNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; - final String planName; - final String clientName; + final String userName; + final String consultantName; + final String myDietsUrl; @override final DateTime createdAt; - PersonalDietCreationNeededNotification({required this.type, required this.planName, required this.clientName, required this.isRead, required this.createdAt}); + PersonalDietCreationNeededNotification({ + required this.type, + required this.userName, + required this.consultantName, + required this.myDietsUrl, + required this.isRead, + required this.createdAt, + }); factory PersonalDietCreationNeededNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return PersonalDietCreationNeededNotification( type: type, - planName: json['plan_name'] ?? '', - clientName: json['client_name'] ?? '', + userName: json['user_name'] ?? '', + consultantName: json['consultant_name'] ?? '', + myDietsUrl: json['myDietsUrl'] ?? json['my_diets_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -992,15 +1191,29 @@ class ChosenPlanClientLastDayFinishedNotification implements BaseNotificationMod @override final bool isRead; final String planName; + final int chosenPlanId; + final String complaintUrl; + final String chatUrl; @override final DateTime createdAt; - ChosenPlanClientLastDayFinishedNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); + ChosenPlanClientLastDayFinishedNotification({ + required this.type, + required this.planName, + required this.chosenPlanId, + required this.complaintUrl, + required this.chatUrl, + required this.isRead, + required this.createdAt, + }); factory ChosenPlanClientLastDayFinishedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanClientLastDayFinishedNotification( type: type, planName: json['plan_name'] ?? '', + chosenPlanId: json['chosen_plan_id'] ?? 0, + complaintUrl: json['complaintUrl'] ?? json['complaint_url'] ?? '', + chatUrl: json['chatUrl'] ?? json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1013,15 +1226,26 @@ class ChosenPlanClientLastDayStartedNotification implements BaseNotificationMode @override final bool isRead; final String planName; + final int chosenPlanId; + final String lastDayDiaryUrl; @override final DateTime createdAt; - ChosenPlanClientLastDayStartedNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); + ChosenPlanClientLastDayStartedNotification({ + required this.type, + required this.planName, + required this.chosenPlanId, + required this.lastDayDiaryUrl, + required this.isRead, + required this.createdAt, + }); factory ChosenPlanClientLastDayStartedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ChosenPlanClientLastDayStartedNotification( type: type, planName: json['plan_name'] ?? '', + chosenPlanId: json['chosen_plan_id'] ?? 0, + lastDayDiaryUrl: json['last_day_diary_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1034,18 +1258,18 @@ class StayOnTrackToAuthorNotification implements BaseNotificationModel { @override final bool isRead; final String planName; - final String clientName; - final int planId; - final int clientId; + final String userName; + final int dayNum; + final int diaryDayId; @override final DateTime createdAt; StayOnTrackToAuthorNotification({ required this.type, required this.planName, - required this.clientName, - required this.planId, - required this.clientId, + required this.userName, + required this.dayNum, + required this.diaryDayId, required this.isRead, required this.createdAt, }); @@ -1054,9 +1278,9 @@ class StayOnTrackToAuthorNotification implements BaseNotificationModel { return StayOnTrackToAuthorNotification( type: type, planName: json['plan_name'] ?? '', - clientName: json['client_name'] ?? '', - planId: json['plan_id'] ?? 0, - clientId: json['client_id'] ?? 0, + userName: json['user_name'] ?? '', + dayNum: json['day_num'] ?? 0, + diaryDayId: json['diary_day_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1069,15 +1293,41 @@ class ClientStartPlanSubscriptionToSubscriberNotification implements BaseNotific @override final bool isRead; final String planName; + final int planId; + final String authorName; + final int authorId; + final String authorPhotoPath; + final String chatUrl; + final String chatId; + final String startDate; @override final DateTime createdAt; - ClientStartPlanSubscriptionToSubscriberNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); + ClientStartPlanSubscriptionToSubscriberNotification({ + required this.type, + required this.planName, + required this.planId, + required this.authorName, + required this.authorId, + required this.authorPhotoPath, + required this.chatUrl, + required this.chatId, + required this.startDate, + required this.isRead, + required this.createdAt, + }); factory ClientStartPlanSubscriptionToSubscriberNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ClientStartPlanSubscriptionToSubscriberNotification( type: type, planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + authorName: json['author_name'] ?? '', + authorId: json['author_id'] ?? 0, + authorPhotoPath: json['author_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', + chatId: json['chat_id']?.toString() ?? '', + startDate: json['start_date']?.toString() ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1090,14 +1340,22 @@ class ConsultantConfirmPlanSubscriptionToSubscriberNotification implements BaseN @override final bool isRead; final String planName; + final int planId; final String consultantName; + final int authorId; + final String authorPhotoPath; + final String chatUrl; @override final DateTime createdAt; ConsultantConfirmPlanSubscriptionToSubscriberNotification({ required this.type, required this.planName, + required this.planId, required this.consultantName, + required this.authorId, + required this.authorPhotoPath, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -1111,7 +1369,11 @@ class ConsultantConfirmPlanSubscriptionToSubscriberNotification implements BaseN return ConsultantConfirmPlanSubscriptionToSubscriberNotification( type: type, planName: json['plan_name'] ?? '', - consultantName: json['consultant_name'] ?? '', + planId: json['plan_id'] ?? 0, + consultantName: json['author_name'] ?? json['consultant_name'] ?? '', + authorId: json['author_id'] ?? 0, + authorPhotoPath: json['author_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1124,16 +1386,22 @@ class ClientPaidPlanSubscriptionToAuthorNotification implements BaseNotification @override final bool isRead; final String planName; + final int planId; final String clientName; - final double amount; + final int userId; + final String userPhotoPath; + final String chatUrl; @override final DateTime createdAt; ClientPaidPlanSubscriptionToAuthorNotification({ required this.type, required this.planName, + required this.planId, required this.clientName, - required this.amount, + required this.userId, + required this.userPhotoPath, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -1142,8 +1410,11 @@ class ClientPaidPlanSubscriptionToAuthorNotification implements BaseNotification return ClientPaidPlanSubscriptionToAuthorNotification( type: type, planName: json['plan_name'] ?? '', - clientName: json['client_name'] ?? '', - amount: (json['amount'] as num?)?.toDouble() ?? 0.0, + planId: json['plan_id'] ?? 0, + clientName: json['user_name'] ?? '', + userId: json['user_id'] ?? 0, + userPhotoPath: json['user_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1155,7 +1426,10 @@ class ConsultantLeftDiaryCommentNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; + final int chosenPlanId; + final int diaryId; final String planName; + final String userName; final String consultantName; final DateTime diaryDate; @override @@ -1163,7 +1437,10 @@ class ConsultantLeftDiaryCommentNotification implements BaseNotificationModel { ConsultantLeftDiaryCommentNotification({ required this.type, + required this.chosenPlanId, + required this.diaryId, required this.planName, + required this.userName, required this.consultantName, required this.diaryDate, required this.isRead, @@ -1173,7 +1450,10 @@ class ConsultantLeftDiaryCommentNotification implements BaseNotificationModel { factory ConsultantLeftDiaryCommentNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ConsultantLeftDiaryCommentNotification( type: type, + chosenPlanId: json['chosen_plan_id'] ?? 0, + diaryId: json['diary_id'] ?? 0, planName: json['plan_name'] ?? '', + userName: json['user_name'] ?? '', consultantName: json['consultant_name'] ?? '', diaryDate: parseDate(json['diary_date'] ?? DateTime.now().toIso8601String()), isRead: isRead ?? false, @@ -1188,14 +1468,22 @@ class ConsultantConfirmPlanSubscriptionToAuthorNotification implements BaseNotif @override final bool isRead; final String planName; - final String consultantName; + final int planId; + final String clientName; + final int userId; + final String userPhotoPath; + final String chatUrl; @override final DateTime createdAt; ConsultantConfirmPlanSubscriptionToAuthorNotification({ required this.type, required this.planName, - required this.consultantName, + required this.planId, + required this.clientName, + required this.userId, + required this.userPhotoPath, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -1204,7 +1492,11 @@ class ConsultantConfirmPlanSubscriptionToAuthorNotification implements BaseNotif return ConsultantConfirmPlanSubscriptionToAuthorNotification( type: type, planName: json['plan_name'] ?? '', - consultantName: json['consultant_name'] ?? '', + planId: json['plan_id'] ?? 0, + clientName: json['user_name'] ?? '', + userId: json['user_id'] ?? 0, + userPhotoPath: json['user_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1217,14 +1509,22 @@ class ClientPaidPlanSubscriptionToSubscriberNotification implements BaseNotifica @override final bool isRead; final String planName; - final double amount; + final int planId; + final String authorName; + final int authorId; + final String authorPhotoPath; + final String chatUrl; @override final DateTime createdAt; ClientPaidPlanSubscriptionToSubscriberNotification({ required this.type, required this.planName, - required this.amount, + required this.planId, + required this.authorName, + required this.authorId, + required this.authorPhotoPath, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -1233,7 +1533,11 @@ class ClientPaidPlanSubscriptionToSubscriberNotification implements BaseNotifica return ClientPaidPlanSubscriptionToSubscriberNotification( type: type, planName: json['plan_name'] ?? '', - amount: (json['amount'] as num?)?.toDouble() ?? 0.0, + planId: json['plan_id'] ?? 0, + authorName: json['author_name'] ?? '', + authorId: json['author_id'] ?? 0, + authorPhotoPath: json['author_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1246,14 +1550,24 @@ class ClientStartPlanSubscriptionToAuthorNotification implements BaseNotificatio @override final bool isRead; final String planName; + final int planId; final String clientName; + final int userId; + final String userPhotoPath; + final String chatId; + final String chatUrl; @override final DateTime createdAt; ClientStartPlanSubscriptionToAuthorNotification({ required this.type, required this.planName, + required this.planId, required this.clientName, + required this.userId, + required this.userPhotoPath, + required this.chatId, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -1262,7 +1576,12 @@ class ClientStartPlanSubscriptionToAuthorNotification implements BaseNotificatio return ClientStartPlanSubscriptionToAuthorNotification( type: type, planName: json['plan_name'] ?? '', - clientName: json['client_name'] ?? '', + planId: json['plan_id'] ?? 0, + clientName: json['user_name'] ?? '', + userId: json['user_id'] ?? 0, + userPhotoPath: json['user_photo_path'] ?? '', + chatId: json['chat_id']?.toString() ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1275,14 +1594,22 @@ class ClientCancelPlanSubscriptionToAuthorNotification implements BaseNotificati @override final bool isRead; final String planName; + final int planId; final String clientName; + final int userId; + final String userPhotoPath; + final String chatUrl; @override final DateTime createdAt; ClientCancelPlanSubscriptionToAuthorNotification({ required this.type, required this.planName, + required this.planId, required this.clientName, + required this.userId, + required this.userPhotoPath, + required this.chatUrl, required this.isRead, required this.createdAt, }); @@ -1291,7 +1618,11 @@ class ClientCancelPlanSubscriptionToAuthorNotification implements BaseNotificati return ClientCancelPlanSubscriptionToAuthorNotification( type: type, planName: json['plan_name'] ?? '', - clientName: json['client_name'] ?? '', + planId: json['plan_id'] ?? 0, + clientName: json['user_name'] ?? '', + userId: json['user_id'] ?? 0, + userPhotoPath: json['user_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1304,15 +1635,35 @@ class ClientCancelPlanSubscriptionToSubscriberNotification implements BaseNotifi @override final bool isRead; final String planName; + final int planId; + final String authorName; + final int authorId; + final String authorPhotoPath; + final String chatUrl; @override final DateTime createdAt; - ClientCancelPlanSubscriptionToSubscriberNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); + ClientCancelPlanSubscriptionToSubscriberNotification({ + required this.type, + required this.planName, + required this.planId, + required this.authorName, + required this.authorId, + required this.authorPhotoPath, + required this.chatUrl, + required this.isRead, + required this.createdAt, + }); factory ClientCancelPlanSubscriptionToSubscriberNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ClientCancelPlanSubscriptionToSubscriberNotification( type: type, planName: json['plan_name'] ?? '', + planId: json['plan_id'] ?? 0, + authorName: json['author_name'] ?? '', + authorId: json['author_id'] ?? 0, + authorPhotoPath: json['author_photo_path'] ?? '', + chatUrl: json['chat_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1324,17 +1675,21 @@ class UserDailyReportCompletedNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; + final int chosenPlanId; + final int diaryId; final String planName; final String clientName; - final DateTime reportDate; + final String consultantName; @override final DateTime createdAt; UserDailyReportCompletedNotification({ required this.type, + required this.chosenPlanId, + required this.diaryId, required this.planName, required this.clientName, - required this.reportDate, + required this.consultantName, required this.isRead, required this.createdAt, }); @@ -1342,9 +1697,11 @@ class UserDailyReportCompletedNotification implements BaseNotificationModel { factory UserDailyReportCompletedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return UserDailyReportCompletedNotification( type: type, + chosenPlanId: json['chosen_plan_id'] ?? 0, + diaryId: json['diary_id'] ?? 0, planName: json['plan_name'] ?? '', - clientName: json['client_name'] ?? '', - reportDate: parseDate(json['report_date'] ?? DateTime.now().toIso8601String()), + clientName: json['user_name'] ?? '', + consultantName: json['consultant_name'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1357,18 +1714,16 @@ class StayOnTrackToCustomerNotification implements BaseNotificationModel { @override final bool isRead; final String planName; - final String consultantName; - final int planId; - final int consultantId; + final int dayNum; + final int diaryDayId; @override final DateTime createdAt; StayOnTrackToCustomerNotification({ required this.type, required this.planName, - required this.consultantName, - required this.planId, - required this.consultantId, + required this.dayNum, + required this.diaryDayId, required this.isRead, required this.createdAt, }); @@ -1377,9 +1732,8 @@ class StayOnTrackToCustomerNotification implements BaseNotificationModel { return StayOnTrackToCustomerNotification( type: type, planName: json['plan_name'] ?? '', - consultantName: json['consultant_name'] ?? '', - planId: json['plan_id'] ?? 0, - consultantId: json['consultant_id'] ?? 0, + dayNum: json['day_num'] ?? 0, + diaryDayId: json['diary_day_id'] ?? 0, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1414,18 +1768,27 @@ class SubscriptionNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; - final String subscriptionType; - final String planName; + final String userName; + final String userPhotoPath; + final String userProfileUrl; @override final DateTime createdAt; - SubscriptionNotification({required this.type, required this.subscriptionType, required this.planName, required this.isRead, required this.createdAt}); + SubscriptionNotification({ + required this.type, + required this.userName, + required this.userPhotoPath, + required this.userProfileUrl, + required this.isRead, + required this.createdAt, + }); factory SubscriptionNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return SubscriptionNotification( type: type, - subscriptionType: json['subscription_type'] ?? '', - planName: json['plan_name'] ?? '', + userName: json['user_name'] ?? '', + userPhotoPath: json['user_photo_path'] ?? '', + userProfileUrl: json['user_profile_url'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1437,18 +1800,27 @@ class BadgeAwardedNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; - final String badgeName; - final String badgeDescription; + final String badgeTitle; + final String badgeType; + final String badgeImage; @override final DateTime createdAt; - BadgeAwardedNotification({required this.type, required this.badgeName, required this.badgeDescription, required this.isRead, required this.createdAt}); + BadgeAwardedNotification({ + required this.type, + required this.badgeTitle, + required this.badgeType, + required this.badgeImage, + required this.isRead, + required this.createdAt, + }); factory BadgeAwardedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return BadgeAwardedNotification( type: type, - badgeName: json['badge_name'] ?? '', - badgeDescription: json['badge_description'] ?? '', + badgeTitle: json['badge_title'] ?? '', + badgeType: json['badge_type'] ?? '', + badgeImage: json['badge_image'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1460,18 +1832,27 @@ class BadgeRevokedNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; - final String badgeName; - final String reason; + final String badgeTitle; + final String badgeType; + final String badgeImage; @override final DateTime createdAt; - BadgeRevokedNotification({required this.type, required this.badgeName, required this.reason, required this.isRead, required this.createdAt}); + BadgeRevokedNotification({ + required this.type, + required this.badgeTitle, + required this.badgeType, + required this.badgeImage, + required this.isRead, + required this.createdAt, + }); factory BadgeRevokedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return BadgeRevokedNotification( type: type, - badgeName: json['badge_name'] ?? '', - reason: json['reason'] ?? '', + badgeTitle: json['badge_title'] ?? '', + badgeType: json['badge_type'] ?? '', + badgeImage: json['badge_image'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1504,15 +1885,13 @@ class ClientCloseComplaintSuccessNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; - final String complaintType; - final String entityName; + final String planName; @override final DateTime createdAt; ClientCloseComplaintSuccessNotification({ required this.type, - required this.complaintType, - required this.entityName, + required this.planName, required this.isRead, required this.createdAt, }); @@ -1520,8 +1899,7 @@ class ClientCloseComplaintSuccessNotification implements BaseNotificationModel { factory ClientCloseComplaintSuccessNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ClientCloseComplaintSuccessNotification( type: type, - complaintType: json['complaint_type'] ?? '', - entityName: json['entity_name'] ?? '', + planName: json['plan_name'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1598,17 +1976,15 @@ class ClientRejectSuccessNotification implements BaseNotificationModel { @override final bool isRead; final String planName; - final String reason; @override final DateTime createdAt; - ClientRejectSuccessNotification({required this.type, required this.planName, required this.reason, required this.isRead, required this.createdAt}); + ClientRejectSuccessNotification({required this.type, required this.planName, required this.isRead, required this.createdAt}); factory ClientRejectSuccessNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { return ClientRejectSuccessNotification( type: type, planName: json['plan_name'] ?? '', - reason: json['reason'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1622,7 +1998,6 @@ class ConsultantRejectSuccessNotification implements BaseNotificationModel { final bool isRead; final String planName; final String clientName; - final String reason; @override final DateTime createdAt; @@ -1630,7 +2005,6 @@ class ConsultantRejectSuccessNotification implements BaseNotificationModel { required this.type, required this.planName, required this.clientName, - required this.reason, required this.isRead, required this.createdAt, }); @@ -1639,8 +2013,7 @@ class ConsultantRejectSuccessNotification implements BaseNotificationModel { return ConsultantRejectSuccessNotification( type: type, planName: json['plan_name'] ?? '', - clientName: json['client_name'] ?? '', - reason: json['reason'] ?? '', + clientName: json['user_name'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1694,18 +2067,24 @@ class ProductStatusUpdatedForAuthorNotification implements BaseNotificationModel @override final bool isRead; final String productName; - final String oldStatus; - final String newStatus; - final String status; // Added to capture single status if old/new aren't provided + final int productId; + final String author; + final int authorId; + final String status; + final String subject; + final String messageLine; @override final DateTime createdAt; ProductStatusUpdatedForAuthorNotification({ required this.type, required this.productName, - this.oldStatus = '', - this.newStatus = '', - this.status = '', + required this.productId, + required this.author, + required this.authorId, + required this.status, + required this.subject, + required this.messageLine, required this.isRead, required this.createdAt, }); @@ -1714,9 +2093,12 @@ class ProductStatusUpdatedForAuthorNotification implements BaseNotificationModel return ProductStatusUpdatedForAuthorNotification( type: type, productName: json['product_name'] ?? '', - oldStatus: json['old_status'] ?? '', - newStatus: json['new_status'] ?? '', + productId: json['product_id'] ?? 0, + author: json['author'] ?? '', + authorId: json['author_id'] ?? 0, status: json['status'] ?? '', + subject: json['subject'] ?? '', + messageLine: json['messageLine'] ?? json['message_line'] ?? '', isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); @@ -1728,15 +2110,26 @@ class DiaryReminderForUsersNotification implements BaseNotificationModel { final NotificationType type; @override final bool isRead; + final int? diaryId; + final String? message; @override final DateTime createdAt; - DiaryReminderForUsersNotification({required this.type, required this.isRead, required this.createdAt}); + DiaryReminderForUsersNotification({ + required this.type, + required this.isRead, + this.diaryId, + this.message, + required this.createdAt, + }); factory DiaryReminderForUsersNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { + final data = json['data'] is Map ? json['data'] as Map : json; return DiaryReminderForUsersNotification( type: type, isRead: isRead ?? false, + diaryId: data['diary_id'], + message: data['message'], createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), ); } @@ -1749,15 +2142,29 @@ class PersonalDietIsDefinedToCustomerNotification implements BaseNotificationMod final bool isRead; @override final DateTime createdAt; - final String dietName; + final int chosenPlanId; final int dietId; + final String planName; + final String userName; + final String consultantName; + final String dietName; + final String role; + final String peerSlug; + final String peerImagePath; PersonalDietIsDefinedToCustomerNotification({ required this.type, required this.isRead, required this.createdAt, - required this.dietName, + required this.chosenPlanId, required this.dietId, + required this.planName, + required this.userName, + required this.consultantName, + required this.dietName, + required this.role, + required this.peerSlug, + required this.peerImagePath, }); factory PersonalDietIsDefinedToCustomerNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { @@ -1765,8 +2172,15 @@ class PersonalDietIsDefinedToCustomerNotification implements BaseNotificationMod type: type, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), + chosenPlanId: json['chosen_plan_id'] ?? 0, + dietId: json['diet_id'] ?? 0, + planName: json['plan_name'] ?? '', + userName: json['user_name'] ?? '', + consultantName: json['consultant_name'] ?? '', dietName: json['diet_name'] ?? '', - dietId: json['diet_id'], + role: json['role']?.toString() ?? '', + peerSlug: json['peer_slug'] ?? '', + peerImagePath: json['peer_image_path'] ?? '', ); } } @@ -1806,14 +2220,18 @@ class ComplaintRegisteredNotification implements BaseNotificationModel { final bool isRead; @override final DateTime createdAt; - final String complaintType; + final String message; + final int entityId; + final String entityType; final String entityName; ComplaintRegisteredNotification({ required this.type, required this.isRead, required this.createdAt, - required this.complaintType, + required this.message, + required this.entityId, + required this.entityType, required this.entityName, }); @@ -1822,7 +2240,9 @@ class ComplaintRegisteredNotification implements BaseNotificationModel { type: type, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), - complaintType: json['complaint_type'] ?? '', + message: json['message'] ?? '', + entityId: json['entity_id'] ?? 0, + entityType: json['entity_type'] ?? '', entityName: json['entity_name'] ?? '', ); } @@ -1946,12 +2366,14 @@ class ChosenDietExpiredNotification implements BaseNotificationModel { @override final DateTime createdAt; final String dietName; + final int dietId; ChosenDietExpiredNotification({ required this.type, required this.isRead, required this.createdAt, required this.dietName, + required this.dietId, }); factory ChosenDietExpiredNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { @@ -1960,6 +2382,7 @@ class ChosenDietExpiredNotification implements BaseNotificationModel { isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), dietName: json['diet_name'] ?? '', + dietId: json['diet_id'] ?? 0, ); } } @@ -2052,15 +2475,17 @@ class UserSubscribedNotification implements BaseNotificationModel { final bool isRead; @override final DateTime createdAt; - final String subscriberName; - final int subscriberId; + final String userName; + final String userPhotoPath; + final String userProfileUrl; UserSubscribedNotification({ required this.type, required this.isRead, required this.createdAt, - required this.subscriberName, - required this.subscriberId, + required this.userName, + required this.userPhotoPath, + required this.userProfileUrl, }); factory UserSubscribedNotification.fromJson(Map json, NotificationType type, {bool? isRead, String? createdAt}) { @@ -2068,8 +2493,9 @@ class UserSubscribedNotification implements BaseNotificationModel { type: type, isRead: isRead ?? false, createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()), - subscriberName: json['subscriber_name'] ?? '', - subscriberId: json['subscriber_id'] ?? 0, + userName: json['user_name'] ?? '', + userPhotoPath: json['user_photo_path'] ?? '', + userProfileUrl: json['user_profile_url'] ?? '', ); } } diff --git a/lib/features/notifications/presentation/widgets/notification_widget.dart b/lib/features/notifications/presentation/widgets/notification_widget.dart index 1c2d1c58..f33b9cb1 100644 --- a/lib/features/notifications/presentation/widgets/notification_widget.dart +++ b/lib/features/notifications/presentation/widgets/notification_widget.dart @@ -1,202 +1,21 @@ import 'package:eatvio_app/core/routing/route_constants.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/features/bottom_navigation_bar/presentation/widgets/bottom_nav_scope.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.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/notifications/models/notification_model.dart'; +import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - class NotificationWidget extends StatelessWidget { const NotificationWidget({super.key, required this.notification}); final BaseNotificationModel notification; - void _onTap(BuildContext context) { - // Mark as read first - context.read().markNotificationsAsRead(); - - switch (notification) { - case CommentReplied n: - _navigateToEntity(context, n.commentedEntityType, n.commentedEntityId, n.commentId); - case EntityCommented n: - _navigateToEntity(context, n.entityType, n.entityId, n.commentId); - case PersonalDietIsDefinedToCustomerNotification n: - context.push(RouteConstants.aboutDietScreen, extra: {'id': n.dietId}); - case PlanSubscriptionStartedToAuthorNotification n: - if (n.chatId.isNotEmpty && n.chatId != '0') { - context.push(RouteConstants.chatScreen, extra: int.tryParse(n.chatId) ?? 0); - } else { - // Fallback if no chat ID - context.push(RouteConstants.profileScreen); - } - case ConsultantAssignedNotification n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case StayOnTrackToAuthorNotification n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case StayOnTrackToCustomerNotification n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case ChosenPlanStartedToClientNotification n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case ChosenPlanStartedToConsultantNotification n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case ProductStatusUpdatedForAuthorNotification(): - context.push(RouteConstants.profileScreen); - // New Cases with Safety Checks - case NewChatMessageNotification n: - if (n.chatId != 0) context.push(RouteConstants.chatScreen, extra: n.chatId); - case FinanceQuestionCreatedNotification(): - context.push(RouteConstants.userFinanceScreen); - case ChosenDietExpiredNotification(): - context.push(RouteConstants.myDietsScreen); - case ContentUnpublishedNotification(): - context.push(RouteConstants.profileScreen); - case CompleteRegistrationNotification(): - context.push(RouteConstants.profileScreen); // Or edit profile - case ConsultantEducationUpdatedNotification(): - context.push(RouteConstants.profileScreen); - case ComplaintRegisteredNotification(): - context.push(RouteConstants.profileScreen); - case ConsultantRequestStartedNotification(): - // Navigate to where requests are shown - context.push(RouteConstants.profileScreen); - case ClientCloseComplaintSuccessNotification(): - context.push(RouteConstants.profileScreen); - case ClientRefundSuccessNotification(): - context.push(RouteConstants.userFinanceScreen); - case ConsultantRefundSuccessNotification(): - context.push(RouteConstants.userFinanceScreen); - case ClientRejectSuccessNotification(): - context.push(RouteConstants.aboutPlanScreen); // Best guess - case ConsultantRejectSuccessNotification(): - context.push(RouteConstants.aboutPlanScreen); - case ScoreSubstractedNotification(): - context.push(RouteConstants.profileScreen); - case ScoreAdded(): - context.push(RouteConstants.profileScreen); - case DiaryReminderForUsersNotification(): - context.push(RouteConstants.myDietsScreen); // Likely where diary is - case MotivateUserNotification(): - context.push(RouteConstants.myDietsScreen); - case CommentComplaintNotification(): - context.push(RouteConstants.profileScreen); - case CommentDeletedNotification(): - context.push(RouteConstants.profileScreen); - case UserSubscribedNotification(): - context.push(RouteConstants.profileScreen); - case BadgeAwardedNotification(): - context.push(RouteConstants.profileScreen); - case BadgeRevokedNotification(): - context.push(RouteConstants.profileScreen); - case SubscriptionNotification(): - context.push(RouteConstants.userFinanceScreen); - case PhoneChangedNotification(): - context.push(RouteConstants.profileScreen); - case UserDailyReportCompletedNotification(): - // Attempt to go to plan if we had ID, but we don't in this model. Go to plans list. - context.push(RouteConstants.myPlansScreen); - case ClientCancelPlanSubscriptionToSubscriberNotification(): - context.push(RouteConstants.myPlansScreen); - case ClientCancelPlanSubscriptionToAuthorNotification(): - context.push(RouteConstants.myPlansScreen); - case ClientStartPlanSubscriptionToAuthorNotification(): - context.push(RouteConstants.myPlansScreen); - case ClientPaidPlanSubscriptionToSubscriberNotification(): - context.push(RouteConstants.userFinanceScreen); - case ConsultantConfirmPlanSubscriptionToAuthorNotification(): - context.push(RouteConstants.myPlansScreen); - case ConsultantLeftDiaryCommentNotification(): - // Needs planID to go to specific plan, model has planName. - // Optimally we'd go to the diary. For now plans screen. - context.push(RouteConstants.myPlansScreen); - case ClientPaidPlanSubscriptionToAuthorNotification(): - context.push(RouteConstants.userFinanceScreen); - case ConsultantConfirmPlanSubscriptionToSubscriberNotification(): - context.push(RouteConstants.myPlansScreen); - case ClientStartPlanSubscriptionToSubscriberNotification(): - context.push(RouteConstants.myPlansScreen); - case ChosenPlanClientLastDayStartedNotification(): - context.push(RouteConstants.myPlansScreen); - case ChosenPlanClientLastDayFinishedNotification(): - context.push(RouteConstants.myPlansScreen); - case PersonalDietCreationNeededNotification(): - context.push(RouteConstants.myPlansScreen); - case DietCanNotBeStartedNotification(): - context.push(RouteConstants.myDietsScreen); - case DietWillStartedInNotification(): - context.push(RouteConstants.myDietsScreen); - case DietExpiredNotification(): - context.push(RouteConstants.myDietsScreen); - case DietStartedNotification(): - context.push(RouteConstants.myDietsScreen); - case DietCancelledNotification(): - context.push(RouteConstants.myDietsScreen); - case MealReminderNotification(): - context.push(RouteConstants.myDietsScreen); - case WorkoutReminderNotification(): - context.push(RouteConstants.myDietsScreen); - case GoalAchievedNotification(): - context.push(RouteConstants.profileScreen); - case PaymentSuccessfulNotification(): - context.push(RouteConstants.userFinanceScreen); - case PaymentFailedNotification(): - context.push(RouteConstants.userFinanceScreen); - case ConsultantUnassignedNotification n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case PlanSubscriptionRenewedNotification(): - context.push(RouteConstants.userFinanceScreen); - case PlanSubscriptionExpiredNotification(): - context.push(RouteConstants.userFinanceScreen); - case ComplaintToModerationForUser(): - context.push(RouteConstants.profileScreen); - case ChosenPlanConsultantLastDayStarted n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case ChosenPlanLastDayFinishedForUser(): - context.push(RouteConstants.myPlansScreen); - case ChosenPlanFinishedToConsultantNotification n: - context.push(RouteConstants.aboutPlanScreen, extra: {'id': n.planId}); - case PlanSubscriptionPaidToAuthorNotification(): - context.push(RouteConstants.userFinanceScreen); - case ChosenPlanTomorrowReminderToConsultantNotification(): - context.push(RouteConstants.myPlansScreen); - case ChosenPlanTomorrowReminderToClientNotification(): - context.push(RouteConstants.myPlansScreen); - case PlanSubscriptionCanceledToCustomerNotification(): - context.push(RouteConstants.myPlansScreen); - case ChosenPlanFinishedToClientNotification(): - context.push(RouteConstants.myPlansScreen); - case PersonalizedDietIsDefinedNotification(): - context.push(RouteConstants.myDietsScreen); - case ConsultantLeftCommentNotification(): - context.push(RouteConstants.myPlansScreen); - case DiaryReminderNotification(): - context.push(RouteConstants.myDietsScreen); - case PlanSubscriptionCanceledToAuthorNotification(): - context.push(RouteConstants.myPlansScreen); - default: - // Do nothing if no logical destination - break; - } - } - - void _navigateToEntity(BuildContext context, String type, int id, int? commentId) { - if (type.contains('BlogPost')) { - context.push(RouteConstants.blogPostScreen, extra: {'id': id, 'highlightedCommentId': commentId}); - } else if (type.contains('Recipe')) { - context.push(RouteConstants.aboutRecipesScreen, extra: {'id': id, 'highlightedCommentId': commentId}); - } else if (type.contains('Plan')) { - context.push(RouteConstants.aboutPlanScreen, extra: {'id': id, 'highlightedCommentId': commentId}); - } else if (type.contains('Diet')) { - context.push(RouteConstants.aboutDietScreen, extra: {'id': id, 'highlightedCommentId': commentId}); - } - } - -// }; -// } @override Widget build(BuildContext context) { return switch (notification) { @@ -303,9 +122,7 @@ class NotificationWidget extends StatelessWidget { notification: notification as PersonalDietIsDefinedToCustomerNotification, ), // New Widgets - ConsultantEducationUpdatedNotification() => _ConsultantEducationUpdatedWidget( - notification: notification as ConsultantEducationUpdatedNotification, - ), + ConsultantEducationUpdatedNotification() => _ConsultantEducationUpdatedWidget(notification: notification as ConsultantEducationUpdatedNotification), ComplaintRegisteredNotification() => _ComplaintRegisteredWidget(notification: notification as ComplaintRegisteredNotification), ContentUnpublishedNotification() => _ContentUnpublishedWidget(notification: notification as ContentUnpublishedNotification), FinanceQuestionCreatedNotification() => _FinanceQuestionCreatedWidget(notification: notification as FinanceQuestionCreatedNotification), @@ -321,6 +138,29 @@ class NotificationWidget extends StatelessWidget { } } +void _navigateToBottomNavRoute(BuildContext context, String path, [Object? extra]) { + final controller = BottomNavScope.maybeOf(context); + if (controller != null) { + controller.switchToRoute(path, extra); + } else { + context.push(path, extra: extra); + } +} + +void _navigateToEntity(BuildContext context, String type, int id, int? commentId) { + if (type.contains('BlogPost')) { + context.push(RouteConstants.blogPostScreen, extra: {'id': id, 'highlightedCommentId': commentId}); + } else if (type.contains('Recipe')) { + // TODO: aboutRecipesScreen требует recipe slug, в уведомлении только id — навигация отключена + } else if (type.contains('Plan')) { + // TODO: aboutPlanScreen требует slug плана, в уведомлении только id — навигация отключена + } else if (type.contains('Diet')) { + if (id > 0) { + context.push(RouteConstants.aboutDietScreen, extra: {'id': id, 'highlightedCommentId': commentId}); + } + } +} + class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget { const _PlanSubscriptionStartedToAuthorWidget({required this.notification}); final PlanSubscriptionStartedToAuthorNotification notification; @@ -332,7 +172,9 @@ class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: '${s.userSubscribedToPlan(notification.customerName, notification.planName)}! 🎉', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.customerName.isNotEmpty ? notification.customerName : null); + }, ); } } @@ -342,13 +184,14 @@ class _PlanSubscriptionCanceledToAuthorWidget extends StatelessWidget { final PlanSubscriptionCanceledToAuthorNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Отмена подписки. Клиент ${notification.customerName} отказался от плана "${notification.planName}"', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.customerName.isNotEmpty ? notification.customerName : null); + }, ); } } @@ -358,13 +201,14 @@ class _ChosenPlanStartedToClientWidget extends StatelessWidget { final ChosenPlanStartedToClientNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Ваш план "${notification.planName}" начался! Удачи! 🚀', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.diaryScreen); + }, ); } } @@ -374,13 +218,11 @@ class _ChosenPlanTomorrowReminderToClientWidget extends StatelessWidget { final ChosenPlanTomorrowReminderToClientNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Напоминание: Завтра старт плана "${notification.planName}"', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -390,12 +232,15 @@ class _DiaryReminderWidget extends StatelessWidget { final DiaryReminderNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Не забудьте заполнить дневник за вчера! 📝', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + final yesterday = notification.createdAt.subtract(const Duration(days: 1)); + final dateOnly = DateTime(yesterday.year, yesterday.month, yesterday.day); + _navigateToBottomNavRoute(context, RouteConstants.diaryScreen, dateOnly); + }, ); } } @@ -405,13 +250,14 @@ class _ConsultantLeftCommentWidget extends StatelessWidget { final ConsultantLeftCommentNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Новый комментарий от консультанта (за ${notification.date.format('dd.MM.yyyy')})', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.diaryScreen, notification.date); + }, ); } } @@ -425,7 +271,9 @@ class _PersonalizedDietIsDefinedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Персональная диета определена', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.consultationsScreen); + }, ); } } @@ -435,13 +283,11 @@ class _ChosenPlanFinishedToClientWidget extends StatelessWidget { final ChosenPlanFinishedToClientNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Поздравляем! План «${notification.planName}» успешно завершен! 🏆', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -451,13 +297,11 @@ class _PlanSubscriptionCanceledToCustomerWidget extends StatelessWidget { final PlanSubscriptionCanceledToCustomerNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Подписка на план "${notification.planName}" отменена.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -467,13 +311,11 @@ class _ChosenPlanStartedToConsultantWidget extends StatelessWidget { final ChosenPlanStartedToConsultantNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Начало работы: План "${notification.planName}" с клиентом ${notification.customerName}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -483,13 +325,11 @@ class _ChosenPlanTomorrowReminderToConsultantWidget extends StatelessWidget { final ChosenPlanTomorrowReminderToConsultantNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Напоминание: Завтра старт работы с ${notification.customerName} по плану "${notification.planName}"', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -499,13 +339,14 @@ class _PlanSubscriptionPaidToAuthorWidget extends StatelessWidget { final PlanSubscriptionPaidToAuthorNotification notification; @override Widget build(BuildContext context) { - final s = S.of(context); return _BaseNotificationWidget( isRead: notification.isRead, notificationsText: 'Оплата получена! Клиент ${notification.customerName} оплатил план "${notification.planName}" 💰', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.consultantFinancesScreen); + }, ); } } @@ -517,46 +358,46 @@ class _ChosenPlanFinishedToConsultantWidget extends StatelessWidget { Widget build(BuildContext context) { final s = S.of(context); return GestureDetector( - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () {}, behavior: HitTestBehavior.opaque, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('План «${notification.planName}» завершен! Поздравляем! 🎉', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)), - const SizedBox(height: 12), - Text(s.userConfirmedPlanCompletionWithoutSum, style: TextStyles.textSMedium.copyWith(color: appTheme.black)), - const SizedBox(height: 12), - CustomOutlinedButton( - text: s.collectCharge, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), - buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), - onPressed: () { - GoRouter.of(context).push(CardSettingsScreen.path, extra: notification.planId); - }, - ), - ], + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('План «${notification.planName}» завершен! Поздравляем! 🎉', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)), + const SizedBox(height: 12), + Text(s.userConfirmedPlanCompletionWithoutSum, style: TextStyles.textSMedium.copyWith(color: appTheme.black)), + const SizedBox(height: 12), + CustomOutlinedButton( + text: s.collectCharge, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), + onPressed: () { + GoRouter.of(context).push(CardSettingsScreen.path, extra: notification.planId); + }, + ), + ], + ), ), - ), - if (!notification.isRead) - Container( - decoration: BoxDecoration(color: appTheme.blueA400, shape: BoxShape.circle), - width: 8, - height: 8, - ), - ], - ), - const SizedBox(height: 4), - Text( - '${notification.createdAt.day} ${_getMonthName(notification.createdAt.month)} ${notification.createdAt.hour.toString().padLeft(2, '0')}:${notification.createdAt.minute.toString().padLeft(2, '0')}', - style: TextStyles.textSMedium.copyWith(color: appTheme.gray600), - ), - ], + if (!notification.isRead) + Container( + decoration: BoxDecoration(color: appTheme.blueA400, shape: BoxShape.circle), + width: 8, + height: 8, + ), + ], + ), + const SizedBox(height: 4), + Text( + '${notification.createdAt.day} ${_getMonthName(notification.createdAt.month)} ${notification.createdAt.hour.toString().padLeft(2, '0')}:${notification.createdAt.minute.toString().padLeft(2, '0')}', + style: TextStyles.textSMedium.copyWith(color: appTheme.gray600), + ), + ], ), ); } @@ -674,7 +515,11 @@ class _ChosenPlanLastDayFinishedForUserWidget extends StatelessWidget { final ChosenPlanLastDayFinishedForUser notification; @override Widget build(BuildContext context) { - return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: 'Финишная прямая! Последний день плана завершен.', createdAt: notification.createdAt); + return _BaseNotificationWidget( + isRead: notification.isRead, + notificationsText: 'Финишная прямая! Последний день плана завершен.', + createdAt: notification.createdAt, + ); } } @@ -687,7 +532,9 @@ class _ChosenPlanConsultantLastDayStartedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Внимание: Начался последний день плана "${notification.planName}"', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.consultationsScreen); + }, ); } } @@ -701,7 +548,11 @@ class _ComplaintToModerationForUserWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Ваша жалоба принята и находится на модерации.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: (['Plan', 'Recipe'].contains(notification.entityType) || notification.entityId <= 0 || notification.entityType.isEmpty) + ? null + : () { + _navigateToEntity(context, notification.entityType, notification.entityId, null); + }, ); } } @@ -715,7 +566,11 @@ class _CommentRepliedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Вам ответили в комментариях к: ${notification.entityName}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: (['Plan', 'Recipe'].contains(notification.commentedEntityType) || notification.commentedEntityId <= 0 || notification.commentId == null) + ? null + : () { + _navigateToEntity(context, notification.commentedEntityType, notification.commentedEntityId, notification.commentId); + }, ); } } @@ -729,7 +584,9 @@ class _ScoreAddedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Поздравляем! Вам начислены баллы: ${notification.scoreValue} ⭐', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.consultantRatingScreen); + }, ); } } @@ -743,7 +600,9 @@ class _EntityCommentedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Новый комментарий к вашей публикации "${notification.entityTranslatedType}": ${notification.entityName}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToEntity(context, notification.entityType, notification.entityId, notification.commentId); + }, ); } } @@ -753,12 +612,7 @@ class _UnknownNotificationWidget extends StatelessWidget { final UnknownNotification notification; @override Widget build(BuildContext context) { - return _BaseNotificationWidget( - isRead: notification.isRead, - notificationsText: 'У вас новое уведомление! Пожалуйста, обновите приложение, чтобы увидеть детали.', - createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), - ); + return _BaseNotificationWidget(isRead: false, notificationsText: 'Неизвестное уведомление', createdAt: DateTime.now(), onTap: null); } } @@ -772,7 +626,6 @@ class _PlanSubscriptionExpiredWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Срок действия подписки на план "${notification.planName}" истек. Продлите, чтобы продолжить! ⏳', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -786,7 +639,6 @@ class _PlanSubscriptionRenewedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Успешное продление! Подписка на план "${notification.planName}" активна до ${notification.renewalDate} ✅', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -800,7 +652,9 @@ class _ConsultantAssignedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Вам назначен консультант ${notification.consultantName} по плану "${notification.planName}". Приветствуйте! 👋', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.consultantName.isNotEmpty ? notification.consultantName : null); + }, ); } } @@ -814,7 +668,6 @@ class _ConsultantUnassignedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Изменение в плане "${notification.planName}": консультант ${notification.consultantName} отстранен.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -828,7 +681,9 @@ class _PaymentFailedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Не удалось провести оплату за план "${notification.planName}". Причина: ${notification.errorMessage} ❌', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.userFinanceScreen); + }, ); } } @@ -842,7 +697,9 @@ class _PaymentSuccessfulWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Оплата прошла успешно! План "${notification.planName}" оплачен на сумму ${notification.amount} ${notification.currency} 💳', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.userFinanceScreen); + }, ); } } @@ -856,7 +713,6 @@ class _GoalAchievedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Цель достигнута! "${notification.goalName}": ${notification.achievedValue}/${notification.targetValue} 🎯', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -870,7 +726,6 @@ class _WorkoutReminderWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Время тренировки! "${notification.workoutName}" ждет вас (${notification.duration} мин) 💪', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -884,7 +739,9 @@ class _MealReminderWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Пора подкрепиться! "${notification.mealName}" (${notification.calories} ккал) 🍎', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.diaryScreen); + }, ); } } @@ -899,7 +756,11 @@ class _DietCancelledWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Диета "${notification.dietName}" была отменена.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + if (notification.dietId > 0) { + context.push(RouteConstants.aboutDietScreen, extra: {'id': notification.dietId}); + } + }, ); } } @@ -913,7 +774,11 @@ class _DietStartedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Старт диеты "${notification.dietName}"! Успехов! 🥗', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + if (notification.dietId > 0) { + context.push(RouteConstants.aboutDietScreen, extra: {'id': notification.dietId}); + } + }, ); } } @@ -927,7 +792,11 @@ class _DietExpiredWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Срок действия диеты "${notification.dietName}" подошел к концу.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + if (notification.dietId > 0) { + context.push(RouteConstants.aboutDietScreen, extra: {'id': notification.dietId}); + } + }, ); } } @@ -941,7 +810,11 @@ class _DietWillStartedInWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Скоро старт! Диета "${notification.dietName}" начнется ${notification.startDate} ⏰', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + if (notification.dietId > 0) { + context.push(RouteConstants.aboutDietScreen, extra: {'id': notification.dietId}); + } + }, ); } } @@ -955,7 +828,11 @@ class _DietCanNotBeStartedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Внимание: Не удалось начать диету "${notification.dietName}". Причина: ${notification.reason}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + if (notification.dietId > 0) { + context.push(RouteConstants.aboutDietScreen, extra: {'id': notification.dietId}); + } + }, ); } } @@ -967,9 +844,11 @@ class _PersonalDietCreationNeededWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Задача: Создать персональную диету для клиента ${notification.clientName} (План "${notification.planName}")', + notificationsText: 'Задача: Создать персональную диету для клиента ${notification.userName}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.myDietsScreen); + }, ); } } @@ -983,7 +862,6 @@ class _ChosenPlanClientLastDayFinishedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Поздравляем с завершением плана "${notification.planName}"! 🎉', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -997,7 +875,9 @@ class _ChosenPlanClientLastDayStartedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Сегодня последний день плана "${notification.planName}"! Финиш близко! 🏁', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.diaryScreen); + }, ); } } @@ -1009,9 +889,15 @@ class _StayOnTrackToAuthorWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Клиент ${notification.clientName} продолжает уверенно идти по плану "${notification.planName}" 🔥', + notificationsText: 'Клиент ${notification.userName} завершил день ${notification.dayNum} по плану "${notification.planName}" 💪', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + if (notification.diaryDayId > 0) { + context.push(RouteConstants.userDiaryScreen, extra: {'diaryId': notification.diaryDayId}); + } else { + _navigateToBottomNavRoute(context, RouteConstants.consultationsScreen); + } + }, ); } } @@ -1025,7 +911,9 @@ class _ClientStartPlanSubscriptionToSubscriberWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'У вас новая подписка на план "${notification.planName}"! 🌟', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen); + }, ); } } @@ -1039,7 +927,9 @@ class _ConsultantConfirmPlanSubscriptionToSubscriberWidget extends StatelessWidg isRead: notification.isRead, notificationsText: 'Ваша подписка на план "${notification.planName}" подтверждена консультантом ${notification.consultantName} ✅', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.consultantName.isNotEmpty ? notification.consultantName : null); + }, ); } } @@ -1051,9 +941,11 @@ class _ClientPaidPlanSubscriptionToAuthorWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Поступление средств: Клиент ${notification.clientName} оплатил подписку на план "${notification.planName}" (${notification.amount}) 💸', + notificationsText: 'Поступление средств: Клиент ${notification.clientName} оплатил подписку на план "${notification.planName}" 💸', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.consultantFinancesScreen); + }, ); } } @@ -1067,7 +959,9 @@ class _ConsultantLeftDiaryCommentWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Ваш консультант ${notification.consultantName} прокомментировал дневник по плану "${notification.planName}" 💬', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.diaryScreen, notification.diaryDate); + }, ); } } @@ -1079,9 +973,11 @@ class _ConsultantConfirmPlanSubscriptionToAuthorWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Консультант ${notification.consultantName} подтвердил подписку на план "${notification.planName}" 🤝', + notificationsText: 'Клиент ${notification.clientName} подтвердил подписку на план "${notification.planName}" 🤝', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.clientName.isNotEmpty ? notification.clientName : null); + }, ); } } @@ -1093,9 +989,11 @@ class _ClientPaidPlanSubscriptionToSubscriberWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Оплата подписки на план "${notification.planName}" прошла успешно (${notification.amount}) ✅', + notificationsText: 'Оплата подписки на план "${notification.planName}" прошла успешно ✅', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.consultantFinancesScreen); + }, ); } } @@ -1109,7 +1007,9 @@ class _ClientStartPlanSubscriptionToAuthorWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Новый подписчик! Клиент ${notification.clientName} начал подписку на план "${notification.planName}" 🎈', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.clientName.isNotEmpty ? notification.clientName : null); + }, ); } } @@ -1123,7 +1023,9 @@ class _ClientCancelPlanSubscriptionToAuthorWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Отмена подписки: Клиент ${notification.clientName} отказался от плана "${notification.planName}"', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.clientName.isNotEmpty ? notification.clientName : null); + }, ); } } @@ -1137,7 +1039,9 @@ class _ClientCancelPlanSubscriptionToSubscriberWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Ваша подписка на план "${notification.planName}" была отменена.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen); + }, ); } } @@ -1151,7 +1055,9 @@ class _UserDailyReportCompletedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Отчет получен: Клиент ${notification.clientName} заполнил дневник по плану "${notification.planName}" 📝', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(UserDiaryScreen.path, extra: {'diaryId': notification.diaryId}); + }, ); } } @@ -1163,9 +1069,8 @@ class _StayOnTrackToCustomerWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Консультант ${notification.consultantName} продолжает работу с вами по плану "${notification.planName}" 💪', + notificationsText: 'Вы продолжаете идти по плану "${notification.planName}" (день ${notification.dayNum}) 💪', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1179,7 +1084,6 @@ class _PhoneChangedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Ваш номер телефона был успешно изменен на ${notification.newPhone} 📱', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1191,9 +1095,11 @@ class _SubscriptionWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Оформлена подписка ${notification.subscriptionType} на план "${notification.planName}" 🎉', + notificationsText: 'У вас новый подписчик: ${notification.userName} 🎉', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.subsScreen, extra: SubType.subscribers); + }, ); } } @@ -1205,9 +1111,11 @@ class _BadgeAwardedWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Новая награда! Вы получили значок "${notification.badgeName}": ${notification.badgeDescription} 🏅', + notificationsText: 'Новая награда! Вы получили значок "${notification.badgeTitle}" (${notification.badgeType}) 🏅', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.myProfileScreen); + }, ); } } @@ -1219,9 +1127,11 @@ class _BadgeRevokedWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Значок "${notification.badgeName}" был отозван. Причина: ${notification.reason}', + notificationsText: 'Значок "${notification.badgeTitle}" (${notification.badgeType}) был отозван.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.myProfileScreen); + }, ); } } @@ -1235,7 +1145,6 @@ class _ConsultantRequestStartedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Запрос отправлен консультанту ${notification.consultantName} 📩', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1247,9 +1156,8 @@ class _ClientCloseComplaintSuccessWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Ваша жалоба на ${notification.complaintType} "${notification.entityName}" была рассмотрена и закрыта.', + notificationsText: 'Ваша жалоба по плану "${notification.planName}" была рассмотрена и закрыта.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1261,9 +1169,11 @@ class _ClientRefundSuccessWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Вам одобрен возврат средств по плану "${notification.planName}" (${notification.amount}). Причина: ${notification.reason}', + notificationsText: 'Вам одобрен возврат средств по плану "${notification.planName}" (${notification.amount}).', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.userFinanceScreen); + }, ); } } @@ -1277,7 +1187,9 @@ class _ConsultantRefundSuccessWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Выполнен возврат средств клиенту ${notification.clientName} по плану "${notification.planName}" (${notification.amount})', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + context.push(RouteConstants.consultantFinancesScreen); + }, ); } } @@ -1289,9 +1201,11 @@ class _ClientRejectSuccessWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Отказ по плану "${notification.planName}" принят: ${notification.reason}', + notificationsText: 'Отказ по плану "${notification.planName}" принят.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen); + }, ); } } @@ -1303,9 +1217,11 @@ class _ConsultantRejectSuccessWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'Клиент ${notification.clientName} отказался от плана "${notification.planName}": ${notification.reason}', + notificationsText: 'Клиент ${notification.clientName} отказался от плана "${notification.planName}".', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.clientName.isNotEmpty ? notification.clientName : null); + }, ); } } @@ -1319,7 +1235,9 @@ class _ScoreSubstractedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Списание баллов: -${notification.scoreValue} за ${notification.scorableName}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.consultantRatingScreen); + }, ); } } @@ -1329,23 +1247,13 @@ class _ProductStatusUpdatedForAuthorWidget extends StatelessWidget { final ProductStatusUpdatedForAuthorNotification notification; @override Widget build(BuildContext context) { - String text; - if (notification.oldStatus.isNotEmpty && notification.newStatus.isNotEmpty) { - text = 'Статус продукта "${notification.productName}" изменен с ${notification.oldStatus} на ${notification.newStatus}'; - } else if (notification.newStatus.isNotEmpty) { - text = 'Статус продукта "${notification.productName}" изменен на ${notification.newStatus}'; - } else if (notification.status.isNotEmpty) { - text = 'Статус продукта "${notification.productName}": ${notification.status}'; - } else { - text = 'Статус продукта "${notification.productName}" обновлен'; - } + final text = notification.subject.isNotEmpty + ? notification.subject + : notification.status.isNotEmpty + ? 'Статус продукта "${notification.productName}": ${notification.status}' + : 'Статус продукта "${notification.productName}" обновлен'; - return _BaseNotificationWidget( - isRead: notification.isRead, - notificationsText: text, - createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), - ); + return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: text, createdAt: notification.createdAt); } } @@ -1358,7 +1266,10 @@ class _DiaryReminderForUsersWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Не забудьте заполнить дневник сегодня! 📓', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + final date = notification.createdAt; + _navigateToBottomNavRoute(context, RouteConstants.diaryScreen, date); + }, ); } } @@ -1372,7 +1283,11 @@ class _PersonalDietIsDefinedToCustomerWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Для вас составлена персональная диета "${notification.dietName}"! 🥗', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + if (notification.dietId > 0) { + context.push(RouteConstants.aboutDietScreen, extra: {'id': notification.dietId}); + } + }, ); } } @@ -1386,7 +1301,9 @@ class _ConsultantEducationUpdatedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Консультант ${notification.consultantName} обновил данные об образовании.', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.myProfileScreen); + }, ); } } @@ -1400,7 +1317,6 @@ class _ComplaintRegisteredWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Мы получили вашу жалобу на ${notification.entityName}. Спасибо за сигнал!', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1414,7 +1330,6 @@ class _ContentUnpublishedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Ваша публикация ${notification.entityName} была скрыта. Причина: ${notification.reason}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1428,7 +1343,9 @@ class _FinanceQuestionCreatedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Служба поддержки: Новый вопрос по финансам: ${notification.questionText}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen); + }, ); } } @@ -1442,7 +1359,9 @@ class _NewChatMessageWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Входящее сообщение от ${notification.senderName}: "${notification.messagePreview}"', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), + onTap: () { + _navigateToBottomNavRoute(context, RouteConstants.chatSelectScreen, notification.senderName.isNotEmpty ? notification.senderName : null); + }, ); } } @@ -1456,7 +1375,6 @@ class _CompleteRegistrationWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Завершите регистрацию, чтобы получить доступ ко всем функциям! 🚀', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1470,7 +1388,6 @@ class _ChosenDietExpiredWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Диета "${notification.dietName}" завершена. Время подвести итоги!', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1480,12 +1397,7 @@ class _MotivateUserWidget extends StatelessWidget { final MotivateUserNotification notification; @override Widget build(BuildContext context) { - return _BaseNotificationWidget( - isRead: notification.isRead, - notificationsText: notification.message, - createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), - ); + return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: notification.message, createdAt: notification.createdAt); } } @@ -1498,7 +1410,6 @@ class _CommentComplaintWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Поступила жалоба на ваш комментарий: "${notification.commentPreview}". Причина: ${notification.reason}', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1512,7 +1423,6 @@ class _CommentDeletedWidget extends StatelessWidget { isRead: notification.isRead, notificationsText: 'Ваш комментарий был удален по правилам сообщества: "${notification.commentPreview}"', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } @@ -1524,9 +1434,8 @@ class _UserSubscribedWidget extends StatelessWidget { Widget build(BuildContext context) { return _BaseNotificationWidget( isRead: notification.isRead, - notificationsText: 'У вас новый подписчик: ${notification.subscriberName}! 👋', + notificationsText: 'У вас новый подписчик: ${notification.userName}! 👋', createdAt: notification.createdAt, - onTap: () => (context.findAncestorWidgetOfExactType() as NotificationWidget)._onTap(context), ); } } diff --git a/lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart b/lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart index b6d40645..693fe8d5 100644 --- a/lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart +++ b/lib/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart @@ -5,22 +5,30 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class SubCubit extends Cubit { final ProfileRepository profileRepository; - SubCubit({ - required this.profileRepository, - }) : super(const SubState()); + SubCubit({required this.profileRepository}) : super(const SubState()); - Future getSubscriptions(String slug, [String? query]) async { + Future getSubscriptions(String? slug, [String? query]) async { emit(const SubState.loading()); try { + if (slug == null) { + final myProfile = await profileRepository.getMyProfile(); + emit(SubState.loaded(subList: await profileRepository.getSubscriptionsBySlug(myProfile.slug, query))); + return; + } emit(SubState.loaded(subList: await profileRepository.getSubscriptionsBySlug(slug, query))); } catch (e) { emit(SubState.error(errorMessage: e.toString())); } } - Future getSubscribers(String slug, [String? query]) async { + Future getSubscribers(String? slug, [String? query]) async { emit(const SubState.loading()); try { + if (slug == null) { + final myProfile = await profileRepository.getMyProfile(); + emit(SubState.loaded(subList: await profileRepository.getSubscribersBySlug(myProfile.slug, query))); + return; + } emit(SubState.loaded(subList: await profileRepository.getSubscribersBySlug(slug, query))); } catch (e) { emit(SubState.error(errorMessage: e.toString())); diff --git a/lib/features/profile/presentation/pages/subs_screen.dart b/lib/features/profile/presentation/pages/subs_screen.dart index d5d782f2..6de5194e 100644 --- a/lib/features/profile/presentation/pages/subs_screen.dart +++ b/lib/features/profile/presentation/pages/subs_screen.dart @@ -12,7 +12,7 @@ enum SubType { subscribers, subscriptions } class SubsScreen extends StatefulWidget { final SubType subType; - final String slug; + final String? slug; const SubsScreen({ super.key,