|
|
|
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<ChatScreen> {
|
|
|
|
final datakey = new GlobalKey();
|
|
|
|
TesoUser? user;
|
|
|
|
_ChatScreenState({this.user});
|
|
|
|
TextEditingController controller = new TextEditingController();
|
|
|
|
bool first = true;
|
|
|
|
String? id;
|
|
|
|
|
|
|
|
List<QueryDocumentSnapshot>? listMessage = new List.from([]);
|
|
|
|
int _limit = 20;
|
|
|
|
final int _limitIncrement = 20;
|
|
|
|
String? groupChatId;
|
|
|
|
late SharedPreferences prefs;
|
|
|
|
final ScrollController listScrollController = ScrollController();
|
|
|
|
File? imageFile;
|
|
|
|
bool? isLoading;
|
|
|
|
bool? isShowSticker;
|
|
|
|
String? imageUrl;
|
|
|
|
TesoUser? currentUser;
|
|
|
|
Future<TesoUser?>? _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<UserProvider>(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<TesoUser?> findUser() async {
|
|
|
|
TesoUser? loadedUser;
|
|
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
|
|
Map<String, String> 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 as Map<String, dynamic>);
|
|
|
|
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<dynamic> snapshot) {
|
|
|
|
if (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: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
|
|
|
|
if (!snapshot.hasData) {
|
|
|
|
return Center(
|
|
|
|
child: CircularProgressIndicator(
|
|
|
|
valueColor: AlwaysStoppedAnimation<Color>(
|
|
|
|
Theme.of(context).primaryColor)));
|
|
|
|
} else if (snapshot.data == null || listMessage == null) {
|
|
|
|
return Center(
|
|
|
|
child: CircularProgressIndicator(
|
|
|
|
valueColor: AlwaysStoppedAnimation<Color>(
|
|
|
|
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]['idTo'] == id &&
|
|
|
|
snapshot.data!.docs[index]['read'] == false) {
|
|
|
|
readMessage(listMessage![index].reference);
|
|
|
|
}
|
|
|
|
int timeInMillis = int.parse(
|
|
|
|
snapshot.data!.docs[index]['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]['idFrom'];
|
|
|
|
message.content =
|
|
|
|
snapshot.data!.docs[index]['content'];
|
|
|
|
message.idTo = snapshot.data!.docs[index]['idTo'];
|
|
|
|
message.timestamp = DateTime.parse(formattedDate);
|
|
|
|
message.type = int.parse(snapshot.data!
|
|
|
|
.docs[index]
|
|
|
|
['type']
|
|
|
|
.toString());
|
|
|
|
if (snapshot.data!.docs[index]['idFrom'] == id) {
|
|
|
|
return buildSender(context, message);
|
|
|
|
} else {
|
|
|
|
return buildRecipient(context, message);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
reverse: true,
|
|
|
|
controller: listScrollController,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.bottomCenter,
|
|
|
|
child: buildBottom(context, controller, sendMessage),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|