From 5e41f3885f574ab85f87e7594b0503862bc31812 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Fri, 27 Oct 2023 16:16:42 +0200 Subject: [PATCH] feat: show amount of unread messages --- .../lib/service/firebase_chat_service.dart | 87 +++++++++++++------ .../lib/src/model/chat.dart | 2 + .../lib/src/model/group_chat.dart | 22 +++++ .../lib/src/model/personal_chat.dart | 18 ++++ .../lib/src/components/chat_row.dart | 52 +++++++++-- .../lib/src/screens/chat_screen.dart | 4 + 6 files changed, 150 insertions(+), 35 deletions(-) diff --git a/packages/flutter_community_chat_firebase/lib/service/firebase_chat_service.dart b/packages/flutter_community_chat_firebase/lib/service/firebase_chat_service.dart index adc3554..02921ec 100644 --- a/packages/flutter_community_chat_firebase/lib/service/firebase_chat_service.dart +++ b/packages/flutter_community_chat_firebase/lib/service/firebase_chat_service.dart @@ -29,6 +29,24 @@ class FirebaseChatService implements ChatService { _options = options ?? const FirebaseChatOptions(); } + StreamSubscription _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 _addChatSubscription( List chatIds, Function(List) onReceivedChats, @@ -79,6 +97,8 @@ class FirebaseChatService implements ChatService { ); } } + ChatModel? chatModel; + if (chatData.personal) { var otherUserId = List.from(chatData.users).firstWhere( (element) => element != currentUser?.id, @@ -86,18 +106,16 @@ class FirebaseChatService implements ChatService { var otherUser = await _userService.getUser(otherUserId); if (otherUser != null) { - chats.add( - PersonalChatModel( - id: chatDoc.id, - user: otherUser, - lastMessage: messages.isNotEmpty ? messages.last : null, - messages: messages, - lastUsed: chatData.lastUsed == null - ? null - : DateTime.fromMillisecondsSinceEpoch( - chatData.lastUsed!.millisecondsSinceEpoch, - ), - ), + chatModel = PersonalChatModel( + id: chatDoc.id, + user: otherUser, + lastMessage: messages.isNotEmpty ? messages.last : null, + messages: messages, + lastUsed: chatData.lastUsed == null + ? null + : DateTime.fromMillisecondsSinceEpoch( + chatData.lastUsed!.millisecondsSinceEpoch, + ), ); } } else { @@ -109,22 +127,39 @@ class FirebaseChatService implements ChatService { users.add(user); } } - chats.add( - GroupChatModel( - id: chatDoc.id, - title: chatData.title ?? '', - imageUrl: chatData.imageUrl ?? '', - lastMessage: messages.isNotEmpty ? messages.last : null, - messages: messages, - users: users, - lastUsed: chatData.lastUsed == null - ? null - : DateTime.fromMillisecondsSinceEpoch( - chatData.lastUsed!.millisecondsSinceEpoch, - ), - ), + chatModel = GroupChatModel( + id: chatDoc.id, + title: chatData.title ?? '', + imageUrl: chatData.imageUrl ?? '', + lastMessage: messages.isNotEmpty ? messages.last : null, + messages: messages, + users: users, + lastUsed: chatData.lastUsed == null + ? null + : DateTime.fromMillisecondsSinceEpoch( + 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); }); diff --git a/packages/flutter_community_chat_interface/lib/src/model/chat.dart b/packages/flutter_community_chat_interface/lib/src/model/chat.dart index 7f2c1d4..0b39a27 100644 --- a/packages/flutter_community_chat_interface/lib/src/model/chat.dart +++ b/packages/flutter_community_chat_interface/lib/src/model/chat.dart @@ -8,12 +8,14 @@ abstract class ChatModel { ChatModel({ this.id, this.messages = const [], + this.unreadMessages, this.lastUsed, this.lastMessage, }); String? id; List? messages; + int? unreadMessages; DateTime? lastUsed; ChatMessageModel? lastMessage; } diff --git a/packages/flutter_community_chat_interface/lib/src/model/group_chat.dart b/packages/flutter_community_chat_interface/lib/src/model/group_chat.dart index 934f9dc..cdb816e 100644 --- a/packages/flutter_community_chat_interface/lib/src/model/group_chat.dart +++ b/packages/flutter_community_chat_interface/lib/src/model/group_chat.dart @@ -13,9 +13,31 @@ class GroupChatModel extends ChatModel { super.messages, super.lastUsed, super.lastMessage, + super.unreadMessages, }); final String title; final String imageUrl; final List users; + + GroupChatModel copyWith({ + String? id, + List? messages, + int? unreadMessages, + DateTime? lastUsed, + ChatMessageModel? lastMessage, + String? title, + String? imageUrl, + List? 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, + ); } diff --git a/packages/flutter_community_chat_interface/lib/src/model/personal_chat.dart b/packages/flutter_community_chat_interface/lib/src/model/personal_chat.dart index 4e3d161..0733907 100644 --- a/packages/flutter_community_chat_interface/lib/src/model/personal_chat.dart +++ b/packages/flutter_community_chat_interface/lib/src/model/personal_chat.dart @@ -9,9 +9,27 @@ class PersonalChatModel extends ChatModel { required this.user, super.id, super.messages, + super.unreadMessages, super.lastUsed, super.lastMessage, }); final ChatUserModel user; + + PersonalChatModel copyWith({ + String? id, + List? 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, + ); } diff --git a/packages/flutter_community_chat_view/lib/src/components/chat_row.dart b/packages/flutter_community_chat_view/lib/src/components/chat_row.dart index 9a699cf..5b87f54 100644 --- a/packages/flutter_community_chat_view/lib/src/components/chat_row.dart +++ b/packages/flutter_community_chat_view/lib/src/components/chat_row.dart @@ -7,12 +7,14 @@ import 'package:flutter/material.dart'; class ChatRow extends StatelessWidget { const ChatRow({ required this.title, + this.unreadMessages = 0, this.lastUsed, this.subTitle, this.avatar, super.key, }); final String title; + final int unreadMessages; final Widget? avatar; final String? subTitle; final String? lastUsed; @@ -35,9 +37,11 @@ class ChatRow extends StatelessWidget { title, maxLines: 1, overflow: TextOverflow.ellipsis, - style: const TextStyle( + style: TextStyle( fontSize: 18, - fontWeight: FontWeight.w500, + fontWeight: unreadMessages > 0 + ? FontWeight.w600 + : FontWeight.w400, ), ), if (subTitle != null) @@ -45,8 +49,11 @@ class ChatRow extends StatelessWidget { padding: const EdgeInsets.only(top: 3.0), child: Text( subTitle!, - style: const TextStyle( + style: TextStyle( fontSize: 16, + fontWeight: unreadMessages > 0 + ? FontWeight.w600 + : FontWeight.w400, ), overflow: TextOverflow.ellipsis, maxLines: 1, @@ -56,12 +63,39 @@ class ChatRow extends StatelessWidget { ), ), ), - Text( - lastUsed ?? '', - style: const TextStyle( - color: Color(0xFFBBBBBB), - fontSize: 14, - ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + lastUsed ?? '', + style: const TextStyle( + color: Color(0xFFBBBBBB), + 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, + ), + ), + ), + ), + ), + ], + ], ), ], ); diff --git a/packages/flutter_community_chat_view/lib/src/screens/chat_screen.dart b/packages/flutter_community_chat_view/lib/src/screens/chat_screen.dart index 7fdfa29..fe038d1 100644 --- a/packages/flutter_community_chat_view/lib/src/screens/chat_screen.dart +++ b/packages/flutter_community_chat_view/lib/src/screens/chat_screen.dart @@ -99,6 +99,8 @@ class _ChatScreenState extends State { child: widget.options.chatRowContainerBuilder( (chat is PersonalChatModel) ? ChatRow( + unreadMessages: + chat.unreadMessages ?? 0, avatar: widget.options.userAvatarBuilder( chat.user, @@ -122,6 +124,8 @@ class _ChatScreenState extends State { ) : ChatRow( title: (chat as GroupChatModel).title, + unreadMessages: + chat.unreadMessages ?? 0, subTitle: chat.lastMessage != null ? chat.lastMessage is ChatTextMessageModel