From 222df30a182c2eed87c11fe1797f90b59671cd19 Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:37:13 +1100 Subject: [PATCH] Fix compilation issues --- .../securesms/configs/ConfigToDatabaseSync.kt | 45 ++++--- .../securesms/dependencies/ConfigFactory.kt | 120 ++++++++++-------- .../messaging/jobs/BatchMessageReceiveJob.kt | 28 ++-- .../utilities/ConfigFactoryProtocol.kt | 13 +- 4 files changed, 118 insertions(+), 88 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt b/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt index fc73b2cfe4..323056eecd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/configs/ConfigToDatabaseSync.kt @@ -35,6 +35,7 @@ import org.session.libsession.utilities.ConfigUpdateNotification import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.SSKEnvironment.ProfileManagerProtocol.Companion.NAME_PADDED_LENGTH import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.UserConfigType import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.crypto.ecc.DjbECPrivateKey import org.session.libsignal.crypto.ecc.DjbECPublicKey @@ -86,7 +87,7 @@ class ConfigToDatabaseSync @Inject constructor( .collect { config -> try { Log.i(TAG, "Start syncing user configs") - syncUserConfigs(config.timestamp) + syncUserConfigs(config.configType, config.timestamp) Log.i(TAG, "Finished syncing user configs") } catch (e: Exception) { Log.e(TAG, "Error syncing user configs", e) @@ -116,23 +117,23 @@ class ConfigToDatabaseSync @Inject constructor( updateGroup(info) } - private fun syncUserConfigs(updateTimestamp: Long) { - lateinit var updateUserInfo: UpdateUserInfo - lateinit var updateUserGroupsInfo: UpdateUserGroupsInfo - lateinit var updateContacts: List - lateinit var updateConvoVolatile: List - - configFactory.withUserConfigs { configs -> - updateUserInfo = UpdateUserInfo(configs.userProfile) - updateUserGroupsInfo = UpdateUserGroupsInfo(configs.userGroups) - updateContacts = configs.contacts.all() - updateConvoVolatile = configs.convoInfoVolatile.all() + private fun syncUserConfigs(userConfigType: UserConfigType, updateTimestamp: Long) { + val configUpdate = configFactory.withUserConfigs { configs -> + when (userConfigType) { + UserConfigType.USER_PROFILE -> UpdateUserInfo(configs.userProfile) + UserConfigType.USER_GROUPS -> UpdateUserGroupsInfo(configs.userGroups) + UserConfigType.CONTACTS -> UpdateContacts(configs.contacts.all()) + UserConfigType.CONVO_INFO_VOLATILE -> UpdateConvoVolatile(configs.convoInfoVolatile.all()) + } } - updateUser(updateUserInfo, updateTimestamp) - updateContacts(updateContacts, updateTimestamp) - updateUserGroups(updateUserGroupsInfo, updateTimestamp) - updateConvoVolatile(updateConvoVolatile) + when (configUpdate) { + is UpdateUserInfo -> updateUser(configUpdate, updateTimestamp) + is UpdateUserGroupsInfo -> updateUserGroups(configUpdate, updateTimestamp) + is UpdateContacts -> updateContacts(configUpdate, updateTimestamp) + is UpdateConvoVolatile -> updateConvoVolatile(configUpdate) + else -> error("Unknown config update type: $configUpdate") + } } private data class UpdateUserInfo( @@ -219,8 +220,10 @@ class ConfigToDatabaseSync @Inject constructor( } } - private fun updateContacts(contacts: List, messageTimestamp: Long) { - storage.addLibSessionContacts(contacts, messageTimestamp) + private data class UpdateContacts(val contacts: List) + + private fun updateContacts(contacts: UpdateContacts, messageTimestamp: Long) { + storage.addLibSessionContacts(contacts.contacts, messageTimestamp) } private data class UpdateUserGroupsInfo( @@ -368,8 +371,10 @@ class ConfigToDatabaseSync @Inject constructor( } } - private fun updateConvoVolatile(convos: List) { - val extracted = convos.filterNotNull() + private data class UpdateConvoVolatile(val convos: List) + + private fun updateConvoVolatile(convos: UpdateConvoVolatile) { + val extracted = convos.convos.filterNotNull() for (conversation in extracted) { val threadId = when (conversation) { is Conversation.OneToOne -> storage.getThreadIdFor(conversation.accountId, null, null, createThread = false) diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt index fd4fe2525d..4df886c57a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ConfigFactory.kt @@ -49,6 +49,7 @@ import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.database.ConfigDatabase +import org.thoughtcrime.securesms.database.ConfigVariant import org.thoughtcrime.securesms.database.LokiThreadDatabase import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.groups.GroupManager @@ -142,14 +143,18 @@ class ConfigFactory @Inject constructor( * * @param cb A function that takes a [UserConfigsImpl] and returns a pair of the result of the operation and a boolean indicating if the configs were changed. */ - private fun doWithMutableUserConfigs(cb: (UserConfigsImpl) -> Pair): T { + private fun doWithMutableUserConfigs(cb: (UserConfigsImpl) -> Pair>): T { val (lock, configs) = ensureUserConfigsInitialized() val (result, changed) = lock.write { cb(configs) } - if (changed != null) { - _configUpdateNotifications.tryEmit(changed) + if (changed.isNotEmpty()) { + for (notification in changed) { + if (!_configUpdateNotifications.tryEmit(notification)) { + Log.e("ConfigFactory", "Unable to deliver config update notification") + } + } } return result @@ -163,7 +168,7 @@ class ConfigFactory @Inject constructor( return } - return doWithMutableUserConfigs { configs -> + val toDump = doWithMutableUserConfigs { configs -> val config = when (userConfigType) { UserConfigType.CONTACTS -> configs.contacts UserConfigType.USER_PROFILE -> configs.userProfile @@ -171,22 +176,42 @@ class ConfigFactory @Inject constructor( UserConfigType.USER_GROUPS -> configs.userGroups } + // Merge the list of config messages, we'll be told which messages have been merged + // and we will then find out which message has the max timestamp val maxTimestamp = config.merge(messages.map { it.hash to it.data }.toTypedArray()) .asSequence() .mapNotNull { hash -> messages.firstOrNull { it.hash == hash } } .maxOfOrNull { it.timestamp } - Unit to maxTimestamp?.let(ConfigUpdateNotification::UserConfigsMerged) + maxTimestamp?.let { + (config.dump() to it) to + listOf(ConfigUpdateNotification.UserConfigsMerged(userConfigType, it)) + } ?: (null to emptyList()) + } + + // Dump now regardless so we can save the timestamp to the database + if (toDump != null) { + val (dump, timestamp) = toDump + configDatabase.storeConfig( + variant = userConfigType.configVariant, + publicKey = requiresCurrentUserAccountId().hexString, + data = dump, + timestamp = timestamp + ) } } override fun withMutableUserConfigs(cb: (MutableUserConfigs) -> T): T { return doWithMutableUserConfigs { val result = cb(it) - val changed = if (it.persistIfDirty(clock)) { - ConfigUpdateNotification.UserConfigsModified + + val changed = if (it.userGroups.dirty() || + it.convoInfoVolatile.dirty() || + it.userProfile.dirty() || + it.contacts.dirty()) { + listOf(ConfigUpdateNotification.UserConfigsModified) } else { - null + emptyList() } result to changed @@ -293,15 +318,30 @@ class ConfigFactory @Inject constructor( return } - doWithMutableUserConfigs { configs -> - contacts?.let { (push, result) -> configs.contacts.confirmPushed(push.seqNo, result.hash) } - userProfile?.let { (push, result) -> configs.userProfile.confirmPushed(push.seqNo, result.hash) } - convoInfoVolatile?.let { (push, result) -> configs.convoInfoVolatile.confirmPushed(push.seqNo, result.hash) } - userGroups?.let { (push, result) -> configs.userGroups.confirmPushed(push.seqNo, result.hash) } + // Confirm push for the configs and gather the dumped data to be saved into the db. + // For this operation, we will no notify the users as there won't be any real change in terms + // of the displaying data. + val dump = doWithMutableUserConfigs { configs -> + sequenceOf(contacts, userProfile, convoInfoVolatile, userGroups) + .zip( + sequenceOf( + UserConfigType.CONTACTS to configs.contacts, + UserConfigType.USER_PROFILE to configs.userProfile, + UserConfigType.CONVO_INFO_VOLATILE to configs.convoInfoVolatile, + UserConfigType.USER_GROUPS to configs.userGroups + ) + ) + .filter { (push, _) -> push != null } + .onEach { (push, config) -> config.second.confirmPushed(push!!.first.seqNo, push.second.hash) } + .map { (push, config) -> + Triple(config.first.configVariant, config.second.dump(), push!!.second.timestamp) + }.toList() to emptyList() + } - configs.persistIfDirty(clock) - - Unit to null + // We need to persist the data to the database to save timestamp after the push + val userAccountId = requiresCurrentUserAccountId() + for ((variant, data, timestamp) in dump) { + configDatabase.storeConfig(variant, userAccountId.hexString, data, timestamp) } } @@ -329,18 +369,6 @@ class ConfigFactory @Inject constructor( } } - override fun getConfigTimestamp(forConfigObject: ConfigBase, publicKey: String): Long { - val variant = when (forConfigObject) { - is UserProfile -> SharedConfigMessage.Kind.USER_PROFILE.name - is Contacts -> SharedConfigMessage.Kind.CONTACTS.name - is ConversationVolatileConfig -> SharedConfigMessage.Kind.CONVO_INFO_VOLATILE.name - is UserGroupsConfig -> SharedConfigMessage.Kind.GROUPS.name - else -> throw UnsupportedOperationException("Can't support type of ${forConfigObject::class.simpleName} yet") - } - - return configDatabase.retrieveConfigLastUpdateTimestamp(variant, publicKey) - } - override fun conversationInConfig( publicKey: String?, groupPublicKey: String?, @@ -391,6 +419,10 @@ class ConfigFactory @Inject constructor( return (changeTimestampMs >= (lastUpdateTimestampMs - CONFIG_CHANGE_BUFFER_PERIOD)) } + override fun getConfigTimestamp(userConfigType: UserConfigType, publicKey: String): Long { + return configDatabase.retrieveConfigLastUpdateTimestamp(userConfigType.configVariant, publicKey) + } + override fun getGroupAuth(groupId: AccountId): SwarmAuth? { val (adminKey, authData) = withUserConfigs { val group = it.userGroups.getClosedGroup(groupId.hexString) @@ -441,6 +473,14 @@ class ConfigFactory @Inject constructor( } } +private val UserConfigType.configVariant: ConfigVariant + get() = when (this) { + UserConfigType.CONTACTS -> ConfigDatabase.CONTACTS_VARIANT + UserConfigType.USER_PROFILE -> ConfigDatabase.USER_PROFILE_VARIANT + UserConfigType.CONVO_INFO_VOLATILE -> ConfigDatabase.CONVO_INFO_VARIANT + UserConfigType.USER_GROUPS -> ConfigDatabase.USER_GROUPS_VARIANT + } + /** * Sync group data from our local database */ @@ -639,30 +679,6 @@ private class UserConfigsImpl( convoInfoVolatile.initFrom(storage, threadDb) } } - - /** - * Persists the config if it is dirty and returns the list of classes that were persisted - */ - fun persistIfDirty(clock: SnodeClock): Boolean { - return sequenceOf( - contacts to ConfigDatabase.CONTACTS_VARIANT, - userGroups to ConfigDatabase.USER_GROUPS_VARIANT, - userProfile to ConfigDatabase.USER_PROFILE_VARIANT, - convoInfoVolatile to ConfigDatabase.CONVO_INFO_VARIANT - ).fold(false) { acc, (config, variant) -> - if (config.needsDump()) { - configDatabase.storeConfig( - variant = variant, - publicKey = userAccountId.hexString, - data = config.dump(), - timestamp = clock.currentTimeMills() - ) - true - } else { - acc - } - } - } } private class GroupConfigsImpl( diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt index 31f7f4a622..35ad848f06 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt @@ -35,6 +35,7 @@ import org.session.libsession.messaging.sending_receiving.handleVisibleMessage import org.session.libsession.messaging.utilities.Data import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.utilities.SSKEnvironment +import org.session.libsession.utilities.UserConfigType import org.session.libsignal.protos.UtilProtos import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.IdPrefix @@ -103,24 +104,23 @@ class BatchMessageReceiveJob( executeAsync(dispatcherName) } - private fun isHidden(message: Message): Boolean{ + private fun isHidden(message: Message): Boolean { // if the contact is marked as hidden for 1on1 messages // and the message's sentTimestamp is earlier than the sentTimestamp of the last config - val config = MessagingModuleConfiguration.shared.configFactory + val configFactory = MessagingModuleConfiguration.shared.configFactory val publicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey() - if(config.contacts == null || message.sentTimestamp == null || publicKey == null) return false - val contactConfigTimestamp = config.getConfigTimestamp(config.contacts!!, publicKey) - if(message.groupPublicKey == null && // not a group - message.openGroupServerMessageID == null && // not a community - // not marked as hidden - config.contacts?.get(message.senderOrSync)?.priority == ConfigBase.PRIORITY_HIDDEN && - // the message's sentTimestamp is earlier than the sentTimestamp of the last config - message.sentTimestamp!! < contactConfigTimestamp - ) { - return true - } + if (message.sentTimestamp == null || publicKey == null) return false - return false + val contactConfigTimestamp = configFactory.getConfigTimestamp(UserConfigType.CONTACTS, publicKey) + + return configFactory.withUserConfigs { configs -> + message.groupPublicKey == null && // not a group + message.openGroupServerMessageID == null && // not a community + // not marked as hidden + configs.contacts.get(message.senderOrSync)?.priority == ConfigBase.PRIORITY_HIDDEN && + // the message's sentTimestamp is earlier than the sentTimestamp of the last config + message.sentTimestamp!! < contactConfigTimestamp + } } suspend fun executeAsync(dispatcherName: String) { diff --git a/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt b/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt index 326b653311..87c8b08dc9 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/ConfigFactoryProtocol.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withTimeoutOrNull -import network.loki.messenger.libsession_util.ConfigBase import network.loki.messenger.libsession_util.MutableConfig import network.loki.messenger.libsession_util.MutableContacts import network.loki.messenger.libsession_util.MutableConversationVolatileConfig @@ -196,8 +195,18 @@ interface MutableGroupConfigs : GroupConfigs { sealed interface ConfigUpdateNotification { + /** + * The user configs have been modified locally. + */ data object UserConfigsModified : ConfigUpdateNotification - data class UserConfigsMerged(val timestamp: Long) : ConfigUpdateNotification + + /** + * The user configs have been merged from the server. + */ + data class UserConfigsMerged( + val configType: UserConfigType, + val timestamp: Long + ) : ConfigUpdateNotification data class GroupConfigsUpdated(val groupId: AccountId) : ConfigUpdateNotification }