import 'package:easy_refresh/easy_refresh.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../core/theme/app_colors.dart'; import '../../shared/widgets/empty_state.dart'; import '../shell/nav_bar_config.dart'; import '../../core/i18n/app_localizations.dart'; import '../../shared/widgets/loading_widget.dart'; import '../../shared/widgets/message_item.dart'; import 'message_controller.dart'; import 'message_model.dart'; class MessageListPage extends ConsumerWidget { const MessageListPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final messagesAsync = ref.watch(messageListProvider); final location = GoRouterState.of(context).uri.toString(); final l10n = AppLocalizations.of(context); if (location.startsWith('/messages')) { ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('tabMessages'), showBack: true, leadingIcon: Icons.close, ), ); } return EasyRefresh( header: TDRefreshHeader(), onRefresh: () async { ref.invalidate(messageListProvider); await ref.read(messageListProvider.future); }, child: messagesAsync.when( loading: () => const SingleChildScrollView( physics: AlwaysScrollableScrollPhysics(), child: LoadingWidget(), ), error: (_, _) => SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: EmptyState(message: l10n.get('loadFailed')), ), data: (messages) => messages.isEmpty ? SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: EmptyState(message: l10n.get('noMessages')), ) : ListView.separated( padding: const EdgeInsets.fromLTRB(16, 16, 16, 24), itemCount: messages.length, separatorBuilder: (_, _) => const SizedBox(height: 12), itemBuilder: (_, i) => _buildItem(context, messages[i]), ), ), ); } Widget _buildItem(BuildContext context, MessageModel msg) { final l10n = AppLocalizations.of(context); final (icon, iconColor, iconBg) = _messageIconProps(msg.msgType); final sender = _messageSender(msg.msgType, l10n); return MessageItem( icon: icon, iconColor: iconColor, iconBackground: iconBg, title: msg.title, time: _formatTime(msg.createTime, l10n), sender: sender, summary: msg.content, unread: !msg.isRead, onTap: () => _navigateToBiz(context, msg), onPin: () { TDMessage.showMessage( context: context, content: '${l10n.get("pinnedToast")}${msg.title}', theme: MessageTheme.info, icon: true, duration: 3000, ); }, onToggleRead: () { final action = msg.isRead ? l10n.get('markUnread') : l10n.get('markRead'); TDMessage.showMessage( context: context, content: '$action:${msg.title}', theme: MessageTheme.success, icon: true, duration: 3000, ); }, onDelete: () { TDMessage.showMessage( context: context, content: '${l10n.get("deletedToast")}${msg.title}', theme: MessageTheme.warning, icon: true, duration: 3000, ); }, ); } (IconData, Color, Color) _messageIconProps(String msgType) { switch (msgType) { case 'announcement': return (Icons.campaign, AppColors.warning, AppColors.warningBg); case 'approval_result': return ( Icons.check_circle_outline, AppColors.success, AppColors.successBg, ); case 'approval_notice': default: return ( Icons.assignment_outlined, AppColors.primary, AppColors.primaryLight, ); } } String _messageSender(String msgType, AppLocalizations l10n) { switch (msgType) { case 'announcement': return l10n.get('systemNotice'); case 'approval_notice': return l10n.get('approvalNotice'); case 'approval_result': return l10n.get('systemMessage'); default: return l10n.get('systemMessage'); } } String _formatTime(DateTime time, AppLocalizations l10n) { final now = DateTime.now(); final diff = now.difference(time); if (diff.inMinutes < 60) return '${diff.inMinutes}${l10n.get("minutesAgo")}'; if (diff.inHours < 24) return '${diff.inHours}${l10n.get("hoursAgo")}'; if (diff.inDays < 7) return '${diff.inDays}${l10n.get("daysAgo")}'; return '${time.month}/${time.day}'; } void _navigateToBiz(BuildContext context, MessageModel msg) { if (msg.bizType == null || msg.bizId == null) return; switch (msg.bizType) { case 'expense': context.push('/expense/detail/${msg.bizId}'); break; case 'overtime': context.push('/overtime/detail/${msg.bizId}'); break; case 'vehicle': context.push('/vehicle/detail/${msg.bizId}'); break; case 'announcement': context.push('/announcement/detail/${msg.bizId}'); break; case 'expense_application': context.push('/expense-apply/detail/${msg.bizId}'); break; } } }