added logic of unread notifications

This commit is contained in:
Username1548
2025-05-28 00:30:05 +07:00
parent 963ff78c25
commit b80b4e3a9e
14 changed files with 291 additions and 85 deletions

View File

@@ -25,8 +25,7 @@ class NotificationsCubit extends Cubit<NotificationsState> {
NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(notificationJson, type),
NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson, type),
NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(notificationJson, type),
NotificationType.chosenPlanTomorrowReminderToClientNotification =>
ChosenPlanTomorrowReminderToClientNotification.fromJson(notificationJson, type),
NotificationType.chosenPlanTomorrowReminderToClientNotification => ChosenPlanTomorrowReminderToClientNotification.fromJson(notificationJson, type),
NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson, type),
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson, type),
NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(notificationJson, type),
@@ -54,6 +53,7 @@ class NotificationsCubit extends Cubit<NotificationsState> {
NotificationsState.loaded(
notifications: _notifications,
notificationsCount: _notifications.length,
hasUnreadNotifications: true,
),
);
}
@@ -67,10 +67,26 @@ class NotificationsCubit extends Cubit<NotificationsState> {
NotificationsState.loaded(
notifications: _notifications,
notificationsCount: _notifications.length,
hasUnreadNotifications: (notifications ?? []).where((elem) => elem.isRead == false).isNotEmpty,
),
);
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
}
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));
}
}
}
}

View File

@@ -10,8 +10,10 @@ abstract class NotificationsState with _$NotificationsState {
const factory NotificationsState.empty() = NotificationsStateEmpty;
const factory NotificationsState.loaded({
required List<BaseNotificationModel> notifications,
required List<BaseNotificationModel> notifications,
required int notificationsCount,
@Default(false) bool isLoading,
@Default(false) bool hasUnreadNotifications,
}) = NotificationsStateLoaded;
const factory NotificationsState.error({

View File

@@ -1,8 +1,12 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart';
import 'package:eatvio_app/features/notifications/presentation/widgets/notification_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -11,6 +15,7 @@ class NotificationsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<NotificationsCubit, NotificationsState>(
builder: (BuildContext context, NotificationsState state) {
return Scaffold(
@@ -19,38 +24,58 @@ class NotificationsScreen extends StatelessWidget {
title: const Text('Уведомления'),
),
body: state is NotificationsStateLoaded
? Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Container(
padding: const EdgeInsets.only(top: 16),
decoration: BoxDecoration(
color: appTheme.whiteA700,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
? Stack(children: [
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Container(
padding: const EdgeInsets.only(top: 16),
decoration: BoxDecoration(
color: appTheme.whiteA700,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
),
child: CustomScrollView(
slivers: [
SliverList.separated(
separatorBuilder: (context, index) => const Padding(
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: CustomDivider(),
),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: NotificationWidget(
notification: state.notifications[index],
),
);
},
itemCount: state.notifications.length,
)
],
child: state.isLoading
? const Center(child: Loader())
: CustomScrollView(
slivers: [
SliverList.separated(
separatorBuilder: (context, index) => const Padding(
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
child: CustomDivider(),
),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: NotificationWidget(
notification: state.notifications[index],
),
);
},
itemCount: state.notifications.length,
)
],
),
),
),
)
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
child: CustomOutlinedButton(
text: s.markAsRead,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle:
ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
onPressed: () async {
context.read<NotificationsCubit>().markNotificationsAsRead();
},
),
),
)
])
: const Placeholder(),
);
},

View File

@@ -65,7 +65,8 @@ class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.userSubscribedToPlan(notification.customerName, notification.planName),
isRead: notification.isRead,
notificationsText: s.fillYasterday,
);
}
}
@@ -78,7 +79,8 @@ class _PlanSubscriptionCanceledToAuthorWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.userCanceldThePlanSubscription(notification.customerName, notification.planName),
isRead: notification.isRead,
notificationsText: s.fillYasterday,
);
}
}
@@ -91,7 +93,8 @@ class _ChosenPlanStartedToClientWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.planStarted(notification.planName),
isRead: notification.isRead,
notificationsText: s.fillYasterday,
);
}
}
@@ -104,7 +107,8 @@ class _ChosenPlanTomorrowReminderToClientWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.tomorrowYourPlanWillStart(notification.planName),
isRead: notification.isRead,
notificationsText: s.fillYasterday,
);
}
}
@@ -116,6 +120,7 @@ class _DiaryReminderWidget extends StatelessWidget {
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: s.fillYasterday,
);
}
@@ -129,6 +134,7 @@ class _ConsultantLeftCommentWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: s.consultantLeftComment(notification.date),
);
}
@@ -151,6 +157,7 @@ class _ChosenPlanFinishedToClientWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: '${s.congratsYouHaveFinishedPlan} «${notification.planName}»',
);
}
@@ -164,6 +171,7 @@ class _PlanSubscriptionCanceledToCustomerWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: s.youCanceledSubscriptionToPlan(notification.planName),
);
}
@@ -177,6 +185,7 @@ class _ChosenPlanStartedToConsultantWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: s.planWithUserStarted(notification.planName, notification.customerName),
);
}
@@ -190,6 +199,7 @@ class _ChosenPlanTomorrowReminderToConsultantWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: s.tommorowPlanWithUserWillStart(notification.planName, notification.customerName),
);
}
@@ -203,6 +213,7 @@ class _PlanSubscriptionPaidToAuthorWidget extends StatelessWidget {
final s = S.of(context);
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: s.userPaidThePlan(notification.customerName, notification.planName),
);
}
@@ -240,16 +251,32 @@ class _ChosenPlanFinishedToConsultantWidget extends StatelessWidget {
// CardSettingsScreen
class _BaseNotificationWidget extends StatelessWidget {
const _BaseNotificationWidget({required this.notificationsText});
const _BaseNotificationWidget({required this.notificationsText, required this.isRead});
final String notificationsText;
final bool isRead;
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.sizeOf(context).width * 0.6,
child: Text(
notificationsText,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
return Row(
children: [
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.6,
child: Text(
notificationsText,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
if (!isRead) ...[
const Spacer(),
Container(
decoration: BoxDecoration(
color: appTheme.blueA400,
shape: BoxShape.circle,
),
width: 8,
height: 8,
)
]
],
);
}
}