Add reply shortcut for messages

This commit is contained in:
Janez T
2026-03-08 20:10:51 +01:00
parent a96f96ca4c
commit f72db76538
3 changed files with 62 additions and 0 deletions

View File

@@ -304,6 +304,51 @@ class _MessagesTabState extends State<MessagesTab> {
if (!mounted) return;
}
Future<void> _replyToMessage(Message message) async {
final l10n = AppLocalizations.of(context)!;
final contactsProvider = context.read<ContactsProvider>();
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<MessagesTab> {
onRefresh: _handleRefresh,
onNavigateToMap: widget.onNavigateToMap,
onMessageTap: _handleMessageTap,
onReplyToMessage: _replyToMessage,
),
),
Positioned(

View File

@@ -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<MessageBubble> {
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),

View File

@@ -14,6 +14,7 @@ class MessagesContent extends StatelessWidget {
final Future<void> Function() onRefresh;
final VoidCallback? onNavigateToMap;
final ValueChanged<Message>? onMessageTap;
final ValueChanged<Message>? 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),
);
},
),