Skip to content

Commit a23ee29

Browse files
authored
fix: infinite recomposition when showing a preview of a file (WPB-22325) (#4485)
1 parent 0f2a5ca commit a23ee29

File tree

3 files changed

+73
-35
lines changed

3 files changed

+73
-35
lines changed

app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/multipart/standalone/EditableAssetPreview.kt

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
package com.wire.android.ui.home.conversations.model.messagetypes.multipart.standalone
1919

2020
import android.graphics.drawable.Drawable
21+
import androidx.compose.foundation.Image
2122
import androidx.compose.foundation.background
2223
import androidx.compose.foundation.border
2324
import androidx.compose.foundation.layout.Arrangement
2425
import androidx.compose.foundation.layout.Box
2526
import androidx.compose.foundation.layout.Column
27+
import androidx.compose.foundation.layout.aspectRatio
2628
import androidx.compose.foundation.layout.fillMaxSize
2729
import androidx.compose.foundation.layout.fillMaxWidth
30+
import androidx.compose.foundation.layout.heightIn
2831
import androidx.compose.foundation.layout.padding
2932
import androidx.compose.foundation.layout.sizeIn
3033
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -39,12 +42,14 @@ import androidx.compose.ui.Alignment
3942
import androidx.compose.ui.Modifier
4043
import androidx.compose.ui.draw.clip
4144
import androidx.compose.ui.graphics.Color
45+
import androidx.compose.ui.graphics.asImageBitmap
46+
import androidx.compose.ui.graphics.painter.BitmapPainter
4247
import androidx.compose.ui.layout.ContentScale
43-
import androidx.compose.ui.platform.LocalContext
4448
import androidx.compose.ui.text.style.TextOverflow
45-
import coil.compose.AsyncImage
46-
import coil.request.CachePolicy
47-
import coil.request.ImageRequest
49+
import androidx.core.graphics.drawable.toBitmap
50+
import coil.compose.AsyncImagePainter
51+
import coil.compose.SubcomposeAsyncImage
52+
import coil.compose.SubcomposeAsyncImageContent
4853
import com.wire.android.ui.common.applyIf
4954
import com.wire.android.ui.common.attachmentdraft.ui.FileHeaderView
5055
import com.wire.android.ui.common.colorsScheme
@@ -67,6 +72,7 @@ internal fun EditableAssetPreview(
6772
) {
6873
Column(
6974
modifier = Modifier
75+
.heightIn(min = dimensions().spacing80x)
7076
.applyIf(messageStyle == MessageStyle.BUBBLE_SELF) {
7177
background(colorsScheme().selfBubble.secondary)
7278
}
@@ -100,16 +106,23 @@ internal fun EditableAssetPreview(
100106
)
101107

102108
item.fileName?.let {
103-
Text(
104-
modifier = Modifier
105-
.fillMaxWidth()
106-
.padding(start = dimensions().spacing8x, end = dimensions().spacing8x),
107-
text = it,
108-
style = MaterialTheme.wireTypography.body02,
109-
color = messageStyle.textColor(),
110-
maxLines = 2,
111-
overflow = TextOverflow.Ellipsis
112-
)
109+
Box(
110+
modifier = Modifier.then(
111+
if (item.previewUrl == null) Modifier.weight(1f) else Modifier
112+
)
113+
) {
114+
Text(
115+
modifier = Modifier
116+
.fillMaxWidth()
117+
.padding(start = dimensions().spacing8x, end = dimensions().spacing8x)
118+
.align(Alignment.BottomStart),
119+
text = it,
120+
style = MaterialTheme.wireTypography.body02,
121+
maxLines = 2,
122+
color = messageStyle.textColor(),
123+
overflow = TextOverflow.Ellipsis
124+
)
125+
}
113126
}
114127

115128
Box(
@@ -124,32 +137,49 @@ internal fun EditableAssetPreview(
124137

125138
item.previewUrl?.let {
126139

127-
// Remember recent drawable to use as placeholder to avoid blink on update
128140
var drawable by remember { mutableStateOf<Drawable?>(null) }
129141

130-
val request = ImageRequest.Builder(LocalContext.current)
131-
.diskCacheKey(item.contentHash)
132-
.memoryCacheKey(item.contentHash)
133-
.placeholderMemoryCacheKey(item.contentHash)
134-
.diskCachePolicy(CachePolicy.ENABLED)
135-
.memoryCachePolicy(CachePolicy.ENABLED)
136-
.placeholder(drawable)
137-
.crossfade(true)
138-
.data(item.previewUrl)
139-
.build()
140-
141-
AsyncImage(
142+
SubcomposeAsyncImage(
143+
model = item.previewUrl,
144+
contentDescription = null,
142145
modifier = Modifier
143146
.fillMaxSize()
147+
.aspectRatio(.8f)
144148
.sizeIn(maxHeight = dimensions().messageDocumentPreviewMaxHeight),
145-
model = request,
146-
contentDescription = null,
147-
alignment = Alignment.TopStart,
148149
contentScale = ContentScale.FillWidth,
149-
onSuccess = { result ->
150-
drawable = result.result.drawable
150+
) {
151+
when (painter.state) {
152+
is AsyncImagePainter.State.Loading -> {
153+
drawable?.let {
154+
val painter = drawable?.toBitmap()?.asImageBitmap()?.let { BitmapPainter(it) }
155+
painter?.let { paint ->
156+
Image(
157+
painter = paint,
158+
contentDescription = null,
159+
contentScale = ContentScale.FillWidth,
160+
modifier = Modifier.fillMaxSize()
161+
)
162+
}
163+
}
164+
}
165+
166+
is AsyncImagePainter.State.Success -> {
167+
// Show the loaded image
168+
SubcomposeAsyncImageContent()
169+
170+
// Update drawable state to use as placeholder next time
171+
drawable = (painter.state as AsyncImagePainter.State.Success).result.drawable
172+
}
173+
174+
else -> {
175+
Box(
176+
modifier = Modifier
177+
.fillMaxSize()
178+
.background(Color.Gray)
179+
)
180+
}
151181
}
152-
)
182+
}
153183
}
154184

155185
// Download progress

app/src/main/kotlin/com/wire/android/ui/home/conversations/model/messagetypes/multipart/standalone/ImageAssetPreview.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ internal fun ImageAssetPreview(
6565
if (LocalInspectionMode.current) {
6666
Image(
6767
modifier = Modifier.fillMaxSize(),
68-
painter = painterResource(com.wire.android.ui.common.R.drawable.mock_image),
68+
painter = painterResource(R.drawable.mock_image),
6969
contentDescription = null,
7070
contentScale = if (messageStyle.isBubble()) ContentScale.Crop else ContentScale.Fit
7171
)

features/cells/src/test/kotlin/com/wire/android/feature/cells/ui/versioning/VersionHistoryViewModelTest.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ import org.junit.jupiter.api.Assertions.assertEquals
4242
import org.junit.jupiter.api.Assertions.assertTrue
4343
import org.junit.jupiter.api.BeforeEach
4444
import org.junit.jupiter.api.Test
45+
import java.time.Instant
4546
import java.time.LocalDate
47+
import java.time.ZoneId
4648
import java.time.ZoneOffset
4749
import java.time.format.DateTimeFormatter
50+
import java.time.format.FormatStyle
4851

4952
@ExperimentalCoroutinesApi
5053
class VersionHistoryViewModelTest {
@@ -145,7 +148,12 @@ class VersionHistoryViewModelTest {
145148
assertEquals("Today, $todayFormattedDate", actualTodayText)
146149
assertEquals(1, groupedVersions[0].versions.size)
147150
assertEquals("User A", groupedVersions[0].versions[0].modifiedBy)
148-
assertEquals("11:30 AM", groupedVersions[0].versions[0].modifiedAt)
151+
val expectedTime = Instant
152+
.ofEpochSecond(versionNode.modifiedTime!!.toLong())
153+
.atZone(ZoneId.systemDefault())
154+
.toLocalTime()
155+
.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT))
156+
assertEquals(expectedTime, groupedVersions[0].versions[0].modifiedAt)
149157

150158
// Verify "Yesterday" group is correct
151159
every { fileSizeFormatter.formatSize(any()) } returns groupedVersions[1].versions[0].fileSize

0 commit comments

Comments
 (0)