feat: Enhance message handling with recipient public key and delivery status for improved retry support

This commit is contained in:
Janez T
2025-10-15 10:56:52 +02:00
parent 276d0f4470
commit 823a3c50f7
2 changed files with 62 additions and 9 deletions

View File

@@ -170,7 +170,7 @@ class _MessagesTabState extends State<MessagesTab> {
final devicePublicKey = connectionProvider.deviceInfo.publicKey; final devicePublicKey = connectionProvider.deviceInfo.publicKey;
final senderPublicKeyPrefix = devicePublicKey?.sublist(0, 6); final senderPublicKeyPrefix = devicePublicKey?.sublist(0, 6);
// Create sent message object // Create sent message object with recipient public key for retry support
final sentMessage = Message( final sentMessage = Message(
id: messageId, id: messageId,
messageType: MessageType.contact, messageType: MessageType.contact,
@@ -181,6 +181,7 @@ class _MessagesTabState extends State<MessagesTab> {
text: fullMessage, text: fullMessage,
receivedAt: DateTime.now(), receivedAt: DateTime.now(),
deliveryStatus: MessageDeliveryStatus.sending, deliveryStatus: MessageDeliveryStatus.sending,
recipientPublicKey: roomPublicKey, // Store recipient for retry
// SAR marker data is automatically added by SarMessageParser.enhanceMessage in MessagesProvider // SAR marker data is automatically added by SarMessageParser.enhanceMessage in MessagesProvider
); );
@@ -408,15 +409,42 @@ class _MessageBubble extends StatelessWidget {
// Resend the message // Resend the message
if (failedMessage.messageType == MessageType.contact) { if (failedMessage.messageType == MessageType.contact) {
// Direct message retry - NOT YET IMPLEMENTED // Direct message retry (for SAR markers sent to rooms)
// Would need to look up contact's full public key by senderKeyShort if (failedMessage.recipientPublicKey == null) {
messagesProvider.markMessageFailed(retryMessageId); messagesProvider.markMessageFailed(retryMessageId);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Direct message retry not yet implemented'), content: Text('Cannot retry: recipient information missing'),
backgroundColor: Colors.orange, backgroundColor: Colors.red,
), ),
);
return;
}
// Resend to the same room
final sentSuccessfully = await connectionProvider.sendTextMessage(
contactPublicKey: failedMessage.recipientPublicKey!,
text: failedMessage.text,
messageId: retryMessageId,
); );
if (!sentSuccessfully) {
messagesProvider.markMessageFailed(retryMessageId);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Failed to resend message'),
backgroundColor: Colors.red,
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Retrying message...'),
backgroundColor: Colors.orange,
duration: Duration(seconds: 2),
),
);
}
} else if (failedMessage.messageType == MessageType.channel) { } else if (failedMessage.messageType == MessageType.channel) {
// Channel message retry // Channel message retry
await connectionProvider.sendChannelMessage( await connectionProvider.sendChannelMessage(

View File

@@ -120,6 +120,14 @@ class MessageStorageService {
'sarGpsLon': message.sarGpsCoordinates?.longitude, 'sarGpsLon': message.sarGpsCoordinates?.longitude,
'receivedAtMillis': message.receivedAt.millisecondsSinceEpoch, 'receivedAtMillis': message.receivedAt.millisecondsSinceEpoch,
'senderName': message.senderName, 'senderName': message.senderName,
'deliveryStatus': message.deliveryStatus.name,
'expectedAckTag': message.expectedAckTag,
'suggestedTimeoutMs': message.suggestedTimeoutMs,
'roundTripTimeMs': message.roundTripTimeMs,
'deliveredAtMillis': message.deliveredAt?.millisecondsSinceEpoch,
'recipientPublicKey': message.recipientPublicKey != null
? base64Encode(message.recipientPublicKey!)
: null,
}; };
} }
@@ -155,6 +163,23 @@ class MessageStorageService {
receivedAt: DateTime.fromMillisecondsSinceEpoch( receivedAt: DateTime.fromMillisecondsSinceEpoch(
json['receivedAtMillis'] as int), json['receivedAtMillis'] as int),
senderName: json['senderName'] as String?, senderName: json['senderName'] as String?,
deliveryStatus: json['deliveryStatus'] != null
? MessageDeliveryStatus.values.firstWhere(
(e) => e.name == json['deliveryStatus'],
orElse: () => MessageDeliveryStatus.received,
)
: MessageDeliveryStatus.received,
expectedAckTag: json['expectedAckTag'] as int?,
suggestedTimeoutMs: json['suggestedTimeoutMs'] as int?,
roundTripTimeMs: json['roundTripTimeMs'] as int?,
deliveredAt: json['deliveredAtMillis'] != null
? DateTime.fromMillisecondsSinceEpoch(
json['deliveredAtMillis'] as int)
: null,
recipientPublicKey: json['recipientPublicKey'] != null
? Uint8List.fromList(
base64Decode(json['recipientPublicKey'] as String))
: null,
); );
} catch (e) { } catch (e) {
print('❌ [MessageStorage] Error parsing message from JSON: $e'); print('❌ [MessageStorage] Error parsing message from JSON: $e');