@@ -34,7 +34,6 @@ import androidx.compose.runtime.rememberUpdatedState
3434import androidx.compose.ui.Alignment
3535import androidx.compose.ui.Modifier
3636import androidx.compose.ui.graphics.vector.ImageVector
37- import androidx.compose.ui.platform.LocalConfiguration
3837import androidx.compose.ui.res.stringResource
3938import androidx.compose.ui.tooling.preview.PreviewParameter
4039import androidx.compose.ui.unit.dp
@@ -73,7 +72,6 @@ import io.element.android.libraries.mediaviewer.impl.gallery.ui.VideoItemView
7372import io.element.android.libraries.mediaviewer.impl.gallery.ui.VoiceItemView
7473import io.element.android.libraries.voiceplayer.api.VoiceMessageState
7574import 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
390379private 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
0 commit comments