forked from eatvio/eatvio_app
fix(blackList+notifications): Issue-523, 191
This commit is contained in:
@@ -13,6 +13,8 @@ import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.da
|
||||
import 'package:eatvio_app/features/chat/presentation/widgets/about_order_button.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/widgets/chat_bubble_widget.dart';
|
||||
import 'package:eatvio_app/core/locator_service.dart';
|
||||
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart';
|
||||
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_state.dart';
|
||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart';
|
||||
@@ -465,7 +467,27 @@ class _ChatScreenState extends State<ChatScreen> {
|
||||
const SizedBox(width: 8),
|
||||
InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
// Проверяем, заблокирован ли пользователь
|
||||
final blockedUsersCubit = sl<BlockedUsersCubit>();
|
||||
await blockedUsersCubit.getBlockedUsers();
|
||||
final blockedState = blockedUsersCubit.state;
|
||||
|
||||
if (blockedState is BlockedUsersStateLoaded) {
|
||||
final isBlocked = blockedState.profilesList.any((profile) => profile.slug == state.chatRoom.users.receiver.slug);
|
||||
if (isBlocked) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Вы не можете отправить сообщение заблокированному пользователю'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (textEditingController.text.isNotEmpty || pickedFiles.isNotEmpty) {
|
||||
chatRoomCubit.sendMessage(
|
||||
data: textEditingController.text.isEmpty ? null : textEditingController.text.trim(),
|
||||
|
||||
@@ -12,6 +12,8 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart';
|
||||
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_state.dart';
|
||||
|
||||
class EmptyChatScreen extends StatefulWidget {
|
||||
final Author author;
|
||||
@@ -133,21 +135,45 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
||||
InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: () async {
|
||||
// Проверяем, заблокирован ли пользователь
|
||||
final blockedUsersCubit = sl<BlockedUsersCubit>();
|
||||
await blockedUsersCubit.getBlockedUsers();
|
||||
final blockedState = blockedUsersCubit.state;
|
||||
|
||||
if (blockedState is BlockedUsersStateLoaded) {
|
||||
final isBlocked = blockedState.profilesList.any((profile) => profile.slug == widget.author.authorSlug);
|
||||
if (isBlocked) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Вы не можете отправить сообщение заблокированному пользователю'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (textEditingController.text.isNotEmpty) {
|
||||
await sl<ChatRepository>().sendMessage(
|
||||
data: textEditingController.text.trim(),
|
||||
slug: widget.author.authorSlug,
|
||||
);
|
||||
setState(() {
|
||||
textEditingController.clear();
|
||||
});
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
textEditingController.clear();
|
||||
});
|
||||
}
|
||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
if (context.mounted) {
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Ink(
|
||||
@@ -175,20 +201,44 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
||||
CupertinoActionSheetAction(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
// Проверяем, заблокирован ли пользователь
|
||||
final blockedUsersCubit = sl<BlockedUsersCubit>();
|
||||
await blockedUsersCubit.getBlockedUsers();
|
||||
final blockedState = blockedUsersCubit.state;
|
||||
|
||||
if (blockedState is BlockedUsersStateLoaded) {
|
||||
final isBlocked = blockedState.profilesList.any((profile) => profile.slug == widget.author.authorSlug);
|
||||
if (isBlocked) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Вы не можете отправить сообщение заблокированному пользователю'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final picker = ImagePicker();
|
||||
XFile? image = await picker.pickImage(source: ImageSource.camera);
|
||||
if (image != null) {
|
||||
List<XFile>? images = await picker.pickMultiImage();
|
||||
if (images.isNotEmpty) {
|
||||
List<File> files = images.map((image) => File(image.path)).toList();
|
||||
await sl<ChatRepository>().sendMessage(
|
||||
files: [File(image.path)],
|
||||
files: files,
|
||||
slug: widget.author.authorSlug,
|
||||
);
|
||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
if (mounted) {
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Row(
|
||||
@@ -211,12 +261,14 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
||||
slug: widget.author.authorSlug,
|
||||
);
|
||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
if (mounted) {
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Row(
|
||||
@@ -261,12 +313,14 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
||||
slug: widget.author.authorSlug,
|
||||
);
|
||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
if (mounted) {
|
||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||
'chatScreen',
|
||||
extra: {
|
||||
'uuid': chatIdOrError,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Row(
|
||||
|
||||
@@ -538,6 +538,297 @@ class EntityCommented implements BaseNotificationModel {
|
||||
}
|
||||
}
|
||||
|
||||
class PlanSubscriptionExpiredNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String planName;
|
||||
final String customerName;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
PlanSubscriptionExpiredNotification({
|
||||
required this.type,
|
||||
required this.planName,
|
||||
required this.customerName,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory PlanSubscriptionExpiredNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return PlanSubscriptionExpiredNotification(
|
||||
type: type,
|
||||
planName: json['plan_name'],
|
||||
customerName: json['user_name'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PlanSubscriptionRenewedNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String planName;
|
||||
final String customerName;
|
||||
final String renewalDate;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
PlanSubscriptionRenewedNotification({
|
||||
required this.type,
|
||||
required this.planName,
|
||||
required this.customerName,
|
||||
required this.renewalDate,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory PlanSubscriptionRenewedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return PlanSubscriptionRenewedNotification(
|
||||
type: type,
|
||||
planName: json['plan_name'],
|
||||
customerName: json['user_name'],
|
||||
renewalDate: json['renewal_date'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConsultantAssignedNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String consultantName;
|
||||
final String planName;
|
||||
final int planId;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
ConsultantAssignedNotification({
|
||||
required this.type,
|
||||
required this.consultantName,
|
||||
required this.planName,
|
||||
required this.planId,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory ConsultantAssignedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return ConsultantAssignedNotification(
|
||||
type: type,
|
||||
consultantName: json['consultant_name'],
|
||||
planName: json['plan_name'],
|
||||
planId: json['plan_id'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConsultantUnassignedNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String consultantName;
|
||||
final String planName;
|
||||
final int planId;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
ConsultantUnassignedNotification({
|
||||
required this.type,
|
||||
required this.consultantName,
|
||||
required this.planName,
|
||||
required this.planId,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory ConsultantUnassignedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return ConsultantUnassignedNotification(
|
||||
type: type,
|
||||
consultantName: json['consultant_name'],
|
||||
planName: json['plan_name'],
|
||||
planId: json['plan_id'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PaymentFailedNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String planName;
|
||||
final String errorMessage;
|
||||
final String paymentMethod;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
PaymentFailedNotification({
|
||||
required this.type,
|
||||
required this.planName,
|
||||
required this.errorMessage,
|
||||
required this.paymentMethod,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory PaymentFailedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return PaymentFailedNotification(
|
||||
type: type,
|
||||
planName: json['plan_name'],
|
||||
errorMessage: json['error_message'],
|
||||
paymentMethod: json['payment_method'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PaymentSuccessfulNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String planName;
|
||||
final double amount;
|
||||
final String currency;
|
||||
final String transactionId;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
PaymentSuccessfulNotification({
|
||||
required this.type,
|
||||
required this.planName,
|
||||
required this.amount,
|
||||
required this.currency,
|
||||
required this.transactionId,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory PaymentSuccessfulNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return PaymentSuccessfulNotification(
|
||||
type: type,
|
||||
planName: json['plan_name'],
|
||||
amount: (json['amount'] as num).toDouble(),
|
||||
currency: json['currency'],
|
||||
transactionId: json['transaction_id'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GoalAchievedNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String goalName;
|
||||
final String goalType;
|
||||
final double achievedValue;
|
||||
final double targetValue;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
GoalAchievedNotification({
|
||||
required this.type,
|
||||
required this.goalName,
|
||||
required this.goalType,
|
||||
required this.achievedValue,
|
||||
required this.targetValue,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory GoalAchievedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return GoalAchievedNotification(
|
||||
type: type,
|
||||
goalName: json['goal_name'],
|
||||
goalType: json['goal_type'],
|
||||
achievedValue: (json['achieved_value'] as num).toDouble(),
|
||||
targetValue: (json['target_value'] as num).toDouble(),
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WorkoutReminderNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String workoutName;
|
||||
final String workoutType;
|
||||
final int duration;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
WorkoutReminderNotification({
|
||||
required this.type,
|
||||
required this.workoutName,
|
||||
required this.workoutType,
|
||||
required this.duration,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory WorkoutReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return WorkoutReminderNotification(
|
||||
type: type,
|
||||
workoutName: json['workout_name'],
|
||||
workoutType: json['workout_type'],
|
||||
duration: json['duration'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MealReminderNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@override
|
||||
final bool isRead;
|
||||
final String mealName;
|
||||
final String mealType;
|
||||
final int calories;
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
MealReminderNotification({
|
||||
required this.type,
|
||||
required this.mealName,
|
||||
required this.mealType,
|
||||
required this.calories,
|
||||
required this.isRead,
|
||||
required this.createdAt,
|
||||
});
|
||||
|
||||
factory MealReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||
return MealReminderNotification(
|
||||
type: type,
|
||||
mealName: json['meal_name'],
|
||||
mealType: json['meal_type'],
|
||||
calories: json['calories'],
|
||||
isRead: isRead ?? false,
|
||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownNotification implements BaseNotificationModel {
|
||||
@override
|
||||
final NotificationType type;
|
||||
@@ -570,6 +861,16 @@ enum NotificationType {
|
||||
commentReplied,
|
||||
scoreAdded,
|
||||
entityCommented,
|
||||
// Дополнительные типы уведомлений
|
||||
planSubscriptionExpired,
|
||||
planSubscriptionRenewed,
|
||||
consultantAssigned,
|
||||
consultantUnassigned,
|
||||
paymentFailed,
|
||||
paymentSuccessful,
|
||||
goalAchieved,
|
||||
workoutReminder,
|
||||
mealReminder,
|
||||
unknown;
|
||||
|
||||
static NotificationType getTypeFromString(String type) => switch (type) {
|
||||
@@ -593,6 +894,16 @@ enum NotificationType {
|
||||
'App\\Notifications\\Comment\\Replied' => NotificationType.commentReplied,
|
||||
'App\\Notifications\\Score\\ScoreAdded' => NotificationType.scoreAdded,
|
||||
'App\\Notifications\\Comment\\EntityCommented' => NotificationType.entityCommented,
|
||||
// Дополнительные маппинги
|
||||
'App\\Notifications\\Plan\\PlanSubscriptionExpired' => NotificationType.planSubscriptionExpired,
|
||||
'App\\Notifications\\Plan\\PlanSubscriptionRenewed' => NotificationType.planSubscriptionRenewed,
|
||||
'App\\Notifications\\Plan\\ConsultantAssigned' => NotificationType.consultantAssigned,
|
||||
'App\\Notifications\\Plan\\ConsultantUnassigned' => NotificationType.consultantUnassigned,
|
||||
'App\\Notifications\\Payment\\PaymentFailed' => NotificationType.paymentFailed,
|
||||
'App\\Notifications\\Payment\\PaymentSuccessful' => NotificationType.paymentSuccessful,
|
||||
'App\\Notifications\\Goal\\GoalAchieved' => NotificationType.goalAchieved,
|
||||
'App\\Notifications\\Workout\\WorkoutReminder' => NotificationType.workoutReminder,
|
||||
'App\\Notifications\\Meal\\MealReminder' => NotificationType.mealReminder,
|
||||
_ => NotificationType.unknown,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -113,6 +113,16 @@ class NotificationsCubit extends Cubit<NotificationsState> {
|
||||
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),
|
||||
NotificationType.unknown => UnknownNotification(type: type),
|
||||
};
|
||||
} catch (e) {
|
||||
|
||||
@@ -47,6 +47,16 @@ class NotificationWidget extends StatelessWidget {
|
||||
CommentReplied() => _CommentRepliedWidget(notification: notification as CommentReplied),
|
||||
ScoreAdded() => _ScoreAddedWidget(notification: notification as ScoreAdded),
|
||||
EntityCommented() => _EntityCommentedWidget(notification: notification as EntityCommented),
|
||||
// Новые типы уведомлений
|
||||
PlanSubscriptionExpiredNotification() => _PlanSubscriptionExpiredWidget(notification: notification as PlanSubscriptionExpiredNotification),
|
||||
PlanSubscriptionRenewedNotification() => _PlanSubscriptionRenewedWidget(notification: notification as PlanSubscriptionRenewedNotification),
|
||||
ConsultantAssignedNotification() => _ConsultantAssignedWidget(notification: notification as ConsultantAssignedNotification),
|
||||
ConsultantUnassignedNotification() => _ConsultantUnassignedWidget(notification: notification as ConsultantUnassignedNotification),
|
||||
PaymentFailedNotification() => _PaymentFailedWidget(notification: notification as PaymentFailedNotification),
|
||||
PaymentSuccessfulNotification() => _PaymentSuccessfulWidget(notification: notification as PaymentSuccessfulNotification),
|
||||
GoalAchievedNotification() => _GoalAchievedWidget(notification: notification as GoalAchievedNotification),
|
||||
WorkoutReminderNotification() => _WorkoutReminderWidget(notification: notification as WorkoutReminderNotification),
|
||||
MealReminderNotification() => _MealReminderWidget(notification: notification as MealReminderNotification),
|
||||
UnknownNotification() => _UnknownNotificationWidget(notification: notification as UnknownNotification),
|
||||
};
|
||||
}
|
||||
@@ -443,3 +453,121 @@ class _UnknownNotificationWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Новые виджеты для дополнительных типов уведомлений
|
||||
class _PlanSubscriptionExpiredWidget extends StatelessWidget {
|
||||
const _PlanSubscriptionExpiredWidget({required this.notification});
|
||||
final PlanSubscriptionExpiredNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Подписка на план "${notification.planName}" истекла',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PlanSubscriptionRenewedWidget extends StatelessWidget {
|
||||
const _PlanSubscriptionRenewedWidget({required this.notification});
|
||||
final PlanSubscriptionRenewedNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Подписка на план "${notification.planName}" продлена до ${notification.renewalDate}',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ConsultantAssignedWidget extends StatelessWidget {
|
||||
const _ConsultantAssignedWidget({required this.notification});
|
||||
final ConsultantAssignedNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'К плану "${notification.planName}" назначен консультант ${notification.consultantName}',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ConsultantUnassignedWidget extends StatelessWidget {
|
||||
const _ConsultantUnassignedWidget({required this.notification});
|
||||
final ConsultantUnassignedNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Консультант ${notification.consultantName} отстранен от плана "${notification.planName}"',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaymentFailedWidget extends StatelessWidget {
|
||||
const _PaymentFailedWidget({required this.notification});
|
||||
final PaymentFailedNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Ошибка оплаты плана "${notification.planName}": ${notification.errorMessage}',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaymentSuccessfulWidget extends StatelessWidget {
|
||||
const _PaymentSuccessfulWidget({required this.notification});
|
||||
final PaymentSuccessfulNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Успешная оплата плана "${notification.planName}" на сумму ${notification.amount} ${notification.currency}',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GoalAchievedWidget extends StatelessWidget {
|
||||
const _GoalAchievedWidget({required this.notification});
|
||||
final GoalAchievedNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Достигнута цель "${notification.goalName}": ${notification.achievedValue}/${notification.targetValue}',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _WorkoutReminderWidget extends StatelessWidget {
|
||||
const _WorkoutReminderWidget({required this.notification});
|
||||
final WorkoutReminderNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Напоминание о тренировке "${notification.workoutName}" (${notification.duration} мин)',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MealReminderWidget extends StatelessWidget {
|
||||
const _MealReminderWidget({required this.notification});
|
||||
final MealReminderNotification notification;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _BaseNotificationWidget(
|
||||
isRead: notification.isRead,
|
||||
notificationsText: 'Напоминание о приеме пищи "${notification.mealName}" (${notification.calories} ккал)',
|
||||
createdAt: notification.createdAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,61 @@ class NotificationsService {
|
||||
NotificationType.commentReplied => CommentReplied.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
||||
NotificationType.scoreAdded => ScoreAdded.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
||||
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
||||
// Новые типы уведомлений
|
||||
NotificationType.planSubscriptionExpired => PlanSubscriptionExpiredNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.planSubscriptionRenewed => PlanSubscriptionRenewedNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.consultantAssigned => ConsultantAssignedNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.consultantUnassigned => ConsultantUnassignedNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.paymentFailed => PaymentFailedNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.paymentSuccessful => PaymentSuccessfulNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.goalAchieved => GoalAchievedNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.workoutReminder => WorkoutReminderNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.mealReminder => MealReminderNotification.fromJson(
|
||||
notificationJson['data'],
|
||||
type,
|
||||
isRead: readDate != null,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
NotificationType.unknown => UnknownNotification(type: type, createdAt: parseDate(createdAt)),
|
||||
};
|
||||
} catch (e) {
|
||||
|
||||
@@ -19,6 +19,7 @@ abstract class ProfileModel with _$ProfileModel {
|
||||
@JsonKey(name: "updated_at") DateTime? updatedAt,
|
||||
@JsonKey(name: "is_public_profile", fromJson: parseBool) required bool isPublicProfile,
|
||||
@JsonKey(name: "is_blocked_by_me", fromJson: parseBool) required bool isBlockedByMe,
|
||||
@JsonKey(name: "is_blocked_by_user", fromJson: parseBool, defaultValue: false) required bool isBlockedByUser,
|
||||
@JsonKey(name: 'is_entity_subscribed_by_me') required bool isEntitySubscribedByMe,
|
||||
@JsonKey(name: "slug") required String slug,
|
||||
@JsonKey(name: "profile_photo_url") required String profilePhotoUrl,
|
||||
|
||||
@@ -93,6 +93,39 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
}
|
||||
isFirstFetch = false;
|
||||
}
|
||||
|
||||
// Проверяем, заблокировал ли пользователь меня
|
||||
if (state.profile.isBlockedByUser) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.block,
|
||||
size: 64,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Пользователь ограничил доступ к своей странице',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return widget.author == null || widget.author!.authorIsMe ? _myProfileScreen(state) : _profileScreen(state);
|
||||
} else if (state is ProfileStateError) {
|
||||
return Scaffold(
|
||||
@@ -174,10 +207,10 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
),
|
||||
_subscriptionsAndSubscribers(state),
|
||||
if (state.profile.badges.isNotEmpty) Padding(padding: const EdgeInsets.only(bottom: 16), child: _badges(state)),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null) _socialLinks(state),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null) _education(state),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null) _diplomasAndCertificates(state),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !hidePlans) _activePlans(state),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !state.profile.isBlockedByMe) _socialLinks(state),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !state.profile.isBlockedByMe) _education(state),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !state.profile.isBlockedByMe) _diplomasAndCertificates(state),
|
||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !hidePlans && !state.profile.isBlockedByMe) _activePlans(state),
|
||||
_blogPosts(state),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -17,6 +17,7 @@ void main() {
|
||||
(_) async => await Future.value(
|
||||
const ProfileModel(
|
||||
id: 0,
|
||||
isBlockedByUser: false,
|
||||
name: 'Alexander Avanesyan',
|
||||
email: 'mso7nueh@mail.ru',
|
||||
phoneConfirmationState: '',
|
||||
|
||||
Reference in New Issue
Block a user