Last active
May 13, 2024 11:58
-
-
Save TasyaShark/ec1db6a029eddc89bd88b7c57bd4b175 to your computer and use it in GitHub Desktop.
ChatScreen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ChatScreen extends StatelessWidget { | |
final CubeUser _cubeUser; | |
final CubeDialog _cubeDialog; | |
const ChatScreen(this._cubeUser, this._cubeDialog, {super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(_cubeDialog.name ?? ''), | |
), | |
body: ChatScreenBody(_cubeUser, _cubeDialog), | |
); | |
} | |
} | |
class ChatScreenBody extends StatefulWidget { | |
final CubeUser currentUser; | |
final CubeDialog cubeDialog; | |
const ChatScreenBody(this.currentUser, this.cubeDialog, {super.key}); | |
@override | |
State createState() => ChatScreenBodyState(); | |
} | |
class ChatScreenBodyState extends State<ChatScreenBody> { | |
final Map<int?, CubeUser?> _occupants = {}; | |
late bool isLoading; | |
List<CubeMessage> listMessage = []; | |
String userStatus = ''; | |
final TextEditingController textEditingController = TextEditingController(); | |
final ScrollController listScrollController = ScrollController(); | |
StreamSubscription<CubeMessage>? msgSubscription; | |
StreamSubscription<MessageStatus>? deliveredSubscription; | |
StreamSubscription<MessageStatus>? readSubscription; | |
@override | |
void initState() { | |
super.initState(); | |
_initCubeChat(); | |
isLoading = false; | |
} | |
@override | |
void dispose() { | |
msgSubscription?.cancel(); | |
deliveredSubscription?.cancel(); | |
readSubscription?.cancel(); | |
textEditingController.dispose(); | |
super.dispose(); | |
} | |
void onReceiveMessage(CubeMessage message) { | |
if (message.dialogId != widget.cubeDialog.dialogId) return; | |
addMessageToListView(message); | |
} | |
void onDeliveredMessage(MessageStatus status) { | |
updateReadDeliveredStatusMessage(status, false); | |
} | |
void onReadMessage(MessageStatus status) { | |
updateReadDeliveredStatusMessage(status, true); | |
} | |
void onSendChatMessage(String content) { | |
if (content.trim() != '') { | |
final message = createCubeMsg(); | |
message.body = content.trim(); | |
onSendMessage(message); | |
} else { | |
print('Nothing to send'); | |
} | |
} | |
CubeMessage createCubeMsg() { | |
var message = CubeMessage(); | |
message.dateSent = DateTime.now().millisecondsSinceEpoch ~/ 1000; | |
message.markable = true; | |
message.saveToHistory = true; | |
return message; | |
} | |
void onSendMessage(CubeMessage message) async { | |
textEditingController.clear(); | |
await widget.cubeDialog.sendMessage(message); | |
message.senderId = widget.currentUser.id; | |
addMessageToListView(message); | |
listScrollController.animateTo(0.0, | |
duration: const Duration(milliseconds: 300), curve: Curves.easeOut); | |
} | |
updateReadDeliveredStatusMessage(MessageStatus status, bool isRead) { | |
setState(() { | |
CubeMessage? msg = listMessage | |
.where((msg) => msg.messageId == status.messageId) | |
.firstOrNull; | |
if (msg == null) return; | |
if (isRead) { | |
msg.readIds == null | |
? msg.readIds = [status.userId] | |
: msg.readIds?.add(status.userId); | |
} else { | |
msg.deliveredIds == null | |
? msg.deliveredIds = [status.userId] | |
: msg.deliveredIds?.add(status.userId); | |
} | |
}); | |
} | |
addMessageToListView(CubeMessage message) { | |
setState(() { | |
isLoading = false; | |
int existMessageIndex = listMessage.indexWhere((cubeMessage) { | |
return cubeMessage.messageId == message.messageId; | |
}); | |
if (existMessageIndex != -1) { | |
listMessage[existMessageIndex] = message; | |
} else { | |
listMessage.insert(0, message); | |
} | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return SafeArea( | |
child: Stack( | |
children: <Widget>[ | |
Column( | |
children: <Widget>[ | |
buildListMessage(), | |
buildInput(), | |
], | |
), | |
buildLoading(), | |
], | |
), | |
); | |
} | |
Widget buildItem(int index, CubeMessage message) { | |
markAsReadIfNeed() { | |
var isOpponentMsgRead = message.readIds != null && | |
message.readIds!.contains(widget.currentUser.id); | |
if (message.senderId != widget.currentUser.id && !isOpponentMsgRead) { | |
if (message.readIds == null) { | |
message.readIds = [widget.currentUser.id!]; | |
} else { | |
message.readIds!.add(widget.currentUser.id!); | |
} | |
if (CubeChatConnection.instance.chatConnectionState == | |
CubeChatConnectionState.Ready) { | |
widget.cubeDialog.readMessage(message); | |
} | |
} | |
} | |
Widget getReadDeliveredWidget() { | |
bool messageIsRead() { | |
if (widget.cubeDialog.type == CubeDialogType.PRIVATE) { | |
return message.readIds != null && | |
(message.recipientId == null || | |
message.readIds!.contains(message.recipientId)); | |
} | |
return message.readIds != null && | |
message.readIds!.any((int id) => | |
id != widget.currentUser.id && _occupants.keys.contains(id)); | |
} | |
bool messageIsDelivered() { | |
if (widget.cubeDialog.type == CubeDialogType.PRIVATE) { | |
return message.deliveredIds != null && | |
(message.recipientId == null || | |
message.deliveredIds!.contains(message.recipientId)); | |
} | |
return message.deliveredIds != null && | |
message.deliveredIds!.any((int id) => | |
id != widget.currentUser.id && _occupants.keys.contains(id)); | |
} | |
if (messageIsRead()) { | |
return getMessageStateWidget(MessageState.read); | |
} else if (messageIsDelivered()) { | |
return getMessageStateWidget(MessageState.delivered); | |
} else { | |
return getMessageStateWidget(MessageState.sent); | |
} | |
} | |
Widget getDateWidget() { | |
return Text( | |
DateFormat('HH:mm').format( | |
DateTime.fromMillisecondsSinceEpoch(message.dateSent! * 1000)), | |
style: const TextStyle( | |
color: Colors.grey, fontSize: 12.0, fontStyle: FontStyle.italic), | |
); | |
} | |
Widget getMessageBubble(bool isOwnMessage) { | |
if (!isOwnMessage) { | |
markAsReadIfNeed(); | |
} | |
return Column( | |
key: Key('${message.messageId}'), | |
children: <Widget>[ | |
Row( | |
mainAxisAlignment: | |
isOwnMessage ? MainAxisAlignment.end : MainAxisAlignment.start, | |
children: <Widget>[ | |
Flexible( | |
child: Container( | |
constraints: const BoxConstraints(maxWidth: 480), | |
padding: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 4.0), | |
decoration: BoxDecoration( | |
color: isOwnMessage | |
? Colors.grey.shade200 | |
: Colors.blueGrey.shade800, | |
borderRadius: BorderRadius.circular(8.0)), | |
margin: EdgeInsets.only( | |
bottom: isOwnMessage ? 20.0 : 10.0, | |
right: isOwnMessage ? 10.0 : 0, | |
left: isOwnMessage ? 0 : 10.0), | |
child: Column( | |
crossAxisAlignment: isOwnMessage | |
? CrossAxisAlignment.end | |
: CrossAxisAlignment.start, | |
children: [ | |
Text( | |
message.body!, | |
style: TextStyle( | |
color: isOwnMessage | |
? Colors.blueGrey.shade800 | |
: Colors.grey.shade200), | |
), | |
Row( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
getDateWidget(), | |
if (isOwnMessage) getReadDeliveredWidget(), | |
], | |
), | |
]), | |
), | |
) | |
], | |
), | |
], | |
); | |
} | |
return getMessageBubble(message.senderId == widget.currentUser.id); | |
} | |
Widget buildLoading() { | |
return Positioned( | |
child: isLoading | |
? Container( | |
color: Colors.white.withOpacity(0.8), | |
child: const Center( | |
child: CircularProgressIndicator( | |
valueColor: AlwaysStoppedAnimation<Color>(Colors.green), | |
), | |
)) | |
: const SizedBox.shrink()); | |
} | |
Widget buildInput() { | |
return Container( | |
width: double.infinity, | |
height: 50.0, | |
decoration: BoxDecoration( | |
border: Border( | |
top: BorderSide(color: Colors.blueGrey.shade800, width: 0.5)), | |
color: Colors.white), | |
child: Row( | |
children: <Widget>[ | |
Flexible( | |
child: TextField( | |
autofocus: true, | |
keyboardType: TextInputType.multiline, | |
maxLines: null, | |
style: TextStyle(color: Colors.blueGrey.shade800, fontSize: 15.0), | |
controller: textEditingController, | |
decoration: const InputDecoration.collapsed( | |
hintText: 'Type your message...', | |
hintStyle: TextStyle(color: Colors.grey), | |
), | |
), | |
), | |
// Button send message | |
Material( | |
color: Colors.white, | |
child: Container( | |
margin: const EdgeInsets.only(left: 4, right: 2.0), | |
child: IconButton( | |
icon: const Icon(Icons.send), | |
onPressed: () => onSendChatMessage(textEditingController.text), | |
color: Colors.blueGrey.shade800, | |
), | |
), | |
), | |
], | |
), | |
); | |
} | |
Widget buildListMessage() { | |
getWidgetMessages(listMessage) { | |
return ListView.builder( | |
padding: const EdgeInsets.all(10.0), | |
itemBuilder: (context, index) => buildItem(index, listMessage[index]), | |
itemCount: listMessage.length, | |
reverse: true, | |
controller: listScrollController, | |
); | |
} | |
return Flexible( | |
child: StreamBuilder<List<CubeMessage>>( | |
stream: getMessagesList().asStream(), | |
builder: (context, snapshot) { | |
if (!snapshot.hasData) { | |
return const Center( | |
child: CircularProgressIndicator( | |
valueColor: AlwaysStoppedAnimation<Color>(Colors.green))); | |
} else { | |
listMessage = snapshot.data ?? []; | |
return getWidgetMessages(listMessage); | |
} | |
}, | |
), | |
); | |
} | |
Future<List<CubeMessage>> getMessagesList() { | |
if (listMessage.isNotEmpty) return Future.value(listMessage); | |
var params = GetMessagesParameters(); | |
params.sorter = RequestSorter('desc', '', 'date_sent'); | |
return getMessages( | |
widget.cubeDialog.dialogId!, params.getRequestParameters()) | |
.then((getMessagesResult) { | |
return getAllUsersByIds(widget.cubeDialog.occupantsIds!.toSet()) | |
.then((getUsersResult) { | |
_occupants | |
.addAll({for (var item in getUsersResult!.items) item.id: item}); | |
return getMessagesResult?.items ?? []; | |
}); | |
}); | |
} | |
_initCubeChat() { | |
msgSubscription = CubeChatConnection | |
.instance.chatMessagesManager!.chatMessagesStream | |
.listen(onReceiveMessage); | |
deliveredSubscription = CubeChatConnection | |
.instance.messagesStatusesManager!.deliveredStream | |
.listen(onDeliveredMessage); | |
readSubscription = CubeChatConnection | |
.instance.messagesStatusesManager!.readStream | |
.listen(onReadMessage); | |
} | |
} | |
Widget getMessageStateWidget(MessageState? state) { | |
Widget result; | |
switch (state) { | |
case MessageState.read: | |
result = const Stack(children: <Widget>[ | |
Icon( | |
Icons.check_rounded, | |
size: 15.0, | |
color: Colors.green, | |
), | |
Padding( | |
padding: EdgeInsets.only( | |
left: 4, | |
), | |
child: Icon( | |
Icons.check_rounded, | |
size: 15.0, | |
color: Colors.green, | |
), | |
) | |
]); | |
break; | |
case MessageState.delivered: | |
result = Stack(children: <Widget>[ | |
Icon( | |
Icons.check_rounded, | |
size: 15.0, | |
color: Colors.grey.shade700, | |
), | |
Padding( | |
padding: const EdgeInsets.only(left: 4), | |
child: Icon( | |
Icons.check_rounded, | |
size: 15.0, | |
color: Colors.grey.shade700, | |
), | |
) | |
]); | |
break; | |
case MessageState.sent: | |
result = Icon( | |
Icons.check_rounded, | |
size: 15.0, | |
color: Colors.grey.shade700, | |
); | |
break; | |
default: | |
result = const SizedBox.shrink(); | |
break; | |
} | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment