import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:matrix/matrix.dart'; import 'package:pyramid/core/app_state.dart'; import 'package:pyramid/core/matrix_client.dart'; import 'package:pyramid/core/theme.dart'; import 'package:pyramid/features/chat/attachment_dialog.dart'; import 'package:pyramid/widgets/mxc_image.dart'; /// "Teilen nach Pyramid": Raum-Picker für geteilte Inhalte (Text/Dateien). /// Nach Auswahl wird der Raum geöffnet; Dateien laufen durch den /// AttachmentDialog (mit Vorschau), Text wird direkt gesendet. class ShareTargetDialog { static Future show( BuildContext context, WidgetRef ref, PendingShare share, ) async { final client = await ref.read(matrixClientProvider.future); if (!context.mounted) return; final room = await showGeneralDialog( context: context, barrierDismissible: true, barrierLabel: 'Teilen', barrierColor: Colors.black54, transitionDuration: const Duration(milliseconds: 180), pageBuilder: (_, _, _) => _ShareTargetPicker(client: client, share: share), transitionBuilder: (_, anim, _, child) { final curved = CurvedAnimation(parent: anim, curve: Curves.easeOutBack); return FadeTransition( opacity: anim, child: ScaleTransition( scale: Tween(begin: 0.94, end: 1.0).animate(curved), child: child, ), ); }, ); if (room == null || !context.mounted) return; // Zielraum öffnen. ref.read(activeRoomIdProvider.notifier).state = room.id; ref.read(viewModeProvider.notifier).state = ViewMode.chat; var filesSent = false; if (share.paths.isNotEmpty) { final files = share.paths .map(File.new) .where((f) => f.existsSync()) .toList(); if (files.isNotEmpty) { filesSent = await AttachmentDialog.show(context, files, room); for (final f in files) { f.delete().catchError((_) => f); } } } final text = share.text?.trim() ?? ''; if (text.isNotEmpty && (share.paths.isEmpty || filesSent)) { try { await room.sendTextEvent(text); } catch (_) {} } } } class _ShareTargetPicker extends StatefulWidget { final Client client; final PendingShare share; const _ShareTargetPicker({required this.client, required this.share}); @override State<_ShareTargetPicker> createState() => _ShareTargetPickerState(); } class _ShareTargetPickerState extends State<_ShareTargetPicker> { String _filter = ''; List get _rooms { final rooms = widget.client.rooms .where((r) => r.membership == Membership.join && !r.isSpace && (_filter.isEmpty || r .getLocalizedDisplayname() .toLowerCase() .contains(_filter.toLowerCase()))) .toList() // Zuletzt aktive Chats zuerst — wie bei Discord/WhatsApp-Share. ..sort((a, b) => (b.lastEvent?.originServerTs ?? DateTime.fromMillisecondsSinceEpoch(0)) .compareTo(a.lastEvent?.originServerTs ?? DateTime.fromMillisecondsSinceEpoch(0))); return rooms; } @override Widget build(BuildContext context) { final pt = PyramidTheme.of(context); final size = MediaQuery.sizeOf(context); final rooms = _rooms; final share = widget.share; final fileCount = share.paths.length; return Center( child: Material( color: Colors.transparent, child: Container( width: (size.width - 32).clamp(280.0, 440.0), height: (size.height - 80).clamp(320.0, 560.0), decoration: BoxDecoration( color: pt.bg1, borderRadius: BorderRadius.circular(pt.rXl), border: Border.all(color: pt.border), boxShadow: const [BoxShadow(color: Colors.black54, blurRadius: 40)], ), child: Column( children: [ // Header Padding( padding: const EdgeInsets.fromLTRB(20, 16, 12, 4), child: Row( children: [ Icon(Icons.share_rounded, size: 18, color: pt.accent), const SizedBox(width: 10), Expanded( child: Text( 'Teilen nach…', style: TextStyle( color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700), ), ), IconButton( icon: Icon(Icons.close_rounded, size: 18, color: pt.fgDim), onPressed: () => Navigator.of(context).pop(), ), ], ), ), // Vorschau des geteilten Inhalts Padding( padding: const EdgeInsets.fromLTRB(20, 4, 20, 10), child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: pt.bg2, borderRadius: BorderRadius.circular(pt.rBase), border: Border.all(color: pt.border), ), child: Row( children: [ Icon( fileCount > 0 ? Icons.attach_file_rounded : Icons.notes_rounded, size: 14, color: pt.fgMuted, ), const SizedBox(width: 8), Expanded( child: Text( fileCount > 0 ? '$fileCount Datei${fileCount == 1 ? '' : 'en'}' '${(share.text?.isNotEmpty ?? false) ? ' + Text' : ''}' : (share.text ?? ''), style: TextStyle(color: pt.fgMuted, fontSize: 12), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ], ), ), ), // Suche Padding( padding: const EdgeInsets.fromLTRB(20, 0, 20, 8), child: TextField( autofocus: false, onChanged: (v) => setState(() => _filter = v), style: TextStyle(color: pt.fg, fontSize: 13), decoration: InputDecoration( hintText: 'Chat suchen…', hintStyle: TextStyle(color: pt.fgDim, fontSize: 13), prefixIcon: Icon(Icons.search_rounded, size: 16, color: pt.fgDim), isDense: true, filled: true, fillColor: pt.bg2, contentPadding: const EdgeInsets.symmetric(vertical: 8), border: OutlineInputBorder( borderRadius: BorderRadius.circular(pt.rBase), borderSide: BorderSide(color: pt.border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(pt.rBase), borderSide: BorderSide(color: pt.border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(pt.rBase), borderSide: BorderSide(color: pt.accent), ), ), ), ), // Raumliste Expanded( child: rooms.isEmpty ? Center( child: Text('Keine Chats gefunden', style: TextStyle(color: pt.fgDim, fontSize: 13)), ) : ListView.builder( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), itemCount: rooms.length, itemBuilder: (_, i) => _RoomRow(room: rooms[i], pt: pt, client: widget.client), ), ), ], ), ), ), ); } } class _RoomRow extends StatelessWidget { final Room room; final PyramidTheme pt; final Client client; const _RoomRow({required this.room, required this.pt, required this.client}); @override Widget build(BuildContext context) { final name = room.getLocalizedDisplayname(); final initial = name.isNotEmpty ? name[0].toUpperCase() : '?'; return InkWell( borderRadius: BorderRadius.circular(pt.rBase), onTap: () => Navigator.of(context).pop(room), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ if (room.avatar != null) MxcAvatar( mxcUri: room.avatar, client: client, size: 30, borderRadius: BorderRadius.circular(15), placeholder: (_) => _initialCircle(initial), ) else _initialCircle(initial), const SizedBox(width: 12), Expanded( child: Text( name, style: TextStyle( color: pt.fg, fontSize: 13, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis, ), ), if (room.isDirectChat) Icon(Icons.person_outline_rounded, size: 14, color: pt.fgDim) else Icon(Icons.tag_rounded, size: 14, color: pt.fgDim), ], ), ), ); } Widget _initialCircle(String initial) => Container( width: 30, height: 30, decoration: BoxDecoration( color: pt.accent.withAlpha(60), shape: BoxShape.circle, ), child: Center( child: Text( initial, style: TextStyle( color: pt.fg, fontSize: 13, fontWeight: FontWeight.w600), ), ), ); }