diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt index c582f909e1..91040365c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -137,9 +137,19 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) return openGroupMessagingDatabase.getMessageID(serverID) } - override fun deleteMessage(messageID: Long) { - val messagingDatabase = DatabaseFactory.getSmsDatabase(context) - messagingDatabase.deleteMessage(messageID) + override fun getMessageID(serverId: Long, threadId: Long): Pair? { + val messageDB = DatabaseFactory.getLokiMessageDatabase(context) + return messageDB.getMessageID(serverId, threadId) + } + + override fun deleteMessage(messageID: Long, isSms: Boolean) { + if (isSms) { + val db = DatabaseFactory.getSmsDatabase(context) + db.deleteMessage(messageID) + } else { + val db = DatabaseFactory.getMmsDatabase(context) + db.delete(messageID) + } } override fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 74a9d42af0..addf213317 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -1718,7 +1718,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity boolean initiating = threadId == -1; boolean needsSplit = message.length() > characterCalculator.calculateCharacters(message).maxPrimaryMessageSize; boolean isMediaMessage = attachmentManager.isAttachmentPresent() || - recipient.isGroupRecipient() || +// recipient.isGroupRecipient() || inputPanel.getQuote().isPresent() || linkPreviewViewModel.hasLinkPreview() || LinkPreviewUtil.isValidMediaUrl(message) || // Loki - Send GIFs as media messages diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 4a10c169b1..38cb56be5b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -533,7 +533,7 @@ public class ConversationFragment extends Fragment boolean isSentByUser = true; for (MessageRecord messageRecord : messageRecords) { isSentByUser = isSentByUser && messageRecord.isOutgoing(); - Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); + Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms()); if (serverID != null) { serverIDs.add(serverID); } else { @@ -545,7 +545,7 @@ public class ConversationFragment extends Fragment .deleteMessages(serverIDs, publicChat.getChannel(), publicChat.getServer(), isSentByUser) .success(l -> { for (MessageRecord messageRecord : messageRecords) { - Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); + Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms()); if (l.contains(serverID)) { if (messageRecord.isMms()) { DatabaseFactory.getMmsDatabase(getActivity()).delete(messageRecord.getId()); @@ -568,7 +568,7 @@ public class ConversationFragment extends Fragment .deleteMessage(serverId, openGroupChat.getRoom(), openGroupChat.getServer()) .success(l -> { for (MessageRecord messageRecord : messageRecords) { - Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id); + Long serverID = DatabaseFactory.getLokiMessageDatabase(getContext()).getServerID(messageRecord.id, !messageRecord.isMms()); if (serverID != null && serverID.equals(serverId)) { if (messageRecord.isMms()) { DatabaseFactory.getMmsDatabase(getContext()).delete(messageRecord.getId()); 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 d0b12365d3..55fa1d2adf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -388,8 +388,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, return database.getMessageFor(timestamp, address)?.getId() } - override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long) { - DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID) + override fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean) { + DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, serverID, isSms) + DatabaseFactory.getLokiMessageDatabase(context).setOriginalThreadID(messageID, serverID, threadID) } override fun getQuoteServerID(quoteID: Long, publicKey: String): Long? { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 88d8c679f3..05a940b443 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -54,9 +54,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV20 = 41; private static final int lokiV21 = 42; private static final int lokiV22 = 43; + private static final int lokiV23 = 44; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes - private static final int DATABASE_VERSION = lokiV22; + private static final int DATABASE_VERSION = lokiV23; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -124,6 +125,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { db.execSQL(LokiUserDatabase.getCreateServerDisplayNameTableCommand()); db.execSQL(LokiBackupFilesDatabase.getCreateTableCommand()); db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); + db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType()); + db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable()); executeStatements(db, SmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS); @@ -272,6 +275,11 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { "SendDeliveryReceiptJob"); } + if (oldVersion < lokiV23) { + db.execSQL(LokiMessageDatabase.getUpdateMessageIDTableForType()); + db.execSQL(LokiMessageDatabase.getUpdateMessageMappingTable()); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt index 66c1907405..eedff1a964 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/database/LokiMessageDatabase.kt @@ -6,11 +6,8 @@ import org.session.libsession.messaging.threads.Address import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper -import org.thoughtcrime.securesms.loki.utilities.get -import org.thoughtcrime.securesms.loki.utilities.getInt -import org.thoughtcrime.securesms.loki.utilities.getString -import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol +import org.thoughtcrime.securesms.loki.utilities.* class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiMessageDatabaseProtocol { @@ -23,14 +20,21 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab private val friendRequestStatus = "friend_request_status" private val threadID = "thread_id" private val errorMessage = "error_message" + private val messageType = "message_type" @JvmStatic val createMessageIDTableCommand = "CREATE TABLE $messageIDTable ($messageID INTEGER PRIMARY KEY, $serverID INTEGER DEFAULT 0, $friendRequestStatus INTEGER DEFAULT 0);" @JvmStatic val createMessageToThreadMappingTableCommand = "CREATE TABLE IF NOT EXISTS $messageThreadMappingTable ($messageID INTEGER PRIMARY KEY, $threadID INTEGER);" @JvmStatic val createErrorMessageTableCommand = "CREATE TABLE IF NOT EXISTS $errorMessageTable ($messageID INTEGER PRIMARY KEY, $errorMessage STRING);" + @JvmStatic val updateMessageIDTableForType = "ALTER TABLE $messageIDTable ADD COLUMN $messageType INTEGER DEFAULT 0; ALTER TABLE $messageIDTable ADD CONSTRAINT PK_$messageIDTable PRIMARY KEY ($messageID, $serverID);" + @JvmStatic val updateMessageMappingTable = "ALTER TABLE $messageThreadMappingTable ADD COLUMN $serverID INTEGER DEFAULT 0; ALTER TABLE $messageThreadMappingTable ADD CONSTRAINT PK_$messageThreadMappingTable PRIMARY KEY ($messageID, $serverID);" + + const val SMS_TYPE = 0 + const val MMS_TYPE = 1 + } override fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? { val message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quoteID, quoteePublicKey) - return if (message != null) getServerID(message.getId()) else null + return if (message != null) getServerID(message.getId(), !message.isMms) else null } fun getServerID(messageID: Long): Long? { @@ -40,6 +44,13 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab }?.toLong() } + fun getServerID(messageID: Long, isSms: Boolean): Long? { + val database = databaseHelper.readableDatabase + return database.get(messageIDTable, "${Companion.messageID} = ? AND $messageType = ?", arrayOf( messageID.toString(), if (isSms) SMS_TYPE.toString() else MMS_TYPE.toString() )) { cursor -> + cursor.getInt(serverID) + }?.toLong() + } + fun getMessageID(serverID: Long): Long? { val database = databaseHelper.readableDatabase return database.get(messageIDTable, "${Companion.serverID} = ?", arrayOf( serverID.toString() )) { cursor -> @@ -47,12 +58,22 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab }?.toLong() } - override fun setServerID(messageID: Long, serverID: Long) { + fun getMessageID(serverID: Long, threadID: Long): Pair? { + val database = databaseHelper.readableDatabase + return database.get("$messageIDTable INNER JOIN $messageThreadMappingTable ON $messageIDTable.$messageID = $messageThreadMappingTable.$messageID", + "${Companion.serverID} = ? AND ${Companion.threadID} = ?", + arrayOf(serverID.toString(),threadID.toString())) { cursor -> + cursor.getLong(messageID) to (cursor.getInt(messageType) == SMS_TYPE) + } + } + + override fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) { val database = databaseHelper.writableDatabase val contentValues = ContentValues(2) contentValues.put(Companion.messageID, messageID) contentValues.put(Companion.serverID, serverID) - database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) + contentValues.put(messageType, if (isSms) SMS_TYPE else MMS_TYPE) + database.insertOrUpdate(messageIDTable, contentValues, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf( messageID.toString(), serverID.toString() )) } fun getOriginalThreadID(messageID: Long): Long { @@ -62,12 +83,13 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab }?.toLong() ?: -1L } - fun setOriginalThreadID(messageID: Long, threadID: Long) { + fun setOriginalThreadID(messageID: Long, serverID: Long, threadID: Long) { val database = databaseHelper.writableDatabase val contentValues = ContentValues(2) contentValues.put(Companion.messageID, messageID) + contentValues.put(Companion.serverID, serverID) contentValues.put(Companion.threadID, threadID) - database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ?", arrayOf( messageID.toString() )) + database.insertOrUpdate(messageThreadMappingTable, contentValues, "${Companion.messageID} = ? AND ${Companion.serverID} = ?", arrayOf( messageID.toString(), serverID.toString() )) } fun getErrorMessage(messageID: Long): String? { diff --git a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt index 1584f41de4..b34a4ac3be 100644 --- a/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt +++ b/libsession/src/main/java/org/session/libsession/database/MessageDataProvider.kt @@ -11,7 +11,8 @@ import java.io.InputStream interface MessageDataProvider { fun getMessageID(serverID: Long): Long? - fun deleteMessage(messageID: Long) + fun getMessageID(serverId: Long, threadId: Long): Pair? + fun deleteMessage(messageID: Long, isSms: Boolean) fun getDatabaseAttachment(attachmentId: Long): DatabaseAttachment? diff --git a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt index 51762b16a6..2fab491203 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/StorageProtocol.kt @@ -64,7 +64,7 @@ interface StorageProtocol { // Open Groups fun getThreadID(openGroupID: String): String? fun addOpenGroup(server: String, channel: Long) - fun setOpenGroupServerMessageID(messageID: Long, serverID: Long) + fun setOpenGroupServerMessageID(messageID: Long, serverID: Long, threadID: Long, isSms: Boolean) fun getQuoteServerID(quoteID: Long, publicKey: String): Long? // Open Group Public Keys @@ -137,7 +137,7 @@ interface StorageProtocol { fun getOrCreateThreadIdFor(address: Address): Long fun getOrCreateThreadIdFor(publicKey: String, groupPublicKey: String?, openGroupID: String?): Long fun getThreadIdFor(address: Address): Long? - fun getThreadIdForMms(messageId: Long): Long + fun getThreadIdForMms(mmsId: Long): Long // Session Request fun getSessionRequestSentTimestamp(publicKey: String): Long? diff --git a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupAPIV2.kt b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupAPIV2.kt index 8d45269ed6..f58b76ad17 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupAPIV2.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupAPIV2.kt @@ -2,6 +2,7 @@ package org.session.libsession.messaging.open_groups import com.fasterxml.jackson.databind.PropertyNamingStrategy import com.fasterxml.jackson.databind.annotation.JsonNaming +import com.fasterxml.jackson.databind.type.TypeFactory import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import nl.komponents.kovenant.Kovenant @@ -28,6 +29,8 @@ import org.session.libsignal.utilities.logging.Log import org.whispersystems.curve25519.Curve25519 import java.util.* +typealias DeletionList = List + object OpenGroupAPIV2 { private val moderators: HashMap> = hashMapOf() // Server URL to (channel ID to set of moderator IDs) @@ -60,7 +63,7 @@ object OpenGroupAPIV2 { val imageID: String? ) - @JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy::class) + @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class) data class CompactPollRequest(val roomId: String, val authToken: String, val fromDeletionServerId: Long?, @@ -72,6 +75,11 @@ object OpenGroupAPIV2 { val moderators: List ) + @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class) + data class MessageDeletion @JvmOverloads constructor(val id: Long = 0, + val deletedMessageId: Long = 0 + ) + data class Request( val verb: HTTP.Verb, val room: String?, @@ -93,7 +101,7 @@ object OpenGroupAPIV2 { return RequestBody.create(MediaType.get("application/json"), parametersAsJSON) } - private fun send(request: Request): Promise, Exception> { + private fun send(request: Request, isJsonRequired: Boolean = true): Promise, Exception> { val parsed = HttpUrl.parse(request.server) ?: return Promise.ofFail(Error.INVALID_URL) val urlBuilder = HttpUrl.Builder() .scheme(parsed.scheme()) @@ -128,7 +136,7 @@ object OpenGroupAPIV2 { if (request.useOnionRouting) { val publicKey = MessagingModuleConfiguration.shared.storage.getOpenGroupPublicKey(request.server) ?: return Promise.ofFail(Error.NO_PUBLIC_KEY) - return OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey) + return OnionRequestAPI.sendOnionRequest(requestBuilder.build(), request.server, publicKey, isJSONRequired = isJsonRequired) .fail { e -> if (e is OnionRequestAPI.HTTPRequestFailedAtDestinationException && e.statusCode == 401) { val storage = MessagingModuleConfiguration.shared.storage @@ -289,7 +297,7 @@ object OpenGroupAPIV2 { } } - fun getDeletedMessages(room: String, server: String): Promise, Exception> { + fun getDeletedMessages(room: String, server: String): Promise, Exception> { val storage = MessagingModuleConfiguration.shared.storage val queryParameters = mutableMapOf() storage.getLastDeletionServerId(room, server)?.let { last -> @@ -297,12 +305,13 @@ object OpenGroupAPIV2 { } val request = Request(verb = GET, room = room, server = server, endpoint = "deleted_messages", queryParameters = queryParameters) return send(request).map(sharedContext) { json -> - @Suppress("UNCHECKED_CAST") val serverIDs = (json["ids"] as? List)?.map { it.toLong() } - ?: throw Error.PARSING_FAILED + val type = TypeFactory.defaultInstance().constructCollectionType(List::class.java, MessageDeletion::class.java) + val idsAsString = JsonUtil.toJson(json["ids"]) + val serverIDs = JsonUtil.fromJson>(idsAsString, type) ?: throw Error.PARSING_FAILED val lastMessageServerId = storage.getLastDeletionServerId(room, server) ?: 0 - val serverID = serverIDs.maxOrNull() ?: 0 - if (serverID > lastMessageServerId) { - storage.setLastDeletionServerId(room, server, serverID) + val serverID = serverIDs.maxByOrNull {it.id } ?: serverIDs.first() + if (serverID.id > lastMessageServerId) { + storage.setLastDeletionServerId(room, server, serverID.id) } serverIDs } @@ -343,14 +352,14 @@ object OpenGroupAPIV2 { // endregion // region General - fun getCompactPoll(rooms: List, server: String): Promise, Exception> { - val requestAuths = rooms.associateWith { room -> getAuthToken(room,server) } + fun getCompactPoll(rooms: List, server: String): Promise, Exception> { + val requestAuths = rooms.associateWith { room -> getAuthToken(room, server) } val storage = MessagingModuleConfiguration.shared.storage val requests = rooms.mapNotNull { room -> val authToken = try { requestAuths[room]?.get() } catch (e: Exception) { - Log.e("Loki", "Failed to get auth token for $room",e) + Log.e("Loki", "Failed to get auth token for $room", e) null } ?: return@mapNotNull null @@ -363,7 +372,7 @@ object OpenGroupAPIV2 { val request = Request(verb = POST, room = null, server = server, endpoint = "compact_poll", isAuthRequired = false, parameters = mapOf("requests" to requests)) // build a request for all rooms return send(request = request).map(sharedContext) { json -> - val results = json["results"] as? Map<*,*> ?: throw Error.PARSING_FAILED + val results = json["results"] as? Map<*, *> ?: throw Error.PARSING_FAILED TODO() } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index e12d0df42b..9df7423863 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -12,16 +12,15 @@ import org.session.libsession.messaging.messages.control.ClosedGroupControlMessa import org.session.libsession.messaging.messages.control.ConfigurationMessage import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate import org.session.libsession.messaging.messages.visible.* -import org.session.libsession.messaging.open_groups.OpenGroupAPI -import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 -import org.session.libsession.messaging.open_groups.OpenGroupMessage -import org.session.libsession.messaging.open_groups.OpenGroupMessageV2 +import org.session.libsession.messaging.open_groups.* import org.session.libsession.messaging.threads.Address +import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.snode.RawResponsePromise import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeModule import org.session.libsession.snode.SnodeMessage +import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.SSKEnvironment import org.session.libsignal.service.internal.push.PushTransportDetails import org.session.libsignal.service.internal.push.SignalServiceProtos @@ -290,8 +289,12 @@ object MessageSender { // Ignore future self-sends storage.addReceivedMessageTimestamp(message.sentTimestamp!!) // Track the open group server message ID - if (message.openGroupServerMessageID != null) { - storage.setOpenGroupServerMessageID(messageId, message.openGroupServerMessageID!!) + if (message.openGroupServerMessageID != null && destination is Destination.OpenGroupV2) { + val encoded = GroupUtil.getEncodedOpenGroupID("${destination.server}.${destination.room}".toByteArray()) + val threadID = storage.getThreadIdFor(Address.fromSerialized(encoded)) + if (threadID != null && threadID >= 0) { + storage.setOpenGroupServerMessageID(messageId, message.openGroupServerMessageID!!, threadID, !(message as VisibleMessage).isMediaMessage()) + } } // Mark the message as sent storage.markAsSent(message.sentTimestamp!!, message.sender?:userPublicKey) 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 b9cd90745e..9e4ad9ecec 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 @@ -151,9 +151,19 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS val storage = MessagingModuleConfiguration.shared.storage val context = MessagingModuleConfiguration.shared.context val userPublicKey = storage.getUserPublicKey() + + // Get or create thread + val threadID = storage.getOrCreateThreadIdFor(message.syncTarget + ?: message.sender!!, message.groupPublicKey, openGroupID) + + val openGroup = threadID.let { + storage.getOpenGroup(it.toString()) + } + // Update profile if needed val newProfile = message.profile - if (newProfile != null && openGroupID.isNullOrEmpty() && userPublicKey != message.sender) { + + if (newProfile != null && userPublicKey != message.sender && openGroup == null) { val profileManager = SSKEnvironment.shared.profileManager val recipient = Recipient.from(context, Address.fromSerialized(message.sender!!), false) val displayName = newProfile.displayName!! @@ -172,9 +182,6 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS } } } - // Get or create thread - val threadID = storage.getOrCreateThreadIdFor(message.syncTarget - ?: message.sender!!, message.groupPublicKey, openGroupID) // Parse quote if needed var quoteModel: QuoteModel? = null if (message.quote != null && proto.dataMessage.hasQuote()) { @@ -224,6 +231,10 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS JobQueue.shared.add(downloadJob) } } + val openGroupServerID = message.openGroupServerMessageID + if (openGroupServerID != null) { + storage.setOpenGroupServerMessageID(messageID, openGroupServerID, threadID, !(message.isMediaMessage() || attachments.isNotEmpty())) + } // Cancel any typing indicators if needed cancelTypingIndicatorsIfNeeded(message.sender!!) //Notify the user if needed diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt index 825d8d8c44..309bfa20bb 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt @@ -9,6 +9,8 @@ import org.session.libsession.messaging.jobs.MessageReceiveJob import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroupAPI import org.session.libsession.messaging.open_groups.OpenGroupMessage +import org.session.libsession.messaging.threads.Address +import org.session.libsession.utilities.GroupUtil import org.session.libsignal.service.internal.push.SignalServiceProtos.* import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.successBackground @@ -210,10 +212,13 @@ class OpenGroupPoller(private val openGroup: OpenGroup, private val executorServ } private fun pollForDeletedMessages() { + val messagingModule = MessagingModuleConfiguration.shared + val address = GroupUtil.getEncodedOpenGroupID(openGroup.id.toByteArray()) + val threadId = messagingModule.storage.getThreadIdFor(Address.fromSerialized(address)) ?: return OpenGroupAPI.getDeletedMessageServerIDs(openGroup.channel, openGroup.server).success { deletedMessageServerIDs -> - val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { MessagingModuleConfiguration.shared.messageDataProvider.getMessageID(it) } - deletedMessageIDs.forEach { - MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(it) + val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { messagingModule.messageDataProvider.getMessageID(it, threadId) } + deletedMessageIDs.forEach { (messageId, isSms) -> + MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(messageId, isSms) } }.fail { Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${openGroup.channel} on server: ${openGroup.server}.") diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupV2Poller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupV2Poller.kt index edd770adfb..3eb07b78ea 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupV2Poller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupV2Poller.kt @@ -7,7 +7,10 @@ import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.MessageReceiveJob import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.open_groups.OpenGroupV2 +import org.session.libsession.messaging.threads.Address +import org.session.libsession.utilities.GroupUtil import org.session.libsignal.service.internal.push.SignalServiceProtos +import org.session.libsignal.service.loki.database.LokiMessageDatabaseProtocol import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.successBackground import java.util.concurrent.ScheduledExecutorService @@ -101,10 +104,17 @@ class OpenGroupV2Poller(private val openGroup: OpenGroupV2, private val executor } private fun pollForDeletedMessages() { + val messagingModule = MessagingModuleConfiguration.shared + val address = GroupUtil.getEncodedOpenGroupID(openGroup.id.toByteArray()) + val threadId = messagingModule.storage.getThreadIdFor(Address.fromSerialized(address)) ?: return + OpenGroupAPIV2.getDeletedMessages(openGroup.room, openGroup.server).success { deletedMessageServerIDs -> - val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { MessagingModuleConfiguration.shared.messageDataProvider.getMessageID(it) } - deletedMessageIDs.forEach { - MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(it) + + val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { serverId -> + messagingModule.messageDataProvider.getMessageID(serverId.deletedMessageId, threadId) + } + deletedMessageIDs.forEach { (messageId, isSms) -> + MessagingModuleConfiguration.shared.messageDataProvider.deleteMessage(messageId, isSms) } }.fail { Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${openGroup.room} on server: ${openGroup.server}.") diff --git a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiMessageDatabaseProtocol.kt b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiMessageDatabaseProtocol.kt index 45f162de63..0a279bc1b7 100644 --- a/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiMessageDatabaseProtocol.kt +++ b/libsignal/src/main/java/org/session/libsignal/service/loki/database/LokiMessageDatabaseProtocol.kt @@ -3,5 +3,5 @@ package org.session.libsignal.service.loki.database interface LokiMessageDatabaseProtocol { fun getQuoteServerID(quoteID: Long, quoteePublicKey: String): Long? - fun setServerID(messageID: Long, serverID: Long) + fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) } diff --git a/libsignal/src/main/java/org/session/libsignal/utilities/JsonUtil.java b/libsignal/src/main/java/org/session/libsignal/utilities/JsonUtil.java index b20a870d10..71ac9d548f 100644 --- a/libsignal/src/main/java/org/session/libsignal/utilities/JsonUtil.java +++ b/libsignal/src/main/java/org/session/libsignal/utilities/JsonUtil.java @@ -3,8 +3,10 @@ package org.session.libsignal.utilities; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.ResolvedType; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; @@ -42,6 +44,10 @@ public class JsonUtil { return objectMapper.readValue(serialized, clazz); } + public static T fromJson(String serialized, JavaType clazz) throws IOException { + return objectMapper.readValue(serialized, clazz); + } + public static T fromJson(InputStream serialized, Class clazz) throws IOException { return objectMapper.readValue(serialized, clazz); }