Avoid using MutableStateFlow, just MutableState in presenter.
This commit is contained in:
parent
4689607150
commit
5f27c7107a
2 changed files with 48 additions and 40 deletions
|
|
@ -34,18 +34,12 @@ import io.element.android.libraries.textcomposer.model.TextEditorState
|
||||||
import io.element.android.libraries.textcomposer.model.rememberMarkdownTextEditorState
|
import io.element.android.libraries.textcomposer.model.rememberMarkdownTextEditorState
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.flow.flatMapConcat
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
class AttachmentsPreviewPresenter @AssistedInject constructor(
|
class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
@Assisted private val attachment: Attachment,
|
@Assisted private val attachment: Attachment,
|
||||||
@Assisted private val onDoneListener: OnDoneListener,
|
@Assisted private val onDoneListener: OnDoneListener,
|
||||||
|
|
@ -77,41 +71,34 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
val ongoingSendAttachmentJob = remember { mutableStateOf<Job?>(null) }
|
val ongoingSendAttachmentJob = remember { mutableStateOf<Job?>(null) }
|
||||||
|
|
||||||
val userSentAttachment = remember {
|
val userSentAttachment = remember { mutableStateOf(false) }
|
||||||
MutableStateFlow(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val mediaUploadInfoStateFlow = remember { MutableStateFlow<AsyncData<MediaUploadInfo>>(AsyncData.Uninitialized) }
|
val mediaUploadInfoState = remember { mutableStateOf<AsyncData<MediaUploadInfo>>(AsyncData.Uninitialized) }
|
||||||
var prePropressingJob: Job? = null
|
var prePropressingJob: Job? = null
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
prePropressingJob = preProcessAttachment(
|
prePropressingJob = preProcessAttachment(
|
||||||
attachment,
|
attachment,
|
||||||
mediaUploadInfoStateFlow,
|
mediaUploadInfoState,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(userSentAttachment.value, mediaUploadInfoState.value) {
|
||||||
userSentAttachment.filter { it }
|
val mediaUploadInfo = mediaUploadInfoState.value
|
||||||
.flatMapConcat {
|
if (userSentAttachment.value && mediaUploadInfo.isReady())
|
||||||
mediaUploadInfoStateFlow.filter { it.isReady() }
|
if (mediaUploadInfo is AsyncData.Success) {
|
||||||
}
|
val caption = markdownTextEditorState.getMessageMarkdown(permalinkBuilder)
|
||||||
.distinctUntilChanged()
|
.takeIf { it.isNotEmpty() }
|
||||||
.collect { mediaUploadInfo ->
|
ongoingSendAttachmentJob.value = coroutineScope.launch {
|
||||||
if (mediaUploadInfo is AsyncData.Success) {
|
sendPreProcessedMedia(
|
||||||
val caption = markdownTextEditorState.getMessageMarkdown(permalinkBuilder)
|
mediaUploadInfo = mediaUploadInfo.data,
|
||||||
.takeIf { it.isNotEmpty() }
|
caption = caption,
|
||||||
ongoingSendAttachmentJob.value = coroutineScope.launch {
|
sendActionState = sendActionState,
|
||||||
sendPreProcessedMedia(
|
)
|
||||||
mediaUploadInfo = mediaUploadInfo.data,
|
|
||||||
caption = caption,
|
|
||||||
sendActionState = sendActionState,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (mediaUploadInfo is AsyncData.Failure) {
|
|
||||||
sendActionState.value = SendActionState.Failure(mediaUploadInfo.error)
|
|
||||||
}
|
}
|
||||||
// else: cannot happen since we filtered with isReady()
|
} else if (mediaUploadInfo is AsyncData.Failure) {
|
||||||
|
sendActionState.value = SendActionState.Failure(mediaUploadInfo.error)
|
||||||
}
|
}
|
||||||
|
// else: cannot happen since we filtered with isReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvents(attachmentsPreviewEvents: AttachmentsPreviewEvents) {
|
fun handleEvents(attachmentsPreviewEvents: AttachmentsPreviewEvents) {
|
||||||
|
|
@ -119,7 +106,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
is AttachmentsPreviewEvents.SendAttachment -> coroutineScope.launch {
|
is AttachmentsPreviewEvents.SendAttachment -> coroutineScope.launch {
|
||||||
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue)
|
||||||
userSentAttachment.value = true
|
userSentAttachment.value = true
|
||||||
val instantSending = mediaUploadInfoStateFlow.value.isReady() && useSendQueue
|
val instantSending = mediaUploadInfoState.value.isReady() && useSendQueue
|
||||||
sendActionState.value = if (instantSending) {
|
sendActionState.value = if (instantSending) {
|
||||||
SendActionState.Sending.InstantSending
|
SendActionState.Sending.InstantSending
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -130,7 +117,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
coroutineScope.cancel(
|
coroutineScope.cancel(
|
||||||
attachment,
|
attachment,
|
||||||
prePropressingJob,
|
prePropressingJob,
|
||||||
mediaUploadInfoStateFlow.value,
|
mediaUploadInfoState.value,
|
||||||
sendActionState,
|
sendActionState,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +141,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private fun CoroutineScope.preProcessAttachment(
|
private fun CoroutineScope.preProcessAttachment(
|
||||||
attachment: Attachment,
|
attachment: Attachment,
|
||||||
mediaUploadInfoState: MutableStateFlow<AsyncData<MediaUploadInfo>>,
|
mediaUploadInfoState: MutableState<AsyncData<MediaUploadInfo>>,
|
||||||
) = launch {
|
) = launch {
|
||||||
when (attachment) {
|
when (attachment) {
|
||||||
is Attachment.Media -> {
|
is Attachment.Media -> {
|
||||||
|
|
@ -168,22 +155,22 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
private suspend fun preProcessMedia(
|
private suspend fun preProcessMedia(
|
||||||
mediaAttachment: Attachment.Media,
|
mediaAttachment: Attachment.Media,
|
||||||
mediaUploadInfoState: MutableStateFlow<AsyncData<MediaUploadInfo>>,
|
mediaUploadInfoState: MutableState<AsyncData<MediaUploadInfo>>,
|
||||||
) {
|
) {
|
||||||
mediaUploadInfoState.emit(AsyncData.Loading())
|
mediaUploadInfoState.value = AsyncData.Loading()
|
||||||
mediaSender.preProcessMedia(
|
mediaSender.preProcessMedia(
|
||||||
uri = mediaAttachment.localMedia.uri,
|
uri = mediaAttachment.localMedia.uri,
|
||||||
mimeType = mediaAttachment.localMedia.info.mimeType,
|
mimeType = mediaAttachment.localMedia.info.mimeType,
|
||||||
).fold(
|
).fold(
|
||||||
onSuccess = { mediaUploadInfo ->
|
onSuccess = { mediaUploadInfo ->
|
||||||
mediaUploadInfoState.emit(AsyncData.Success(mediaUploadInfo))
|
mediaUploadInfoState.value = AsyncData.Success(mediaUploadInfo)
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
Timber.e(it, "Failed to pre-process media")
|
Timber.e(it, "Failed to pre-process media")
|
||||||
if (it is CancellationException) {
|
if (it is CancellationException) {
|
||||||
throw it
|
throw it
|
||||||
} else {
|
} else {
|
||||||
mediaUploadInfoState.emit(AsyncData.Failure(it))
|
mediaUploadInfoState.value = AsyncData.Failure(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0f))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0f))
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0.5f))
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0.5f))
|
||||||
|
|
@ -124,6 +126,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
processLatch.complete(Unit)
|
processLatch.complete(Unit)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.InstantSending)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.InstantSending)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
sendFileResult.assertions().isCalledOnce()
|
sendFileResult.assertions().isCalledOnce()
|
||||||
|
|
@ -154,9 +158,11 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
// Pre-processing finishes
|
// Pre-processing finishes
|
||||||
processLatch.complete(Unit)
|
processLatch.complete(Unit)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
sendFileResult.assertions().isCalledOnce()
|
sendFileResult.assertions().isCalledOnce()
|
||||||
onDoneListener.assertions().isCalledOnce()
|
onDoneListener.assertions().isCalledOnce()
|
||||||
|
|
@ -181,6 +187,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
// Pre-processing finishes
|
// Pre-processing finishes
|
||||||
processLatch.complete(Unit)
|
processLatch.complete(Unit)
|
||||||
|
|
@ -209,6 +217,9 @@ class AttachmentsPreviewPresenterTest {
|
||||||
processLatch.complete(Unit)
|
processLatch.complete(Unit)
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.InstantSending)
|
||||||
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Failure::class.java)
|
assertThat(awaitItem().sendActionState).isInstanceOf(SendActionState.Failure::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -227,6 +238,7 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.Cancel)
|
initialState.eventSink(AttachmentsPreviewEvents.Cancel)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
deleteCallback.assertions().isCalledOnce()
|
deleteCallback.assertions().isCalledOnce()
|
||||||
onDoneListener.assertions().isCalledOnce()
|
onDoneListener.assertions().isCalledOnce()
|
||||||
|
|
@ -258,6 +270,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
sendImageResult.assertions().isCalledOnce().with(
|
sendImageResult.assertions().isCalledOnce().with(
|
||||||
|
|
@ -297,6 +311,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
sendVideoResult.assertions().isCalledOnce().with(
|
sendVideoResult.assertions().isCalledOnce().with(
|
||||||
|
|
@ -334,6 +350,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.textEditorState.setMarkdown(A_CAPTION)
|
initialState.textEditorState.setMarkdown(A_CAPTION)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Done)
|
||||||
sendAudioResult.assertions().isCalledOnce().with(
|
sendAudioResult.assertions().isCalledOnce().with(
|
||||||
|
|
@ -363,8 +381,9 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
val loadingState = awaitItem()
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
val failureState = awaitItem()
|
val failureState = awaitItem()
|
||||||
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure))
|
assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure))
|
||||||
sendFileResult.assertions().isCalledOnce()
|
sendFileResult.assertions().isCalledOnce()
|
||||||
|
|
@ -383,6 +402,8 @@ class AttachmentsPreviewPresenterTest {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitItem()
|
||||||
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
|
||||||
initialState.eventSink(AttachmentsPreviewEvents.ClearSendState)
|
initialState.eventSink(AttachmentsPreviewEvents.ClearSendState)
|
||||||
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Idle)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue