import 'dart:async'; import 'package:teso/Classes/TesoUser.dart'; import 'package:teso/Classes/ChatMessage.dart'; import 'package:teso/Pages/PageWidgets/ChatScreen/bottomBar.dart'; import 'package:teso/Pages/PageWidgets/ChatScreen/header.dart'; import 'package:teso/Pages/PageWidgets/ChatScreen/recipient.dart'; import 'package:teso/Pages/PageWidgets/ChatScreen/sender.dart'; import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:io'; import 'dart:convert'; import 'package:intl/intl.dart'; import 'package:teso/providers/user_provider.dart'; import 'package:provider/provider.dart'; import 'package:teso/util/consts.dart'; import 'package:http/http.dart' as http; class ChatScreen extends StatefulWidget { final TesoUser user; const ChatScreen({Key key, this.user}) : super(key: key); @override _ChatScreenState createState() => _ChatScreenState(user: this.user); } class _ChatScreenState extends State { final datakey = new GlobalKey(); TesoUser user; _ChatScreenState({this.user}); TextEditingController controller = new TextEditingController(); bool first = true; String id; List listMessage = new List.from([]); int _limit = 20; final int _limitIncrement = 20; String groupChatId; SharedPreferences prefs; final ScrollController listScrollController = ScrollController(); File imageFile; bool isLoading; bool isShowSticker; String imageUrl; TesoUser currentUser; Future _future; Timer timer; int counter = 0; _scrollListener() { if (listScrollController.offset >= listScrollController.position.maxScrollExtent && !listScrollController.position.outOfRange) { print("reach the bottom"); setState(() { print("reach the bottom"); _limit += _limitIncrement; }); } if (listScrollController.offset <= listScrollController.position.minScrollExtent && !listScrollController.position.outOfRange) { print("reach the top"); setState(() { print("reach the top"); }); } } void sendMessage(String content, int type) { if (content.trim() != '') { controller.clear(); var documentReference = FirebaseFirestore.instance .collection('messages') .doc(groupChatId) .collection(groupChatId) .doc(DateTime.now().millisecondsSinceEpoch.toString()); FirebaseFirestore.instance.runTransaction((transaction) async { transaction.set( documentReference, { 'idFrom': id, 'idTo': user.userGUID, 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(), 'content': content.trim(), 'type': type, 'read': false }, ); }); var inboxReference1 = FirebaseFirestore.instance .collection('inbox') .doc(id) .collection("lastMessage") .doc(groupChatId); FirebaseFirestore.instance.runTransaction((transaction) async { transaction.set( inboxReference1, { 'senderID': id, 'peerID': user.userGUID, 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(), 'content': content.trim(), 'firstname': user.firstname, 'surname': user.lastname, 'thumbnail': user.thumbnail_dp, 'username': user.username, 'read': false }, ); }); var inboxReference2 = FirebaseFirestore.instance .collection('inbox') .doc(user.userGUID) .collection("lastMessage") .doc(groupChatId); FirebaseFirestore.instance.runTransaction((transaction) async { transaction.set( inboxReference2, { 'senderID': id, 'peerID': id, 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(), 'content': content.trim(), 'firstname': currentUser.firstname, 'surname': currentUser.lastname, 'thumbnail': currentUser.thumbnail_dp, 'username': currentUser.username, 'read': false }, ); }); listScrollController.animateTo(0.0, duration: Duration(milliseconds: 300), curve: Curves.easeOut); } else { // Fluttertoast.showToast( // msg: 'Nothing to send', // backgroundColor: Colors.black, // textColor: Colors.red); } } void readMessage(DocumentReference reference) { reference.update({"read": true}); FirebaseFirestore.instance .collection('inbox') .doc(id) .collection("lastMessage") .doc(groupChatId) .update({"read": true}); FirebaseFirestore.instance .collection('inbox') .doc(user.userGUID) .collection("lastMessage") .doc(groupChatId) .update({"read": true}); } @override void initState() { super.initState(); readLocal(); listScrollController.addListener(_scrollListener); controller.addListener(() { if (controller.text.isNotEmpty) { isTyping(true); } else { isTyping(false); } }); currentUser = Provider.of(context, listen: false).currentUser; _future = findUser(); timer = Timer.periodic(Duration(seconds: 30), (Timer t) => addValue()); } void addValue() { setState(() { counter++; }); } readLocal() async { prefs = await SharedPreferences.getInstance(); id = prefs.getString('id') ?? ''; if (id.hashCode <= user.userGUID.hashCode) { groupChatId = id + user.userGUID; } else { groupChatId = user.userGUID + id; } FirebaseFirestore.instance .collection('users') .doc(id) .update({'chattingWith': user.userGUID, 'typing': false}); setState(() {}); } void isTyping(bool value) { FirebaseFirestore.instance .collection('users') .doc(id) .update({'chattingWith': user.userGUID, 'typing': value}); } Future findUser() async { TesoUser loadedUser; SharedPreferences prefs = await SharedPreferences.getInstance(); Map requestHeaders = { 'Content-type': 'application/json', 'Authorization': prefs.getString('tokensTeso') }; String auth = user.userGUID; var register2 = serverLocation + 'users/finduser'; var client1 = await http.post(Uri.parse(register2), body: json.encode(auth), headers: requestHeaders); if (client1.statusCode == 200) { Map handler = jsonDecode(client1.body); TesoUser tokenHandler = TesoUser.fromJSON(handler); setState(() { user = tokenHandler; loadedUser = user; }); } return loadedUser; } @override void dispose() { listScrollController.dispose(); controller.dispose(); timer?.cancel(); FirebaseFirestore.instance .collection('users') .doc(id) .update({'chattingWith': "", 'typing': false}); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: PreferredSize( preferredSize: Size.fromHeight(90), child: FutureBuilder( future: _future, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData == null || snapshot.connectionState == ConnectionState.waiting) { return buildChatHead(context, user); } else { return buildChatHead(context, user); } }, ), ), body: Container( height: MediaQuery.of(context).size.height, child: Stack( children: [ Container( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, margin: EdgeInsets.only(bottom: 60), child: StreamBuilder( stream: FirebaseFirestore.instance .collection('messages') .doc(groupChatId) .collection(groupChatId) .orderBy('timestamp', descending: true) .limit(_limit) .snapshots(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Theme.of(context).primaryColor))); } else if (snapshot.data == null || listMessage == null) { return Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Theme.of(context).primaryColor))); } else { listMessage = snapshot.data.docs; return ListView.builder( padding: EdgeInsets.all(10.0), itemCount: listMessage.length, itemBuilder: (context, index) { if (snapshot.data.docs[index].data()['idTo'] == id && snapshot.data.docs[index].data()['read'] == false) { readMessage(listMessage[index].reference); } int timeInMillis = int.parse( snapshot.data.docs[index].data()['timestamp']); var date = DateTime.fromMillisecondsSinceEpoch(timeInMillis); var formattedDate = DateFormat("yyyy-MM-dd HH:mm:ss").format(date); ChatMessage message = new ChatMessage(); message.idFrom = snapshot.data.docs[index].data()['idFrom']; message.content = snapshot.data.docs[index].data()['content']; message.idTo = snapshot.data.docs[index].data()['idTo']; message.timestamp = DateTime.parse(formattedDate); message.type = int.parse(snapshot.data.docs[index] .data()['type'] .toString()); if (snapshot.data.docs[index].data()['idFrom'] == id) { return buildSender(context, message); } else { return buildRecipient(context, message); } }, reverse: true, controller: listScrollController, ); } }, ), ), Align( alignment: Alignment.bottomCenter, child: buildBottom(context, controller, sendMessage), ), ], ), ), ); } }