Merge pull request 'main' (#2) from eatvio/eatvio_app:main into main

Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
mso7nueh
2025-05-23 10:15:12 +00:00
14 changed files with 850 additions and 44 deletions

View File

@@ -103,7 +103,15 @@ class AppRouter {
return const NoTransitionPage(child: ProfileScreen());
},
),
GoRoute(path: '/feed-screen', name: 'feedScreen', pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen())),
GoRoute(
path: '/feed-screen',
name: 'feedScreen',
pageBuilder: (context, state) => const NoTransitionPage(child: FeedScreen()),
redirect: (context, state) async {
await sl<WsManager>().initialize();
return null;
},
),
GoRoute(path: '/diary-screen', name: 'diaryScreen', pageBuilder: (context, state) => const NoTransitionPage(child: DiaryScreen())),
GoRoute(
path: ConsultationsScreen.path,

View File

@@ -65,6 +65,8 @@ import 'package:eatvio_app/features/my_finance/services/cards_service.dart';
import 'package:eatvio_app/features/my_plans/presentation/cubit/my_plans_cubit.dart';
import 'package:eatvio_app/features/my_plans/repositories/my_diets_repository.dart';
import 'package:eatvio_app/features/my_plans/services/my_plans_service.dart';
import 'package:eatvio_app/features/notifications/repository/notifications_repository.dart';
import 'package:eatvio_app/features/notifications/service/notifications_service.dart';
import 'package:eatvio_app/features/order_info/cubit/order_info_cubit.dart';
import 'package:eatvio_app/features/plans/repositories/plans_repository.dart';
import 'package:eatvio_app/features/plans/services/plans_service.dart';
@@ -98,7 +100,7 @@ Future<void> init() async {
// Cubit singletons
sl.registerLazySingleton<WsManager>(() => WsManager(wsService: sl()));
sl.registerLazySingleton<NewMessagesCubit>(() => NewMessagesCubit());
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl()));
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit(wsManager: sl(), notificationsRepository: sl()));
sl.registerLazySingleton<ProfileAvatarCubit>(() => ProfileAvatarCubit(profileRepository: sl()));
sl.registerLazySingleton<ChatsCubit>(() => ChatsCubit(chatRepository: sl(), wsManager: sl(), profileRepository: sl()));
@@ -162,6 +164,7 @@ Future<void> init() async {
sl.registerLazySingleton<ConsultationsRepository>(() => ConsultationsRepository(consultationService: sl()));
sl.registerLazySingleton<CancelPlanRepository>(() => CancelPlanRepository(cancelService: sl()));
sl.registerLazySingleton<CreatePlanRepository>(() => CreatePlanRepository(createPlanService: sl()));
sl.registerLazySingleton<NotificationsRepository>(() => NotificationsRepository(notificationsService: sl()));
// Services
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
@@ -189,6 +192,7 @@ Future<void> init() async {
sl.registerLazySingleton<ConsultationService>(() => ConsultationService(sl()));
sl.registerLazySingleton<CancelPlanService>(() => CancelPlanService(sl()));
sl.registerLazySingleton<CreatePlanService>(() => CreatePlanService(sl()));
sl.registerLazySingleton<NotificationsService>(() => NotificationsService(sl()));
// External
sl.registerLazySingleton(

View File

@@ -19,7 +19,6 @@ class WsManager {
Stream<ChannelReadEvent>? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated');
// TODO: change name of event to a right one
Stream<ChannelReadEvent>? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated');
Stream<ChannelReadEvent>? get directChatStream => _privateDirectChatChannel?.bind('App\\Events\\ChatMessageUpdated');

View File

@@ -1,25 +1,284 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'notification_model.freezed.dart';
part 'notification_model.g.dart';
@freezed
abstract class NotificationModel with _$NotificationModel {
factory NotificationModel({
required NotificationType type
}) = _NotificationModel;
factory NotificationModel.fromJson(Map<String, dynamic> json) => _$NotificationModelFromJson(json);
sealed class BaseNotificationModel {
abstract final NotificationType type;
}
class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
final String customerName;
final String chatId;
PlanSubscriptionStartedToAuthorNotification({
required this.type,
required this.planName,
required this.customerName,
required this.chatId,
});
factory PlanSubscriptionStartedToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return PlanSubscriptionStartedToAuthorNotification(
type: type,
planName: json['plan_name'],
customerName: json['user_name'],
chatId: json['chat_id'],
);
}
}
class PlanSubscriptionCanceledToAuthorNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
final String customerName;
PlanSubscriptionCanceledToAuthorNotification({
required this.type,
required this.planName,
required this.customerName,
});
factory PlanSubscriptionCanceledToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return PlanSubscriptionCanceledToAuthorNotification(
type: type,
planName: json['plan_name'],
customerName: json['user_name'],
);
}
}
class ChosenPlanStartedToClientNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
final int planId;
ChosenPlanStartedToClientNotification({
required this.type,
required this.planName,
required this.planId,
});
factory ChosenPlanStartedToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return ChosenPlanStartedToClientNotification(
type: type,
planName: json['plan_name'],
planId: json['plan_id'],
);
}
}
class ChosenPlanTomorrowReminderToClientNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
ChosenPlanTomorrowReminderToClientNotification({
required this.type,
required this.planName,
});
factory ChosenPlanTomorrowReminderToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return ChosenPlanTomorrowReminderToClientNotification(
type: type,
planName: json['plan_name'],
);
}
}
class DiaryReminderNotification implements BaseNotificationModel {
@override
final NotificationType type;
DiaryReminderNotification({required this.type});
factory DiaryReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return DiaryReminderNotification(
type: type,
);
}
}
class ConsultantLeftCommentNotification implements BaseNotificationModel {
@override
final NotificationType type;
final DateTime date;
ConsultantLeftCommentNotification({
required this.type,
required this.date,
});
factory ConsultantLeftCommentNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return ConsultantLeftCommentNotification(type: type, date: json['diary_date']);
}
}
class PersonalizedDietIsDefinedNotification implements BaseNotificationModel {
@override
final NotificationType type;
PersonalizedDietIsDefinedNotification({required this.type});
factory PersonalizedDietIsDefinedNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return PersonalizedDietIsDefinedNotification(
type: type,
);
}
}
class ChosenPlanFinishedToClientNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
ChosenPlanFinishedToClientNotification({
required this.type,
required this.planName,
});
factory ChosenPlanFinishedToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return ChosenPlanFinishedToClientNotification(type: type, planName: json['plan_name']);
}
}
class PlanSubscriptionCanceledToCustomerNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
PlanSubscriptionCanceledToCustomerNotification({
required this.type,
required this.planName,
});
factory PlanSubscriptionCanceledToCustomerNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return PlanSubscriptionCanceledToCustomerNotification(
type: type,
planName: json['plan_name'],
);
}
}
class ChosenPlanStartedToConsultantNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
final String customerName;
ChosenPlanStartedToConsultantNotification({
required this.type,
required this.planName,
required this.customerName,
});
factory ChosenPlanStartedToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return ChosenPlanStartedToConsultantNotification(
type: type,
planName: json['plan_name'],
customerName: json['client_name'],
);
}
}
class ChosenPlanTomorrowReminderToConsultantNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
final String customerName;
ChosenPlanTomorrowReminderToConsultantNotification({
required this.type,
required this.planName,
required this.customerName,
});
factory ChosenPlanTomorrowReminderToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return ChosenPlanTomorrowReminderToConsultantNotification(
type: type,
planName: json['plan_name'],
customerName: json['client_name'],
);
}
}
class PlanSubscriptionPaidToAuthorNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
final String customerName;
PlanSubscriptionPaidToAuthorNotification({
required this.type,
required this.planName,
required this.customerName,
});
factory PlanSubscriptionPaidToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return PlanSubscriptionPaidToAuthorNotification(
type: type,
planName: json['plan_name'],
customerName: json['user_name'],
);
}
}
class ChosenPlanFinishedToConsultantNotification implements BaseNotificationModel {
@override
final NotificationType type;
final String planName;
final String customerName;
final int planId;
ChosenPlanFinishedToConsultantNotification({
required this.type,
required this.planName,
required this.customerName,
required this.planId,
});
factory ChosenPlanFinishedToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type) {
return ChosenPlanFinishedToConsultantNotification(
type: type,
planName: json['plan_name'],
customerName: json['client_name'],
planId: json['plan_id'],
);
}
}
class UnknownNotification implements BaseNotificationModel {
@override
final NotificationType type;
UnknownNotification({
required this.type,
});
}
enum NotificationType {
@JsonValue('App\\Notifications\\UserSubscribedNotification')
userSubscribedNotification,
@JsonValue('App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor')
planSubscriptionStartedToAuthor,
@JsonValue('App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor')
planSubscriptionCanceledToAuthor,
chosenPlanStartedToClientNotification,
chosenPlanTomorrowReminderToClientNotification,
diaryReminderNotification,
consultantLeftComment,
personalizedDietIsDefined,
chosenPlanFinishedToClient,
planSubscriptionCanceledToCustomer,
chosenPlanStartedToConsultantNotification,
chosenPlanTomorrowReminderToConsultantNotification,
planSubscriptionPaidToAuthor,
chosenPlanFinishedToConsultant,
unknown;
static NotificationType getTypeFromString(String type) => switch (type) {
'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor' => NotificationType.planSubscriptionStartedToAuthor,
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor' => NotificationType.planSubscriptionCanceledToAuthor,
'App\\Notifications\\Plan\\ChosenPlanStartedToClientNotification' => NotificationType.chosenPlanStartedToClientNotification,
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToClientNotification' => NotificationType.chosenPlanStartedToClientNotification,
'App\\Notifications\\DiaryReminderNotification' => NotificationType.diaryReminderNotification,
'App\\Notifications\\Plan\\ConsultantLeftComment' => NotificationType.consultantLeftComment,
'App\\Notifications\\Plan\\PersonalizedDietIsDefined' => NotificationType.personalizedDietIsDefined,
'App\\Notifications\\Plan\\ChosenPlanFinishedToClient' => NotificationType.chosenPlanFinishedToClient,
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToCustomer' => NotificationType.planSubscriptionCanceledToCustomer,
'App\\Notifications\\Plan\\ChosenPlanStartedToConsultantNotification' => NotificationType.chosenPlanStartedToConsultantNotification,
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToConsultantNotification' => NotificationType.chosenPlanTomorrowReminderToConsultantNotification,
'App\\Notifications\\Plan\\PlanSubscriptionPaidToAuthor' => NotificationType.planSubscriptionPaidToAuthor,
'App\\Notifications\\Plan\\ChosenPlanFinishedToConsultant' => NotificationType.chosenPlanFinishedToConsultant,
_ => NotificationType.unknown,
};
}

