import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:matrix/matrix.dart'; import 'package:pyramid/core/theme.dart'; import 'package:pyramid/features/chat/chat_provider.dart'; class PinnedMessagesPanel extends ConsumerWidget { final String roomId; final VoidCallback onClose; const PinnedMessagesPanel({ super.key, required this.roomId, required this.onClose, }); @override Widget build(BuildContext context, WidgetRef ref) { final pt = PyramidTheme.of(context); final room = ref.watch(roomProvider(roomId)); final timeline = ref.watch(timelineProvider(roomId)).valueOrNull; if (room == null) return const SizedBox.shrink(); final pinnedIds = List.from( room.getState(EventTypes.RoomPinnedEvents)?.content['pinned'] as List? ?? [], ); final pinned = pinnedIds .map((id) => timeline?.events.where((e) => e.eventId == id).firstOrNull) .where((e) => e != null) .cast() .toList(); return Container( width: 320, decoration: BoxDecoration( color: pt.bg1, border: Border(left: BorderSide(color: pt.border)), ), child: Column( children: [ // Header Container( height: 52, padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( border: Border(bottom: BorderSide(color: pt.border)), ), child: Row( children: [ Icon(Icons.push_pin_rounded, size: 16, color: pt.fgMuted), const SizedBox(width: 8), Expanded( child: Text( 'Angepinnte Nachrichten', style: TextStyle( color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600, ), ), ), GestureDetector( onTap: onClose, child: Icon(Icons.close_rounded, size: 18, color: pt.fgDim), ), ], ), ), // List Expanded( child: pinnedIds.isEmpty ? Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.push_pin_outlined, size: 40, color: pt.fgDim), const SizedBox(height: 12), Text( 'Keine angepinnten Nachrichten', style: TextStyle(color: pt.fgMuted, fontSize: 14), ), ], ), ) : ListView.builder( padding: const EdgeInsets.all(12), itemCount: pinned.isEmpty ? pinnedIds.length : pinned.length, itemBuilder: (ctx, i) { if (pinned.isEmpty) { return _PinnedPlaceholder(pt: pt); } return _PinnedMessageCard( event: pinned[i], room: room, pt: pt, onUnpin: () => _unpin(room, pinned[i].eventId), ); }, ), ), ], ), ); } void _unpin(Room room, String eventId) { final pinned = List.from( room.getState(EventTypes.RoomPinnedEvents)?.content['pinned'] as List? ?? [], ); pinned.remove(eventId); room.setPinnedEvents(pinned).catchError((_) => ''); } } class _PinnedPlaceholder extends StatelessWidget { final PyramidTheme pt; const _PinnedPlaceholder({required this.pt}); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: pt.bg2, borderRadius: BorderRadius.circular(pt.rBase), border: Border.all(color: pt.border), ), child: Text( 'Nachricht wird geladen…', style: TextStyle(color: pt.fgDim, fontSize: 13, fontStyle: FontStyle.italic), ), ); } } class _PinnedMessageCard extends StatelessWidget { final Event event; final Room room; final PyramidTheme pt; final VoidCallback onUnpin; const _PinnedMessageCard({ required this.event, required this.room, required this.pt, required this.onUnpin, }); @override Widget build(BuildContext context) { final sender = event.senderFromMemoryOrFallback.calcDisplayname(); final time = _formatTime(event.originServerTs); final body = event.body; return Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: pt.bg2, borderRadius: BorderRadius.circular(pt.rBase), border: Border.all(color: pt.border), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.push_pin_rounded, size: 12, color: pt.accent), const SizedBox(width: 6), Expanded( child: Text( sender, style: TextStyle( color: pt.fg, fontSize: 13, fontWeight: FontWeight.w600, ), ), ), Text(time, style: TextStyle(color: pt.fgDim, fontSize: 11)), const SizedBox(width: 6), GestureDetector( onTap: onUnpin, child: Tooltip( message: 'Entpinnen', child: Icon(Icons.push_pin_outlined, size: 14, color: pt.fgDim), ), ), ], ), const SizedBox(height: 6), Text( body, style: TextStyle(color: pt.fgMuted, fontSize: 13), maxLines: 5, overflow: TextOverflow.ellipsis, ), ], ), ); } String _formatTime(DateTime t) { final now = DateTime.now(); final diff = now.difference(t); if (diff.inDays == 0) { return '${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}'; } if (diff.inDays < 7) return 'vor ${diff.inDays}d'; return '${t.day}.${t.month}.${t.year}'; } }