From 41e174becbc72c59addfe583271b227037f6d85e Mon Sep 17 00:00:00 2001 From: otsmr Date: Sat, 22 Aug 2026 21:59:41 +0200 Subject: [PATCH] Improve: Show delivery and read receipt indicators for text messages --- CHANGELOG.md | 1 + .../views/chats/chat_messages.view.dart | 5 +- .../entries/friendly_message_time.comp.dart | 61 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55ad76c1..8927671f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.5.1 +- Improve: Show delivery and read receipt indicators for text messages - Fix: Background audio correctly pauses and resumes when viewing videos ## 0.5.0 diff --git a/lib/src/visual/views/chats/chat_messages.view.dart b/lib/src/visual/views/chats/chat_messages.view.dart index e0cb543f..2c8ef886 100644 --- a/lib/src/visual/views/chats/chat_messages.view.dart +++ b/lib/src/visual/views/chats/chat_messages.view.dart @@ -84,7 +84,10 @@ class _ChatMessagesViewState extends State contactSub?.cancel(); groupActionsSub?.cancel(); _nextTypingIndicator?.cancel(); - textFieldFocus?.dispose(); + try { + textFieldFocus?.dispose(); + // ignore: empty_catches + } catch (e) {} WidgetsBinding.instance.removeObserver(this); super.dispose(); } diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart b/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart index 3fdf953b..6ca2ff66 100644 --- a/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart +++ b/lib/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart @@ -2,8 +2,11 @@ import 'package:clock/clock.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart' show DateFormat; +import 'package:twonly/locator.dart'; +import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/twonly.db.dart'; import 'package:twonly/src/utils/misc.dart'; +import 'package:twonly/src/visual/loader/three_rotating_dots.loader.dart'; class FriendlyMessageTime extends StatelessWidget { const FriendlyMessageTime({ @@ -17,6 +20,8 @@ class FriendlyMessageTime extends StatelessWidget { @override Widget build(BuildContext context) { + final statusIcon = _buildStatusIcon(Colors.grey.shade400); + return Padding( padding: const EdgeInsets.only(left: 6), child: Row( @@ -48,10 +53,66 @@ class FriendlyMessageTime extends StatelessWidget { fontWeight: FontWeight.normal, ), ), + ?statusIcon, ], ), ); } + + Widget? _buildStatusIcon(Color iconColor) { + if (message.type != MessageType.text.name || message.senderId != null) { + return null; + } + + if (message.ackByServer == null) { + return Padding( + padding: const EdgeInsets.only(left: 4), + child: ThreeRotatingDots(size: 8, color: iconColor), + ); + } + + if (message.openedByAll != null || message.openedAt != null) { + return Padding( + padding: const EdgeInsets.only(left: 4), + child: FaIcon( + FontAwesomeIcons.solidEye, + size: 8, + color: iconColor, + ), + ); + } + + // Now check message actions for ackByUserAt + return StreamBuilder>( + stream: twonlyDB.messagesDao.watchMessageActions(message.messageId), + builder: (context, snapshot) { + final actions = snapshot.data ?? []; + final hasAckByUser = actions.any( + (t) => t.$1.type == MessageActionType.ackByUserAt, + ); + + if (hasAckByUser) { + return Padding( + padding: const EdgeInsets.only(left: 4), + child: FaIcon( + FontAwesomeIcons.checkDouble, + size: 8, + color: iconColor, + ), + ); + } + + return Padding( + padding: const EdgeInsets.only(left: 4), + child: FaIcon( + FontAwesomeIcons.check, + size: 8, + color: iconColor, + ), + ); + }, + ); + } } String friendlyTime(BuildContext context, DateTime dt) {