View File

@@ -1,22 +1,55 @@
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<NotificationModel> _notifications = [];
final List<BaseNotificationModel> _notifications = [];
final WsManager wsManager;
NotificationsCubit({required this.wsManager}) : super(const NotificationsState.empty()) {
final NotificationsRepository notificationsRepository;
NotificationsCubit({
required this.wsManager,
required this.notificationsRepository,
}) : super(const NotificationsState.empty()) {
wsManager.newNotificationsStream?.listen((event) {
_handleNotification(event.data);
});
}
BaseNotificationModel _parseNotification(Map<String, dynamic> notificationJson, NotificationType type) {
try {
return switch (type) {
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.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson, type),
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson, type),
NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(notificationJson, type),
NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(notificationJson, type),
NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson, type),
NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(notificationJson, type),
NotificationType.chosenPlanTomorrowReminderToConsultantNotification =>
ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson, type),
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson, type),
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson, type),
NotificationType.unknown => UnknownNotification(type: type),
};
} catch (e) {
return UnknownNotification(type: NotificationType.unknown);
}
}
void _handleNotification(String notificationData) {
// final data = jsonDecode(notificationData) as Map<String, dynamic>;
// final notification = NotificationModel.fromJson(data);
// _notifications.add(notification);
final json = jsonDecode(notificationData) as Map<String, dynamic>;
final stringifiedType = json['notification_class_name'];
final type = NotificationType.getTypeFromString(stringifiedType);
final notification = _parseNotification(json, type);
_notifications.insert(0, notification);
emit(
NotificationsState.loaded(
notifications: _notifications,
@@ -24,4 +57,20 @@ class NotificationsCubit extends Cubit<NotificationsState> {
),
);
}
Future<void> getNotifications() async {
try {
final notifications = await notificationsRepository.getNotifications(page: 1);
_notifications.clear();
_notifications.addAll(notifications ?? []);
emit(
NotificationsState.loaded(
notifications: _notifications,
notificationsCount: _notifications.length,
),
);
} catch (e, st) {
log(e.toString(), stackTrace: st);
}
}
}

