feat: show amount of unread messages

This commit is contained in:
Freek van de Ven 2023-10-27 16:16:42 +02:00
parent f6a2a26def
commit 5e41f3885f
6 changed files with 150 additions and 35 deletions

View file

@ -29,6 +29,24 @@ class FirebaseChatService implements ChatService {
_options = options ?? const FirebaseChatOptions(); _options = options ?? const FirebaseChatOptions();
} }
StreamSubscription<DocumentSnapshot> _addUnreadChatSubscription(
String chatId,
String userId,
Function(int) onUnreadChatsUpdated,
) {
var snapshots = _db
.collection(_options.usersCollectionName)
.doc(userId)
.collection('chats')
.doc(chatId)
.snapshots();
return snapshots.listen((snapshot) {
var data = snapshot.data();
onUnreadChatsUpdated(data?['amount_unread_messages'] ?? 0);
});
}
StreamSubscription<QuerySnapshot> _addChatSubscription( StreamSubscription<QuerySnapshot> _addChatSubscription(
List<String> chatIds, List<String> chatIds,
Function(List<ChatModel>) onReceivedChats, Function(List<ChatModel>) onReceivedChats,
@ -79,6 +97,8 @@ class FirebaseChatService implements ChatService {
); );
} }
} }
ChatModel? chatModel;
if (chatData.personal) { if (chatData.personal) {
var otherUserId = List<String>.from(chatData.users).firstWhere( var otherUserId = List<String>.from(chatData.users).firstWhere(
(element) => element != currentUser?.id, (element) => element != currentUser?.id,
@ -86,8 +106,7 @@ class FirebaseChatService implements ChatService {
var otherUser = await _userService.getUser(otherUserId); var otherUser = await _userService.getUser(otherUserId);
if (otherUser != null) { if (otherUser != null) {
chats.add( chatModel = PersonalChatModel(
PersonalChatModel(
id: chatDoc.id, id: chatDoc.id,
user: otherUser, user: otherUser,
lastMessage: messages.isNotEmpty ? messages.last : null, lastMessage: messages.isNotEmpty ? messages.last : null,
@ -97,7 +116,6 @@ class FirebaseChatService implements ChatService {
: DateTime.fromMillisecondsSinceEpoch( : DateTime.fromMillisecondsSinceEpoch(
chatData.lastUsed!.millisecondsSinceEpoch, chatData.lastUsed!.millisecondsSinceEpoch,
), ),
),
); );
} }
} else { } else {
@ -109,8 +127,7 @@ class FirebaseChatService implements ChatService {
users.add(user); users.add(user);
} }
} }
chats.add( chatModel = GroupChatModel(
GroupChatModel(
id: chatDoc.id, id: chatDoc.id,
title: chatData.title ?? '', title: chatData.title ?? '',
imageUrl: chatData.imageUrl ?? '', imageUrl: chatData.imageUrl ?? '',
@ -122,9 +139,27 @@ class FirebaseChatService implements ChatService {
: DateTime.fromMillisecondsSinceEpoch( : DateTime.fromMillisecondsSinceEpoch(
chatData.lastUsed!.millisecondsSinceEpoch, chatData.lastUsed!.millisecondsSinceEpoch,
), ),
),
); );
} }
if (chatModel != null) {
_addUnreadChatSubscription(chatModel.id ?? '', currentUser?.id ?? '',
(unreadMessages) {
// the chatmodel should be updated to reflect the amount of unread messages
if (chatModel is PersonalChatModel) {
chatModel = (chatModel as PersonalChatModel)
.copyWith(unreadMessages: unreadMessages);
} else if (chatModel is GroupChatModel) {
chatModel = (chatModel as GroupChatModel)
.copyWith(unreadMessages: unreadMessages);
}
chats = chats
.map((chat) => chat.id == chatModel?.id ? chatModel! : chat)
.toList();
onReceivedChats(chats);
});
chats.add(chatModel!);
}
} }
onReceivedChats(chats); onReceivedChats(chats);
}); });

View file

@ -8,12 +8,14 @@ abstract class ChatModel {
ChatModel({ ChatModel({
this.id, this.id,
this.messages = const [], this.messages = const [],
this.unreadMessages,
this.lastUsed, this.lastUsed,
this.lastMessage, this.lastMessage,
}); });
String? id; String? id;
List<ChatMessageModel>? messages; List<ChatMessageModel>? messages;
int? unreadMessages;
DateTime? lastUsed; DateTime? lastUsed;
ChatMessageModel? lastMessage; ChatMessageModel? lastMessage;
} }

View file

