import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:intl/intl.dart'; import '/core/auth_provider.dart'; import '/models/message.dart'; import '/models/profile.dart'; const _bg = Color(0xFF0D1117); const _card = Color(0xFF161B22); const _blue = Color(0xFF4FC3F7); class ChatScreen extends ConsumerStatefulWidget { final String toUserId; // profiles.id (não auth uid) const ChatScreen({super.key, required this.toUserId}); @override ConsumerState createState() => _ChatScreenState(); } class _ChatScreenState extends ConsumerState { final _msgCtrl = TextEditingController(); final _scrollCtrl = ScrollController(); String? _myProfileId; Profile? _otherProfile; bool _loadingProfile = true; @override void initState() { super.initState(); _loadProfiles(); } @override void dispose() { _msgCtrl.dispose(); _scrollCtrl.dispose(); super.dispose(); } Future _loadProfiles() async { final sb = Supabase.instance.client; final uid = sb.auth.currentUser?.id; if (uid == null) return; // Buscar o meu profile id final me = await sb.from('profiles').select('id').eq('user_id', uid).maybeSingle(); // Buscar perfil do outro final other = await sb.from('profiles').select().eq('id', widget.toUserId).maybeSingle(); if (mounted) { setState(() { _myProfileId = me?['id'] as String?; _otherProfile = other != null ? Profile.fromMap(other) : null; _loadingProfile = false; }); } _scrollToBottom(); } Future _send() async { final text = _msgCtrl.text.trim(); if (text.isEmpty || _myProfileId == null) return; final sb = Supabase.instance.client; _msgCtrl.clear(); try { await sb.from('messages').insert({ 'from_user': _myProfileId, 'to_user': widget.toUserId, 'content': text, 'is_read': false, }); _scrollToBottom(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('Erro ao enviar: $e'), backgroundColor: Colors.red, behavior: SnackBarBehavior.floating)); } } } void _scrollToBottom() { WidgetsBinding.instance.addPostFrameCallback((_) { if (_scrollCtrl.hasClients) { _scrollCtrl.animateTo(_scrollCtrl.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut); } }); } @override Widget build(BuildContext context) { if (_loadingProfile) { return const Scaffold(backgroundColor: _bg, body: Center(child: CircularProgressIndicator(color: _blue))); } if (_myProfileId == null) { return const Scaffold(backgroundColor: _bg, body: Center(child: Text('Perfil não encontrado.', style: TextStyle(color: Colors.white)))); } final sb = Supabase.instance.client; final myId = _myProfileId!; return Scaffold( backgroundColor: _bg, appBar: AppBar( backgroundColor: _card, elevation: 0, title: Row(children: [ CircleAvatar( radius: 18, backgroundColor: _blue.withOpacity(0.15), backgroundImage: _otherProfile?.avatarUrl != null ? NetworkImage(_otherProfile!.avatarUrl!) : null, child: _otherProfile?.avatarUrl == null ? Text((_otherProfile?.fullName ?? '?')[0].toUpperCase(), style: const TextStyle(color: _blue, fontWeight: FontWeight.bold)) : null, ), const SizedBox(width: 10), Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(_otherProfile?.fullName ?? 'Utilizador', style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold)), if (_otherProfile?.role != null) Text(_otherProfile!.role, style: const TextStyle(color: Color(0xFF888888), fontSize: 11)), ]), ]), ), body: Column(children: [ Expanded( child: StreamBuilder>>( stream: sb.from('messages').stream(primaryKey: ['id']).order('created_at'), builder: (context, snapshot) { if (snapshot.hasError) { return Center(child: Text('Erro: ${snapshot.error}', style: const TextStyle(color: Colors.red))); } if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator(color: _blue)); } final msgs = snapshot.data! .where((m) => (m['from_user'] == myId && m['to_user'] == widget.toUserId) || (m['from_user'] == widget.toUserId && m['to_user'] == myId)) .map(Message.fromMap) .toList(); if (msgs.isEmpty) { return Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.chat_bubble_outline, color: Colors.white.withOpacity(0.15), size: 60), const SizedBox(height: 12), Text('Começa a conversa!', style: TextStyle(color: Colors.white.withOpacity(0.3), fontSize: 14)), ])); } WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom()); return ListView.builder( controller: _scrollCtrl, padding: const EdgeInsets.all(16), itemCount: msgs.length, itemBuilder: (_, i) { final msg = msgs[i]; final isMe = msg.fromUser == myId; return _Bubble(msg: msg, isMe: isMe); }, ); }, ), ), // Input Container( color: _card, padding: EdgeInsets.only( left: 12, right: 12, top: 10, bottom: MediaQuery.of(context).viewInsets.bottom + 10, ), child: Row(children: [ Expanded( child: TextField( controller: _msgCtrl, style: const TextStyle(color: Colors.white, fontSize: 14), maxLines: 4, minLines: 1, textInputAction: TextInputAction.send, onSubmitted: (_) => _send(), decoration: InputDecoration( hintText: 'Escreve uma mensagem...', hintStyle: TextStyle(color: Colors.white.withOpacity(0.3), fontSize: 13), filled: true, fillColor: Colors.white.withOpacity(0.05), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), border: OutlineInputBorder(borderRadius: BorderRadius.circular(20), borderSide: BorderSide.none), ), ), ), const SizedBox(width: 8), GestureDetector( onTap: _send, child: Container( width: 44, height: 44, decoration: BoxDecoration( gradient: const LinearGradient(colors: [_blue, Color(0xFF0288D1)]), shape: BoxShape.circle, boxShadow: [BoxShadow(color: _blue.withOpacity(0.3), blurRadius: 10)], ), child: const Icon(Icons.send_rounded, color: Colors.white, size: 20), ), ), ]), ), ]), ); } } class _Bubble extends StatelessWidget { final Message msg; final bool isMe; const _Bubble({required this.msg, required this.isMe}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( mainAxisAlignment: isMe ? MainAxisAlignment.end : MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ if (!isMe) ...[ CircleAvatar(radius: 14, backgroundColor: _blue.withOpacity(0.15), child: const Icon(Icons.person, color: _blue, size: 14)), const SizedBox(width: 6), ], Flexible( child: Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( color: isMe ? const Color(0xFF1A4A6A) : _card, borderRadius: BorderRadius.only( topLeft: const Radius.circular(16), topRight: const Radius.circular(16), bottomLeft: Radius.circular(isMe ? 16 : 4), bottomRight: Radius.circular(isMe ? 4 : 16), ), border: Border.all( color: isMe ? _blue.withOpacity(0.3) : Colors.white.withOpacity(0.07)), ), child: Column(crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(msg.content, style: const TextStyle(color: Colors.white, fontSize: 14, height: 1.4)), const SizedBox(height: 4), Text( DateFormat('HH:mm').format(msg.createdAt.toLocal()), style: TextStyle(color: Colors.white.withOpacity(0.35), fontSize: 10), ), ]), ), ), if (isMe) const SizedBox(width: 4), ], ), ); } }