import 'package:chat_repository/chat_repository.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:injector/injector.dart'; import 'package:intl/intl.dart'; import 'package:prosappco/blocs/chat_bloc/chat_bloc.dart'; import 'package:prosappco/components/general_reputation.dart'; import 'package:prosappco/local_notifications/local_notifications.dart'; import 'package:service_repository/service_repository.dart'; import 'package:user_repository/user_repository.dart'; class ChatScreen extends StatefulWidget { final ServiceEntity service; const ChatScreen({super.key, required this.service}); @override State createState() => _ChatScreenState(); } class _ChatScreenState extends State { late final ChatBloc chatBloc; final TextEditingController _messageController = TextEditingController(); final FocusNode _messageFocusNode = FocusNode(); List? _userInfo; DateTime? _lastNotificationTime; @override void initState() { super.initState(); chatBloc = Injector.appInstance.get(); chatBloc.add(LoadChatEvent( serviceId: widget.service.id!, userId: widget.service.userId, professionalId: widget.service.professionalId, )); _getUserAndProfessionalInfo(widget.service).then((userInfo) { setState(() { _userInfo = userInfo; }); }); } @override void dispose() { _messageController.dispose(); _messageFocusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Chat'), ), body: BlocProvider( create: (context) => chatBloc, child: BlocBuilder( builder: (context, state) { if (state is ChatLoaded) { return Column( children: [ FutureBuilder( future: _userInfo != null ? Future.value(_userInfo) : null, builder: (BuildContext context, AsyncSnapshot?> snapshot) { if (_userInfo != null) { final userInfo = _userInfo![0] as MyUser; return Container( padding: const EdgeInsets.symmetric(vertical: 8), color: const Color(0xFFD6F4FF), alignment: Alignment.topCenter, child: ListTile( leading: Container( width: 60, height: 60, decoration: BoxDecoration( color: Colors.grey.shade300, shape: BoxShape.circle, image: userInfo.picture == null ? null : DecorationImage( image: NetworkImage( userInfo.picture ?? ''), fit: BoxFit.contain, ), ), child: userInfo.picture == null ? Icon( CupertinoIcons.person, color: Colors.grey.shade400, size: 40, ) : null, ), title: Text( userInfo.name ?? '', overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w600, ), ), subtitle: widget.service.userId == FirebaseAuth.instance.currentUser!.uid ? GeneralReputation( userId: widget.service.professionalId, builder: (context, reputation) { final average = reputation.averagePro; final total = reputation.totalPro; return Row( children: [ RatingBar.builder( initialRating: calculoRating(average), minRating: 1, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 25, maxRating: 5, itemBuilder: (context, _) => const Icon( Icons.star, color: Color(0xFF2BA4EC), ), onRatingUpdate: (rating) {}, ignoreGestures: true, ), const SizedBox(width: 5), Text( '${average.toStringAsFixed(1)} (${total.toString()})', ), ], ); }) : GeneralReputation( userId: widget.service.userId, builder: (context, reputation) { final average = reputation.average; final total = reputation.total; return Row( children: [ RatingBar.builder( initialRating: calculoRating(average), minRating: 1, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 25, maxRating: 5, itemBuilder: (context, _) => const Icon( Icons.star, color: Color(0xFF2BA4EC), ), onRatingUpdate: (rating) {}, ignoreGestures: true, ), const SizedBox(width: 5), Text( '${average.toStringAsFixed(1)} (${total.toString()})', ), ], ); }, ), ), ); } else { return const Center(child: CircularProgressIndicator()); } }, ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.only(top: 10), reverse: true, child: Column( children: _messagesList(state.chat.messages), ), ), ), Container( alignment: Alignment.bottomCenter, child: Container( padding: const EdgeInsets.symmetric( horizontal: 15, vertical: 15), child: Row( children: [ Expanded( child: TextFormField( focusNode: _messageFocusNode, controller: _messageController, style: const TextStyle(color: Colors.black), decoration: InputDecoration( hintText: 'Mensaje', hintStyle: TextStyle( color: Colors.grey[600], fontSize: 16), border: OutlineInputBorder( borderSide: const BorderSide( color: Colors.grey, width: 1.0), borderRadius: BorderRadius.circular(50)), focusedBorder: OutlineInputBorder( borderSide: const BorderSide( color: Colors.grey, width: 1.0), borderRadius: BorderRadius.circular(50)), contentPadding: const EdgeInsets.symmetric( horizontal: 20, vertical: 15), filled: true, fillColor: Colors.grey[200], ), onFieldSubmitted: (value) async { if (_messageController.text.isEmpty) { return; } MessageEntity message = MessageEntity( ownerId: FirebaseAuth.instance.currentUser!.uid, content: _messageController.text.trim(), createdAt: DateTime.now(), ); chatBloc.add(SendMessageEvent( serviceId: widget.service.id!, message: message, )); if (_userInfo != null) { final userInfo = _userInfo![0] as MyUser; final myInfo = _userInfo![1] as MyUser; if (userInfo.token == null) return; if (_lastNotificationTime == null || DateTime.now().difference( _lastNotificationTime!) > const Duration(minutes: 5)) { LocalNotifications.sendPushNotification( userInfo.token!, 'Nuevo mensaje', 'Tienes un nuevo mensaje de ${myInfo.name}', ); _lastNotificationTime = DateTime.now(); } } _messageController.clear(); _messageFocusNode.requestFocus(); }, ), ), const SizedBox(width: 12), GestureDetector( onTap: () async { if (_messageController.text.isEmpty) { return; } MessageEntity message = MessageEntity( ownerId: FirebaseAuth.instance.currentUser!.uid, content: _messageController.text.trim(), createdAt: DateTime.now(), ); chatBloc.add(SendMessageEvent( serviceId: widget.service.id!, message: message, )); if (_userInfo != null) { final userInfo = _userInfo![0] as MyUser; final myInfo = _userInfo![1] as MyUser; if (userInfo.token == null) return; if (_lastNotificationTime == null || DateTime.now().difference( _lastNotificationTime!) > const Duration(minutes: 5)) { LocalNotifications.sendPushNotification( userInfo.token!, 'Nuevo mensaje', 'Tienes un nuevo mensaje de ${myInfo.name}', ); _lastNotificationTime = DateTime.now(); } } _messageController.clear(); _messageFocusNode.requestFocus(); }, child: Container( height: 50, width: 50, decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(30), ), child: const Center( child: Icon( Icons.send, color: Colors.white, ), ), ), ) ], ), ), ) ], ); } return const Center( child: CircularProgressIndicator(), ); }, ), ), ); } List _messagesList(List messages) { return messages .map( (e) => e.ownerId != FirebaseAuth.instance.currentUser!.uid ? ListTile( title: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: const EdgeInsets.only(right: 60), padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 16), decoration: BoxDecoration( color: Colors.grey.shade200, borderRadius: const BorderRadius.only( topRight: Radius.circular(20), bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20), ), ), child: Text( e.content, style: const TextStyle(fontSize: 16), ), ), const SizedBox(width: 5), Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: Text( _getFechaHoraFormateada(e.createdAt), style: const TextStyle(color: Colors.grey, fontSize: 11), ), ), ], ), ) : ListTile( title: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( margin: const EdgeInsets.only(left: 60), padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 16, ), decoration: const BoxDecoration( color: Color(0xFFD5EFFF), borderRadius: BorderRadius.only( topLeft: Radius.circular(20), bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20), ), ), child: Text( e.content, style: const TextStyle(fontSize: 16), ), ), const SizedBox(width: 5), Padding( padding: const EdgeInsets.symmetric(horizontal: 10), child: Text( _getFechaHoraFormateada(e.createdAt), style: const TextStyle( color: Colors.grey, fontSize: 11, ), ), ), ], ), ), ) .toList(); } String _getFechaHoraFormateada(DateTime fecha) { final now = DateTime.now(); final yesterday = now.subtract(const Duration(days: 1)); final formatter = DateFormat('h:mm a'); if (fecha.day == now.day && fecha.month == now.month && fecha.year == now.year) { return 'Hoy - ${formatter.format(fecha)}'; } else if (fecha.day == yesterday.day && fecha.month == yesterday.month && fecha.year == yesterday.year) { return 'Ayer - ${formatter.format(fecha)}'; } else { return '${DateFormat('dd/MM/yyyy').format(fecha)} - ${formatter.format(fecha)}'; } } Future> _getUserAndProfessionalInfo( ServiceEntity service) async { final userRepo = FirebaseUserRepository(FirebaseAuth.instance); final MyUser? userInfo; final MyUser? myInfo; if (FirebaseAuth.instance.currentUser!.uid == service.userId) { userInfo = await userRepo.getMyUser(service.professionalId); myInfo = await userRepo.getMyUser(service.userId); } else { userInfo = await userRepo.getMyUser(service.userId); myInfo = await userRepo.getMyUser(service.professionalId); } return [userInfo, myInfo]; } double calculoRating(double average) { String numeroString = average.toString(); List partes = numeroString.split('.'); int parteEntera = int.parse(partes[0]); int parteFraccionaria = partes.length > 1 ? int.parse(partes[1]) : 0; if (parteFraccionaria >= 3) { parteFraccionaria = 5; } else { parteFraccionaria = 0; } // Unir la parte entera y fraccionaria y convertirlo nuevamente a double double resultado = double.parse('$parteEntera.$parteFraccionaria'); return resultado; } }