Skip to content

Commit 21d5ff8

Browse files
authored
Merge pull request #4059 from element-hq/feature/bma/mediaGalleryUpdate
Media gallery update
2 parents d9a7e13 + 2a141de commit 21d5ff8

File tree

37 files changed

+184
-305
lines changed

37 files changed

+184
-305
lines changed

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/EventItemFactory.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ class EventItemFactory @Inject constructor(
104104
waveform = null,
105105
),
106106
mediaSource = type.source,
107-
duration = type.info?.duration?.inWholeMilliseconds?.toHumanReadableDuration(),
108107
)
109108
is FileMessageType -> MediaItem.File(
110109
id = currentTimelineItem.uniqueId,

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt

Lines changed: 57 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import androidx.compose.runtime.rememberUpdatedState
3434
import androidx.compose.ui.Alignment
3535
import androidx.compose.ui.Modifier
3636
import androidx.compose.ui.graphics.vector.ImageVector
37-
import androidx.compose.ui.platform.LocalConfiguration
3837
import androidx.compose.ui.res.stringResource
3938
import androidx.compose.ui.tooling.preview.PreviewParameter
4039
import androidx.compose.ui.unit.dp
@@ -73,7 +72,6 @@ import io.element.android.libraries.mediaviewer.impl.gallery.ui.VideoItemView
7372
import io.element.android.libraries.mediaviewer.impl.gallery.ui.VoiceItemView
7473
import io.element.android.libraries.voiceplayer.api.VoiceMessageState
7574
import kotlinx.collections.immutable.ImmutableList
76-
import kotlin.math.max
7775

7876
@OptIn(ExperimentalMaterial3Api::class)
7977
@Composable
@@ -266,44 +264,46 @@ private fun MediaGalleryFilesList(
266264
LazyColumn(
267265
modifier = Modifier.fillMaxSize(),
268266
) {
269-
items(files) { item ->
267+
items(
268+
items = files,
269+
key = { it.id() },
270+
contentType = { it::class.java },
271+
) { item ->
270272
when (item) {
271273
is MediaItem.File -> FileItemView(
272-
item,
274+
modifier = Modifier.animateItem(),
275+
file = item,
273276
onClick = { onItemClick(item) },
274-
onShareClick = { eventSink(MediaGalleryEvents.Share(item)) },
275-
onDownloadClick = { eventSink(MediaGalleryEvents.SaveOnDisk(item)) },
276-
onInfoClick = { eventSink(MediaGalleryEvents.OpenInfo(item)) },
277277
)
278278
is MediaItem.Audio -> AudioItemView(
279-
item,
279+
modifier = Modifier.animateItem(),
280+
audio = item,
280281
onClick = { onItemClick(item) },
281-
onShareClick = { eventSink(MediaGalleryEvents.Share(item)) },
282-
onDownloadClick = { eventSink(MediaGalleryEvents.SaveOnDisk(item)) },
283-
onInfoClick = { eventSink(MediaGalleryEvents.OpenInfo(item)) },
284282
)
285283
is MediaItem.Voice -> {
286284
val presenter: Presenter<VoiceMessageState> = presenterFactories.rememberPresenter(item)
287285
VoiceItemView(
288-
presenter.present(),
289-
item,
286+
modifier = Modifier.animateItem(),
287+
state = presenter.present(),
288+
voice = item,
290289
onShareClick = { eventSink(MediaGalleryEvents.Share(item)) },
291290
onDownloadClick = { eventSink(MediaGalleryEvents.SaveOnDisk(item)) },
292291
onInfoClick = { eventSink(MediaGalleryEvents.OpenInfo(item)) },
293292
)
294293
}
295-
is MediaItem.DateSeparator -> DateItemView(item)
294+
is MediaItem.DateSeparator -> DateItemView(
295+
modifier = Modifier.animateItem(),
296+
item = item
297+
)
296298
is MediaItem.Image,
297299
is MediaItem.Video -> {
298300
// Should not happen
299301
}
300-
is MediaItem.LoadingIndicator -> {
301-
LoadingMoreIndicator(item.direction)
302-
val latestEventSink by rememberUpdatedState(eventSink)
303-
LaunchedEffect(item.timestamp) {
304-
latestEventSink(MediaGalleryEvents.LoadMore(item.direction))
305-
}
306-
}
302+
is MediaItem.LoadingIndicator -> LoadingMoreIndicator(
303+
modifier = Modifier.animateItem(),
304+
item = item,
305+
eventSink = eventSink,
306+
)
307307
}
308308
}
309309
}
@@ -315,38 +315,31 @@ private fun MediaGalleryImageGrid(
315315
eventSink: (MediaGalleryEvents) -> Unit,
316316
onItemClick: (MediaItem.Event) -> Unit,
317317
) {
318-
val configuration = LocalConfiguration.current
319-
val screenWidth = configuration.screenWidthDp.dp
320-
val horizontalPadding = 16.dp
321-
val itemSpacing = 4.dp
322-
val availableWidth = screenWidth - horizontalPadding * 2
323-
val minCellWidth = 80.dp
324-
// Calculate the number of columns
325-
val columns = max(1, (availableWidth / (minCellWidth + itemSpacing)).toInt())
326318
LazyVerticalGrid(
327319
modifier = Modifier
328320
.fillMaxSize()
329-
.padding(horizontal = horizontalPadding),
330-
columns = GridCells.Fixed(columns),
321+
.padding(horizontal = 16.dp),
322+
columns = GridCells.Adaptive(80.dp),
331323
horizontalArrangement = Arrangement.spacedBy(4.dp),
332324
verticalArrangement = Arrangement.spacedBy(4.dp),
333325
) {
334326
items(
335-
imagesAndVideos,
327+
items = imagesAndVideos,
336328
span = { item ->
337329
when (item) {
338330
is MediaItem.LoadingIndicator,
339-
is MediaItem.DateSeparator -> GridItemSpan(columns)
331+
is MediaItem.DateSeparator -> GridItemSpan(maxLineSpan)
340332
is MediaItem.Event -> GridItemSpan(1)
341333
}
342334
},
343335
key = { it.id() },
344336
contentType = { it::class.java },
345337
) { item ->
346338
when (item) {
347-
is MediaItem.DateSeparator -> {
348-
DateItemView(item)
349-
}
339+
is MediaItem.DateSeparator -> DateItemView(
340+
modifier = Modifier.animateItem(),
341+
item = item,
342+
)
350343
is MediaItem.Audio -> {
351344
// Should not happen
352345
}
@@ -356,46 +349,43 @@ private fun MediaGalleryImageGrid(
356349
is MediaItem.File -> {
357350
// Should not happen
358351
}
359-
is MediaItem.Image -> {
360-
ImageItemView(
361-
image = item,
362-
onClick = { onItemClick(item) },
363-
onLongClick = {
364-
eventSink(MediaGalleryEvents.OpenInfo(item))
365-
},
366-
)
367-
}
368-
is MediaItem.Video -> {
369-
VideoItemView(
370-
video = item,
371-
onClick = { onItemClick(item) },
372-
onLongClick = {
373-
eventSink(MediaGalleryEvents.OpenInfo(item))
374-
},
375-
)
376-
}
377-
is MediaItem.LoadingIndicator -> {
378-
LoadingMoreIndicator(item.direction)
379-
val latestEventSink by rememberUpdatedState(eventSink)
380-
LaunchedEffect(item.timestamp) {
381-
latestEventSink(MediaGalleryEvents.LoadMore(item.direction))
382-
}
383-
}
352+
is MediaItem.Image -> ImageItemView(
353+
modifier = Modifier.animateItem(),
354+
image = item,
355+
onClick = { onItemClick(item) },
356+
onLongClick = {
357+
eventSink(MediaGalleryEvents.OpenInfo(item))
358+
},
359+
)
360+
is MediaItem.Video -> VideoItemView(
361+
modifier = Modifier.animateItem(),
362+
video = item,
363+
onClick = { onItemClick(item) },
364+
onLongClick = {
365+
eventSink(MediaGalleryEvents.OpenInfo(item))
366+
},
367+
)
368+
is MediaItem.LoadingIndicator -> LoadingMoreIndicator(
369+
modifier = Modifier.animateItem(),
370+
item = item,
371+
eventSink = eventSink,
372+
)
384373
}
385374
}
386375
}
387376
}
388377

389378
@Composable
390379
private fun LoadingMoreIndicator(
391-
direction: Timeline.PaginationDirection,
380+
item: MediaItem.LoadingIndicator,
381+
eventSink: (MediaGalleryEvents) -> Unit,
392382
modifier: Modifier = Modifier
393383
) {
394384
Box(
395385
modifier = modifier.fillMaxWidth(),
396386
contentAlignment = Alignment.Center,
397387
) {
398-
when (direction) {
388+
when (item.direction) {
399389
Timeline.PaginationDirection.FORWARDS -> {
400390
LinearProgressIndicator(
401391
modifier = Modifier
@@ -411,6 +401,10 @@ private fun LoadingMoreIndicator(
411401
)
412402
}
413403
}
404+
val latestEventSink by rememberUpdatedState(eventSink)
405+
LaunchedEffect(item.timestamp) {
406+
latestEventSink(MediaGalleryEvents.LoadMore(item.direction))
407+
}
414408
}
415409
}
416410

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaItem.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ sealed interface MediaItem {
5757
val eventId: EventId?,
5858
val mediaInfo: MediaInfo,
5959
val mediaSource: MediaSource,
60-
val duration: String?,
6160
) : Event
6261

6362
data class Voice(

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaItemsPostProcessor.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ class MediaItemsPostProcessor @Inject constructor() {
5454
when (item) {
5555
is MediaItem.Image,
5656
is MediaItem.Video -> {
57-
imageAndVideoItemsSubList.add(0, item)
57+
imageAndVideoItemsSubList.add(item)
5858
}
5959
is MediaItem.Audio,
6060
is MediaItem.Voice,
6161
is MediaItem.File -> {
62-
fileItemsSublist.add(0, item)
62+
fileItemsSublist.add(item)
6363
}
6464
}
6565
}

libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt

Lines changed: 5 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package io.element.android.libraries.mediaviewer.impl.gallery.ui
99

1010
import androidx.compose.foundation.background
1111
import androidx.compose.foundation.clickable
12-
import androidx.compose.foundation.layout.Arrangement
1312
import androidx.compose.foundation.layout.Column
1413
import androidx.compose.foundation.layout.Row
1514
import androidx.compose.foundation.layout.Spacer
@@ -30,45 +29,36 @@ import androidx.compose.ui.text.style.TextOverflow
3029
import androidx.compose.ui.tooling.preview.PreviewParameter
3130
import androidx.compose.ui.unit.dp
3231
import io.element.android.compound.theme.ElementTheme
33-
import io.element.android.compound.tokens.generated.CompoundIcons
3432
import io.element.android.libraries.core.extensions.withBrackets
3533
import io.element.android.libraries.designsystem.preview.ElementPreview
3634
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
3735
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
3836
import io.element.android.libraries.designsystem.theme.components.Icon
39-
import io.element.android.libraries.designsystem.theme.components.IconButton
4037
import io.element.android.libraries.designsystem.theme.components.Text
4138
import io.element.android.libraries.mediaviewer.impl.gallery.MediaItem
4239

4340
@Composable
4441
fun AudioItemView(
4542
audio: MediaItem.Audio,
4643
onClick: () -> Unit,
47-
onShareClick: () -> Unit,
48-
onDownloadClick: () -> Unit,
49-
onInfoClick: () -> Unit,
5044
modifier: Modifier = Modifier,
5145
) {
5246
Column(
5347
modifier = modifier
5448
.fillMaxWidth()
55-
.padding(top = 20.dp, start = 16.dp, end = 16.dp),
49+
.padding(horizontal = 16.dp),
5650
) {
51+
Spacer(modifier = Modifier.height(20.dp))
5752
FilenameRow(
5853
audio = audio,
5954
onClick = onClick,
6055
)
6156
val caption = audio.mediaInfo.caption
6257
if (caption != null) {
63-
Spacer(modifier = Modifier.height(16.dp))
64-
Caption(caption)
58+
CaptionView(caption)
59+
} else {
60+
Spacer(modifier = Modifier.height(20.dp))
6561
}
66-
Spacer(modifier = Modifier.height(16.dp))
67-
ActionIconsRow(
68-
onShareClick = onShareClick,
69-
onDownloadClick = onDownloadClick,
70-
onInfoClick = onInfoClick,
71-
)
7262
HorizontalDivider()
7363
}
7464
}
@@ -101,16 +91,6 @@ private fun FilenameRow(
10191
imageVector = Icons.Outlined.GraphicEq,
10292
contentDescription = null,
10393
)
104-
audio.duration?.let {
105-
Spacer(modifier = Modifier.width(8.dp))
106-
Text(
107-
text = audio.duration,
108-
style = ElementTheme.typography.fontBodyMdMedium,
109-
color = ElementTheme.colors.textSecondary,
110-
maxLines = 1,
111-
overflow = TextOverflow.Ellipsis,
112-
)
113-
}
11494
Spacer(modifier = Modifier.width(8.dp))
11595
Text(
11696
text = audio.mediaInfo.filename,
@@ -131,55 +111,6 @@ private fun FilenameRow(
131111
}
132112
}
133113

134-
@Composable
135-
private fun Caption(caption: String) {
136-
Text(
137-
modifier = Modifier.fillMaxWidth(),
138-
text = caption,
139-
maxLines = 5,
140-
overflow = TextOverflow.Ellipsis,
141-
style = ElementTheme.typography.fontBodyLgRegular,
142-
color = ElementTheme.colors.textPrimary,
143-
)
144-
}
145-
146-
@Composable
147-
private fun ActionIconsRow(
148-
onShareClick: () -> Unit,
149-
onDownloadClick: () -> Unit,
150-
onInfoClick: () -> Unit,
151-
) {
152-
Row(
153-
modifier = Modifier.fillMaxWidth(),
154-
horizontalArrangement = Arrangement.End
155-
) {
156-
IconButton(
157-
onClick = onShareClick,
158-
) {
159-
Icon(
160-
imageVector = CompoundIcons.ShareAndroid(),
161-
contentDescription = null,
162-
)
163-
}
164-
IconButton(
165-
onClick = onDownloadClick,
166-
) {
167-
Icon(
168-
imageVector = CompoundIcons.Download(),
169-
contentDescription = null,
170-
)
171-
}
172-
IconButton(
173-
onClick = onInfoClick,
174-
) {
175-
Icon(
176-
imageVector = CompoundIcons.Info(),
177-
contentDescription = null,
178-
)
179-
}
180-
}
181-
}
182-
183114
@PreviewsDayNight
184115
@Composable
185116
internal fun AudioItemViewPreview(
@@ -188,8 +119,5 @@ internal fun AudioItemViewPreview(
188119
AudioItemView(
189120
audio = audio,
190121
onClick = {},
191-
onShareClick = {},
192-
onDownloadClick = {},
193-
onInfoClick = {},
194122
)
195123
}

0 commit comments

Comments
 (0)