View File

@@ -10,7 +10,7 @@ abstract class NotificationsState with _$NotificationsState {
const factory NotificationsState.empty() = NotificationsStateEmpty;
const factory NotificationsState.loaded({
required List<NotificationModel> notifications,
required List<BaseNotificationModel> notifications,
required int notificationsCount,
}) = NotificationsStateLoaded;

View File

@@ -1,3 +1,5 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_divider.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';
@@ -12,19 +14,42 @@ class NotificationsScreen extends StatelessWidget {
return BlocBuilder<NotificationsCubit, NotificationsState>(
builder: (BuildContext context, NotificationsState state) {
return Scaffold(
backgroundColor: appTheme.gray100,
appBar: AppBar(
title: const Text('Уведомления'),
),
body: state is NotificationsStateLoaded
? CustomScrollView(
? 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.builder(
itemBuilder: (context, index) => NotificationWidget(
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,
)
],
),
),
)
: const Placeholder(),
);

View File

@@ -1,15 +1,255 @@
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_outlined_button.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/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class NotificationWidget extends StatelessWidget {
const NotificationWidget({super.key, required this.notification});
final NotificationModel notification;
final BaseNotificationModel notification;
@override
Widget build(BuildContext context) {
return switch (notification) {
PlanSubscriptionStartedToAuthorNotification() => _PlanSubscriptionStartedToAuthorWidget(
notification: notification as PlanSubscriptionStartedToAuthorNotification,
),
PlanSubscriptionCanceledToAuthorNotification() => _PlanSubscriptionCanceledToAuthorWidget(
notification: notification as PlanSubscriptionCanceledToAuthorNotification,
),
ChosenPlanStartedToClientNotification() => _ChosenPlanStartedToClientWidget(
notification: notification as ChosenPlanStartedToClientNotification,
),
ChosenPlanTomorrowReminderToClientNotification() => _ChosenPlanTomorrowReminderToClientWidget(
notification: notification as ChosenPlanTomorrowReminderToClientNotification,
),
DiaryReminderNotification() => _DiaryReminderWidget(
notification: notification as DiaryReminderNotification,
),
ConsultantLeftCommentNotification() => _ConsultantLeftCommentWidget(
notification: notification as ConsultantLeftCommentNotification,
),
PersonalizedDietIsDefinedNotification() => _PersonalizedDietIsDefinedWidget(
notification: notification as PersonalizedDietIsDefinedNotification,
),
ChosenPlanFinishedToClientNotification() => _ChosenPlanFinishedToClientWidget(
notification: notification as ChosenPlanFinishedToClientNotification,
),
PlanSubscriptionCanceledToCustomerNotification() => _PlanSubscriptionCanceledToCustomerWidget(
notification: notification as PlanSubscriptionCanceledToCustomerNotification,
),
ChosenPlanStartedToConsultantNotification() => _ChosenPlanStartedToConsultantWidget(
notification: notification as ChosenPlanStartedToConsultantNotification,
),
ChosenPlanTomorrowReminderToConsultantNotification() => _ChosenPlanTomorrowReminderToConsultantWidget(
notification: notification as ChosenPlanTomorrowReminderToConsultantNotification,
),
PlanSubscriptionPaidToAuthorNotification() => throw _PlanSubscriptionPaidToAuthorWidget(
notification: notification as PlanSubscriptionPaidToAuthorNotification,
),
ChosenPlanFinishedToConsultantNotification() => _ChosenPlanFinishedToConsultantWidget(
notification: notification as ChosenPlanFinishedToConsultantNotification,
),
UnknownNotification() => const SizedBox.shrink(),
};
}
}
class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget {
const _PlanSubscriptionStartedToAuthorWidget({required this.notification});
final PlanSubscriptionStartedToAuthorNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.userSubscribedToPlan(notification.customerName, notification.planName),
);
}
}
class _PlanSubscriptionCanceledToAuthorWidget extends StatelessWidget {
const _PlanSubscriptionCanceledToAuthorWidget({required this.notification});
final PlanSubscriptionCanceledToAuthorNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.userCanceldThePlanSubscription(notification.customerName, notification.planName),
);
}
}
class _ChosenPlanStartedToClientWidget extends StatelessWidget {
const _ChosenPlanStartedToClientWidget({required this.notification});
final ChosenPlanStartedToClientNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.planStarted(notification.planName),
);
}
}
class _ChosenPlanTomorrowReminderToClientWidget extends StatelessWidget {
const _ChosenPlanTomorrowReminderToClientWidget({required this.notification});
final ChosenPlanTomorrowReminderToClientNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.tomorrowYourPlanWillStart(notification.planName),
);
}
}
class _DiaryReminderWidget extends StatelessWidget {
const _DiaryReminderWidget({required this.notification});
final DiaryReminderNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.fillYasterday,
);
}
}
class _ConsultantLeftCommentWidget extends StatelessWidget {
const _ConsultantLeftCommentWidget({required this.notification});
final ConsultantLeftCommentNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.consultantLeftComment(notification.date),
);
}
}
class _PersonalizedDietIsDefinedWidget extends StatelessWidget {
const _PersonalizedDietIsDefinedWidget({required this.notification});
final PersonalizedDietIsDefinedNotification notification;
@override
Widget build(BuildContext context) {
// return (notification.type) {
// NotificationType.userSubscribedNotification => const _UserSubscribedNotification(),
// };
return Container();
}
}
class _ChosenPlanFinishedToClientWidget extends StatelessWidget {
const _ChosenPlanFinishedToClientWidget({required this.notification});
final ChosenPlanFinishedToClientNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: '${s.congratsYouHaveFinishedPlan} «${notification.planName}»',
);
}
}
class _PlanSubscriptionCanceledToCustomerWidget extends StatelessWidget {
const _PlanSubscriptionCanceledToCustomerWidget({required this.notification});
final PlanSubscriptionCanceledToCustomerNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.youCanceledSubscriptionToPlan(notification.planName),
);
}
}
class _ChosenPlanStartedToConsultantWidget extends StatelessWidget {
const _ChosenPlanStartedToConsultantWidget({required this.notification});
final ChosenPlanStartedToConsultantNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.planWithUserStarted(notification.planName, notification.customerName),
);
}
}
class _ChosenPlanTomorrowReminderToConsultantWidget extends StatelessWidget {
const _ChosenPlanTomorrowReminderToConsultantWidget({required this.notification});
final ChosenPlanTomorrowReminderToConsultantNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.tommorowPlanWithUserWillStart(notification.planName, notification.customerName),
);
}
}
class _PlanSubscriptionPaidToAuthorWidget extends StatelessWidget {
const _PlanSubscriptionPaidToAuthorWidget({required this.notification});
final PlanSubscriptionPaidToAuthorNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return _BaseNotificationWidget(
notificationsText: s.userPaidThePlan(notification.customerName, notification.planName),
);
}
}
class _ChosenPlanFinishedToConsultantWidget extends StatelessWidget {
const _ChosenPlanFinishedToConsultantWidget({required this.notification});
final ChosenPlanFinishedToConsultantNotification notification;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(children: [
Text(
'${s.congratsYouHaveFinishedPlan} «${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).pushNamed(CardSettingsScreen.name, extra: notification.planId);
},
),
const SizedBox(height: 12),
]);
}
}
// CardSettingsScreen
class _BaseNotificationWidget extends StatelessWidget {
const _BaseNotificationWidget({required this.notificationsText});
final String notificationsText;
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.sizeOf(context).width * 0.6,
child: Text(
notificationsText,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
import 'package:eatvio_app/features/notifications/service/notifications_service.dart';
class NotificationsRepository {
final NotificationsService notificationsService;
NotificationsRepository({required this.notificationsService});
Future<List<BaseNotificationModel>?> getNotifications({required int page}) {
try {
return notificationsService.getNotifications(page: page);
} catch (e) {
rethrow;
}
}
}

View File

@@ -0,0 +1,46 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
class NotificationsService {
final Dio dio;
NotificationsService(this.dio);
Future<List<BaseNotificationModel>?> getNotifications({required int page}) async {
try {
final response = await dio.get('/api/profile/notifications?page=$page');
if (response.statusCode == 200) {
final data = response.data['data'] as List<dynamic>;
final notifications = data.map((notificationJson) {
try {
final stringifiedType = notificationJson['data']['notification_class_name'];
final type = NotificationType.getTypeFromString(stringifiedType);
return switch (type) {
NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(notificationJson['data'], type),
NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson['data'], type),
NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(notificationJson['data'], type),
NotificationType.chosenPlanTomorrowReminderToClientNotification =>
ChosenPlanTomorrowReminderToClientNotification.fromJson(notificationJson['data'], type),
NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson['data'], type),
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson['data'], type),
NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(notificationJson['data'], type),
NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(notificationJson['data'], type),
NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson['data'], type),
NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(notificationJson['data'], type),
NotificationType.chosenPlanTomorrowReminderToConsultantNotification =>
ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson['data'], type),
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson['data'], type),
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson['data'], type),
NotificationType.unknown => UnknownNotification(type: type),
};
} catch (e) {
return UnknownNotification(type: NotificationType.unknown);
}
}).toList();
return notifications.where((notification) => notification.type != NotificationType.unknown).toList();
}
return null;
} catch (e) {
rethrow;
}
}
}