@ -13,9 +13,31 @@ class GroupChatModel extends ChatModel {
super.messages, super.messages,
super.lastUsed, super.lastUsed,
super.lastMessage, super.lastMessage,
super.unreadMessages,
}); });
final String title; final String title;
final String imageUrl; final String imageUrl;
final List<ChatUserModel> users; final List<ChatUserModel> users;
GroupChatModel copyWith({
String? id,
List<ChatMessageModel>? messages,
int? unreadMessages,
DateTime? lastUsed,
ChatMessageModel? lastMessage,
String? title,
String? imageUrl,
List<ChatUserModel>? users,
}) =>
GroupChatModel(
id: id ?? this.id,
messages: messages ?? this.messages,
unreadMessages: unreadMessages ?? this.unreadMessages,
lastUsed: lastUsed ?? this.lastUsed,
lastMessage: lastMessage ?? this.lastMessage,
title: title ?? this.title,
imageUrl: imageUrl ?? this.imageUrl,
users: users ?? this.users,
);
} }

View file

@ -9,9 +9,27 @@ class PersonalChatModel extends ChatModel {
required this.user, required this.user,
super.id, super.id,
super.messages, super.messages,
super.unreadMessages,
super.lastUsed, super.lastUsed,
super.lastMessage, super.lastMessage,
}); });
final ChatUserModel user; final ChatUserModel user;
PersonalChatModel copyWith({
String? id,
List<ChatMessageModel>? messages,
int? unreadMessages,
DateTime? lastUsed,
ChatMessageModel? lastMessage,
ChatUserModel? user,
}) =>
PersonalChatModel(
id: id ?? this.id,
messages: messages ?? this.messages,
unreadMessages: unreadMessages ?? this.unreadMessages,
lastUsed: lastUsed ?? this.lastUsed,
lastMessage: lastMessage ?? this.lastMessage,
user: user ?? this.user,
);
} }

View file

@ -7,12 +7,14 @@ import 'package:flutter/material.dart';
class ChatRow extends StatelessWidget { class ChatRow extends StatelessWidget {
const ChatRow({ const ChatRow({
required this.title, required this.title,
this.unreadMessages = 0,
this.lastUsed, this.lastUsed,
this.subTitle, this.subTitle,
this.avatar, this.avatar,
super.key, super.key,
}); });
final String title; final String title;
final int unreadMessages;
final Widget? avatar; final Widget? avatar;
final String? subTitle; final String? subTitle;
final String? lastUsed; final String? lastUsed;
@ -35,9 +37,11 @@ class ChatRow extends StatelessWidget {
title, title,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w500, fontWeight: unreadMessages > 0
? FontWeight.w600
: FontWeight.w400,
), ),
), ),
if (subTitle != null) if (subTitle != null)
@ -45,8 +49,11 @@ class ChatRow extends StatelessWidget {
padding: const EdgeInsets.only(top: 3.0), padding: const EdgeInsets.only(top: 3.0),
child: Text( child: Text(
subTitle!, subTitle!,
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: unreadMessages > 0
? FontWeight.w600
: FontWeight.w400,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1, maxLines: 1,
@ -56,6 +63,10 @@ class ChatRow extends StatelessWidget {
), ),
), ),
), ),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text( Text(
lastUsed ?? '', lastUsed ?? '',
style: const TextStyle( style: const TextStyle(
@ -63,6 +74,29 @@ class ChatRow extends StatelessWidget {
fontSize: 14, fontSize: 14,
), ),
), ),
if (unreadMessages > 0) ...[
Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
shape: BoxShape.circle,
),
child: Center(
child: Text(
unreadMessages.toString(),
style: const TextStyle(
fontSize: 14,
),
),
),
),
),
],
],
),
], ],
); );
} }

View file

@ -99,6 +99,8 @@ class _ChatScreenState extends State<ChatScreen> {
child: widget.options.chatRowContainerBuilder( child: widget.options.chatRowContainerBuilder(
(chat is PersonalChatModel) (chat is PersonalChatModel)
? ChatRow( ? ChatRow(
unreadMessages:
chat.unreadMessages ?? 0,
avatar: avatar:
widget.options.userAvatarBuilder( widget.options.userAvatarBuilder(
chat.user, chat.user,
@ -122,6 +124,8 @@ class _ChatScreenState extends State<ChatScreen> {
) )
: ChatRow( : ChatRow(
title: (chat as GroupChatModel).title, title: (chat as GroupChatModel).title,
unreadMessages:
chat.unreadMessages ?? 0,
subTitle: chat.lastMessage != null subTitle: chat.lastMessage != null
? chat.lastMessage ? chat.lastMessage
is ChatTextMessageModel is ChatTextMessageModel