import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '/core/constants.dart'; import '/data/models/message_model.dart'; import '/data/models/contact_model.dart'; import '/logic/contact_provider.dart'; import '/domain/services/api_service.dart'; class ForwardContactPickerScreen extends StatefulWidget { final MessageModel message; const ForwardContactPickerScreen({super.key, required this.message}); @override State createState() => _ForwardContactPickerScreenState(); } class _ForwardContactPickerScreenState extends State { Contact? _selectedContact; bool _isInitLoading = true; SharedPreferences? _prefs; String? token; @override void initState() { super.initState(); _loadActiveChats(); } Future _loadActiveChats() async { try { final contactProvider = context.read(); await contactProvider.loadContacts(); final apiService = ApiService(); final accessToken = await apiService.getAccessToken(); final shared = await SharedPreferences.getInstance(); if (mounted) { setState(() { _prefs = shared; token = accessToken; }); } } catch (e) { debugPrint("Ошибка при загрузке данных для пересылки: $e"); } finally { if (mounted) { setState(() { _isInitLoading = false; }); } } } String _getDisplayName(Contact contact) { if (_prefs == null) return contact.name; final id = contact.id; final savedName = _prefs!.getString('firstname_$id'); if (savedName != null && savedName.isNotEmpty) { return savedName; } return contact.name; } String _formatTime(DateTime? time) { if (time == null) return ''; final localTime = time.toLocal(); final hour = localTime.hour.toString().padLeft(2, '0'); final minute = localTime.minute.toString().padLeft(2, '0'); return '$hour:$minute'; } @override Widget build(BuildContext context) { final contactProvider = context.watch(); final contacts = contactProvider.contacts; final isLoading = _isInitLoading || contactProvider.isLoading; final primaryColor = Theme.of(context).colorScheme.primary; return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_rounded), onPressed: () => Navigator.of(context).pop(), ), title: const Text( 'Переслать...', style: TextStyle(fontWeight: FontWeight.w600), ), actions: [ AnimatedOpacity( duration: const Duration(milliseconds: 200), opacity: _selectedContact != null ? 1.0 : 0.4, child: TextButton( onPressed: _selectedContact != null ? () => Navigator.of(context).pop(_selectedContact) : null, child: const Text( 'Продолжить', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ), const SizedBox(width: 8), ], ), body: () { if (isLoading) { return const Center(child: CircularProgressIndicator()); } if (contactProvider.error != null) { return Center( child: Padding( padding: const EdgeInsets.all(24.0), child: Text( 'Ошибка: ${contactProvider.error}', textAlign: TextAlign.center, style: const TextStyle(color: Colors.grey), ), ), ); } if (contacts.isEmpty) { return const Center( child: Text( 'Нет активных чатов для пересылки.', style: TextStyle(color: Colors.grey, fontSize: 15), ), ); } return ListView.builder( itemCount: contacts.length, itemBuilder: (context, index) { final contact = contacts[index]; final isSelected = _selectedContact?.id == contact.id; // Логика формирования текста сообщения (1-в-1 как в твоем ContactTile) final bool isDecrypted = contact.isLastMsgDecrypted ?? false; final String subtitleText = isDecrypted ? (contact.lastMessage == null ? "Нет сообщений" : "${contact.lastMessageType != null ? MessageModel.getMediaPreview(contact.lastMessageType!) : ''} ${contact.lastMessage}" .trim()) : (contact.lastMessage != null ? "Ожидание дешифровки..." : "Нет сообщений"); // Логика формирования URL аватарки final avatarUrl = contact.effectiveAvatarUrl; final bool hasAvatar = avatarUrl != null && avatarUrl.isNotEmpty; return InkWell( onTap: () { setState(() { if (isSelected) { _selectedContact = null; } else { _selectedContact = contact; } }); }, child: Container( color: isSelected ? primaryColor.withOpacity(0.08) : Colors.transparent, child: ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 4, ), // 1. АВАТАРКА leading: Stack( children: [ if (hasAvatar) CircleAvatar( radius: 24, backgroundColor: Colors.grey[200], child: ClipOval( child: ClipOval( child: Image.network( avatarUrl, // Первым аргументом идет строка, без "imageUrl:" width: 48, height: 48, fit: BoxFit.cover, headers: token != null ? {'Authorization': 'Bearer $token'} : null, // Заменено на headers // Аналог placeholder loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return const SizedBox( width: 48, height: 48, child: Center( child: CircularProgressIndicator( strokeWidth: 2, ), ), ); }, // Аналог errorWidget errorBuilder: (context, error, stackTrace) { return CircleAvatar( radius: 24, // 24 радиус = 48 ширина/высота backgroundColor: primaryColor.withOpacity( 0.1, ), child: Text( _getDisplayName(contact).isNotEmpty ? _getDisplayName( contact, )[0].toUpperCase() : '?', style: TextStyle( color: primaryColor, fontWeight: FontWeight.bold, ), ), ); }, ), ), ), ) else CircleAvatar( radius: 24, backgroundColor: primaryColor.withOpacity(0.1), child: Text( _getDisplayName(contact).isNotEmpty ? _getDisplayName(contact)[0].toUpperCase() : '?', style: TextStyle( color: primaryColor, fontWeight: FontWeight.bold, ), ), ), if (contact.isOnline == true) Positioned( right: 0, bottom: 0, child: Container( width: 12, height: 12, decoration: BoxDecoration( color: Colors.green, shape: BoxShape.circle, border: Border.all( color: Theme.of( context, ).scaffoldBackgroundColor, width: 2, ), ), ), ), ], ), // 2. ИМЯ title: Text( _getDisplayName(contact), maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 16, ), ), // 3. ПОСЛЕДНЕЕ СООБЩЕНИЕ subtitle: Text( subtitleText, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(color: Colors.grey), ), // 4. ПРАВАЯ ЧАСТЬ (Анимация переключения Время <-> Галочка) trailing: AnimatedSwitcher( duration: const Duration(milliseconds: 200), transitionBuilder: (Widget child, Animation animation) { return ScaleTransition( scale: animation, child: child, ); }, child: isSelected ? Container( key: const ValueKey('checkmark'), width: 24, height: 24, decoration: BoxDecoration( color: primaryColor, shape: BoxShape.circle, ), child: const Icon( Icons.check_rounded, color: Colors.white, size: 16, ), ) : Column( key: const ValueKey('time_and_badge'), mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ Text( _formatTime(contact.lastMessageTime), style: const TextStyle( color: Colors.grey, fontSize: 12, ), ), if (contact.unreadCount > 0) ...[ const SizedBox(height: 4), Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: primaryColor.withAlpha( (0.5 * 255).round(), ), shape: BoxShape.circle, ), child: Text( '${contact.unreadCount}', style: const TextStyle( color: Colors.white, fontSize: 10, ), ), ), ], ], ), ), ), ), ); }, ); }(), ); } }