View File

@@ -20,6 +20,30 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'en';
static String m0(date) => "Консультант прокомментировал день ${date}";
static String m1(planName) => "План «${planName}» начался";
static String m2(planName, name) =>
"План «${planName}» с пользователем ${name} начался";
static String m3(planName, name) =>
"Завтра начинается план «${planName}» с пользователем ${name}";
static String m4(planName) =>
"Завтра начнется прохождение плана «${planName}»";
static String m5(name, planName) =>
"Пользователь ${name} отменил подписку на план «${planName}»";
static String m6(name, planName) =>
"Пользователь ${name} оплатил план «${planName}»";
static String m7(name, planName) =>
"Пользователь ${name} подписался на план «${planName}»";
static String m8(planName) => "Вы отменили подписку на план «${planName}»";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"Moscow": MessageLookupByLibrary.simpleMessage("Москва"),
@@ -153,6 +177,7 @@ class MessageLookup extends MessageLookupByLibrary {
"congratsYouHaveFinishedPlan": MessageLookupByLibrary.simpleMessage(
"Поздравляем, Вы закончили план",
),
"consultantLeftComment": m0,
"consultations_count": MessageLookupByLibrary.simpleMessage(
"Кол-во проведенных консулдьтаций",
),
@@ -301,6 +326,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Ваши любимые/нелюбимые продукты питания?",
),
"female": MessageLookupByLibrary.simpleMessage("Женский"),
"fillYasterday": MessageLookupByLibrary.simpleMessage(
"Заполните вчерашний день",
),
"fill_in_information": MessageLookupByLibrary.simpleMessage(
"Заполните информацию:",
),
@@ -504,9 +532,11 @@ class MessageLookup extends MessageLookupByLibrary {
"Физическая форма",
),
"place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"),
"planStarted": m1,
"planWillBeConfirmedAfterThreeDays": MessageLookupByLibrary.simpleMessage(
"Если вы не подтвердите, то план через три дня подтвердится автоматически",
),
"planWithUserStarted": m2,
"plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"),
"plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"),
"plan_desc_example": MessageLookupByLibrary.simpleMessage(
@@ -640,6 +670,8 @@ class MessageLookup extends MessageLookupByLibrary {
"toOffer": MessageLookupByLibrary.simpleMessage("Предложить"),
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
"to_order": MessageLookupByLibrary.simpleMessage("Заказать"),
"tommorowPlanWithUserWillStart": m3,
"tomorrowYourPlanWillStart": m4,
"try_again": MessageLookupByLibrary.simpleMessage("Попробовать снова"),
"try_again_2": MessageLookupByLibrary.simpleMessage("Попробуйте ещё раз"),
"typeAmoutOfReturn": MessageLookupByLibrary.simpleMessage(
@@ -664,9 +696,16 @@ class MessageLookup extends MessageLookupByLibrary {
"Верхние показатели давления:",
),
"upper": MessageLookupByLibrary.simpleMessage("Верхнее"),
"userCanceldThePlanSubscription": m5,
"userConfirmedPlanCompletion": MessageLookupByLibrary.simpleMessage(
"Пользователь подтвердил прохождения плана. В ближайшее время Вы получите",
),
"userConfirmedPlanCompletionWithoutSum":
MessageLookupByLibrary.simpleMessage(
"Пользователь подтвердил прохождения плана.",
),
"userPaidThePlan": m6,
"userSubscribedToPlan": m7,
"user_authorization": MessageLookupByLibrary.simpleMessage(
"Аутентификация",
),
@@ -716,6 +755,7 @@ class MessageLookup extends MessageLookupByLibrary {
"youCanComplain": MessageLookupByLibrary.simpleMessage(
"Если вас что-то не устроило, вы можете подать жалобу",
),
"youCanceledSubscriptionToPlan": m8,
"youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите отменить заказ?",
),

View File

@@ -3095,6 +3095,16 @@ class S {
);
}
/// `Пользователь подтвердил прохождения плана.`
String get userConfirmedPlanCompletionWithoutSum {
return Intl.message(
'Пользователь подтвердил прохождения плана.',
name: 'userConfirmedPlanCompletionWithoutSum',
desc: '',
args: [],
);
}
/// `Получить выплату`
String get collectCharge {
return Intl.message(
@@ -3224,6 +3234,106 @@ class S {
args: [],
);
}
/// `Пользователь {name} оплатил план «{planName}»`
String userPaidThePlan(Object name, Object planName) {
return Intl.message(
'Пользователь $name оплатил план «$planName»',
name: 'userPaidThePlan',
desc: '',
args: [name, planName],
);
}
/// `Завтра начинается план «{planName}» с пользователем {name}`
String tommorowPlanWithUserWillStart(Object planName, Object name) {
return Intl.message(
'Завтра начинается план «$planName» с пользователем $name',
name: 'tommorowPlanWithUserWillStart',
desc: '',
args: [planName, name],
);
}
/// `План «{planName}» с пользователем {name} начался`
String planWithUserStarted(Object planName, Object name) {
return Intl.message(
'План «$planName» с пользователем $name начался',
name: 'planWithUserStarted',
desc: '',
args: [planName, name],
);
}
/// `Вы отменили подписку на план «{planName}»`
String youCanceledSubscriptionToPlan(Object planName) {
return Intl.message(
'Вы отменили подписку на план «$planName»',
name: 'youCanceledSubscriptionToPlan',
desc: '',
args: [planName],
);
}
/// `Консультант прокомментировал день {date}`
String consultantLeftComment(Object date) {
return Intl.message(
'Консультант прокомментировал день $date',
name: 'consultantLeftComment',
desc: '',
args: [date],
);
}
/// `Заполните вчерашний день`
String get fillYasterday {
return Intl.message(
'Заполните вчерашний день',
name: 'fillYasterday',
desc: '',
args: [],
);
}
/// `Завтра начнется прохождение плана «{planName}»`
String tomorrowYourPlanWillStart(Object planName) {
return Intl.message(
'Завтра начнется прохождение плана «$planName»',
name: 'tomorrowYourPlanWillStart',
desc: '',
args: [planName],
);
}
/// `План «{planName}» начался`
String planStarted(Object planName) {
return Intl.message(
'План «$planName» начался',
name: 'planStarted',
desc: '',
args: [planName],
);
}
/// `Пользователь {name} отменил подписку на план «{planName}»`
String userCanceldThePlanSubscription(Object name, Object planName) {
return Intl.message(
'Пользователь $name отменил подписку на план «$planName»',
name: 'userCanceldThePlanSubscription',
desc: '',
args: [name, planName],
);
}
/// `Пользователь {name} подписался на план «{planName}»`
String userSubscribedToPlan(Object name, Object planName) {
return Intl.message(
'Пользователь $name подписался на план «$planName»',
name: 'userSubscribedToPlan',
desc: '',
args: [name, planName],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -373,6 +373,7 @@
"mealComment": "Комментарий к приему пищи",
"congratsYouHaveFinishedPlan": "Поздравляем, Вы закончили план",
"userConfirmedPlanCompletion": "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите",
"userConfirmedPlanCompletionWithoutSum": "Пользователь подтвердил прохождения плана.",
"collectCharge": "Получить выплату",
"chargeApproved": "Выплата одобрена",
"moneySentToCard": "Средства отправлены на карту",
@@ -385,6 +386,16 @@
"pleaseConfirmPlan": "Просим вас подтвердить прохождение плана",
"planWillBeConfirmedAfterThreeDays": "Если вы не подтвердите, то план через три дня подтвердится автоматически",
"youCanComplain": "Если вас что-то не устроило, вы можете подать жалобу",
"leaveComplainRequest": "Подать жалобу"
"leaveComplainRequest": "Подать жалобу",
"userPaidThePlan": "Пользователь {name} оплатил план «{planName}»",
"tommorowPlanWithUserWillStart": "Завтра начинается план «{planName}» с пользователем {name}",
"planWithUserStarted": "План «{planName}» с пользователем {name} начался",
"youCanceledSubscriptionToPlan": "Вы отменили подписку на план «{planName}»",
"consultantLeftComment": "Консультант прокомментировал день {date}",
"fillYasterday": "Заполните вчерашний день",
"tomorrowYourPlanWillStart": "Завтра начнется прохождение плана «{planName}»",
"planStarted": "План «{planName}» начался",
"userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»",
"userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»"
}

View File

@@ -39,7 +39,7 @@ class MyApp extends StatelessWidget {
return MultiBlocProvider(
providers: [
BlocProvider<NewMessagesCubit>(create: (context) => sl<NewMessagesCubit>()),
BlocProvider<NotificationsCubit>(create: (context) => sl<NotificationsCubit>()),
BlocProvider<NotificationsCubit>(create: (context) => sl<NotificationsCubit>()..getNotifications()),
BlocProvider<FeedCubit>(create: (context) => sl<FeedCubit>()..loadFeedBlogPostList()),
BlocProvider<ProfileAvatarCubit>(create: (context) => sl<ProfileAvatarCubit>()..getMyProfile()),
BlocProvider<ChatsCubit>(create: (context) => sl<ChatsCubit>()..getChatRoomsWithLoading()),