mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-25 02:55:23 +00:00
Group message deletion
This commit is contained in:
parent
86a9e07f31
commit
ea714a60a2
@ -610,13 +610,12 @@ class ConversationViewModel(
|
||||
}
|
||||
|
||||
private fun markAsDeletedForEveryoneGroupsV2(data: DeleteForEveryoneDialogData){
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
// show a loading indicator
|
||||
_uiState.update { it.copy(showLoader = true) }
|
||||
|
||||
//todo GROUPS V2 - uncomment below and use Fanchao's method to delete a group V2
|
||||
try {
|
||||
//repository.callMethodFromFanchao(threadId, recipient, data.messages)
|
||||
repository.deleteGroupV2MessagesRemotely(recipient!!, data.messages)
|
||||
|
||||
// the repo will handle the internal logic (calling `/delete` on the swarm
|
||||
// and sending 'GroupUpdateDeleteMemberContentMessage'
|
||||
@ -638,7 +637,7 @@ class ConversationViewModel(
|
||||
).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w("Loki", "FAILED TO delete messages ${data.messages} ")
|
||||
Log.e("Loki", "FAILED TO delete messages ${data.messages}", e)
|
||||
// failed to delete - show a toast and get back on the modal
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
|
@ -3,31 +3,34 @@ package org.thoughtcrime.securesms.database
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase.CONFLICT_REPLACE
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.json.JSONArray
|
||||
import org.session.libsession.database.ServerHashToMessageId
|
||||
import org.session.libsignal.database.LokiMessageDatabaseProtocol
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.util.asSequence
|
||||
|
||||
class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol {
|
||||
|
||||
companion object {
|
||||
private val messageIDTable = "loki_message_friend_request_database"
|
||||
private val messageThreadMappingTable = "loki_message_thread_mapping_database"
|
||||
private val errorMessageTable = "loki_error_message_database"
|
||||
private val messageHashTable = "loki_message_hash_database"
|
||||
private val smsHashTable = "loki_sms_hash_database"
|
||||
private val mmsHashTable = "loki_mms_hash_database"
|
||||
private const val messageIDTable = "loki_message_friend_request_database"
|
||||
private const val messageThreadMappingTable = "loki_message_thread_mapping_database"
|
||||
private const val errorMessageTable = "loki_error_message_database"
|
||||
private const val messageHashTable = "loki_message_hash_database"
|
||||
private const val smsHashTable = "loki_sms_hash_database"
|
||||
private const val mmsHashTable = "loki_mms_hash_database"
|
||||
const val groupInviteTable = "loki_group_invites"
|
||||
|
||||
private val groupInviteDeleteTrigger = "group_invite_delete_trigger"
|
||||
private const val groupInviteDeleteTrigger = "group_invite_delete_trigger"
|
||||
|
||||
private val messageID = "message_id"
|
||||
private val serverID = "server_id"
|
||||
private val friendRequestStatus = "friend_request_status"
|
||||
private val threadID = "thread_id"
|
||||
private val errorMessage = "error_message"
|
||||
private val messageType = "message_type"
|
||||
private val serverHash = "server_hash"
|
||||
private const val messageID = "message_id"
|
||||
private const val serverID = "server_id"
|
||||
private const val friendRequestStatus = "friend_request_status"
|
||||
private const val threadID = "thread_id"
|
||||
private const val errorMessage = "error_message"
|
||||
private const val messageType = "message_type"
|
||||
private const val serverHash = "server_hash"
|
||||
const val invitingSessionId = "inviting_session_id"
|
||||
|
||||
@JvmStatic
|
||||
@ -236,46 +239,52 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
}
|
||||
|
||||
fun getSendersForHashes(threadId: Long, hashes: Set<String>): List<ServerHashToMessageId> {
|
||||
val smsQuery = "SELECT ${SmsDatabase.TABLE_NAME}.${MmsSmsColumns.ADDRESS}, $smsHashTable.$serverHash, " +
|
||||
"${SmsDatabase.TABLE_NAME}.${MmsSmsColumns.ID} FROM $smsHashTable LEFT OUTER JOIN ${SmsDatabase.TABLE_NAME} " +
|
||||
"ON ${SmsDatabase.TABLE_NAME}.${MmsSmsColumns.ID} = $smsHashTable.$messageID WHERE ${SmsDatabase.TABLE_NAME}.${MmsSmsColumns.THREAD_ID} = ?;"
|
||||
val mmsQuery = "SELECT ${MmsDatabase.TABLE_NAME}.${MmsSmsColumns.ADDRESS}, $mmsHashTable.$serverHash, " +
|
||||
"${MmsDatabase.TABLE_NAME}.${MmsSmsColumns.ID} FROM $mmsHashTable LEFT OUTER JOIN ${MmsDatabase.TABLE_NAME} " +
|
||||
"ON ${MmsDatabase.TABLE_NAME}.${MmsSmsColumns.ID} = $mmsHashTable.$messageID WHERE ${MmsDatabase.TABLE_NAME}.${MmsSmsColumns.THREAD_ID} = ?;"
|
||||
val smsCursor = databaseHelper.readableDatabase.query(smsQuery, arrayOf(threadId))
|
||||
val mmsCursor = databaseHelper.readableDatabase.query(mmsQuery, arrayOf(threadId))
|
||||
@Language("RoomSql")
|
||||
val query = """
|
||||
WITH
|
||||
sender_hash_mapping AS (
|
||||
SELECT
|
||||
sms_hash_table.$serverHash AS hash,
|
||||
sms.${MmsSmsColumns.ID} AS message_id,
|
||||
sms.${MmsSmsColumns.ADDRESS} AS sender,
|
||||
sms.${SmsDatabase.TYPE} AS type,
|
||||
true AS is_sms
|
||||
FROM $smsHashTable sms_hash_table
|
||||
LEFT OUTER JOIN ${SmsDatabase.TABLE_NAME} sms ON sms_hash_table.${messageID} = sms.${MmsSmsColumns.ID}
|
||||
WHERE sms.${MmsSmsColumns.THREAD_ID} = :threadId
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
mms_hash_table.$serverHash,
|
||||
mms.${MmsSmsColumns.ID},
|
||||
mms.${MmsSmsColumns.ADDRESS},
|
||||
mms.${MmsDatabase.MESSAGE_TYPE},
|
||||
false
|
||||
FROM $mmsHashTable mms_hash_table
|
||||
LEFT OUTER JOIN ${MmsDatabase.TABLE_NAME} mms ON mms_hash_table.${messageID} = mms.${MmsSmsColumns.ID}
|
||||
WHERE mms.${MmsSmsColumns.THREAD_ID} = :threadId
|
||||
)
|
||||
SELECT * FROM sender_hash_mapping
|
||||
WHERE hash IN (SELECT value FROM json_each(:hashes))
|
||||
""".trimIndent()
|
||||
|
||||
val serverHashToMessageIds = mutableListOf<ServerHashToMessageId>()
|
||||
|
||||
smsCursor.use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
val hash = cursor.getString(1)
|
||||
if (hash in hashes) {
|
||||
serverHashToMessageIds += ServerHashToMessageId(
|
||||
serverHash = hash,
|
||||
isSms = true,
|
||||
sender = cursor.getString(0),
|
||||
messageId = cursor.getLong(2)
|
||||
)
|
||||
}
|
||||
val result = databaseHelper.readableDatabase.query(query, arrayOf(threadId, JSONArray(hashes).toString()))
|
||||
.use { cursor ->
|
||||
cursor.asSequence()
|
||||
.map {
|
||||
ServerHashToMessageId(
|
||||
serverHash = cursor.getString(0),
|
||||
messageId = cursor.getLong(1),
|
||||
sender = cursor.getString(2),
|
||||
isSms = cursor.getInt(4) == 1,
|
||||
isOutgoing = MmsSmsColumns.Types.isOutgoingMessageType(cursor.getLong(3))
|
||||
)
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
|
||||
mmsCursor.use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
val hash = cursor.getString(1)
|
||||
if (hash in hashes) {
|
||||
serverHashToMessageIds += ServerHashToMessageId(
|
||||
serverHash = hash,
|
||||
isSms = false,
|
||||
sender = cursor.getString(0),
|
||||
messageId = cursor.getLong(2)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serverHashToMessageIds
|
||||
return result
|
||||
}
|
||||
|
||||
fun getMessageServerHash(messageID: Long, mms: Boolean): String? = getMessageTables(mms).firstNotNullOfOrNull {
|
||||
|
@ -296,15 +296,21 @@ open class Storage @Inject constructor(
|
||||
closedGroupId: String
|
||||
): Boolean {
|
||||
val threadId = getThreadId(fromSerialized(closedGroupId))!!
|
||||
val senderIsMe = sender == getUserPublicKey()
|
||||
|
||||
val info = lokiMessageDatabase.getSendersForHashes(threadId, hashes)
|
||||
return info.all { it.sender == sender }
|
||||
|
||||
if (senderIsMe) {
|
||||
return info.all { it.isOutgoing }
|
||||
} else {
|
||||
return info.all { it.sender == sender }
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteMessagesByHash(threadId: Long, hashes: List<String>) {
|
||||
val info = lokiMessageDatabase.getSendersForHashes(threadId, hashes.toSet())
|
||||
for ((serverHash, sender, messageIdToDelete, isSms) in info) {
|
||||
messageDataProvider.deleteMessage(messageIdToDelete, isSms)
|
||||
if (!messageDataProvider.isOutgoingMessage(messageIdToDelete)) {
|
||||
for (info in lokiMessageDatabase.getSendersForHashes(threadId, hashes.toSet())) {
|
||||
messageDataProvider.deleteMessage(info.messageId, info.isSms)
|
||||
if (!info.isOutgoing) {
|
||||
notificationManager.updateNotification(context)
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import network.loki.messenger.libsession_util.util.GroupMember
|
||||
import network.loki.messenger.libsession_util.util.INVITE_STATUS_FAILED
|
||||
import network.loki.messenger.libsession_util.util.INVITE_STATUS_SENT
|
||||
import network.loki.messenger.libsession_util.util.UserPic
|
||||
import org.session.libsession.database.MessageDataProvider
|
||||
import org.session.libsession.database.StorageProtocol
|
||||
import org.session.libsession.database.userAuth
|
||||
import org.session.libsession.messaging.groups.GroupManagerV2
|
||||
@ -74,6 +75,7 @@ class GroupManagerV2Impl @Inject constructor(
|
||||
private val profileManager: SSKEnvironment.ProfileManagerProtocol,
|
||||
@ApplicationContext val application: Context,
|
||||
private val clock: SnodeClock,
|
||||
private val messageDataProvider: MessageDataProvider,
|
||||
) : GroupManagerV2 {
|
||||
private val dispatcher = Dispatchers.Default
|
||||
|
||||
@ -865,7 +867,7 @@ class GroupManagerV2Impl @Inject constructor(
|
||||
|
||||
override suspend fun requestMessageDeletion(
|
||||
groupId: AccountId,
|
||||
messageHashes: List<String>
|
||||
messageHashes: Set<String>
|
||||
): Unit = withContext(dispatcher) {
|
||||
// To delete messages from a group, there are a few considerations:
|
||||
// 1. Messages are stored on every member's device, we need a way to ask them to delete their stored messages
|
||||
@ -883,7 +885,7 @@ class GroupManagerV2Impl @Inject constructor(
|
||||
check(
|
||||
group.hasAdminKey() ||
|
||||
storage.ensureMessageHashesAreSender(
|
||||
messageHashes.toSet(),
|
||||
messageHashes,
|
||||
userPubKey,
|
||||
groupId.hexString
|
||||
)
|
||||
@ -896,7 +898,7 @@ class GroupManagerV2Impl @Inject constructor(
|
||||
SnodeAPI.deleteMessage(
|
||||
publicKey = groupId.hexString,
|
||||
swarmAuth = OwnedSwarmAuth.ofClosedGroup(groupId, adminKey),
|
||||
serverHashes = messageHashes
|
||||
serverHashes = messageHashes.toList()
|
||||
)
|
||||
}
|
||||
|
||||
@ -958,8 +960,8 @@ class GroupManagerV2Impl @Inject constructor(
|
||||
groupId.hexString
|
||||
)
|
||||
) {
|
||||
// ensure that all message hashes belong to user
|
||||
// storage delete
|
||||
// For deleting message by hashes, we'll likely only need to mark
|
||||
// them as deleted
|
||||
storage.deleteMessagesByHash(threadId, hashes)
|
||||
}
|
||||
}
|
||||
|
@ -77,6 +77,8 @@ interface ConversationRepository {
|
||||
messages: Set<MessageRecord>
|
||||
)
|
||||
|
||||
suspend fun deleteGroupV2MessagesRemotely(recipient: Recipient, messages: Set<MessageRecord>)
|
||||
|
||||
suspend fun banUser(threadId: Long, recipient: Recipient): Result<Unit>
|
||||
suspend fun banAndDeleteAll(threadId: Long, recipient: Recipient): Result<Unit>
|
||||
suspend fun deleteThread(threadId: Long): Result<Unit>
|
||||
@ -315,6 +317,20 @@ class DefaultConversationRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteGroupV2MessagesRemotely(
|
||||
recipient: Recipient,
|
||||
messages: Set<MessageRecord>
|
||||
) {
|
||||
require(recipient.isGroupV2Recipient) { "Recipient is not a group v2 recipient" }
|
||||
|
||||
val groupId = AccountId(recipient.address.serialize())
|
||||
val hashes = messages.mapNotNullTo(mutableSetOf()) { msg ->
|
||||
messageDataProvider.getServerHashForMessage(msg.id, msg.isMms)
|
||||
}
|
||||
|
||||
groupManager.requestMessageDeletion(groupId, hashes)
|
||||
}
|
||||
|
||||
override suspend fun deleteNoteToSelfMessagesRemotely(
|
||||
threadId: Long,
|
||||
recipient: Recipient,
|
||||
|
@ -2,7 +2,11 @@ package org.session.libsession.database
|
||||
|
||||
data class ServerHashToMessageId(
|
||||
val serverHash: String,
|
||||
/**
|
||||
* This will only be the "sender" when the message is incoming.
|
||||
*/
|
||||
val sender: String,
|
||||
val messageId: Long,
|
||||
val isSms: Boolean,
|
||||
val isOutgoing: Boolean,
|
||||
)
|
@ -80,7 +80,7 @@ interface GroupManagerV2 {
|
||||
* It can be called by a regular member who wishes to delete their own messages.
|
||||
* It can also called by an admin, who can delete any messages from any member.
|
||||
*/
|
||||
suspend fun requestMessageDeletion(groupId: AccountId, messageHashes: List<String>)
|
||||
suspend fun requestMessageDeletion(groupId: AccountId, messageHashes: Set<String>)
|
||||
|
||||
/**
|
||||
* Handle a request to delete a member's content from the group. This is called when we receive
|
||||
|
Loading…
Reference in New Issue
Block a user