Remove not needed MediaGalleryTimelineProvider
This commit is contained in:
@@ -33,7 +33,6 @@ dependencies {
|
||||
implementation(libs.vanniktech.blurhash)
|
||||
implementation(libs.telephoto.flick)
|
||||
|
||||
implementation(projects.features.networkmonitor.api)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.core)
|
||||
@@ -52,7 +51,6 @@ dependencies {
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
|
||||
testImplementation(projects.features.networkmonitor.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.mediaviewer.test)
|
||||
|
||||
@@ -9,6 +9,7 @@ package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
@@ -50,7 +51,6 @@ import kotlinx.coroutines.launch
|
||||
class MediaGalleryPresenter @AssistedInject constructor(
|
||||
@Assisted private val navigator: MediaGalleryNavigator,
|
||||
private val room: MatrixRoom,
|
||||
private val timelineProvider: MediaGalleryTimelineProvider,
|
||||
private val timelineMediaItemsFactory: TimelineMediaItemsFactory,
|
||||
private val localMediaFactory: LocalMediaFactory,
|
||||
private val mediaLoader: MatrixMediaLoader,
|
||||
@@ -97,27 +97,36 @@ class MediaGalleryPresenter @AssistedInject constructor(
|
||||
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
|
||||
localMediaActions.Configure()
|
||||
|
||||
var timeline by remember { mutableStateOf<AsyncData<Timeline>>(AsyncData.Uninitialized) }
|
||||
LaunchedEffect(Unit) {
|
||||
room.mediaTimeline()
|
||||
.fold(
|
||||
{ timeline = AsyncData.Success(it) },
|
||||
{ timeline = AsyncData.Failure(it) },
|
||||
)
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
timeline.dataOrNull()?.close()
|
||||
}
|
||||
}
|
||||
|
||||
MediaListEffect(
|
||||
timeline = timeline,
|
||||
onItemsChange = { newItems ->
|
||||
mediaItems = newItems
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
timelineProvider.launchIn(this)
|
||||
}
|
||||
|
||||
fun handleEvents(event: MediaGalleryEvents) {
|
||||
when (event) {
|
||||
is MediaGalleryEvents.ChangeMode -> {
|
||||
mode = event.mode
|
||||
}
|
||||
is MediaGalleryEvents.LoadMore -> coroutineScope.launch {
|
||||
timelineProvider.invokeOnTimeline {
|
||||
paginate(event.direction)
|
||||
}
|
||||
timeline.dataOrNull()?.paginate(event.direction)
|
||||
}
|
||||
is MediaGalleryEvents.Delete -> coroutineScope.delete(event.eventId)
|
||||
is MediaGalleryEvents.Delete -> coroutineScope.delete(timeline, event.eventId)
|
||||
is MediaGalleryEvents.SaveOnDisk -> coroutineScope.saveOnDisk(event.mediaItem)
|
||||
is MediaGalleryEvents.Share -> coroutineScope.share(event.mediaItem)
|
||||
is MediaGalleryEvents.ViewInTimeline -> {
|
||||
@@ -166,18 +175,19 @@ class MediaGalleryPresenter @AssistedInject constructor(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MediaListEffect(onItemsChange: (AsyncData<ImmutableList<MediaItem>>) -> Unit) {
|
||||
private fun MediaListEffect(
|
||||
timeline: AsyncData<Timeline>,
|
||||
onItemsChange: (AsyncData<ImmutableList<MediaItem>>) -> Unit,
|
||||
) {
|
||||
val updatedOnItemsChange by rememberUpdatedState(onItemsChange)
|
||||
|
||||
val timelineState by timelineProvider.timelineStateFlow.collectAsState()
|
||||
|
||||
LaunchedEffect(timelineState) {
|
||||
when (val asyncTimeline = timelineState) {
|
||||
LaunchedEffect(timeline) {
|
||||
when (timeline) {
|
||||
AsyncData.Uninitialized -> flowOf(AsyncData.Uninitialized)
|
||||
is AsyncData.Failure -> flowOf(AsyncData.Failure(asyncTimeline.error))
|
||||
is AsyncData.Failure -> flowOf(AsyncData.Failure(timeline.error))
|
||||
is AsyncData.Loading -> flowOf(AsyncData.Loading())
|
||||
is AsyncData.Success -> {
|
||||
asyncTimeline.data.timelineItems
|
||||
timeline.data.timelineItems
|
||||
.onEach { items ->
|
||||
timelineMediaItemsFactory.replaceWith(
|
||||
timelineItems = items,
|
||||
@@ -185,7 +195,7 @@ class MediaGalleryPresenter @AssistedInject constructor(
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
asyncTimeline.data.paginationStatus(Timeline.PaginationDirection.BACKWARDS)
|
||||
timeline.data.paginationStatus(Timeline.PaginationDirection.BACKWARDS)
|
||||
.onEach { backwardPaginationStatus ->
|
||||
if (backwardPaginationStatus.canPaginate) {
|
||||
timelineMediaItemsFactory.onCanPaginate()
|
||||
@@ -205,13 +215,14 @@ class MediaGalleryPresenter @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.delete(eventId: EventId) = launch {
|
||||
timelineProvider.invokeOnTimeline {
|
||||
redactEvent(
|
||||
eventOrTransactionId = eventId.toEventOrTransactionId(),
|
||||
reason = null,
|
||||
)
|
||||
}
|
||||
private fun CoroutineScope.delete(
|
||||
timeline: AsyncData<Timeline>,
|
||||
eventId: EventId,
|
||||
) = launch {
|
||||
timeline.dataOrNull()?.redactEvent(
|
||||
eventOrTransactionId = eventId.toEventOrTransactionId(),
|
||||
reason = null,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun downloadMedia(mediaItem: MediaItem.Event): Result<LocalMedia> {
|
||||
|
||||
@@ -1,113 +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.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.core.coroutine.mapState
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(RoomScope::class)
|
||||
class MediaGalleryTimelineProvider @Inject constructor(
|
||||
private val room: MatrixRoom,
|
||||
private val networkMonitor: NetworkMonitor,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
) : TimelineProvider {
|
||||
private val _timelineStateFlow: MutableStateFlow<AsyncData<Timeline>> =
|
||||
MutableStateFlow(AsyncData.Uninitialized)
|
||||
|
||||
override fun activeTimelineFlow(): StateFlow<Timeline?> {
|
||||
return _timelineStateFlow
|
||||
.mapState { value ->
|
||||
value.dataOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
val timelineStateFlow = _timelineStateFlow
|
||||
|
||||
fun launchIn(scope: CoroutineScope) {
|
||||
_timelineStateFlow.subscriptionCount
|
||||
.map { count -> count > 0 }
|
||||
.distinctUntilChanged()
|
||||
.onEach { isActive ->
|
||||
if (isActive) {
|
||||
onActive()
|
||||
} else {
|
||||
onInactive()
|
||||
}
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
private suspend fun onActive() = coroutineScope {
|
||||
combine(
|
||||
featureFlagService.isFeatureEnabledFlow(FeatureFlags.MediaGallery),
|
||||
networkMonitor.connectivity
|
||||
) { isEnabled, _ ->
|
||||
// do not use connectivity here as data can be loaded from cache, it's just to trigger retry if needed
|
||||
isEnabled
|
||||
}
|
||||
.onEach { isFeatureEnabled ->
|
||||
if (isFeatureEnabled) {
|
||||
loadTimelineIfNeeded()
|
||||
} else {
|
||||
resetTimeline()
|
||||
}
|
||||
}
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
private suspend fun onInactive() {
|
||||
resetTimeline()
|
||||
}
|
||||
|
||||
private suspend fun resetTimeline() {
|
||||
invokeOnTimeline {
|
||||
close()
|
||||
}
|
||||
_timelineStateFlow.emit(AsyncData.Uninitialized)
|
||||
}
|
||||
|
||||
suspend fun invokeOnTimeline(action: suspend Timeline.() -> Unit) {
|
||||
when (val asyncTimeline = timelineStateFlow.value) {
|
||||
is AsyncData.Success -> action(asyncTimeline.data)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadTimelineIfNeeded() {
|
||||
when (timelineStateFlow.value) {
|
||||
is AsyncData.Uninitialized,
|
||||
is AsyncData.Failure -> {
|
||||
timelineStateFlow.emit(AsyncData.Loading())
|
||||
room.mediaTimeline()
|
||||
.fold(
|
||||
{ timelineStateFlow.emit(AsyncData.Success(it)) },
|
||||
{ timelineStateFlow.emit(AsyncData.Failure(it)) }
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,7 @@ package io.element.android.libraries.mediaviewer.impl.gallery
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
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
|
||||
@@ -242,11 +240,6 @@ class MediaGalleryPresenterTest {
|
||||
return MediaGalleryPresenter(
|
||||
navigator = navigator,
|
||||
room = room,
|
||||
timelineProvider = MediaGalleryTimelineProvider(
|
||||
room = room,
|
||||
networkMonitor = FakeNetworkMonitor(),
|
||||
featureFlagService = FakeFeatureFlagService(),
|
||||
),
|
||||
timelineMediaItemsFactory = FakeTimelineMediaItemsFactory(
|
||||
replaceWithLambda = lambdaRecorder<List<MatrixTimelineItem>, Unit> { _ -> },
|
||||
onCanPaginateLambda = lambdaRecorder<Unit> { },
|
||||
|
||||
Reference in New Issue
Block a user