Let the Presenter use real classes.
This commit is contained in:
@@ -51,6 +51,7 @@ dependencies {
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
||||
testImplementation(projects.libraries.dateformatter.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.mediaviewer.test)
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
|
||||
import io.element.android.libraries.dateformatter.api.toHumanReadableDuration
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.CallNotifyContent
|
||||
@@ -45,18 +43,13 @@ import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
interface EventItemFactory {
|
||||
fun create(currentTimelineItem: MatrixTimelineItem.Event): MediaItem.Event?
|
||||
}
|
||||
|
||||
@ContributesBinding(RoomScope::class)
|
||||
class DefaultEventItemFactory @Inject constructor(
|
||||
class EventItemFactory @Inject constructor(
|
||||
private val fileSizeFormatter: FileSizeFormatter,
|
||||
private val fileExtensionExtractor: FileExtensionExtractor,
|
||||
) : EventItemFactory {
|
||||
) {
|
||||
private val timeFormatter = DateFormat.getDateInstance()
|
||||
|
||||
override fun create(
|
||||
fun create(
|
||||
currentTimelineItem: MatrixTimelineItem.Event,
|
||||
): MediaItem.Event? {
|
||||
val event = currentTimelineItem.event
|
||||
|
||||
@@ -7,23 +7,14 @@
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import javax.inject.Inject
|
||||
|
||||
interface MediaItemsPostProcessor {
|
||||
class MediaItemsPostProcessor @Inject constructor() {
|
||||
fun process(
|
||||
mediaItems: AsyncData<ImmutableList<MediaItem>>,
|
||||
): AsyncData<GroupedMediaItems>
|
||||
}
|
||||
|
||||
@ContributesBinding(RoomScope::class)
|
||||
class DefaultMediaItemsPostProcessor @Inject constructor() : MediaItemsPostProcessor {
|
||||
override fun process(
|
||||
mediaItems: AsyncData<ImmutableList<MediaItem>>,
|
||||
): AsyncData<GroupedMediaItems> {
|
||||
return when (mediaItems) {
|
||||
is AsyncData.Uninitialized -> AsyncData.Uninitialized
|
||||
|
||||
@@ -7,12 +7,10 @@
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.androidutils.diff.DefaultDiffCacheInvalidator
|
||||
import io.element.android.libraries.androidutils.diff.DiffCacheUpdater
|
||||
import io.element.android.libraries.androidutils.diff.MutableListDiffCache
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
@@ -24,17 +22,11 @@ import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
interface TimelineMediaItemsFactory {
|
||||
val timelineItems: Flow<ImmutableList<MediaItem>>
|
||||
suspend fun replaceWith(timelineItems: List<MatrixTimelineItem>)
|
||||
}
|
||||
|
||||
@ContributesBinding(RoomScope::class)
|
||||
class DefaultTimelineMediaItemsFactory @Inject constructor(
|
||||
class TimelineMediaItemsFactory @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val virtualItemFactory: VirtualItemFactory,
|
||||
private val eventItemFactory: EventItemFactory,
|
||||
) : TimelineMediaItemsFactory {
|
||||
) {
|
||||
private val _timelineItems = MutableSharedFlow<ImmutableList<MediaItem>>(replay = 1)
|
||||
private val lock = Mutex()
|
||||
private val diffCache = MutableListDiffCache<MediaItem>()
|
||||
@@ -50,9 +42,9 @@ class DefaultTimelineMediaItemsFactory @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override val timelineItems: Flow<ImmutableList<MediaItem>> = _timelineItems.distinctUntilChanged()
|
||||
val timelineItems: Flow<ImmutableList<MediaItem>> = _timelineItems.distinctUntilChanged()
|
||||
|
||||
override suspend fun replaceWith(
|
||||
suspend fun replaceWith(
|
||||
timelineItems: List<MatrixTimelineItem>,
|
||||
) = withContext(dispatchers.computation) {
|
||||
lock.withLock {
|
||||
|
||||
@@ -7,22 +7,15 @@
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.dateformatter.api.DaySeparatorFormatter
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
||||
import javax.inject.Inject
|
||||
|
||||
interface VirtualItemFactory {
|
||||
fun create(timelineItem: MatrixTimelineItem.Virtual): MediaItem?
|
||||
}
|
||||
|
||||
@ContributesBinding(RoomScope::class)
|
||||
class DefaultVirtualItemFactory @Inject constructor(
|
||||
class VirtualItemFactory @Inject constructor(
|
||||
private val daySeparatorFormatter: DaySeparatorFormatter,
|
||||
) : VirtualItemFactory {
|
||||
override fun create(timelineItem: MatrixTimelineItem.Virtual): MediaItem? {
|
||||
) {
|
||||
fun create(timelineItem: MatrixTimelineItem.Virtual): MediaItem? {
|
||||
return when (val virtual = timelineItem.virtual) {
|
||||
is VirtualTimelineItem.DayDivider -> MediaItem.DateSeparator(
|
||||
id = timelineItem.uniqueId,
|
||||
|
||||
@@ -55,7 +55,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
class DefaultEventItemFactoryTest {
|
||||
@Test
|
||||
fun `create check all null cases`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val contents = listOf(
|
||||
CallNotifyContent,
|
||||
FailedToParseMessageLikeContent("", ""),
|
||||
@@ -100,7 +100,7 @@ class DefaultEventItemFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `create MessageContent check all null cases`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val messageTypes = listOf(
|
||||
EmoteMessageType("", null),
|
||||
NoticeMessageType("", null),
|
||||
@@ -125,7 +125,7 @@ class DefaultEventItemFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `create for FileMessageType`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val result = factory.create(
|
||||
MatrixTimelineItem.Event(
|
||||
uniqueId = A_UNIQUE_ID,
|
||||
@@ -169,7 +169,7 @@ class DefaultEventItemFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `create for ImageMessageType`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val result = factory.create(
|
||||
MatrixTimelineItem.Event(
|
||||
uniqueId = A_UNIQUE_ID,
|
||||
@@ -217,7 +217,7 @@ class DefaultEventItemFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `create for AudioMessageType`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val result = factory.create(
|
||||
MatrixTimelineItem.Event(
|
||||
uniqueId = A_UNIQUE_ID,
|
||||
@@ -260,7 +260,7 @@ class DefaultEventItemFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `create for VideoMessageType`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val result = factory.create(
|
||||
MatrixTimelineItem.Event(
|
||||
uniqueId = A_UNIQUE_ID,
|
||||
@@ -310,7 +310,7 @@ class DefaultEventItemFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `create for VoiceMessageType`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val result = factory.create(
|
||||
MatrixTimelineItem.Event(
|
||||
uniqueId = A_UNIQUE_ID,
|
||||
@@ -357,7 +357,7 @@ class DefaultEventItemFactoryTest {
|
||||
|
||||
@Test
|
||||
fun `create for StickerMessageType`() {
|
||||
val factory = createDefaultEventItemFactory()
|
||||
val factory = createEventItemFactory()
|
||||
val result = factory.create(
|
||||
MatrixTimelineItem.Event(
|
||||
uniqueId = A_UNIQUE_ID,
|
||||
@@ -404,7 +404,7 @@ class DefaultEventItemFactoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDefaultEventItemFactory() = DefaultEventItemFactory(
|
||||
private fun createEventItemFactory() = EventItemFactory(
|
||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
||||
)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
|
||||
class FakeEventItemFactory : EventItemFactory {
|
||||
override fun create(currentTimelineItem: MatrixTimelineItem.Event): MediaItem.Event? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
class FakeMediaItemsPostProcessor : MediaItemsPostProcessor {
|
||||
override fun process(mediaItems: AsyncData<ImmutableList<MediaItem>>): AsyncData<GroupedMediaItems> {
|
||||
return AsyncData.Success(
|
||||
GroupedMediaItems(
|
||||
imageAndVideoItems = persistentListOf(),
|
||||
fileItems = persistentListOf()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class FakeTimelineMediaItemsFactory(
|
||||
private val replaceWithLambda: (List<MatrixTimelineItem>) -> Unit = { lambdaError() },
|
||||
) : TimelineMediaItemsFactory {
|
||||
override val timelineItems: Flow<ImmutableList<MediaItem>>
|
||||
get() = flowOf(emptyList<MediaItem>().toImmutableList())
|
||||
|
||||
override suspend fun replaceWith(timelineItems: List<MatrixTimelineItem>) {
|
||||
replaceWithLambda(timelineItems)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
|
||||
class FakeVirtualItemFactory : VirtualItemFactory {
|
||||
override fun create(timelineItem: MatrixTimelineItem.Virtual): MediaItem? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,11 @@ package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
|
||||
import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
@@ -24,12 +25,15 @@ import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetSta
|
||||
import io.element.android.libraries.mediaviewer.impl.gallery.ui.aMediaItemImage
|
||||
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions
|
||||
import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory
|
||||
import io.element.android.libraries.mediaviewer.test.util.FileExtensionExtractorWithoutValidation
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import io.mockk.mockk
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -55,7 +59,7 @@ class MediaGalleryPresenterTest {
|
||||
)
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.mode).isEqualTo(MediaGalleryMode.Images)
|
||||
assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden)
|
||||
@@ -84,7 +88,7 @@ class MediaGalleryPresenterTest {
|
||||
)
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.mode).isEqualTo(MediaGalleryMode.Images)
|
||||
initialState.eventSink(MediaGalleryEvents.ChangeMode(MediaGalleryMode.Files))
|
||||
@@ -106,7 +110,7 @@ class MediaGalleryPresenterTest {
|
||||
`present - bottom sheet state - own message`(canDeleteOwn = false)
|
||||
}
|
||||
|
||||
private suspend fun `present - bottom sheet state - own message`(canDeleteOwn: Boolean) {
|
||||
private suspend fun TestScope.`present - bottom sheet state - own message`(canDeleteOwn: Boolean) {
|
||||
val presenter = createMediaGalleryPresenter(
|
||||
room = FakeMatrixRoom(
|
||||
sessionId = A_USER_ID,
|
||||
@@ -116,7 +120,7 @@ class MediaGalleryPresenterTest {
|
||||
)
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden)
|
||||
val item = aMediaItemImage(
|
||||
@@ -150,7 +154,7 @@ class MediaGalleryPresenterTest {
|
||||
`present - bottom sheet state - other message`(canDeleteOther = false)
|
||||
}
|
||||
|
||||
private suspend fun `present - bottom sheet state - other message`(canDeleteOther: Boolean) {
|
||||
private suspend fun TestScope.`present - bottom sheet state - other message`(canDeleteOther: Boolean) {
|
||||
val presenter = createMediaGalleryPresenter(
|
||||
room = FakeMatrixRoom(
|
||||
sessionId = A_USER_ID,
|
||||
@@ -160,7 +164,7 @@ class MediaGalleryPresenterTest {
|
||||
)
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden)
|
||||
val item = aMediaItemImage(
|
||||
@@ -193,7 +197,7 @@ class MediaGalleryPresenterTest {
|
||||
)
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
// Delete bottom sheet
|
||||
val item = aMediaItemImage()
|
||||
@@ -226,14 +230,14 @@ class MediaGalleryPresenterTest {
|
||||
navigator = navigator,
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
skipItems(2)
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MediaGalleryEvents.ViewInTimeline(AN_EVENT_ID))
|
||||
onViewInTimelineClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMediaGalleryPresenter(
|
||||
private fun TestScope.createMediaGalleryPresenter(
|
||||
matrixMediaLoader: FakeMatrixMediaLoader = FakeMatrixMediaLoader(),
|
||||
localMediaActions: FakeLocalMediaActions = FakeLocalMediaActions(),
|
||||
snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(),
|
||||
@@ -245,14 +249,21 @@ class MediaGalleryPresenterTest {
|
||||
return MediaGalleryPresenter(
|
||||
navigator = navigator,
|
||||
room = room,
|
||||
timelineMediaItemsFactory = FakeTimelineMediaItemsFactory(
|
||||
replaceWithLambda = lambdaRecorder<List<MatrixTimelineItem>, Unit> { _ -> },
|
||||
timelineMediaItemsFactory = TimelineMediaItemsFactory(
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
virtualItemFactory = VirtualItemFactory(
|
||||
daySeparatorFormatter = FakeDaySeparatorFormatter(),
|
||||
),
|
||||
eventItemFactory = EventItemFactory(
|
||||
fileSizeFormatter = FakeFileSizeFormatter(),
|
||||
fileExtensionExtractor = FileExtensionExtractorWithoutValidation(),
|
||||
),
|
||||
),
|
||||
localMediaFactory = localMediaFactory,
|
||||
mediaLoader = matrixMediaLoader,
|
||||
localMediaActions = localMediaActions,
|
||||
snackbarDispatcher = snackbarDispatcher,
|
||||
mediaItemsPostProcessor = FakeMediaItemsPostProcessor(),
|
||||
mediaItemsPostProcessor = MediaItemsPostProcessor(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user