diff --git a/CHANGELOG.md b/CHANGELOG.md index 62f602ff..9f375c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.5.2 + +- Hotfix: Fixed an infinite loop involving delivery receipts + ## 0.5.1 - Improve: Show delivery and read receipt indicators for text messages diff --git a/lib/src/database/daos/receipts.dao.dart b/lib/src/database/daos/receipts.dao.dart index d15b85e0..daa68a97 100644 --- a/lib/src/database/daos/receipts.dao.dart +++ b/lib/src/database/daos/receipts.dao.dart @@ -250,6 +250,25 @@ class ReceiptsDao extends DatabaseAccessor with _$ReceiptsDaoMixin { null; } + /// Claims a new delivery-receipt attempt after [cooldown] has elapsed. + /// + /// Updating the timestamp before sending prevents repeated server batches from + /// starting multiple delivery-receipt attempts during the same cooldown. + Future claimDuplicateReceiptResend( + String receiptId, + Duration cooldown, + ) async { + final now = clock.now(); + final updated = + await (update(receivedReceipts)..where( + (t) => + t.receiptId.equals(receiptId) & + t.createdAt.isSmallerOrEqualValue(now.subtract(cooldown)), + )) + .write(ReceivedReceiptsCompanion(createdAt: Value(now))); + return updated > 0; + } + Future gotReceipt(String receiptId) async { await into( receivedReceipts, diff --git a/lib/src/services/api/api.service.dart b/lib/src/services/api/api.service.dart index 4c918689..3ab2eca6 100644 --- a/lib/src/services/api/api.service.dart +++ b/lib/src/services/api/api.service.dart @@ -293,9 +293,17 @@ class ApiService { return result; } - Future sendResponse(ClientToServer response) async { - if (_channel != null) { - _channel!.sink.add(response.writeToBuffer()); + Future sendResponse(ClientToServer response) async { + final channel = _channel; + if (channel == null || channel.closeCode != null) { + return false; + } + try { + channel.sink.add(response.writeToBuffer()); + return true; + } catch (e) { + Log.warn('Could not send response to server: $e'); + return false; } } diff --git a/lib/src/services/api/server_messages.api.dart b/lib/src/services/api/server_messages.api.dart index 85e7c088..712f28b0 100644 --- a/lib/src/services/api/server_messages.api.dart +++ b/lib/src/services/api/server_messages.api.dart @@ -80,9 +80,16 @@ Future handleServerMessage(server.ServerToClient msg) async { ..seq = msg.v0.seq ..response = response; - await apiService.sendResponse(ClientToServer()..v0 = v0); + final responseSent = await apiService.sendResponse(ClientToServer()..v0 = v0); + if (responseSent) { + Log.info( + 'Successfully queued response for server message ${msg.v0.seq}.', + ); + } else { + Log.warn('Could not send response for server message ${msg.v0.seq}.'); + } AppState.gotMessageFromServer = true; - Log.info('All messages from the server proccessed.'); + Log.info('All messages from the server processed.'); } DateTime lastPushKeyRequest = clock.now().subtract(const Duration(hours: 1)); @@ -141,8 +148,25 @@ Future _handleClient2ClientMessage( } if (await twonlyDB.receiptsDao.isDuplicated(receiptId)) { + if (message.type == Message_Type.SENDER_DELIVERY_RECEIPT) { + Log.info( + '[$receiptId] Delivery receipt is a duplicate. Skipping receipt response.', + ); + return; + } + const duplicateReceiptCooldown = Duration(days: 10); + final shouldResend = await twonlyDB.receiptsDao.claimDuplicateReceiptResend( + receiptId, + duplicateReceiptCooldown, + ); + if (!shouldResend) { + Log.info( + '[$receiptId] Message is a duplicate. Skipping delivery receipt during cooldown.', + ); + return; + } Log.info( - '[$receiptId] Message is a duplicate. Sending delivery receipt again.', + '[$receiptId] Message is a duplicate and cooldown elapsed. Sending delivery receipt again.', ); try { final response = Message(type: Message_Type.SENDER_DELIVERY_RECEIPT); diff --git a/pubspec.yaml b/pubspec.yaml index 1d672695..5e838714 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec publish_to: 'none' -version: 0.5.1+171 +version: 0.5.2+172 environment: sdk: ^3.11.0