Skip to content

Commit a0fb244

Browse files
authored
Merge pull request #3819 from element-hq/feature/bma/fixDeleteOriginalFile
Do not delete the original file if it's not a temporary file when sending it to a room.
2 parents 64b1f30 + 8dfe530 commit a0fb244

File tree

18 files changed

+424
-118
lines changed

18 files changed

+424
-118
lines changed

features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,12 @@ class ConfigureRoomPresenter @Inject constructor(
175175
}
176176

177177
private suspend fun uploadAvatar(avatarUri: Uri): String {
178-
val preprocessed = mediaPreProcessor.process(avatarUri, MimeTypes.Jpeg, compressIfPossible = false).getOrThrow()
178+
val preprocessed = mediaPreProcessor.process(
179+
uri = avatarUri,
180+
mimeType = MimeTypes.Jpeg,
181+
deleteOriginal = false,
182+
compressIfPossible = false,
183+
).getOrThrow()
179184
val byteArray = preprocessed.file.readBytes()
180185
return matrixClient.uploadMedia(MimeTypes.Jpeg, byteArray, null).getOrThrow()
181186
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewEvents.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ import androidx.compose.runtime.Immutable
1212
@Immutable
1313
sealed interface AttachmentsPreviewEvents {
1414
data object SendAttachment : AttachmentsPreviewEvents
15+
data object Cancel : AttachmentsPreviewEvents
1516
data object ClearSendState : AttachmentsPreviewEvents
1617
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewNode.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,21 @@ class AttachmentsPreviewNode @AssistedInject constructor(
3131

3232
private val inputs: Inputs = inputs()
3333

34-
private val presenter = presenterFactory.create(inputs.attachment)
34+
private val onDoneListener = OnDoneListener {
35+
navigateUp()
36+
}
37+
38+
private val presenter = presenterFactory.create(
39+
attachment = inputs.attachment,
40+
onDoneListener = onDoneListener,
41+
)
3542

3643
@Composable
3744
override fun View(modifier: Modifier) {
3845
ForcedDarkElementTheme {
3946
val state = presenter.present()
4047
AttachmentsPreviewView(
4148
state = state,
42-
onDismiss = this::navigateUp,
4349
modifier = modifier
4450
)
4551
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import dagger.assisted.Assisted
1818
import dagger.assisted.AssistedFactory
1919
import dagger.assisted.AssistedInject
2020
import io.element.android.features.messages.impl.attachments.Attachment
21+
import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
2122
import io.element.android.libraries.architecture.Presenter
2223
import io.element.android.libraries.matrix.api.core.ProgressCallback
2324
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
@@ -34,12 +35,17 @@ import kotlin.coroutines.coroutineContext
3435

3536
class AttachmentsPreviewPresenter @AssistedInject constructor(
3637
@Assisted private val attachment: Attachment,
38+
@Assisted private val onDoneListener: OnDoneListener,
3739
private val mediaSender: MediaSender,
3840
private val permalinkBuilder: PermalinkBuilder,
41+
private val temporaryUriDeleter: TemporaryUriDeleter,
3942
) : Presenter<AttachmentsPreviewState> {
4043
@AssistedFactory
4144
interface Factory {
42-
fun create(attachment: Attachment): AttachmentsPreviewPresenter
45+
fun create(
46+
attachment: Attachment,
47+
onDoneListener: OnDoneListener,
48+
): AttachmentsPreviewPresenter
4349
}
4450

4551
@Composable
@@ -68,6 +74,9 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
6874
sendActionState = sendActionState,
6975
)
7076
}
77+
AttachmentsPreviewEvents.Cancel -> {
78+
coroutineScope.cancel(attachment)
79+
}
7180
AttachmentsPreviewEvents.ClearSendState -> {
7281
ongoingSendAttachmentJob.value?.let {
7382
it.cancel()
@@ -102,6 +111,18 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
102111
}
103112
}
104113

114+
private fun CoroutineScope.cancel(
115+
attachment: Attachment,
116+
) = launch {
117+
// Delete the temporary file
118+
when (attachment) {
119+
is Attachment.Media -> {
120+
temporaryUriDeleter.delete(attachment.localMedia.uri)
121+
}
122+
}
123+
onDoneListener()
124+
}
125+
105126
private suspend fun sendMedia(
106127
mediaAttachment: Attachment.Media,
107128
caption: String?,
@@ -124,7 +145,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
124145
).getOrThrow()
125146
}.fold(
126147
onSuccess = {
127-
sendActionState.value = SendActionState.Done
148+
onDoneListener()
128149
},
129150
onFailure = { error ->
130151
Timber.e(error, "Failed to send attachment")

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewState.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,4 @@ sealed interface SendActionState {
3636
}
3737

3838
data class Failure(val error: Throwable) : SendActionState
39-
data object Done : SendActionState
4039
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package io.element.android.features.messages.impl.attachments.preview
99

10+
import androidx.activity.compose.BackHandler
1011
import androidx.compose.foundation.background
1112
import androidx.compose.foundation.layout.Box
1213
import androidx.compose.foundation.layout.IntrinsicSize
@@ -17,9 +18,6 @@ import androidx.compose.foundation.layout.imePadding
1718
import androidx.compose.foundation.layout.navigationBarsPadding
1819
import androidx.compose.material3.ExperimentalMaterial3Api
1920
import androidx.compose.runtime.Composable
20-
import androidx.compose.runtime.LaunchedEffect
21-
import androidx.compose.runtime.getValue
22-
import androidx.compose.runtime.rememberUpdatedState
2321
import androidx.compose.ui.Alignment
2422
import androidx.compose.ui.Modifier
2523
import androidx.compose.ui.res.stringResource
@@ -50,22 +48,22 @@ import me.saket.telephoto.zoomable.rememberZoomableState
5048
@Composable
5149
fun AttachmentsPreviewView(
5250
state: AttachmentsPreviewState,
53-
onDismiss: () -> Unit,
5451
modifier: Modifier = Modifier,
5552
) {
5653
fun postSendAttachment() {
5754
state.eventSink(AttachmentsPreviewEvents.SendAttachment)
5855
}
5956

57+
fun postCancel() {
58+
state.eventSink(AttachmentsPreviewEvents.Cancel)
59+
}
60+
6061
fun postClearSendState() {
6162
state.eventSink(AttachmentsPreviewEvents.ClearSendState)
6263
}
6364

64-
if (state.sendActionState is SendActionState.Done) {
65-
val latestOnDismiss by rememberUpdatedState(onDismiss)
66-
LaunchedEffect(state.sendActionState) {
67-
latestOnDismiss()
68-
}
65+
BackHandler(enabled = state.sendActionState !is SendActionState.Sending) {
66+
postCancel()
6967
}
7068

7169
Scaffold(
@@ -75,7 +73,7 @@ fun AttachmentsPreviewView(
7573
navigationIcon = {
7674
BackButton(
7775
imageVector = CompoundIcons.Close(),
78-
onClick = onDismiss,
76+
onClick = ::postCancel,
7977
)
8078
},
8179
title = {},
@@ -202,6 +200,5 @@ private fun AttachmentsPreviewBottomActions(
202200
internal fun AttachmentsPreviewViewPreview(@PreviewParameter(AttachmentsPreviewStateProvider::class) state: AttachmentsPreviewState) = ElementPreviewDark {
203201
AttachmentsPreviewView(
204202
state = state,
205-
onDismiss = {},
206203
)
207204
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.messages.impl.attachments.preview
9+
10+
fun interface OnDoneListener {
11+
operator fun invoke()
12+
}

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ import app.cash.turbine.test
1616
import com.google.common.truth.Truth.assertThat
1717
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents
1818
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter
19+
import io.element.android.features.messages.impl.attachments.preview.OnDoneListener
1920
import io.element.android.features.messages.impl.attachments.preview.SendActionState
2021
import io.element.android.features.messages.impl.fixtures.aMediaAttachment
22+
import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
2123
import io.element.android.libraries.matrix.api.core.ProgressCallback
2224
import io.element.android.libraries.matrix.api.media.FileInfo
2325
import io.element.android.libraries.matrix.api.media.ImageInfo
@@ -35,11 +37,13 @@ import io.element.android.libraries.mediaviewer.api.local.LocalMedia
3537
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
3638
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
3739
import io.element.android.tests.testutils.WarmUpRule
40+
import io.element.android.tests.testutils.fake.FakeTemporaryUriDeleter
3841
import io.element.android.tests.testutils.lambda.any
3942
import io.element.android.tests.testutils.lambda.lambdaRecorder
4043
import io.element.android.tests.testutils.lambda.value
4144
import io.mockk.mockk
4245
import kotlinx.coroutines.ExperimentalCoroutinesApi
46+
import kotlinx.coroutines.test.advanceUntilIdle
4347
import kotlinx.coroutines.test.runTest
4448
import org.junit.Rule
4549
import org.junit.Test
@@ -67,7 +71,11 @@ class AttachmentsPreviewPresenterTest {
6771
),
6872
sendFileResult = sendFileResult,
6973
)
70-
val presenter = createAttachmentsPreviewPresenter(room = room)
74+
val onDoneListener = lambdaRecorder<Unit> { }
75+
val presenter = createAttachmentsPreviewPresenter(
76+
room = room,
77+
onDoneListener = { onDoneListener() },
78+
)
7179
moleculeFlow(RecompositionMode.Immediate) {
7280
presenter.present()
7381
}.test {
@@ -78,9 +86,28 @@ class AttachmentsPreviewPresenterTest {
7886
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0f))
7987
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(0.5f))
8088
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Uploading(1f))
81-
val successState = awaitItem()
82-
assertThat(successState.sendActionState).isEqualTo(SendActionState.Done)
89+
advanceUntilIdle()
8390
sendFileResult.assertions().isCalledOnce()
91+
onDoneListener.assertions().isCalledOnce()
92+
}
93+
}
94+
95+
@Test
96+
fun `present - cancel scenario`() = runTest {
97+
val onDoneListener = lambdaRecorder<Unit> { }
98+
val deleteCallback = lambdaRecorder<Uri?, Unit> {}
99+
val presenter = createAttachmentsPreviewPresenter(
100+
temporaryUriDeleter = FakeTemporaryUriDeleter(deleteCallback),
101+
onDoneListener = { onDoneListener() },
102+
)
103+
moleculeFlow(RecompositionMode.Immediate) {
104+
presenter.present()
105+
}.test {
106+
val initialState = awaitItem()
107+
assertThat(initialState.sendActionState).isEqualTo(SendActionState.Idle)
108+
initialState.eventSink(AttachmentsPreviewEvents.Cancel)
109+
deleteCallback.assertions().isCalledOnce()
110+
onDoneListener.assertions().isCalledOnce()
84111
}
85112
}
86113

@@ -96,9 +123,11 @@ class AttachmentsPreviewPresenterTest {
96123
val room = FakeMatrixRoom(
97124
sendImageResult = sendImageResult,
98125
)
126+
val onDoneListener = lambdaRecorder<Unit> { }
99127
val presenter = createAttachmentsPreviewPresenter(
100128
room = room,
101129
mediaPreProcessor = mediaPreProcessor,
130+
onDoneListener = { onDoneListener() },
102131
)
103132
moleculeFlow(RecompositionMode.Immediate) {
104133
presenter.present()
@@ -108,8 +137,7 @@ class AttachmentsPreviewPresenterTest {
108137
initialState.textEditorState.setMarkdown(A_CAPTION)
109138
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
110139
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
111-
val successState = awaitItem()
112-
assertThat(successState.sendActionState).isEqualTo(SendActionState.Done)
140+
advanceUntilIdle()
113141
sendImageResult.assertions().isCalledOnce().with(
114142
any(),
115143
any(),
@@ -118,6 +146,7 @@ class AttachmentsPreviewPresenterTest {
118146
any(),
119147
any(),
120148
)
149+
onDoneListener.assertions().isCalledOnce()
121150
}
122151
}
123152

@@ -133,9 +162,11 @@ class AttachmentsPreviewPresenterTest {
133162
val room = FakeMatrixRoom(
134163
sendVideoResult = sendVideoResult,
135164
)
165+
val onDoneListener = lambdaRecorder<Unit> { }
136166
val presenter = createAttachmentsPreviewPresenter(
137167
room = room,
138168
mediaPreProcessor = mediaPreProcessor,
169+
onDoneListener = { onDoneListener() },
139170
)
140171
moleculeFlow(RecompositionMode.Immediate) {
141172
presenter.present()
@@ -145,8 +176,7 @@ class AttachmentsPreviewPresenterTest {
145176
initialState.textEditorState.setMarkdown(A_CAPTION)
146177
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment)
147178
assertThat(awaitItem().sendActionState).isEqualTo(SendActionState.Sending.Processing)
148-
val successState = awaitItem()
149-
assertThat(successState.sendActionState).isEqualTo(SendActionState.Done)
179+
advanceUntilIdle()
150180
sendVideoResult.assertions().isCalledOnce().with(
151181
any(),
152182
any(),
@@ -155,6 +185,7 @@ class AttachmentsPreviewPresenterTest {
155185
any(),
156186
any(),
157187
)
188+
onDoneListener.assertions().isCalledOnce()
158189
}
159190
}
160191

@@ -207,11 +238,15 @@ class AttachmentsPreviewPresenterTest {
207238
room: MatrixRoom = FakeMatrixRoom(),
208239
permalinkBuilder: PermalinkBuilder = FakePermalinkBuilder(),
209240
mediaPreProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
241+
temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(),
242+
onDoneListener: OnDoneListener = OnDoneListener {},
210243
): AttachmentsPreviewPresenter {
211244
return AttachmentsPreviewPresenter(
212245
attachment = aMediaAttachment(localMedia),
246+
onDoneListener = onDoneListener,
213247
mediaSender = MediaSender(mediaPreProcessor, room, InMemorySessionPreferencesStore()),
214248
permalinkBuilder = permalinkBuilder,
249+
temporaryUriDeleter = temporaryUriDeleter,
215250
)
216251
}
217252
}

0 commit comments

Comments
 (0)