diff --git a/lib/src/components/drawer_professional.dart b/lib/src/components/drawer_professional.dart index e8288f9..d8a119c 100644 --- a/lib/src/components/drawer_professional.dart +++ b/lib/src/components/drawer_professional.dart @@ -6,6 +6,8 @@ import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/models/user_model.dart'; import 'package:prosappco/src/screens/calendar.dart'; +import 'package:prosappco/src/screens/messages.dart'; +import 'package:prosappco/src/screens/my_services_pro.dart'; import 'package:prosappco/src/screens/professional.dart'; import 'package:prosappco/src/screens/profile.dart'; import 'package:prosappco/src/screens/profile_pro.dart'; @@ -113,12 +115,13 @@ class _DrawerProfessionalState extends State { children: [ ListTile( onTap: () { - if (ModalRoute.of(context)?.settings.name != - '/misserviciospro') { - Navigator.pushNamed(context, '/misserviciospro'); - } else { - Scaffold.of(context).openEndDrawer(); - } + Navigator.of(context).push( + CupertinoPageRoute( + builder: (BuildContext context) { + return MyServicesProScreen(); + }, + ), + ); }, leading: const Icon( Icons.history, @@ -172,7 +175,15 @@ class _DrawerProfessionalState extends State { ), ), ListTile( - onTap: () {}, + onTap: () { + Navigator.of(context).push( + CupertinoPageRoute( + builder: (BuildContext context) { + return MessagesScreen(); + }, + ), + ); + }, leading: const Icon( Icons.messenger_outline, color: Colors.black, diff --git a/lib/src/models/chat_model.dart b/lib/src/models/chat_model.dart index 751d512..21e922b 100644 --- a/lib/src/models/chat_model.dart +++ b/lib/src/models/chat_model.dart @@ -1,12 +1,24 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:prosappco/src/models/user_model.dart'; class ChatModel { List messages; + String professional_id; + String user_id; + UserModel? user; + UserModel? professional; - ChatModel({required this.messages}); + ChatModel({ + required this.messages, + required this.professional_id, + required this.user_id, + this.user, + this.professional, + }); - static ChatModel fromDocumentSnapshot( - DocumentSnapshot> snapshot) { + static Future fromDocumentSnapshot2( + DocumentSnapshot> snapshot, + bool fillUserModel) async { try { List messages = []; List messagesData = snapshot.get('message') ?? []; @@ -19,12 +31,73 @@ class ChatModel { )); } - return ChatModel(messages: messages); + return ChatModel( + messages: messages, + professional_id: snapshot.get('professional_id'), + user_id: snapshot.get('user_id'), + user: fillUserModel + ? await UserModel.getUser(snapshot.get('user_id')) + : null, + ); } catch (e) { print('error $e'); - return ChatModel(messages: []); + return ChatModel(messages: [], professional_id: '', user_id: ''); } } + + static ChatModel fromDocumentSnapshot( + DocumentSnapshot> snapshot, + ) { + try { + List messages = []; + List messagesData = snapshot.get('message') ?? []; + + for (var data in messagesData) { + messages.add(MessageModel( + user: data['user'] ?? '', + content: data['content'] ?? '', + timestamp: (data['timestamp'] as Timestamp).toDate(), + )); + } + + return ChatModel( + messages: messages, + professional_id: snapshot.get('professional_id'), + user_id: snapshot.get('user_id'), + ); + } catch (e) { + print('error $e'); + return ChatModel(messages: [], professional_id: '', user_id: ''); + } + } + + static Future> getChatsByProId(String userReceived) async { + final receivedScoresQuery = FirebaseFirestore.instance + .collection('chats') + .where('professional_id', isEqualTo: userReceived); + + final receivedScoresSnapshot = await receivedScoresQuery.get(); + + final receivedScores = await Future.wait(receivedScoresSnapshot.docs + .map((doc) async => await fromDocumentSnapshot2(doc, true)) + .toList()); + + return receivedScores; + } + + static Future> getChatsByUserId(String userReceived) async { + final receivedScoresQuery = FirebaseFirestore.instance + .collection('chats') + .where('user_id', isEqualTo: userReceived); + + final receivedScoresSnapshot = await receivedScoresQuery.get(); + + final receivedScores = await Future.wait(receivedScoresSnapshot.docs + .map((doc) async => await fromDocumentSnapshot2(doc, true)) + .toList()); + + return receivedScores; + } } class MessageModel { diff --git a/lib/src/models/event_model.dart b/lib/src/models/event_model.dart index 4df3ee5..6751599 100644 --- a/lib/src/models/event_model.dart +++ b/lib/src/models/event_model.dart @@ -96,6 +96,28 @@ Future> getByProIdAll(String state1, String state2) async { } } +Future> getByUserIdAll(String state1, String state2) async { + try { + var snapshot = await FirebaseFirestore.instance + .collection('services') + .where('user_id', isEqualTo: uid) + .where('status', whereIn: [state1, state2]).get(); + + List eventos = []; + for (var element in snapshot.docs) { + final event = Event.fromJson(element.data()); + event.scoresModel = await ScoresModel.scoreTo(event.userId, false, false); + event.id = element.id; + eventos.add(event); + } + print('eventos $eventos'); + return eventos; + } catch (e) { + print('Error getByUserId $e'); + return []; + } +} + class Event { String? id; String title; @@ -151,9 +173,17 @@ class Event { final snapshot = await FirebaseFirestore.instance .collection('services') .doc(uid) - .get(); - final Map? data = snapshot.data(); - return Event.fromJson(data!); + .get() + .then((docSnapshot) { + if (docSnapshot.exists) { + print('info ${docSnapshot.data()}'); + } else { + print('No se encontró ningún documento con la ruta especificada'); + } + ; + }); + final Map data = snapshot.data(); + return Event.fromJson(data); } catch (e) { print('Error getting user: $e'); return Event( diff --git a/lib/src/models/user_model.dart b/lib/src/models/user_model.dart index a93d5a6..3c4d750 100644 --- a/lib/src/models/user_model.dart +++ b/lib/src/models/user_model.dart @@ -5,26 +5,28 @@ import 'package:prosappco/src/screens/professional.dart'; class UserModel { final String name; final String city; + final String? profession; final String? state; final Reference? photo; - UserModel(this.name, this.city, this.state, this.photo); + UserModel(this.name, this.city, this.profession, this.state, this.photo); static Future fromJson( Map? json, String uid) async { try { - if (json == null) return UserModel('', '', null, null); + if (json == null) return UserModel('', '', '', null, null); String? avatar = json['photo']; return UserModel( json['name'], json['city'], + json['profesion'], json['estado'], avatar != null ? storage.ref().child(avatar) : null, ); } catch (e) { print('XD user $e'); - return UserModel('', '', null, null); + return UserModel('', '', '', null, null); } } @@ -36,7 +38,7 @@ class UserModel { return fromJson(data, uid); } catch (e) { print('Error getting user: $e'); - return UserModel('', '', null, null); + return UserModel('', '', '', null, null); } } } diff --git a/lib/src/screens/chat.dart b/lib/src/screens/chat.dart index c470469..446a4a7 100644 --- a/lib/src/screens/chat.dart +++ b/lib/src/screens/chat.dart @@ -1,12 +1,16 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; +import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/models/chat_model.dart'; +import 'package:prosappco/src/models/event_model.dart'; +import 'package:prosappco/src/models/user_model.dart'; class ChatScreen extends StatefulWidget { - final String eventoId; - const ChatScreen({super.key, required this.eventoId}); + final Event evento; + const ChatScreen({super.key, required this.evento}); @override State createState() => _ChatScreenState(); @@ -15,6 +19,31 @@ class ChatScreen extends StatefulWidget { class _ChatScreenState extends State { final _textController = TextEditingController(); final uid = AuthenticationRepository.instance.getCurrentUserUid(); + UserModel? user; + // ScoresModel? scoresModel; + + @override + void initState() { + super.initState(); + + if (user == null) { + if (uid != widget.evento.userId) { + UserModel.getUser(widget.evento.userId).then( + (UserModel s) => setState(() => user = s), + ); + } else { + UserModel.getUser(widget.evento.professionalId).then( + (UserModel s) => setState(() => user = s), + ); + } + } + + // if (scoresModel == null) { + // ScoresModel.scoreTo(uid.toString(), false, false).then( + // (ScoresModel s) => setState(() => scoresModel = s), + // ); + // } + } @override Widget build(BuildContext context) { @@ -24,86 +53,220 @@ class _ChatScreenState extends State { onPressed: () { Navigator.pop(context); }, - label: 'Mensajes'), + label: 'Chat'), body: Column( children: [ - StreamBuilder( - stream: FirebaseFirestore.instance - .collection('chats') - .doc(widget.eventoId) - .snapshots(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center(child: CircularProgressIndicator()); - } - - final data = snapshot.data!; - final chat = ChatModel.fromDocumentSnapshot(data); - - print('messages ${chat.messages}'); - - return Column(children: [ - ...chat.messages.map( - (e) => ListTile( - title: RichText( - text: TextSpan( - children: [ - TextSpan( - text: '${e.content}, ', - style: const TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - // TextSpan( - // text: DateFormat('dd MMM', 'es') - // .format(DateTime.parse(e.day)), - // style: const TextStyle( - // color: Colors.grey, - // fontSize: 16, - // ), - // ), - ], + Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.3), + spreadRadius: 2, + blurRadius: 3, + offset: const Offset(0, 2), // changes position of shadow + ), + ], + ), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8), + color: const Color(0xFFD6F4FF), + alignment: Alignment.topCenter, + child: ListTile( + leading: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: ReferencePhoto( + ref: user?.photo, + size: 55, + sizeCircle: 60, + ), + ), + title: Text( + '${user?.name}', + style: const TextStyle( + color: Colors.black, fontWeight: FontWeight.w600), + ), + subtitle: Text('${user?.profession}'), + trailing: const Icon(Icons.keyboard_arrow_right), + ), + ), + ), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.only(top: 10), + reverse: true, + child: streamB(uid!), + ), + ), + Container( + alignment: Alignment.bottomCenter, + width: MediaQuery.of(context).size.width, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 18), + width: MediaQuery.of(context).size.width, + child: Row( + children: [ + Expanded( + child: TextFormField( + controller: _textController, + 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], ), ), ), - ) - ]); + const SizedBox(width: 12), + GestureDetector( + onTap: () async { + final nuevoMensaje = MessageModel( + user: uid!, + content: _textController.text, + timestamp: DateTime.now()); - // ListTile( - // title: Text('${}'), - // ); - }, + final nuevoMensajeMap = { + 'user': nuevoMensaje.user, + 'content': nuevoMensaje.content, + 'timestamp': nuevoMensaje.timestamp, + }; + + FirebaseFirestore.instance + .collection('chats') + .doc(widget.evento.id) + .update({ + 'message': FieldValue.arrayUnion([nuevoMensajeMap]) + }); + + _textController.clear(); + }, + 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, + ), + ), + ), + ) + ], + ), + ), ), - TextFormField( - controller: _textController, - ), - IconButton( - onPressed: () async { - final nuevoMensaje = MessageModel( - user: uid!, - content: _textController.text, - timestamp: DateTime.now()); - - final nuevoMensajeMap = { - 'user': nuevoMensaje.user, - 'content': nuevoMensaje.content, - 'timestamp': nuevoMensaje.timestamp, - }; - - FirebaseFirestore.instance - .collection('chats') - .doc(widget.eventoId) - .update({ - 'message': FieldValue.arrayUnion([nuevoMensajeMap]) - }); - - _textController.clear(); - }, - icon: const Icon(Icons.send)) ], ), ); } + + StreamBuilder>> streamB(String uid) { + return StreamBuilder( + stream: FirebaseFirestore.instance + .collection('chats') + .doc(widget.evento.id) + .snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const Center(child: CircularProgressIndicator()); + } + + final data = snapshot.data!; + final chat = ChatModel.fromDocumentSnapshot(data); + + return Column(children: [ + ...chat.messages.map( + (e) => uid != e.user + ? 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.timestamp), + 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.timestamp), + style: const TextStyle( + color: Colors.grey, fontSize: 12), + ), + ), + ], + ), + ), + ) + ]); + + // ListTile( + // title: Text('${}'), + // ); + }, + ); + } } diff --git a/lib/src/screens/cita.dart b/lib/src/screens/cita.dart index a00968d..ad0c770 100644 --- a/lib/src/screens/cita.dart +++ b/lib/src/screens/cita.dart @@ -1,10 +1,10 @@ import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:intl/intl.dart'; +import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/models/event_model.dart'; @@ -23,6 +23,7 @@ class CitaScreen extends StatefulWidget { } class _CitaScreenState extends State { + final uid = AuthenticationRepository.instance.getCurrentUserUid(); UserModel? user; @override String nombre = ''; @@ -54,23 +55,40 @@ class _CitaScreenState extends State { super.initState(); if (scoresModel == null) { - ScoresModel.scoreTo(widget.evento.userId.toString(), false, false).then( - (ScoresModel s) => setState(() { - scoresModel = s; - }), - ); + if (uid != widget.evento.userId) { + ScoresModel.scoreTo(widget.evento.userId, false, false).then( + (ScoresModel s) => setState(() { + scoresModel = s; + }), + ); + } else { + ScoresModel.scoreTo(widget.evento.professionalId, true, false).then( + (ScoresModel s) => setState(() { + scoresModel = s; + }), + ); + } } } @override Widget build(BuildContext context) { if (nombre == '') { - UserModel.getUser(widget.evento.userId).then((value) { - setState(() { - nombre = value.name; - ref_photo = value.photo; + if (uid != widget.evento.userId) { + UserModel.getUser(widget.evento.userId).then((value) { + setState(() { + nombre = value.name; + ref_photo = value.photo; + }); }); - }); + } else { + UserModel.getUser(widget.evento.professionalId).then((value) { + setState(() { + nombre = value.name; + ref_photo = value.photo; + }); + }); + } } return Scaffold( @@ -221,13 +239,17 @@ class _CitaScreenState extends State { await FirebaseFirestore.instance .collection('chats') .doc(widget.evento.id) - .set({}); + .set({ + 'professional_id': widget.evento.professionalId, + 'user_id': widget.evento.userId + }, SetOptions(merge: true)).catchError((error) => + print('Error al crear el documento: $error')); Navigator.push( context, CupertinoPageRoute( builder: (BuildContext context) { - return ChatScreen(eventoId: widget.evento.id!); + return ChatScreen(evento: widget.evento); }, ), ); diff --git a/lib/src/screens/messages.dart b/lib/src/screens/messages.dart new file mode 100644 index 0000000..4503359 --- /dev/null +++ b/lib/src/screens/messages.dart @@ -0,0 +1,80 @@ +import 'package:community_material_icon/community_material_icon.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:prosappco/src/authentication/authentication_repository.dart'; +import 'package:prosappco/src/components/pop_appbar.dart'; +import 'package:prosappco/src/models/chat_model.dart'; +import 'package:prosappco/src/models/event_model.dart'; +import 'package:prosappco/src/models/user_model.dart'; +import 'package:prosappco/src/screens/chat.dart'; + +class MessagesScreen extends StatefulWidget { + const MessagesScreen({super.key}); + + @override + State createState() => _MessagesScreenState(); +} + +class _MessagesScreenState extends State { + final uid = AuthenticationRepository.instance.getCurrentUserUid(); + List list = []; + @override + void initState() { + super.initState(); + + ChatModel.getChatsByProId(uid!).then( + (List s) => setState(() { + list = s; + }), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PopAppbar( + onPressed: () { + Navigator.pop(context); + }, + label: 'Mensajes'), + body: Column( + children: [ + const Padding( + padding: EdgeInsets.all(10), + child: TextField( + // controller: searchController, + decoration: InputDecoration( + hintText: 'Escriba un nombre', + prefixIcon: Icon(CommunityMaterialIcons.stethoscope), + ), + ), + ), + Expanded( + child: ListView.builder( + itemCount: list.length, + itemBuilder: (BuildContext context, int index) { + return ListTile( + title: Text( + list[index].user?.name ?? '', + ), + onTap: () { + Event.getEventById('scOUCJUs6iW7zVo4g2m8').then((value) { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ChatScreen(evento: value); + }, + ), + ); + }); + }, + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/src/screens/my_services.dart b/lib/src/screens/my_services.dart index 3b94771..36954dc 100644 --- a/lib/src/screens/my_services.dart +++ b/lib/src/screens/my_services.dart @@ -45,7 +45,7 @@ class _MyServicesScreenState extends State { Widget _eventList() { return FutureBuilder( - future: getByProIdAll('aprobado', 'denegado'), + future: getByUserIdAll('aprobado', 'denegado'), builder: (BuildContext context, AsyncSnapshot> snapshot) { if (!snapshot.hasData) { return const Center( @@ -72,7 +72,7 @@ class _MyServicesScreenState extends State { ...eventos.map( (event) => ListTile( tileColor: event.status == 'aprobado' - ? Colors.green[100] + ? Colors.blue[100] : Colors.red[100], onTap: () { Navigator.push( @@ -84,7 +84,17 @@ class _MyServicesScreenState extends State { ), ); }, - leading: Text(event.range1Hour1), + leading: event.status == 'aprobado' + ? const Icon( + Icons.check, + color: Colors.blue, + size: 40, + ) + : const Icon( + Icons.close, + color: Colors.red, + size: 40, + ), title: RichText( text: TextSpan( children: [ @@ -97,10 +107,10 @@ class _MyServicesScreenState extends State { ), ), TextSpan( - text: DateFormat('dd MMM', 'es') - .format(DateTime.parse(event.day)), - style: const TextStyle( - color: Colors.grey, + text: + '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${event.range1Hour1}', + style: TextStyle( + color: Colors.grey[600], fontSize: 16, ), ), diff --git a/lib/src/screens/my_services_pro.dart b/lib/src/screens/my_services_pro.dart index 392af9d..be87448 100644 --- a/lib/src/screens/my_services_pro.dart +++ b/lib/src/screens/my_services_pro.dart @@ -109,8 +109,8 @@ class _MyServicesProScreenState extends State { TextSpan( text: '${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${event.range1Hour1}', - style: const TextStyle( - color: Colors.grey, + style: TextStyle( + color: Colors.grey[600], fontSize: 16, ), ),