Files
prosappco/lib/screens/chat/chat_screen.dart
T

449 lines
19 KiB
Dart

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<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
late final ChatBloc chatBloc;
final TextEditingController _messageController = TextEditingController();
final FocusNode _messageFocusNode = FocusNode();
List<dynamic>? _userInfo;
DateTime? _lastNotificationTime;
@override
void initState() {
super.initState();
chatBloc = Injector.appInstance.get<ChatBloc>();
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<ChatBloc, ChatState>(
builder: (context, state) {
if (state is ChatLoaded) {
return Column(
children: [
FutureBuilder(
future: _userInfo != null ? Future.value(_userInfo) : null,
builder: (BuildContext context,
AsyncSnapshot<List<dynamic>?> 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;
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 ${userInfo.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;
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 ${userInfo.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<Widget> _messagesList(List<MessageEntity> 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(
DateFormat('h:mm a').format(e.createdAt),
style:
const TextStyle(color: Colors.grey, fontSize: 12),
),
),
],
),
)
: 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(
DateFormat('h:mm a').format(e.createdAt),
style: const TextStyle(
color: Colors.grey,
fontSize: 12,
),
),
),
],
),
),
)
.toList();
}
Future<List<dynamic>> _getUserAndProfessionalInfo(
ServiceEntity service) async {
final userRepo = FirebaseUserRepository(FirebaseAuth.instance);
final MyUser? userInfo;
if (FirebaseAuth.instance.currentUser!.uid == service.userId) {
userInfo = await userRepo.getMyUser(service.professionalId);
} else {
userInfo = await userRepo.getMyUser(service.userId);
}
return [userInfo];
}
double calculoRating(double average) {
String numeroString = average.toString();
List<String> 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;
}
}