fixing phantom push notification

This commit is contained in:
otsmr 2026-07-07 21:47:58 +02:00
parent 04680e0855
commit 1126d4172b
3 changed files with 62 additions and 53 deletions

View file

@ -14,7 +14,7 @@ import 'package:twonly/src/services/flame.service.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart'; import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
Future<void> handleMedia( Future<bool> handleMedia(
int fromUserId, int fromUserId,
String groupId, String groupId,
EncryptedContent_Media media, EncryptedContent_Media media,
@ -36,7 +36,7 @@ Future<void> handleMedia(
Log.warn( Log.warn(
'[$receiptId] Got reupload for a message that either does not exists (${message == null}) or senderId = ${message?.senderId}', '[$receiptId] Got reupload for a message that either does not exists (${message == null}) or senderId = ${message?.senderId}',
); );
return; return false;
} }
// in case there was already a downloaded file delete it... // in case there was already a downloaded file delete it...
@ -64,7 +64,7 @@ Future<void> handleMedia(
unawaited(startDownloadMedia(mediaFile, false)); unawaited(startDownloadMedia(mediaFile, false));
} }
return; return true;
case EncryptedContent_Media_Type.IMAGE: case EncryptedContent_Media_Type.IMAGE:
mediaType = MediaType.image; mediaType = MediaType.image;
case EncryptedContent_Media_Type.VIDEO: case EncryptedContent_Media_Type.VIDEO:
@ -85,13 +85,13 @@ Future<void> handleMedia(
Log.warn( Log.warn(
'[$receiptId] $fromUserId tried to modify the message from ${messageTmp.senderId}.', '[$receiptId] $fromUserId tried to modify the message from ${messageTmp.senderId}.',
); );
return; return false;
} }
if (messageTmp.mediaId == null) { if (messageTmp.mediaId == null) {
Log.warn( Log.warn(
'[$receiptId] This message already exit without a mediaId. Message is dropped.', '[$receiptId] This message already exit without a mediaId. Message is dropped.',
); );
return; return false;
} }
final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById( final mediaFile = await twonlyDB.mediaFilesDao.getMediaFileById(
messageTmp.mediaId!, messageTmp.mediaId!,
@ -100,7 +100,7 @@ Future<void> handleMedia(
Log.warn( Log.warn(
'[$receiptId] This message and media file already exit and was not requested again. Dropping it.', '[$receiptId] This message and media file already exit and was not requested again. Dropping it.',
); );
return; return false;
} }
if (mediaFile != null) { if (mediaFile != null) {
@ -145,7 +145,7 @@ Future<void> handleMedia(
if (mediaFile == null) { if (mediaFile == null) {
Log.error('[$receiptId] Could not insert media file into database'); Log.error('[$receiptId] Could not insert media file into database');
return; return false;
} }
message = await twonlyDB.messagesDao.insertMessage( message = await twonlyDB.messagesDao.insertMessage(
@ -186,6 +186,7 @@ Future<void> handleMedia(
); );
unawaited(startDownloadMedia(mediaFile!, false)); unawaited(startDownloadMedia(mediaFile!, false));
return true;
} else { } else {
if (mediaFile == null && message == null) { if (mediaFile == null && message == null) {
Log.error( Log.error(
@ -200,6 +201,7 @@ Future<void> handleMedia(
'[$receiptId] Could not insert new message as the message is empty.', '[$receiptId] Could not insert new message as the message is empty.',
); );
} }
return false;
} }
} }

View file

@ -7,7 +7,7 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/api/utils.api.dart'; import 'package:twonly/src/services/api/utils.api.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
Future<void> handleTextMessage( Future<bool> handleTextMessage(
int fromUserId, int fromUserId,
String groupId, String groupId,
EncryptedContent_TextMessage textMessage, EncryptedContent_TextMessage textMessage,
@ -26,7 +26,7 @@ Future<void> handleTextMessage(
Log.warn( Log.warn(
'[$receiptId] $fromUserId tried to overwrite message from ${existing.senderId}. Dropping.', '[$receiptId] $fromUserId tried to overwrite message from ${existing.senderId}. Dropping.',
); );
return; return false;
} }
final message = await twonlyDB.messagesDao.insertMessage( final message = await twonlyDB.messagesDao.insertMessage(
@ -50,4 +50,5 @@ Future<void> handleTextMessage(
if (message != null) { if (message != null) {
Log.info('[$receiptId] Inserted a new text message with ID: ${message.messageId}'); Log.info('[$receiptId] Inserted a new text message with ID: ${message.messageId}');
} }
return message != null;
} }

View file

@ -288,7 +288,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw(
Log.info('[$receiptId] Calling handleEncryptedMessage'); Log.info('[$receiptId] Calling handleEncryptedMessage');
final (a, b) = await handleEncryptedMessage( final result = await handleEncryptedMessage(
fromUserId, fromUserId,
encryptedContent, encryptedContent,
messageType, messageType,
@ -297,9 +297,9 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw(
Log.info('[$receiptId] Finished handleEncryptedMessage'); Log.info('[$receiptId] Finished handleEncryptedMessage');
if (a == null && b == null) { if (result.responseCipherText == null && result.responsePlaintext == null) {
unawaited(FcmNotificationService.updateLastServerMessageTimestamp()); unawaited(FcmNotificationService.updateLastServerMessageTimestamp());
if (Platform.isAndroid) { if (Platform.isAndroid && result.showPushNotification) {
// Message was handled without any error. Show push notification to the user for Android. // Message was handled without any error. Show push notification to the user for Android.
await showPushNotificationFromServerMessages( await showPushNotificationFromServerMessages(
fromUserId, fromUserId,
@ -308,10 +308,10 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw(
} }
} }
return (a, b); return (result.responseCipherText, result.responsePlaintext);
} }
Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage( Future<DecryptedMessageResult> handleEncryptedMessage(
int fromUserId, int fromUserId,
EncryptedContent content, EncryptedContent content,
Message_Type messageType, Message_Type messageType,
@ -352,13 +352,12 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.contactRequest, content.contactRequest,
receiptId, receiptId,
)) { )) {
return ( return DecryptedMessageResult(
null, responsePlaintext: PlaintextContent()
PlaintextContent()
..retryControlError = PlaintextContent_RetryErrorMessage(), ..retryControlError = PlaintextContent_RetryErrorMessage(),
); );
} }
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasErrorMessages()) { if (content.hasErrorMessages()) {
@ -368,7 +367,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
receiptId, receiptId,
groupId: content.hasGroupId() ? content.groupId : null, groupId: content.hasGroupId() ? content.groupId : null,
); );
return (null, null); return const DecryptedMessageResult(showPushNotification: false);
} }
if (content.hasPasswordlessRecovery()) { if (content.hasPasswordlessRecovery()) {
@ -377,7 +376,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.passwordlessRecovery, content.passwordlessRecovery,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasPasswordlessRecoveryHeartbeat()) { if (content.hasPasswordlessRecoveryHeartbeat()) {
@ -386,7 +385,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.passwordlessRecoveryHeartbeat, content.passwordlessRecoveryHeartbeat,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasContactUpdate()) { if (content.hasContactUpdate()) {
@ -396,7 +395,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
senderProfileCounter, senderProfileCounter,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult(showPushNotification: false);
} }
if (content.hasUserDiscoveryRequest()) { if (content.hasUserDiscoveryRequest()) {
@ -405,7 +404,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.userDiscoveryRequest, content.userDiscoveryRequest,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult(showPushNotification: false);
} }
if (content.hasUserDiscoveryUpdate()) { if (content.hasUserDiscoveryUpdate()) {
@ -414,12 +413,12 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.userDiscoveryUpdate, content.userDiscoveryUpdate,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasPushKeys()) { if (content.hasPushKeys()) {
await handlePushKey(fromUserId, content.pushKeys, receiptId); await handlePushKey(fromUserId, content.pushKeys, receiptId);
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasMessageUpdate()) { if (content.hasMessageUpdate()) {
@ -428,7 +427,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.messageUpdate, content.messageUpdate,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasKeyVerificationProof()) { if (content.hasKeyVerificationProof()) {
@ -436,7 +435,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
fromUserId, fromUserId,
content.keyVerificationProof.calculatedMac, content.keyVerificationProof.calculatedMac,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasMediaUpdate()) { if (content.hasMediaUpdate()) {
@ -445,7 +444,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.mediaUpdate, content.mediaUpdate,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (!content.hasGroupId()) { if (!content.hasGroupId()) {
@ -457,7 +456,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
'Messages should have a groupId. Type: $type', 'Messages should have a groupId. Type: $type',
onlyIfSentryEnabled: true, onlyIfSentryEnabled: true,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasGroupCreate()) { if (content.hasGroupCreate()) {
@ -467,7 +466,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.groupCreate, content.groupCreate,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
/// Verify that the user is (still) in that group... /// Verify that the user is (still) in that group...
@ -486,15 +485,14 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
Log.warn( Log.warn(
'[$receiptId] User tries to send message to direct chat while the user does not exist!', '[$receiptId] User tries to send message to direct chat while the user does not exist!',
); );
return ( return DecryptedMessageResult(
EncryptedContent( responseCipherText: EncryptedContent(
errorMessages: EncryptedContent_ErrorMessages( errorMessages: EncryptedContent_ErrorMessages(
type: EncryptedContent_ErrorMessages_Type type: EncryptedContent_ErrorMessages_Type
.ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD, .ERROR_PROCESSING_MESSAGE_CREATED_ACCOUNT_REQUEST_INSTEAD,
relatedReceiptId: receiptId, relatedReceiptId: receiptId,
), ),
), ),
null,
); );
} }
Log.info( Log.info(
@ -512,9 +510,8 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
'[$receiptId] Got group join message, but group does not exist yet, retry later. As probably the GroupCreate was not yet received.', '[$receiptId] Got group join message, but group does not exist yet, retry later. As probably the GroupCreate was not yet received.',
); );
// In case the group join was received before the GroupCreate the sender should send it later again. // In case the group join was received before the GroupCreate the sender should send it later again.
return ( return DecryptedMessageResult(
null, responsePlaintext: PlaintextContent()
PlaintextContent()
..retryControlError = PlaintextContent_RetryErrorMessage(), ..retryControlError = PlaintextContent_RetryErrorMessage(),
); );
} }
@ -522,8 +519,8 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
Log.warn( Log.warn(
'[$receiptId] User $fromUserId tried to access group ${content.groupId}. Sending GROUP_NOT_FOUND_OR_NOT_A_MEMBER error.', '[$receiptId] User $fromUserId tried to access group ${content.groupId}. Sending GROUP_NOT_FOUND_OR_NOT_A_MEMBER error.',
); );
return ( return DecryptedMessageResult(
EncryptedContent( responseCipherText: EncryptedContent(
groupId: content.groupId, groupId: content.groupId,
errorMessages: EncryptedContent_ErrorMessages( errorMessages: EncryptedContent_ErrorMessages(
type: EncryptedContent_ErrorMessages_Type type: EncryptedContent_ErrorMessages_Type
@ -531,14 +528,13 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
relatedReceiptId: receiptId, relatedReceiptId: receiptId,
), ),
), ),
null,
); );
} }
} }
if (content.hasFlameSync()) { if (content.hasFlameSync()) {
await handleFlameSync(content.groupId, content.flameSync, receiptId); await handleFlameSync(content.groupId, content.flameSync, receiptId);
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasGroupUpdate()) { if (content.hasGroupUpdate()) {
@ -548,7 +544,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.groupUpdate, content.groupUpdate,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasGroupJoin()) { if (content.hasGroupJoin()) {
@ -558,13 +554,12 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.groupJoin, content.groupJoin,
receiptId, receiptId,
)) { )) {
return ( return DecryptedMessageResult(
null, responsePlaintext: PlaintextContent()
PlaintextContent()
..retryControlError = PlaintextContent_RetryErrorMessage(), ..retryControlError = PlaintextContent_RetryErrorMessage(),
); );
} }
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasResendGroupPublicKey()) { if (content.hasResendGroupPublicKey()) {
@ -574,7 +569,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.groupJoin, content.groupJoin,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasAdditionalDataMessage()) { if (content.hasAdditionalDataMessage()) {
@ -584,17 +579,17 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.additionalDataMessage, content.additionalDataMessage,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasTextMessage()) { if (content.hasTextMessage()) {
await handleTextMessage( final isNewText = await handleTextMessage(
fromUserId, fromUserId,
content.groupId, content.groupId,
content.textMessage, content.textMessage,
receiptId, receiptId,
); );
return (null, null); return DecryptedMessageResult(showPushNotification: isNewText);
} }
if (content.hasReaction()) { if (content.hasReaction()) {
@ -604,17 +599,17 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
content.reaction, content.reaction,
receiptId, receiptId,
); );
return (null, null); return const DecryptedMessageResult();
} }
if (content.hasMedia()) { if (content.hasMedia()) {
await handleMedia( final isNewMedia = await handleMedia(
fromUserId, fromUserId,
content.groupId, content.groupId,
content.media, content.media,
receiptId, receiptId,
); );
return (null, null); return DecryptedMessageResult(showPushNotification: isNewMedia);
} }
if (content.hasTypingIndicator()) { if (content.hasTypingIndicator()) {
@ -626,7 +621,7 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
); );
} }
return (null, null); return const DecryptedMessageResult();
} }
String _getEncryptedContentType(EncryptedContent content) { String _getEncryptedContentType(EncryptedContent content) {
@ -651,3 +646,14 @@ String _getEncryptedContentType(EncryptedContent content) {
if (content.hasKeyVerificationProof()) return 'keyVerificationProof'; if (content.hasKeyVerificationProof()) return 'keyVerificationProof';
return 'unknown'; return 'unknown';
} }
class DecryptedMessageResult {
const DecryptedMessageResult({
this.responseCipherText,
this.responsePlaintext,
this.showPushNotification = true,
});
final EncryptedContent? responseCipherText;
final PlaintextContent? responsePlaintext;
final bool showPushNotification;
}