diff --git a/lib/screens/messages_tab.dart b/lib/screens/messages_tab.dart index b56a087..4365ea2 100644 --- a/lib/screens/messages_tab.dart +++ b/lib/screens/messages_tab.dart @@ -304,6 +304,51 @@ class _MessagesTabState extends State { if (!mounted) return; } + Future _replyToMessage(Message message) async { + final l10n = AppLocalizations.of(context)!; + final contactsProvider = context.read(); + + String destinationType; + Contact? recipient; + + if (message.isChannelMessage) { + final channelIdx = message.channelIdx ?? 0; + destinationType = MessageDestinationPreferences.destinationTypeChannel; + + if (channelIdx != 0) { + recipient = contactsProvider.channels.where((contact) { + return contact.publicKey.length > 1 && + contact.publicKey[1] == channelIdx; + }).firstOrNull; + + if (recipient == null) { + ToastLogger.error(context, l10n.cannotReplyContactNotFound); + return; + } + } + } else { + final senderPrefix = message.senderPublicKeyPrefix; + if (senderPrefix == null || senderPrefix.length < 6) { + ToastLogger.error(context, l10n.cannotReplySenderMissing); + return; + } + + recipient = contactsProvider.findContactByPrefix(senderPrefix); + if (recipient == null) { + ToastLogger.error(context, l10n.cannotReplyContactNotFound); + return; + } + + destinationType = recipient.isRoom + ? MessageDestinationPreferences.destinationTypeRoom + : MessageDestinationPreferences.destinationTypeContact; + } + + await _onRecipientSelected(destinationType, recipient); + if (!mounted) return; + _focusNode.requestFocus(); + } + /// Get icon for current destination type IconData _getDestinationIcon() { if (_destinationType == @@ -1538,6 +1583,7 @@ class _MessagesTabState extends State { onRefresh: _handleRefresh, onNavigateToMap: widget.onNavigateToMap, onMessageTap: _handleMessageTap, + onReplyToMessage: _replyToMessage, ), ), Positioned( diff --git a/lib/widgets/messages/message_bubble.dart b/lib/widgets/messages/message_bubble.dart index 9d43055..a454633 100644 --- a/lib/widgets/messages/message_bubble.dart +++ b/lib/widgets/messages/message_bubble.dart @@ -44,6 +44,7 @@ import 'system_message_bubble.dart'; class MessageBubble extends StatefulWidget { final Message message; final VoidCallback? onTap; + final VoidCallback? onReply; final bool isHighlighted; final VoidCallback? onNavigateToMap; @@ -54,6 +55,7 @@ class MessageBubble extends StatefulWidget { super.key, required this.message, this.onTap, + this.onReply, this.isHighlighted = false, this.onNavigateToMap, this.isCompact = false, @@ -209,6 +211,15 @@ class _MessageBubbleState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ + if (!isOwnMessage && widget.onReply != null) + ListTile( + leading: const Icon(Icons.reply), + title: Text(AppLocalizations.of(context)!.reply), + onTap: () { + Navigator.pop(context); + widget.onReply?.call(); + }, + ), // Copy text option ListTile( leading: const Icon(Icons.copy), diff --git a/lib/widgets/messages/messages_content.dart b/lib/widgets/messages/messages_content.dart index 80e1315..c7e094a 100644 --- a/lib/widgets/messages/messages_content.dart +++ b/lib/widgets/messages/messages_content.dart @@ -14,6 +14,7 @@ class MessagesContent extends StatelessWidget { final Future Function() onRefresh; final VoidCallback? onNavigateToMap; final ValueChanged? onMessageTap; + final ValueChanged? onReplyToMessage; const MessagesContent({ super.key, @@ -24,6 +25,7 @@ class MessagesContent extends StatelessWidget { required this.onRefresh, this.onNavigateToMap, this.onMessageTap, + this.onReplyToMessage, }); @override @@ -86,6 +88,9 @@ class MessagesContent extends StatelessWidget { onTap: onMessageTap == null ? null : () => onMessageTap!(message), + onReply: onReplyToMessage == null + ? null + : () => onReplyToMessage!(message), ); }, ),