diff --git a/app/assets/locales/app_en.arb b/app/assets/locales/app_en.arb index 75e423dc..91fcf6db 100644 --- a/app/assets/locales/app_en.arb +++ b/app/assets/locales/app_en.arb @@ -133,6 +133,15 @@ "chat_start_new_chat_title": "Start new chat", "chat_type_message_hint_text": "Type message", + "chat_seen_message_text": "seen", + "chat_seen_by_message_text": " seen by {members}", + "@chat_seen_by_message_text": { + "placeholders": { + "members": { + "type": "String" + } + } + }, "edit_profile_title": "Edit profile", "edit_profile_first_name_title": "First name", diff --git a/app/lib/ui/flow/message/chat/chat_screen.dart b/app/lib/ui/flow/message/chat/chat_screen.dart index c43aabd3..bf85c4e2 100644 --- a/app/lib/ui/flow/message/chat/chat_screen.dart +++ b/app/lib/ui/flow/message/chat/chat_screen.dart @@ -79,17 +79,23 @@ class _ChatScreenState extends ConsumerState { } return Column( children: [ - if (state.showMemberSelectionView) - _memberSelectionView(context, state), - Expanded(child: _chatList(context, state.messages, state.sender, state.loadingMessages, state.threadId)), + if (state.showMemberSelectionView) _memberSelectionView(context, state), + Expanded( + child: _chatList(context, state.messages, state.sender, + state.loadingMessages, state.threadId, state.currentUserId)), const SizedBox(height: 24), _textField(context, state), ], ); } - Widget _chatList(BuildContext context, List messages, - List? sender, bool loadingMessage, String threadId) { + Widget _chatList( + BuildContext context, + List messages, + List? sender, + bool loadingMessage, + String threadId, + String currentUserId) { if (sender == null && sender!.isEmpty) { return const AppProgressIndicator(); } @@ -103,7 +109,9 @@ class _ChatScreenState extends ConsumerState { return const AppProgressIndicator(size: AppProgressIndicatorSize.small); } if (index == messages.length - 1) { - runPostFrame(() => notifier.onLoadMore(widget.threadInfo == null ? threadId : widget.threadInfo?.thread.id ?? '')); + runPostFrame(() => notifier.onLoadMore(widget.threadInfo == null + ? threadId + : widget.threadInfo?.thread.id ?? '')); } final message = messages[index]; @@ -114,13 +122,22 @@ class _ChatScreenState extends ConsumerState { ? sender.firstWhere((member) => member.user.id == message.sender_id).user : null; + final seenBy = widget.threadInfo?.members.where((member) => + message.seen_by.contains(member.user.id) && + member.user.id != currentUserId) + .toList(); + + final showSeenText = notifier.isSender(message) && (seenBy?.isNotEmpty ?? false) && message.id == messages.first.id + ? true + : false; + if (messages.isEmpty) { return const AppProgressIndicator(); } return Column( crossAxisAlignment: notifier.isSender(message) - ? CrossAxisAlignment.start + ? showSeenText ? CrossAxisAlignment.end : CrossAxisAlignment.start : CrossAxisAlignment.end, children: [ if (showDateHeader) @@ -137,6 +154,18 @@ class _ChatScreenState extends ConsumerState { memberCount: sender.length, isDifferentSender: isDifferentSender, ), + if (showSeenText) ...[ + const SizedBox(height: 4), + Text( + sender.length > 2 + ? context.l10n.chat_seen_by_message_text(seenBy!.map((e) => e.user.first_name).join(', ')) + : context.l10n.chat_seen_message_text, + style: AppTextStyle.caption.copyWith( + color: context.colorScheme.textDisabled, + ), + textAlign: TextAlign.right, + ), + ], ], ); }, @@ -199,10 +228,10 @@ class _ChatScreenState extends ConsumerState { ? context.colorScheme.containerLowOnSurface : context.colorScheme.primary, borderRadius: radius( - isSender: isSender, - isLastInGroup: isLastInGroup, - isFirstInGroup: isFirstInGroup, - isDifferentSender: isDifferentSender, + isSender: isSender, + isLastInGroup: isLastInGroup, + isFirstInGroup: isFirstInGroup, + isDifferentSender: isDifferentSender, ), ), child: _chatBubbleView( @@ -467,9 +496,14 @@ class _ChatScreenState extends ConsumerState { IconPrimaryButton( onTap: () { if (state.threadInfo != null || widget.threadInfo != null) { - notifier.sendMessage(state.threadId.isEmpty ? widget.threadInfo?.thread.id ?? '' : state.threadId, state.message.text); + notifier.sendMessage( + state.threadId.isEmpty + ? widget.threadInfo?.thread.id ?? '' + : state.threadId, + state.message.text); } else { - notifier.createNewThread(widget.spaceInfo.space.id, state.message.text); + notifier.createNewThread( + widget.spaceInfo.space.id, state.message.text); } }, icon: Icon(Icons.arrow_forward_rounded, @@ -490,8 +524,8 @@ class _ChatScreenState extends ConsumerState { BorderRadius radius({ required bool isSender, - required bool isLastInGroup, - required bool isFirstInGroup, + required bool isLastInGroup, + required bool isFirstInGroup, required bool isDifferentSender, }) { if (isDifferentSender && !isLastInGroup) {