refactor(messages): use MessageActionSheet for all message types

This commit is contained in:
qzl
2026-03-11 21:04:51 +08:00
parent e99725400f
commit 98f22a2127
@@ -8,11 +8,11 @@ import '../../../../core/theme/design_tokens.dart';
import '../../../../shared/widgets/page_header.dart';
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
import '../../../../shared/widgets/app_button.dart';
import '../../../calendar/data/calendar_api.dart';
import '../../../friends/data/friends_api.dart';
import '../../../users/data/users_api.dart';
import '../../data/inbox_api.dart';
import '../../ui/widgets/calendar_message_card.dart';
import '../../ui/widgets/message_action_sheet.dart';
class MessageWithFriend {
final InboxMessageResponse message;
@@ -33,6 +33,7 @@ class _MessageInviteListScreenState extends State<MessageInviteListScreen> {
late final InboxApi _inboxApi;
late final FriendsApi _friendsApi;
late final CalendarApi _calendarApi;
late final UsersApi _usersApi;
List<MessageWithFriend> _unreadMessages = [];
List<MessageWithFriend> _readMessages = [];
@@ -45,6 +46,7 @@ class _MessageInviteListScreenState extends State<MessageInviteListScreen> {
_inboxApi = sl<InboxApi>();
_friendsApi = sl<FriendsApi>();
_calendarApi = sl<CalendarApi>();
_usersApi = sl<UsersApi>();
_loadMessages();
}
@@ -115,14 +117,15 @@ class _MessageInviteListScreenState extends State<MessageInviteListScreen> {
if (type == 'invite') {
if (message.status.value == 'pending') {
await _showCalendarInviteSheet(message);
} else if (message.status.value == 'accepted') {
if (message.scheduleItemId != null) {
context.push('/calendar/${message.scheduleItemId}');
} else {
await _showCalendarInviteReadOnlySheet(message);
if (message.scheduleItemId != null && context.mounted) {
context.push('/calendar/events/${message.scheduleItemId}');
}
}
} else if (type == 'update') {
if (message.scheduleItemId != null) {
context.push('/calendar/${message.scheduleItemId}');
context.push('/calendar/events/${message.scheduleItemId}');
}
}
return;
@@ -131,11 +134,7 @@ class _MessageInviteListScreenState extends State<MessageInviteListScreen> {
Toast.show(context, '发送者信息加载失败,请下拉重试', type: ToastType.error);
return;
}
if (message.isRead) {
_showFriendRequestReadOnlySheet(item);
} else {
_showFriendRequestActionSheet(item);
}
_showFriendRequestSheet(item, isReadOnly: message.isRead);
return;
case InboxMessageType.system:
case InboxMessageType.group:
@@ -152,116 +151,139 @@ class _MessageInviteListScreenState extends State<MessageInviteListScreen> {
}
}
Future<(String calendarTitle, String senderName)?> _getCalendarInviteInfo(
InboxMessageResponse message,
) async {
if (message.scheduleItemId == null || message.senderId == null) {
return null;
}
try {
final calendar = await _calendarApi.getById(message.scheduleItemId!);
final sender = await _usersApi.getById(message.senderId!);
return (calendar.title, sender.username);
} catch (e) {
return null;
}
}
Future<void> _showCalendarInviteSheet(InboxMessageResponse message) async {
final itemId = message.scheduleItemId;
if (itemId == null) return;
final info = await _getCalendarInviteInfo(message);
final title = info != null ? '${info.$2} 邀请你加入日历' : '日历邀请';
final description = info?.$1;
if (!mounted) return;
showModalBottomSheet<void>(
context: context,
backgroundColor: Colors.transparent,
builder: (ctx) => Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: const BoxDecoration(
color: AppColors.background,
borderRadius: BorderRadius.vertical(
top: Radius.circular(AppRadius.lg),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'日历邀请',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
const SizedBox(height: AppSpacing.lg),
Row(
children: [
Expanded(
child: AppButton(
text: '拒绝',
isOutlined: true,
onPressed: () async {
try {
await _calendarApi.rejectSubscription(itemId);
await _inboxApi.markAsRead(message.id);
if (mounted) {
Navigator.pop(ctx);
Toast.show(context, '已拒绝', type: ToastType.success);
_loadMessages();
}
} catch (e) {
if (mounted) {
Toast.show(context, '操作失败', type: ToastType.error);
}
}
},
),
),
const SizedBox(width: AppSpacing.md),
Expanded(
child: AppButton(
text: '接受',
onPressed: () async {
try {
await _calendarApi.acceptSubscription(itemId);
await _inboxApi.markAsRead(message.id);
if (mounted) {
Navigator.pop(ctx);
Toast.show(context, '已接受', type: ToastType.success);
_loadMessages();
}
} catch (e) {
if (mounted) {
Toast.show(context, '操作失败', type: ToastType.error);
}
}
},
),
),
],
),
const SizedBox(height: AppSpacing.md),
],
),
builder: (ctx) => MessageActionSheet(
title: title,
description: description,
icon: Icons.calendar_today,
iconColor: AppColors.blue500,
onAccept: () async {
try {
await _calendarApi.acceptSubscription(itemId);
await _inboxApi.markAsRead(message.id);
if (mounted) {
Toast.show(context, '已接受', type: ToastType.success);
_loadMessages();
}
} catch (e) {
if (mounted) {
Toast.show(context, '操作失败', type: ToastType.error);
}
}
},
onDecline: () async {
try {
await _calendarApi.rejectSubscription(itemId);
await _inboxApi.markAsRead(message.id);
if (mounted) {
Toast.show(context, '已拒绝', type: ToastType.success);
_loadMessages();
}
} catch (e) {
if (mounted) {
Toast.show(context, '操作失败', type: ToastType.error);
}
}
},
),
);
}
void _showFriendRequestReadOnlySheet(MessageWithFriend item) {
Future<void> _showCalendarInviteReadOnlySheet(
InboxMessageResponse message,
) async {
final itemId = message.scheduleItemId;
if (itemId == null) return;
final info = await _getCalendarInviteInfo(message);
final title = info != null ? '${info.$2} 邀请你加入日历' : '日历邀请';
final description = info?.$1;
final statusText = message.status.value == 'accepted' ? '已接受' : '已拒绝';
if (!mounted) return;
showModalBottomSheet<void>(
context: context,
backgroundColor: Colors.transparent,
builder: (ctx) {
return _FriendRequestSheet(
message: item.message,
friendRequest: item.friendRequest!,
isReadOnly: true,
);
},
builder: (ctx) => MessageActionSheet(
title: title,
description: description,
statusText: statusText,
isReadOnly: true,
icon: Icons.calendar_today,
iconColor: AppColors.blue500,
),
);
}
void _showFriendRequestActionSheet(MessageWithFriend item) {
void _showFriendRequestSheet(
MessageWithFriend item, {
bool isReadOnly = false,
}) {
final message = item.message;
final friendRequest = item.friendRequest;
if (friendRequest == null) return;
final title = '${friendRequest.sender.username} 请求添加您为好友';
final description = message.content;
final statusText = isReadOnly
? (friendRequest.status == 'accepted'
? '已接受'
: friendRequest.status == 'rejected'
? '已拒绝'
: '已处理')
: null;
showModalBottomSheet<void>(
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (ctx) {
return _FriendRequestSheet(
message: item.message,
friendRequest: item.friendRequest!,
isReadOnly: false,
onAccept: () async {
Navigator.pop(ctx);
await _processFriendRequest(item, accept: true);
},
onDecline: () async {
Navigator.pop(ctx);
await _processFriendRequest(item, accept: false);
},
);
},
builder: (ctx) => MessageActionSheet(
title: title,
description: description,
statusText: statusText,
isReadOnly: isReadOnly,
icon: Icons.person_add_outlined,
iconColor: AppColors.emerald500,
onAccept: isReadOnly
? null
: () async {
await _processFriendRequest(item, accept: true);
},
onDecline: isReadOnly
? null
: () async {
await _processFriendRequest(item, accept: false);
},
),
);
}
@@ -601,149 +623,3 @@ class _MessageCard extends StatelessWidget {
return message.content ?? '点击查看详情';
}
}
class _FriendRequestSheet extends StatelessWidget {
final InboxMessageResponse message;
final FriendRequestResponse friendRequest;
final bool isReadOnly;
final VoidCallback? onAccept;
final VoidCallback? onDecline;
const _FriendRequestSheet({
required this.message,
required this.friendRequest,
required this.isReadOnly,
this.onAccept,
this.onDecline,
});
@override
Widget build(BuildContext context) {
final status = friendRequest.status;
final statusText = status == 'accepted'
? '已接受'
: status == 'rejected'
? '已拒绝'
: '已处理';
return Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(24, 20, 24, 0),
decoration: const BoxDecoration(
color: AppColors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: AppColors.slate300,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(height: 20),
Container(
width: 72,
height: 72,
decoration: BoxDecoration(
color: AppColors.emerald500.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.person_add_outlined,
size: 32,
color: AppColors.emerald500,
),
),
const SizedBox(height: 16),
Text(
'${friendRequest.sender.username} 请求添加您为好友',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.slate900,
),
textAlign: TextAlign.center,
),
if ((message.content ?? '').isNotEmpty) ...[
const SizedBox(height: 8),
Text(
'备注: ${message.content}',
style: const TextStyle(fontSize: 14, color: AppColors.slate500),
),
],
if (isReadOnly) ...[
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: AppColors.slate100,
borderRadius: BorderRadius.circular(20),
),
child: Text(
statusText,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.slate600,
),
),
),
] else ...[
const SizedBox(height: 28),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: onDecline,
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
side: const BorderSide(color: AppColors.slate300),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text(
'拒绝',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.slate600,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: onAccept,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.blue500,
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text(
'接受',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.white,
),
),
),
),
],
),
],
SizedBox(height: MediaQuery.of(context).padding.bottom + 12),
],
),
);
}
}