Skip to content

Commit 9ce7d4f

Browse files
authored
Update AttachmentStorageHelper.resolveAttachmentFiles to be suspend (#6321)
1 parent 34cc1d4 commit 9ce7d4f

4 files changed

Lines changed: 49 additions & 41 deletions

File tree

stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModelTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ internal class MessageComposerViewModelTest {
446446
private val clientState: ClientState = mock()
447447
private val channelState: ChannelState = mock()
448448
private val storageHelper: AttachmentStorageHelper = mock {
449-
on { resolveAttachmentFiles(any()) } doAnswer { it.getArgument(0) }
449+
onBlocking { resolveAttachmentFiles(any()) } doAnswer { it.getArgument(0) }
450450
}
451451
private val appSettings: AppSettings = AppSettings(
452452
app = App(

stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,7 @@ public class MessageComposerController(
746746

747747
if (resolveAttachments != null) {
748748
scope.launch {
749-
val resolved = withContext(DispatcherProvider.IO) {
750-
resolveAttachments(preparedMessage.attachments)
751-
}
749+
val resolved = resolveAttachments(preparedMessage.attachments)
752750
enqueueSendMessage(preparedMessage.copy(attachments = resolved), callback)
753751
}
754752
} else {
@@ -763,8 +761,7 @@ public class MessageComposerController(
763761
) {
764762
scope.launch {
765763
val resolvedMessage = resolveAttachments?.let { resolve ->
766-
val resolved = withContext(DispatcherProvider.IO) { resolve(message.attachments) }
767-
message.copy(attachments = resolved)
764+
message.copy(attachments = resolve(message.attachments))
768765
} ?: message
769766
chatClient.editMessage(channelType, channelId, resolvedMessage).enqueue(callback)
770767
}

stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/helper/internal/AttachmentStorageHelper.kt

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ import androidx.annotation.WorkerThread
2424
import io.getstream.chat.android.client.utils.attachment.isImage
2525
import io.getstream.chat.android.client.utils.attachment.isVideo
2626
import io.getstream.chat.android.core.internal.InternalStreamChatApi
27+
import io.getstream.chat.android.core.internal.coroutines.DispatcherProvider
2728
import io.getstream.chat.android.models.Attachment
2829
import io.getstream.chat.android.ui.common.helper.internal.AttachmentStorageHelper.Companion.EXTRA_SOURCE_URI
2930
import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMetaData
3031
import io.getstream.log.taggedLogger
32+
import kotlinx.coroutines.withContext
3133
import java.io.File
3234

3335
/**
@@ -98,37 +100,37 @@ public class AttachmentStorageHelper(
98100
* @param attachments The attachments to resolve.
99101
* @return Attachments with [Attachment.upload] populated for every entry that had a source URI.
100102
*/
101-
@WorkerThread
102-
public fun resolveAttachmentFiles(
103-
attachments: List<Attachment>,
104-
): List<Attachment> = attachments.mapNotNull { attachment ->
105-
if (attachment.upload != null) return@mapNotNull attachment
106-
val sourceUri = (attachment.extraData[EXTRA_SOURCE_URI] as? String)
107-
?.let(Uri::parse) ?: return@mapNotNull attachment
108-
val metaData = AttachmentMetaData(
109-
uri = sourceUri,
110-
type = attachment.type,
111-
mimeType = attachment.mimeType,
112-
title = attachment.name,
113-
).apply { size = attachment.fileSize.toLong() }
114-
val file = storageHelper.getCachedFileFromUri(context, metaData)
115-
if (file == null) {
116-
logger.w { "[resolveAttachmentFiles] Failed to resolve file for URI: $sourceUri" }
117-
return@mapNotNull null
118-
}
103+
public suspend fun resolveAttachmentFiles(attachments: List<Attachment>): List<Attachment> =
104+
withContext(DispatcherProvider.IO) {
105+
attachments.mapNotNull { attachment ->
106+
if (attachment.upload != null) return@mapNotNull attachment
107+
val sourceUri = (attachment.extraData[EXTRA_SOURCE_URI] as? String)
108+
?.let(Uri::parse) ?: return@mapNotNull attachment
109+
val metaData = AttachmentMetaData(
110+
uri = sourceUri,
111+
type = attachment.type,
112+
mimeType = attachment.mimeType,
113+
title = attachment.name,
114+
).apply { size = attachment.fileSize.toLong() }
115+
val file = storageHelper.getCachedFileFromUri(context, metaData)
116+
if (file == null) {
117+
logger.w { "[resolveAttachmentFiles] Failed to resolve file for URI: $sourceUri" }
118+
return@mapNotNull null
119+
}
119120

120-
val (width, height) = if (attachment.originalWidth == null && attachment.originalHeight == null) {
121-
resolveLocalDimensions(file, attachment)
122-
} else {
123-
attachment.originalWidth to attachment.originalHeight
121+
val (width, height) = if (attachment.originalWidth == null && attachment.originalHeight == null) {
122+
resolveLocalDimensions(file, attachment)
123+
} else {
124+
attachment.originalWidth to attachment.originalHeight
125+
}
126+
attachment.copy(
127+
upload = file,
128+
extraData = attachment.extraData - EXTRA_SOURCE_URI,
129+
originalWidth = width,
130+
originalHeight = height,
131+
)
132+
}
124133
}
125-
attachment.copy(
126-
upload = file,
127-
extraData = attachment.extraData - EXTRA_SOURCE_URI,
128-
originalWidth = width,
129-
originalHeight = height,
130-
)
131-
}
132134

133135
/**
134136
* Resolves a list of file [Uri]s into [AttachmentMetaData].

stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/helper/internal/AttachmentStorageHelperTest.kt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ package io.getstream.chat.android.ui.common.helper.internal
1919
import android.content.Context
2020
import android.net.Uri
2121
import io.getstream.chat.android.models.Attachment
22+
import io.getstream.chat.android.test.TestCoroutineExtension
2223
import io.getstream.chat.android.ui.common.helper.internal.AttachmentStorageHelper.Companion.EXTRA_SOURCE_URI
2324
import io.getstream.chat.android.ui.common.state.messages.composer.AttachmentMetaData
25+
import kotlinx.coroutines.test.runTest
2426
import org.junit.jupiter.api.Assertions.assertEquals
2527
import org.junit.jupiter.api.Assertions.assertFalse
2628
import org.junit.jupiter.api.Assertions.assertNull
2729
import org.junit.jupiter.api.Assertions.assertSame
2830
import org.junit.jupiter.api.Test
31+
import org.junit.jupiter.api.extension.RegisterExtension
2932
import org.mockito.Mockito
3033
import org.mockito.kotlin.any
3134
import org.mockito.kotlin.doAnswer
@@ -166,7 +169,7 @@ internal class AttachmentStorageHelperTest {
166169
}
167170

168171
@Test
169-
fun `resolveAttachmentFiles skips attachment with existing upload`() {
172+
fun `resolveAttachmentFiles skips attachment with existing upload`() = runTest {
170173
val existingFile = mock<File>()
171174
val attachment = Attachment(upload = existingFile, type = "image")
172175

@@ -176,7 +179,7 @@ internal class AttachmentStorageHelperTest {
176179
}
177180

178181
@Test
179-
fun `resolveAttachmentFiles skips attachment without source URI`() {
182+
fun `resolveAttachmentFiles skips attachment without source URI`() = runTest {
180183
val attachment = Attachment(type = "image", extraData = emptyMap())
181184

182185
val result = sut.resolveAttachmentFiles(listOf(attachment))
@@ -185,7 +188,7 @@ internal class AttachmentStorageHelperTest {
185188
}
186189

187190
@Test
188-
fun `resolveAttachmentFiles resolves file from source URI`() {
191+
fun `resolveAttachmentFiles resolves file from source URI`() = runTest {
189192
val cachedFile = mock<File>()
190193
val sourceUri = "content://media/external/images/42"
191194
val parsedUri = mock<Uri>()
@@ -208,7 +211,7 @@ internal class AttachmentStorageHelperTest {
208211
}
209212

210213
@Test
211-
fun `resolveAttachmentFiles reconstructs metadata from attachment fields`() {
214+
fun `resolveAttachmentFiles reconstructs metadata from attachment fields`() = runTest {
212215
val sourceUri = "content://media/external/images/42"
213216
val parsedUri = mock<Uri>()
214217
val attachment = Attachment(
@@ -237,7 +240,7 @@ internal class AttachmentStorageHelperTest {
237240
}
238241

239242
@Test
240-
fun `resolveAttachmentFiles drops attachment when file resolution fails`() {
243+
fun `resolveAttachmentFiles drops attachment when file resolution fails`() = runTest {
241244
val sourceUri = "content://media/external/images/42"
242245
val parsedUri = mock<Uri>()
243246
val attachment = Attachment(
@@ -255,7 +258,7 @@ internal class AttachmentStorageHelperTest {
255258
}
256259

257260
@Test
258-
fun `resolveAttachmentFiles processes resolved, deferred, and missing-URI attachments independently`() {
261+
fun `resolveAttachmentFiles processes resolved, deferred, and missing-URI attachments independently`() = runTest {
259262
val existingFile = mock<File>()
260263
val cachedFile = mock<File>()
261264
val parsedUri = mock<Uri>()
@@ -303,4 +306,10 @@ internal class AttachmentStorageHelperTest {
303306

304307
assertEquals(emptyList<AttachmentMetaData>(), result)
305308
}
309+
310+
companion object {
311+
@JvmField
312+
@RegisterExtension
313+
val testCoroutineExtension = TestCoroutineExtension()
314+
}
306315
}

0 commit comments

Comments
 (0)