diff --git a/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/channelconfig/internal/DatabaseChannelConfigRepository.kt b/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/channelconfig/internal/DatabaseChannelConfigRepository.kt index e86ec8a104e..639ed23c81c 100644 --- a/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/channelconfig/internal/DatabaseChannelConfigRepository.kt +++ b/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/channelconfig/internal/DatabaseChannelConfigRepository.kt @@ -48,11 +48,15 @@ internal class DatabaseChannelConfigRepository( * Writes many [ChannelConfig] */ override suspend fun insertChannelConfigs(configs: Collection) { + // Channel configs are keyed by type, so a page of same-type channels yields many configs + // that collapse to one row. Dedup by type to avoid the redundant per-row writes. + val configsByType = configs.associateBy(ChannelConfig::type) + // update the local configs - channelConfigs += configs.associateBy(ChannelConfig::type) + channelConfigs += configsByType // insert into room db - channelConfigDao.insert(configs.map(ChannelConfig::toEntity)) + channelConfigDao.insert(configsByType.values.map(ChannelConfig::toEntity)) } /** diff --git a/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/user/internal/DatabaseUserRepository.kt b/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/user/internal/DatabaseUserRepository.kt index 5f1483684cc..6af1575efba 100644 --- a/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/user/internal/DatabaseUserRepository.kt +++ b/stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/domain/user/internal/DatabaseUserRepository.kt @@ -59,6 +59,9 @@ internal class DatabaseUserRepository( override suspend fun insertUsers(users: Collection) { if (users.isEmpty()) return val usersToInsert = users + // Use associateBy instead of distinctBy to keep the *last* occurrence of each user + .associateBy(User::id) + .values .filter { it != userCache[it.id] } .map { it.toEntity() } cacheUsers(users) diff --git a/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/ChannelConfigRepositoryTest.kt b/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/ChannelConfigRepositoryTest.kt index 3a883859741..87724084968 100644 --- a/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/ChannelConfigRepositoryTest.kt +++ b/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/ChannelConfigRepositoryTest.kt @@ -99,6 +99,25 @@ internal class ChannelConfigRepositoryTest { ) } + @Test + fun `When insert configs with duplicate types Should dedup keeping the last per type`() = runTest { + val first = randomChannelConfig(type = "messaging", config = randomConfig(name = "first")) + val last = randomChannelConfig(type = "messaging", config = randomConfig(name = "last")) + val other = randomChannelConfig(type = "livestream", config = randomConfig(name = "other")) + + sut.insertChannelConfigs(listOf(first, other, last)) + + verify(dao).insert( + argThat> { + size == 2 && + single { it.channelConfigInnerEntity.channelType == "messaging" } + .channelConfigInnerEntity.name == "last" && + single { it.channelConfigInnerEntity.channelType == "livestream" } + .channelConfigInnerEntity.name == "other" + }, + ) + } + @Test fun `Given config in cache When select Should return config`() = runTest { val config = randomChannelConfig(type = "messaging", config = randomConfig(name = "configName")) diff --git a/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/UserRepositoryTests.kt b/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/UserRepositoryTests.kt index dddb30c7506..2e3e484addc 100644 --- a/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/UserRepositoryTests.kt +++ b/stream-chat-android-offline/src/test/java/io/getstream/chat/android/offline/repository/UserRepositoryTests.kt @@ -52,6 +52,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times @@ -133,6 +134,21 @@ internal class UserRepositoryTests { verify(userDao, never()).insertMany(any()) } + @Test + fun `When insert users with duplicate ids Should insert each user only once keeping the last`() = runTest { + val id = randomString() + val firstCopy = randomUser(id = id, name = "first") + val lastCopy = firstCopy.copy(name = "last") + val other = randomUser() + + sut.insertUsers(listOf(firstCopy, other, lastCopy)) + + val captor = argumentCaptor>() + verify(userDao).insertMany(captor.capture()) + captor.firstValue.map(UserEntity::id) shouldBeEqualTo listOf(id, other.id) + captor.firstValue.first { it.id == id }.name shouldBeEqualTo "last" + } + @Test fun `When insert current user Should insert entity with me id to dao`() = runTest { val user = randomUser(