From 0391c52f77f89baba6c4b7cd273520ff5ec09e2c Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 1 Oct 2024 16:54:40 +1000 Subject: [PATCH] Deleting reactions when deleting a message --- .../conversation/v2/ConversationViewModel.kt | 2 +- .../securesms/database/ReactionDatabase.kt | 51 ++++++++++++++++++- .../securesms/database/Storage.kt | 6 +++ .../repository/ConversationRepository.kt | 44 ++++------------ .../libsession/database/StorageProtocol.kt | 1 + .../ReceivedMessageHandler.kt | 4 +- 6 files changed, 69 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index ea984af37b..38e160a566 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -239,7 +239,7 @@ class ConversationViewModel( //todo DELETION handle multi select scenarios - //todo DELETION reactions are still visible on "marked as deleted" messages + //todo DELETION seems I can't "delete for everyone" when a message is sent by me on another phone viewModelScope.launch(Dispatchers.IO) { val allSentByCurrentUser = messages.all { it.isOutgoing } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ReactionDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ReactionDatabase.kt index 87c0b6c182..150ec073e1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ReactionDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ReactionDatabase.kt @@ -159,7 +159,7 @@ class ReactionDatabase(context: Context, helper: SQLCipherOpenHelper) : Database ) } - private fun deleteReactions(messageId: MessageId, query: String, args: Array, notifyUnread: Boolean) { + private fun deleteReactions(messageId: MessageId, query: String, args: Array, notifyUnread: Boolean) { writableDatabase.beginTransaction() try { writableDatabase.delete(TABLE_NAME, query, args) @@ -174,7 +174,54 @@ class ReactionDatabase(context: Context, helper: SQLCipherOpenHelper) : Database } finally { writableDatabase.endTransaction() } - } + } + + fun deleteMessageReactions(messageIds: List) { + if (messageIds.isEmpty()) return // Early exit if the list is empty + + val conditions = mutableListOf() + val args = mutableListOf() + + for (messageId in messageIds) { + conditions.add("($MESSAGE_ID = ? AND $IS_MMS = ?)") + args.add(messageId.id.toString()) + args.add(if (messageId.mms) "1" else "0") + } + + val query = conditions.joinToString(" OR ") + + deleteReactions( + messageIds = messageIds, + query = query, + args = args.toTypedArray(), + notifyUnread = false + ) + } + + private fun deleteReactions(messageIds: List, query: String, args: Array, notifyUnread: Boolean) { + writableDatabase.beginTransaction() + try { + writableDatabase.delete(TABLE_NAME, query, args) + + // Update unread status for each message + for (messageId in messageIds) { + val hasReaction = hasReactions(messageId) + if (messageId.mms) { + DatabaseComponent.get(context).mmsDatabase().updateReactionsUnread( + writableDatabase, messageId.id, hasReaction, true, notifyUnread + ) + } else { + DatabaseComponent.get(context).smsDatabase().updateReactionsUnread( + writableDatabase, messageId.id, hasReaction, true, notifyUnread + ) + } + } + + writableDatabase.setTransactionSuccessful() + } finally { + writableDatabase.endTransaction() + } + } private fun hasReactions(messageId: MessageId): Boolean { val query = "$MESSAGE_ID = ? AND $IS_MMS = ?" diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index c2e75120fb..e8ecceddeb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -1732,6 +1732,12 @@ open class Storage( DatabaseComponent.get(context).reactionDatabase().deleteMessageReactions(MessageId(messageId, mms)) } + override fun deleteReactions(messageIds: List, mms: Boolean) { + DatabaseComponent.get(context).reactionDatabase().deleteMessageReactions( + messageIds.map { MessageId(it, mms) } + ) + } + override fun setBlocked(recipients: Iterable, isBlocked: Boolean, fromConfigUpdate: Boolean) { val recipientDb = DatabaseComponent.get(context).recipientDatabase() recipientDb.setBlocked(recipients, isBlocked) diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index 7fa68ad482..40d6fa3e61 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -1,16 +1,13 @@ package org.thoughtcrime.securesms.repository -import network.loki.messenger.libsession_util.util.ExpiryMode - import android.content.ContentResolver import android.content.Context import app.cash.copper.Query import app.cash.copper.flow.observeQuery import dagger.hilt.android.qualifiers.ApplicationContext -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.MarkAsDeletedMessage @@ -27,9 +24,6 @@ import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient -import org.session.libsignal.utilities.Log -import org.session.libsignal.utilities.get -import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.database.DatabaseContentProviders import org.thoughtcrime.securesms.database.DraftDatabase import org.thoughtcrime.securesms.database.ExpirationConfigurationDatabase @@ -47,6 +41,8 @@ import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.DatabaseComponent import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine interface ConversationRepository { fun maybeGetRecipientForThreadId(threadId: Long): Recipient? @@ -212,6 +208,9 @@ class DefaultConversationRepository @Inject constructor( isSms = false, displayedMessage = displayedMessage ) + + // delete reactions + storage.deleteReactions(messageIds = mms.map { it.id }, mms = true) } if(sms.isNotEmpty()){ @@ -222,6 +221,9 @@ class DefaultConversationRepository @Inject constructor( isSms = true, displayedMessage = displayedMessage ) + + // delete reactions + storage.deleteReactions(messageIds = sms.map { it.id }, mms = false) } } @@ -319,34 +321,6 @@ class DefaultConversationRepository @Inject constructor( } } - /* override suspend fun markAsDeletedForEveryone( - threadId: Long, - recipient: Recipient, - message: MessageRecord - ): Result = suspendCoroutine { continuation -> - buildUnsendRequest(recipient, message)?.let { unsendRequest -> - MessageSender.send(unsendRequest, recipient.address) - } - - else // If this thread is NOT in a Community - { - messageDataProvider.deleteMessage(message.id, !message.isMms) - messageDataProvider.getServerHashForMessage(message.id, message.isMms)?.let { serverHash -> - var publicKey = recipient.address.serialize() - if (recipient.isClosedGroupRecipient) { - publicKey = GroupUtil.doubleDecodeGroupID(publicKey).toHexString() - } - SnodeAPI.deleteMessage(publicKey, listOf(serverHash)) - .success { - continuation.resume(Result.success(Unit)) - }.fail { error -> - Log.w("ConversationRepository", "Call to SnodeAPI.deleteMessage failed - attempting to resume..") - continuation.resume(Result.failure(error)) - } - } - } - }*/ - override fun buildUnsendRequest(recipient: Recipient, message: MessageRecord): UnsendRequest? { if (recipient.isCommunityRecipient) return null messageDataProvider.getServerHashForMessage(message.id, message.isMms) ?: return null diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index d81e0f5390..2cb9df77a6 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -227,6 +227,7 @@ interface StorageProtocol { fun removeReaction(emoji: String, messageTimestamp: Long, author: String, notifyUnread: Boolean) fun updateReactionIfNeeded(message: Message, sender: String, openGroupSentTimestamp: Long) fun deleteReactions(messageId: Long, mms: Boolean) + fun deleteReactions(messageIds: List, mms: Boolean) fun setBlocked(recipients: Iterable, isBlocked: Boolean, fromConfigUpdate: Boolean = false) fun setRecipientHash(recipient: Recipient, recipientHash: String?) fun blockedContacts(): List diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index 5fe3e416da..f42e5ad702 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -276,7 +276,6 @@ fun MessageReceiver.handleUnsendRequest(message: UnsendRequest): Long? { val author = message.author ?: return null val (messageIdToDelete, mms) = storage.getMessageIdInDatabase(timestamp, author) ?: return null val messageType = storage.getMessageType(timestamp, author) ?: return null - Log.d("", "*** message type: $messageType") // send a /delete rquest for 1on1 messages if(messageType == MessageType.ONE_ON_ONE) { @@ -300,6 +299,9 @@ fun MessageReceiver.handleUnsendRequest(message: UnsendRequest): Long? { author = author, displayedMessage = context.getString(R.string.deleteMessageDeletedGlobally) ) + // delete reactions + storage.deleteReactions(messageId = messageIdToDelete, mms = mms) + } if (!messageDataProvider.isOutgoingMessage(timestamp)) {