Files
eatvio_app/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart

400 lines
19 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart';
import 'package:eatvio_app/features/notifications/repository/notifications_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class NotificationsCubit extends Cubit<NotificationsState> {
final List<BaseNotificationModel> _notifications = [];
final WsManager wsManager;
final NotificationsRepository notificationsRepository;
StreamSubscription? _newNotificationsSubscription;
NotificationsCubit({required this.wsManager, required this.notificationsRepository}) : super(const NotificationsState.empty());
Future<void> connectToWebSocket() async {
await _newNotificationsSubscription?.cancel();
_newNotificationsSubscription = wsManager.newNotificationsStream?.listen((event) {
_handleNotification(event.data);
});
}
BaseNotificationModel _parseNotification(Map<String, dynamic> notificationJson, NotificationType type) {
try {
final createdAt = notificationJson['created_at'];
final isRead = notificationJson['read_at'] != null;
return switch (type) {
NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanTomorrowReminderToClientNotification => ChosenPlanTomorrowReminderToClientNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanConsultantLastDayStarted => ChosenPlanConsultantLastDayStarted.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanTomorrowReminderToConsultantNotification => ChosenPlanTomorrowReminderToConsultantNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanLastDayFinishedForUser => ChosenPlanLastDayFinishedForUser.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
// New notification types
NotificationType.complaintToModerationForUser => ComplaintToModerationForUser.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.commentReplied => CommentReplied.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.scoreAdded => ScoreAdded.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
// Новые типы уведомлений
NotificationType.planSubscriptionExpired => PlanSubscriptionExpiredNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.planSubscriptionRenewed => PlanSubscriptionRenewedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.consultantAssigned => ConsultantAssignedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.consultantUnassigned => ConsultantUnassignedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.paymentFailed => PaymentFailedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.paymentSuccessful => PaymentSuccessfulNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.goalAchieved => GoalAchievedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.workoutReminder => WorkoutReminderNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.mealReminder => MealReminderNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
// New notification types
NotificationType.dietCancelled => DietCancelledNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.dietStarted => DietStartedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.dietExpired => DietExpiredNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.dietWillStartedIn => DietWillStartedInNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.dietCanNotBeStarted => DietCanNotBeStartedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.personalDietCreationNeeded => PersonalDietCreationNeededNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanClientLastDayFinished => ChosenPlanClientLastDayFinishedNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanClientLastDayStarted => ChosenPlanClientLastDayStartedNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.stayOnTrackToAuthor => StayOnTrackToAuthorNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.chosenPlanStartedToClient => ChosenPlanStartedToClientNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientStartPlanSubscriptionToSubscriber => ClientStartPlanSubscriptionToSubscriberNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.consultantConfirmPlanSubscriptionToSubscriber => ConsultantConfirmPlanSubscriptionToSubscriberNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientPaidPlanSubscriptionToAuthor => ClientPaidPlanSubscriptionToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanTomorrowReminderToClient => ChosenPlanTomorrowReminderToClientNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanTomorrowReminderToConsultant => ChosenPlanTomorrowReminderToConsultantNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanStartedToConsultant => ChosenPlanStartedToConsultantNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.consultantLeftDiaryComment => ConsultantLeftDiaryCommentNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.consultantConfirmPlanSubscriptionToAuthor => ConsultantConfirmPlanSubscriptionToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientPaidPlanSubscriptionToSubscriber => ClientPaidPlanSubscriptionToSubscriberNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientStartPlanSubscriptionToAuthor => ClientStartPlanSubscriptionToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientCancelPlanSubscriptionToAuthor => ClientCancelPlanSubscriptionToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientCancelPlanSubscriptionToSubscriber => ClientCancelPlanSubscriptionToSubscriberNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.userDailyReportCompleted => UserDailyReportCompletedNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.stayOnTrackToCustomer => StayOnTrackToCustomerNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.phoneChanged => PhoneChangedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.subscription => SubscriptionNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.badgeAwarded => BadgeAwardedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.badgeRevoked => BadgeRevokedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.consultantRequestStarted => ConsultantRequestStartedNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientCloseComplaintSuccess => ClientCloseComplaintSuccessNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.clientRefundSuccess => ClientRefundSuccessNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.consultantRefundSuccess => ConsultantRefundSuccessNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.clientRejectSuccess => ClientRejectSuccessNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.consultantRejectSuccess => ConsultantRejectSuccessNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.scoreSubstracted => ScoreSubstractedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.productStatusUpdatedForAuthor => ProductStatusUpdatedForAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.diaryReminderForUsers => DiaryReminderForUsersNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
NotificationType.personalDietIsDefinedToCustomer => PersonalDietIsDefinedToCustomerNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.unknown => UnknownNotification(type: type),
};
} catch (e) {
return UnknownNotification(type: NotificationType.unknown);
}
}
void _handleNotification(String notificationData) {
final json = jsonDecode(notificationData) as Map<String, dynamic>;
// Улучшенная логика определения типа уведомления
String? stringifiedType;
// Сначала проверяем поле 'type' в корне JSON
if (json.containsKey('type') && json['type'] != null) {
stringifiedType = json['type'].toString();
log('Found type in root: $stringifiedType');
}
// Затем проверяем поле 'notification_class_name' в корне JSON
else if (json.containsKey('notification_class_name') && json['notification_class_name'] != null) {
stringifiedType = json['notification_class_name'].toString();
log('Found notification_class_name in root: $stringifiedType');
}
// Если есть поле 'data', проверяем внутри него
else if (json.containsKey('data') && json['data'] is Map<String, dynamic>) {
final data = json['data'] as Map<String, dynamic>;
if (data.containsKey('type') && data['type'] != null) {
stringifiedType = data['type'].toString();
log('Found type in data: $stringifiedType');
} else if (data.containsKey('notification_class_name') && data['notification_class_name'] != null) {
stringifiedType = data['notification_class_name'].toString();
log('Found notification_class_name in data: $stringifiedType');
}
}
// Если тип не найден, используем unknown
final type = stringifiedType != null ? NotificationType.getTypeFromString(stringifiedType) : NotificationType.unknown;
log('Final type: $type, stringifiedType: $stringifiedType');
final notification = _parseNotification(json, type);
_notifications.insert(0, notification);
final currentState = state;
if (currentState is NotificationsStateLoaded) {
emit(currentState.copyWith(notifications: _notifications, notificationsCount: _notifications.length, hasUnreadNotifications: true));
}
}
Future<void> getNotifications() async {
try {
final result = await notificationsRepository.getNotifications(page: 1);
if (result != null) {
final notifications = result['notifications'] as List<BaseNotificationModel>;
final pagination = result['pagination'] as Map<String, dynamic>;
_notifications.clear();
_notifications.addAll(notifications);
final currentPage = pagination['current_page'] as int;
final lastPage = pagination['last_page'] as int;
final hasNextPage = currentPage < lastPage;
emit(
NotificationsState.loaded(
notifications: _notifications,
notificationsCount: _notifications.length,
hasUnreadNotifications: notifications.where((elem) => elem.isRead == false).isNotEmpty,
currentPage: currentPage,
hasNextPage: hasNextPage,
),
);
}
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
}
Future<void> loadMoreNotifications() async {
final currentState = state;
if (currentState is NotificationsStateLoaded && currentState.hasNextPage && !currentState.isLoadingMore) {
try {
emit(currentState.copyWith(isLoadingMore: true));
final nextPage = currentState.currentPage + 1;
final result = await notificationsRepository.getNotifications(page: nextPage);
if (result != null) {
final newNotifications = result['notifications'] as List<BaseNotificationModel>;
final pagination = result['pagination'] as Map<String, dynamic>;
_notifications.addAll(newNotifications);
final currentPage = pagination['current_page'] as int;
final lastPage = pagination['last_page'] as int;
final hasNextPage = currentPage < lastPage;
emit(
currentState.copyWith(
notifications: _notifications,
notificationsCount: _notifications.length,
currentPage: currentPage,
hasNextPage: hasNextPage,
isLoadingMore: false,
),
);
} else {
emit(currentState.copyWith(isLoadingMore: false));
}
} catch (e, st) {
log(e.toString(), stackTrace: st);
emit(currentState.copyWith(isLoadingMore: false));
}
}
}
Future<void> markNotificationsAsRead() async {
final currentState = state;
if (currentState is NotificationsStateLoaded) {
try {
emit(currentState.copyWith(isLoading: true));
await notificationsRepository.markNotificationsAsRead();
await getNotifications();
emit(currentState.copyWith(isLoading: false, hasUnreadNotifications: false));
} catch (e, st) {
log(e.toString(), stackTrace: st);
emit(currentState.copyWith(isLoading: false));
}
}
}
}