From 961adc2f1cc7abb2e610e4639f394e522d3bac2d Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:24:48 +1100 Subject: [PATCH] Better handling of community seqno --- .../libsession/messaging/jobs/JobQueue.kt | 5 -- .../messaging/jobs/OpenGroupDeleteJob.kt | 83 ------------------- .../jobs/SessionJobManagerFactories.kt | 2 - .../pollers/OpenGroupPoller.kt | 27 ++++-- .../securesms/database/LokiAPIDatabase.kt | 16 ++-- 5 files changed, 30 insertions(+), 103 deletions(-) delete mode 100644 app/src/main/java/org/session/libsession/messaging/jobs/OpenGroupDeleteJob.kt diff --git a/app/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt b/app/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt index 3595c928d0..5de0eb9354 100644 --- a/app/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt +++ b/app/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt @@ -42,7 +42,6 @@ class JobQueue @Inject constructor( for (job in channel) { if (!isActive) break val communityAddress = when (job) { - is OpenGroupDeleteJob -> job.address?.address is TrimThreadJob -> job.communityAddress?.address else -> null } @@ -131,9 +130,6 @@ class JobQueue @Inject constructor( is AttachmentDownloadJob -> { mediaQueue.send(job) } - is OpenGroupDeleteJob -> { - openGroupQueue.send(job) - } is TrimThreadJob -> { if (job.communityAddress != null) { openGroupQueue.send(job) @@ -217,7 +213,6 @@ class JobQueue @Inject constructor( AttachmentUploadJob.KEY, AttachmentDownloadJob.KEY, MessageSendJob.KEY, - OpenGroupDeleteJob.KEY, InviteContactsJob.KEY, ) allJobTypes.forEach { type -> diff --git a/app/src/main/java/org/session/libsession/messaging/jobs/OpenGroupDeleteJob.kt b/app/src/main/java/org/session/libsession/messaging/jobs/OpenGroupDeleteJob.kt deleted file mode 100644 index db00184864..0000000000 --- a/app/src/main/java/org/session/libsession/messaging/jobs/OpenGroupDeleteJob.kt +++ /dev/null @@ -1,83 +0,0 @@ -package org.session.libsession.messaging.jobs - -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import org.session.libsession.database.MessageDataProvider -import org.session.libsession.messaging.utilities.Data -import org.session.libsession.utilities.Address -import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.database.ThreadDatabase -import org.thoughtcrime.securesms.database.getRecipientAddress - -/** - * A job to delete open group messages given by their server IDs. - */ -class OpenGroupDeleteJob @AssistedInject constructor( - @Assisted private val messageServerIds: LongArray, - @Assisted private val threadId: Long, - private val dataProvider: MessageDataProvider, - threadDatabase: ThreadDatabase, -): Job { - - companion object { - private const val TAG = "OpenGroupDeleteJob" - const val KEY = "OpenGroupDeleteJob" - private const val MESSAGE_IDS = "messageIds" - private const val THREAD_ID = "threadId" - } - - override var delegate: JobDelegate? = null - override var id: String? = null - override var failureCount: Int = 0 - override val maxFailureCount: Int = 1 - - val address: Address.Community? = threadDatabase.getRecipientAddress(threadId) as? Address.Community - - override suspend fun execute(dispatcherName: String) { - val numberToDelete = messageServerIds.size - Log.d(TAG, "About to attempt to delete $numberToDelete messages") - - // FIXME: This entire process should probably run in a transaction (with the attachment deletion happening only if it succeeded) - try { - val (smsMessages, mmsMessages) = dataProvider.getMessageIDs(messageServerIds.toList(), threadId) - - // Delete the SMS messages - if (smsMessages.isNotEmpty()) { - dataProvider.deleteMessages(smsMessages, true) - } - - // Delete the MMS messages - if (mmsMessages.isNotEmpty()) { - dataProvider.deleteMessages(mmsMessages, false) - } - - Log.d(TAG, "Deleted ${smsMessages.size + mmsMessages.size} messages successfully") - delegate?.handleJobSucceeded(this, dispatcherName) - } - catch (e: Exception) { - Log.w(TAG, "OpenGroupDeleteJob failed: $e") - delegate?.handleJobFailed(this, dispatcherName, e) - } - } - - override fun serialize(): Data = Data.Builder() - .putLongArray(MESSAGE_IDS, messageServerIds) - .putLong(THREAD_ID, threadId) - .build() - - override fun getFactoryKey(): String = KEY - - @AssistedFactory - abstract class Factory: Job.DeserializeFactory { - override fun create(data: Data): OpenGroupDeleteJob { - return create( - messageServerIds = data.getLongArray(MESSAGE_IDS), - threadId = data.getLong(THREAD_ID) - ) - } - - abstract fun create(messageServerIds: LongArray, threadId: Long): OpenGroupDeleteJob - } - -} \ No newline at end of file diff --git a/app/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt b/app/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt index 663ee8079e..bf05b1aa36 100644 --- a/app/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt +++ b/app/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt @@ -7,7 +7,6 @@ class SessionJobManagerFactories @Inject constructor( private val attachmentUploadJobFactory: AttachmentUploadJob.Factory, private val trimThreadFactory: TrimThreadJob.Factory, private val messageSendJobFactory: MessageSendJob.Factory, - private val deleteJobFactory: OpenGroupDeleteJob.Factory, private val inviteContactsJobFactory: InviteContactsJob.Factory, ) { @@ -17,7 +16,6 @@ class SessionJobManagerFactories @Inject constructor( AttachmentUploadJob.KEY to attachmentUploadJobFactory, MessageSendJob.KEY to messageSendJobFactory, TrimThreadJob.KEY to trimThreadFactory, - OpenGroupDeleteJob.KEY to deleteJobFactory, InviteContactsJob.KEY to inviteContactsJobFactory, ) } diff --git a/app/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt b/app/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt index ca51c16b45..187adf92c2 100644 --- a/app/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt +++ b/app/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt @@ -11,9 +11,9 @@ import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.serialization.json.Json +import org.session.libsession.database.MessageDataProvider import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.jobs.JobQueue -import org.session.libsession.messaging.jobs.OpenGroupDeleteJob import org.session.libsession.messaging.jobs.TrimThreadJob import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.messaging.open_groups.OpenGroupApi.Capability @@ -46,13 +46,13 @@ class OpenGroupPoller @AssistedInject constructor( private val storage: StorageProtocol, private val configFactory: ConfigFactoryProtocol, private val trimThreadJobFactory: TrimThreadJob.Factory, - private val openGroupDeleteJobFactory: OpenGroupDeleteJob.Factory, private val communityDatabase: CommunityDatabase, private val receivedMessageProcessor: ReceivedMessageProcessor, private val communityApiExecutor: CommunityApiExecutor, private val getRoomMessagesFactory: GetRoomMessagesApi.Factory, private val getDirectMessageFactory: GetDirectMessagesApi.Factory, private val pollRoomInfoFactory: PollRoomApi.Factory, + private val messageDataProvider: MessageDataProvider, private val getCapsApi: Provider, networkConnectivity: NetworkConnectivity, appVisibilityManager: AppVisibilityManager, @@ -254,12 +254,23 @@ class OpenGroupPoller @AssistedInject constructor( } if (deletions.isNotEmpty()) { - jobQueue.get().add( - openGroupDeleteJobFactory.create( - messageServerIds = LongArray(deletions.size) { i -> deletions[i].id }, - threadId = threadId - ) - ) + try { + val (smsMessages, mmsMessages) = messageDataProvider.getMessageIDs(deletions.map { it.id }, threadId) + + // Delete the SMS messages + if (smsMessages.isNotEmpty()) { + messageDataProvider.deleteMessages(smsMessages, true) + } + + // Delete the MMS messages + if (mmsMessages.isNotEmpty()) { + messageDataProvider.deleteMessages(mmsMessages, false) + } + } catch (e: Exception) { + logE("Error deleting open group messages", e) + } finally { + storage.setLastMessageServerID(roomToken, server, deletions.maxOf { it.seqno }) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt index 0852abcd48..338dc86fcf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/LokiAPIDatabase.kt @@ -63,7 +63,7 @@ class LokiAPIDatabase(context: Context, helper: Provider) : @JvmStatic val createOpenGroupAuthTokenTableCommand = "CREATE TABLE $openGroupAuthTokenTable ($server TEXT PRIMARY KEY, $token TEXT);" // Last message server IDs private const val lastMessageServerIDTable = "loki_api_last_message_server_id_cache" - private val lastMessageServerIDTableIndex = "loki_api_last_message_server_id_cache_index" + private const val lastMessageServerIDTableIndex = "loki_api_last_message_server_id_cache_index" private const val lastMessageServerID = "last_message_server_id" @JvmStatic val createLastMessageServerIDTableCommand = "CREATE TABLE $lastMessageServerIDTable ($lastMessageServerIDTableIndex STRING PRIMARY KEY, $lastMessageServerID INTEGER DEFAULT 0);" // Last deletion server IDs @@ -293,11 +293,17 @@ class LokiAPIDatabase(context: Context, helper: Provider) : }?.toLong() } + /** + * Attempts to set the last message server ID for the given room and server, but + * only if the new value is more recent than the previous value. + */ override fun setLastMessageServerID(room: String, server: String, newValue: Long) { - val database = writableDatabase - val index = "$server.$room" - val row = wrap(mapOf( lastMessageServerIDTableIndex to index, lastMessageServerID to newValue.toString() )) - database.insertOrUpdate(lastMessageServerIDTable, row, "$lastMessageServerIDTableIndex = ?", wrap(index)) + writableDatabase.execSQL(""" + INSERT INTO $lastMessageServerIDTable ($lastMessageServerIDTableIndex, $lastMessageServerID) + VALUES (?1, ?2) + ON CONFLICT($lastMessageServerIDTableIndex) DO UPDATE SET $lastMessageServerID = EXCLUDED.$lastMessageServerID + WHERE EXCLUDED.$lastMessageServerID > $lastMessageServerID + """, arrayOf("$server.$room", newValue)) } fun removeLastMessageServerID(room: String, server:String) {