Skip to content

Commit

Permalink
feat: asset media files (WPB-5378) (#2322)
Browse files Browse the repository at this point in the history
* feat: add new query to get assets without images

* feat: add new usecase for assets only without images

* feat: add missing implementation of dao

* feat: add property NOT IN in sql query and rename mapping function

* feat: add nullable to AssetMessageEntity width and height of asset

* feat: add nullable to AssetMessageEntity width and height of asset

* chore: revert changes to nullable parameters

* feat: get assets from message details view instead of assets due to need of user data

* feat: adjust return type of usecase and repository

* feat: add count query for asset messages without images

* chore: remove get asset messages by conversation id without images

* feat: add usecase and repository extension for paginated asset messages without images

* chore: remove unused usecase from message scope

* chore: remove unused usecase from message scope

* chore: remove receiving mimetype to paginated usecase

* chore: move unique asset scope to message scope

* chore: add logs

* chore: remove logs

* chore: fix detekt

* test: fix existing tests

* fix: media image assets [WPB-5848] (#2332)

* fix: load only downloaded image media assets

---------

Co-authored-by: Jakub Żerko <[email protected]>
  • Loading branch information
alexandreferris and Garzas authored Dec 28, 2023
1 parent 7c64581 commit 10e2eec
Show file tree
Hide file tree
Showing 16 changed files with 331 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.asset

import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.message.Message
import com.wire.kalium.logic.data.message.MessageRepository
import com.wire.kalium.util.KaliumDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn

/**
* This use case will observe and return a flow of paginated asset messages for a given conversation.
* @see PagingData
* @see Message
*/
class GetPaginatedFlowOfAssetMessageByConversationIdUseCase internal constructor(
private val dispatcher: KaliumDispatcher,
private val messageRepository: MessageRepository
) {
suspend operator fun invoke(
conversationId: ConversationId,
startingOffset: Long,
pagingConfig: PagingConfig
): Flow<PagingData<Message.Standalone>> = messageRepository.extensions.getPaginatedMessageAssetsWithoutImageByConversationId(
conversationId = conversationId,
pagingConfig = pagingConfig,
startingOffset = startingOffset
).flowOn(dispatcher.io)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.feature.asset

import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.wire.kalium.logic.data.asset.AssetMessage
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.message.MessageRepository
import com.wire.kalium.util.KaliumDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn

/**
* This use case will observe and return a flow of paginated image asset messages for a given conversation.
* @see PagingData
* @see AssetMessage
*/
class ObservePaginatedAssetImageMessages internal constructor(
private val dispatcher: KaliumDispatcher,
private val messageRepository: MessageRepository
) {
suspend operator fun invoke(
conversationId: ConversationId,
startingOffset: Long,
pagingConfig: PagingConfig
): Flow<PagingData<AssetMessage>> = messageRepository.extensions.observePaginatedMessageAssetImageByConversationId(
conversationId = conversationId,
pagingConfig = pagingConfig,
startingOffset = startingOffset
).flowOn(dispatcher.io)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,17 @@

package com.wire.kalium.logic.feature.message

import com.wire.kalium.logic.feature.asset.GetPaginatedFlowOfAssetMessageByConversationIdUseCase
import com.wire.kalium.logic.feature.asset.ObservePaginatedAssetImageMessages

val MessageScope.getPaginatedFlowOfMessagesByConversation
get() = GetPaginatedFlowOfMessagesByConversationUseCase(dispatcher, messageRepository)

val MessageScope.getPaginatedFlowOfMessagesBySearchQueryAndConversation
get() = GetPaginatedFlowOfMessagesBySearchQueryAndConversationIdUseCase(dispatcher, messageRepository)

val MessageScope.getPaginatedFlowOfAssetMessageByConversationId
get() = GetPaginatedFlowOfAssetMessageByConversationIdUseCase(dispatcher, messageRepository)

val MessageScope.observePaginatedImageAssetMessageByConversationId
get() = ObservePaginatedAssetImageMessages(dispatcher, messageRepository)
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import kotlin.time.toDuration
interface MessageMapper {
fun fromMessageToEntity(message: Message.Standalone): MessageEntity
fun fromEntityToMessage(message: MessageEntity): Message.Standalone
fun fromAssetEntityToMessage(message: AssetMessageEntity): AssetMessage
fun fromAssetEntityToAssetMessage(message: AssetMessageEntity): AssetMessage
fun fromEntityToMessagePreview(message: MessagePreviewEntity): MessagePreview
fun fromMessageToLocalNotificationMessage(message: NotificationMessageEntity): LocalNotificationMessage?
fun toMessageEntityContent(regularMessage: MessageContent.Regular): MessageEntityContent.Regular
Expand Down Expand Up @@ -129,7 +129,7 @@ class MessageMapperImpl(
}
}

override fun fromAssetEntityToMessage(message: AssetMessageEntity): AssetMessage {
override fun fromAssetEntityToAssetMessage(message: AssetMessageEntity): AssetMessage {
return AssetMessage(
message.time,
message.username,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ internal interface MessageRepository {
): Either<StorageFailure, Int>

val extensions: MessageRepositoryExtensions
suspend fun getAssetMessagesByConversationId(conversationId: ConversationId, limit: Int, offset: Int): List<AssetMessage>
suspend fun getImageAssetMessagesByConversationId(
conversationId: ConversationId,
limit: Int,
offset: Int
): List<AssetMessage>
}

// TODO: suppress TooManyFunctions for now, something we need to fix in the future
Expand Down Expand Up @@ -273,17 +277,17 @@ internal class MessageDataSource internal constructor (
messageDAO.getLastMessagesByConversations(conversationIdList.map { it.toDao() })
}.map { it.map { it.key.toModel() to messageMapper.fromEntityToMessage(it.value) }.toMap() }

override suspend fun getAssetMessagesByConversationId(
override suspend fun getImageAssetMessagesByConversationId(
conversationId: ConversationId,
limit: Int,
offset: Int
): List<AssetMessage> = messageDAO.getMessageAssets(
): List<AssetMessage> = messageDAO.getImageMessageAssets(
conversationId.toDao(),
mimeTypes = SUPPORTED_IMAGE_ASSET_MIME_TYPES,
limit,
offset
)
.map(messageMapper::fromAssetEntityToMessage)
.map(messageMapper::fromAssetEntityToAssetMessage)

@OptIn(ExperimentalCoroutinesApi::class)
override suspend fun getNotificationMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ package com.wire.kalium.logic.data.message
import app.cash.paging.PagingConfig
import app.cash.paging.PagingData
import app.cash.paging.map
import com.wire.kalium.logic.data.asset.AssetMessage
import com.wire.kalium.logic.data.asset.SUPPORTED_IMAGE_ASSET_MIME_TYPES
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.toDao
import com.wire.kalium.persistence.dao.asset.AssetMessageEntity
import com.wire.kalium.persistence.dao.message.KaliumPager
import com.wire.kalium.persistence.dao.message.MessageDAO
import com.wire.kalium.persistence.dao.message.MessageEntity
Expand All @@ -43,6 +46,18 @@ internal interface MessageRepositoryExtensions {
pagingConfig: PagingConfig,
startingOffset: Long
): Flow<PagingData<Message.Standalone>>

suspend fun getPaginatedMessageAssetsWithoutImageByConversationId(
conversationId: ConversationId,
pagingConfig: PagingConfig,
startingOffset: Long
): Flow<PagingData<Message.Standalone>>

suspend fun observePaginatedMessageAssetImageByConversationId(
conversationId: ConversationId,
pagingConfig: PagingConfig,
startingOffset: Long
): Flow<PagingData<AssetMessage>>
}

internal class MessageRepositoryExtensionsImpl internal constructor(
Expand Down Expand Up @@ -85,4 +100,38 @@ internal class MessageRepositoryExtensionsImpl internal constructor(
it.map { messageMapper.fromEntityToMessage(it) }
}
}

override suspend fun getPaginatedMessageAssetsWithoutImageByConversationId(
conversationId: ConversationId,
pagingConfig: PagingConfig,
startingOffset: Long
): Flow<PagingData<Message.Standalone>> {
val pager: KaliumPager<MessageEntity> = messageDAO.platformExtensions.getPagerForMessageAssetsWithoutImage(
conversationId = conversationId.toDao(),
mimeTypes = SUPPORTED_IMAGE_ASSET_MIME_TYPES,
pagingConfig = pagingConfig,
startingOffset = startingOffset
)

return pager.pagingDataFlow.map {
it.map { messageEntity -> messageMapper.fromEntityToMessage(messageEntity) }
}
}

override suspend fun observePaginatedMessageAssetImageByConversationId(
conversationId: ConversationId,
pagingConfig: PagingConfig,
startingOffset: Long
): Flow<PagingData<AssetMessage>> {
val pager: KaliumPager<AssetMessageEntity> = messageDAO.platformExtensions.getPagerForMessageAssetImage(
conversationId = conversationId.toDao(),
mimeTypes = SUPPORTED_IMAGE_ASSET_MIME_TYPES,
pagingConfig = pagingConfig,
startingOffset = startingOffset
)

return pager.pagingDataFlow.map {
it.map { messageEntity -> messageMapper.fromAssetEntityToAssetMessage(messageEntity) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import com.wire.kalium.logic.data.message.MessageRepository
import com.wire.kalium.util.KaliumDispatcher
import kotlinx.coroutines.withContext

interface GetAssetMessagesForConversationUseCase {
interface GetImageAssetMessagesForConversationUseCase {
/**
* This use case will return asset messages for a given [conversationId]
* This use case will return image asset messages for a given [conversationId]
* paginated by [limit] and [offset]
* @see AssetMessage
*/
Expand All @@ -37,18 +37,20 @@ interface GetAssetMessagesForConversationUseCase {
): List<AssetMessage>
}

class GetAssetMessagesForConversationUseCaseImpl internal constructor(
class GetImageAssetMessagesForConversationUseCaseImpl internal constructor(
private val dispatcher: KaliumDispatcher,
private val messageRepository: MessageRepository,
) : GetAssetMessagesForConversationUseCase {
) : GetImageAssetMessagesForConversationUseCase {

override suspend operator fun invoke(
conversationId: ConversationId,
limit: Int,
offset: Int,
): List<AssetMessage> = withContext(dispatcher.io) {
messageRepository.getAssetMessagesByConversationId(
conversationId, limit, offset
messageRepository.getImageAssetMessagesByConversationId(
conversationId = conversationId,
limit = limit,
offset = offset
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ import com.wire.kalium.logic.data.sync.IncrementalSyncRepository
import com.wire.kalium.logic.data.sync.SlowSyncRepository
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.data.conversation.LegalHoldStatusMapperImpl
import com.wire.kalium.logic.feature.asset.GetAssetMessagesForConversationUseCase
import com.wire.kalium.logic.feature.asset.GetAssetMessagesForConversationUseCaseImpl
import com.wire.kalium.logic.feature.asset.GetImageAssetMessagesForConversationUseCase
import com.wire.kalium.logic.feature.asset.GetImageAssetMessagesForConversationUseCaseImpl
import com.wire.kalium.logic.feature.asset.GetMessageAssetUseCase
import com.wire.kalium.logic.feature.asset.GetMessageAssetUseCaseImpl
import com.wire.kalium.logic.feature.asset.ScheduleNewAssetMessageUseCase
Expand Down Expand Up @@ -236,8 +236,8 @@ class MessageScope internal constructor(
dispatcher
)

val getAssetMessagesByConversation: GetAssetMessagesForConversationUseCase
get() = GetAssetMessagesForConversationUseCaseImpl(
val getImageAssetMessagesByConversation: GetImageAssetMessagesForConversationUseCase
get() = GetImageAssetMessagesForConversationUseCaseImpl(
dispatcher,
messageRepository
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ class MessageRepositoryTest {
.arrange()

// When
messageRepository.getAssetMessagesByConversationId(TEST_CONVERSATION_ID, 0, 0)
messageRepository.getImageAssetMessagesByConversationId(TEST_CONVERSATION_ID, 0, 0)

// Then
with(arrangement) {
verify(messageDAO)
.suspendFunction(messageDAO::getMessageAssets)
.suspendFunction(messageDAO::getImageMessageAssets)
.with(eq(mappedId), anything(), anything())
.wasInvoked(exactly = once)
}
Expand Down Expand Up @@ -595,7 +595,7 @@ class MessageRepositoryTest {

fun withMappedAssetMessageModel(message: AssetMessage): Arrangement {
given(messageMapper)
.function(messageMapper::fromAssetEntityToMessage)
.function(messageMapper::fromAssetEntityToAssetMessage)
.whenInvokedWith(anything())
.then { message }
return this
Expand Down Expand Up @@ -716,7 +716,7 @@ class MessageRepositoryTest {
result: List<AssetMessageEntity>
) = apply {
given(messageDAO)
.suspendFunction(messageDAO::getMessageAssets)
.suspendFunction(messageDAO::getImageMessageAssets)
.whenInvokedWith(eq(conversationId))
.thenReturn(result)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class GetAssetMessagesForConversationUseCaseTest {

// Then
verify(arrangement.messageRepository)
.suspendFunction(arrangement.messageRepository::getAssetMessagesByConversationId)
.suspendFunction(arrangement.messageRepository::getImageAssetMessagesByConversationId)
.with(eq(someConversationId), eq(limit), eq(offset))
.wasInvoked(exactly = once)
}
Expand All @@ -84,7 +84,7 @@ class GetAssetMessagesForConversationUseCaseTest {
@Mock
val messageRepository = mock(classOf<MessageRepository>())

val getAssetMessagesByConversationUseCase = GetAssetMessagesForConversationUseCaseImpl(
val getAssetMessagesByConversationUseCase = GetImageAssetMessagesForConversationUseCaseImpl(
testDispatcher,
messageRepository
)
Expand All @@ -96,7 +96,7 @@ class GetAssetMessagesForConversationUseCaseTest {
offset: Int
): Arrangement = apply {
given(messageRepository)
.suspendFunction(messageRepository::getAssetMessagesByConversationId)
.suspendFunction(messageRepository::getImageAssetMessagesByConversationId)
.whenInvokedWith(eq(conversationId), eq(limit), eq(offset))
.thenReturn(assetList)
}
Expand Down
Loading

0 comments on commit 10e2eec

Please sign in to comment.