From f8b2f24962797b7ee0a84cf8f0926f72f3d1909d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 19 Dec 2024 16:20:41 +0100 Subject: [PATCH] Move share and download actions to the bottom sheet --- .../messages/impl/MessagesFlowNode.kt | 2 - .../mediaviewer/api/MediaViewerEntryPoint.kt | 2 - .../impl/DefaultMediaViewerEntryPoint.kt | 2 - .../impl/details/MediaDetailsBottomSheet.kt | 20 ++++++ .../impl/gallery/MediaGalleryEvents.kt | 4 +- .../impl/gallery/MediaGalleryPresenter.kt | 24 ++++++-- .../impl/gallery/MediaGalleryView.kt | 18 +++++- .../impl/gallery/root/MediaGalleryRootNode.kt | 2 - .../impl/gallery/ui/AudioItemView.kt | 10 ++- .../impl/gallery/ui/FileItemView.kt | 10 ++- .../impl/gallery/ui/VoiceItemView.kt | 61 +++---------------- .../impl/viewer/MediaViewerPresenter.kt | 17 ++++-- .../impl/viewer/MediaViewerState.kt | 2 - .../impl/viewer/MediaViewerStateProvider.kt | 6 -- .../impl/viewer/MediaViewerView.kt | 36 ++--------- .../impl/viewer/MediaViewerPresenterTest.kt | 52 ---------------- .../impl/viewer/MediaViewerViewTest.kt | 37 ++++++++--- 17 files changed, 127 insertions(+), 178 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 2d0be01de1..1b9c9909f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -251,8 +251,6 @@ class MessagesFlowNode @AssistedInject constructor( mediaSource = navTarget.mediaSource, thumbnailSource = navTarget.thumbnailSource, canShowInfo = true, - canDownload = true, - canShare = true, ) val callback = object : MediaViewerEntryPoint.Callback { override fun onDone() { diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt index 3e262c08f2..598d799723 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt @@ -36,7 +36,5 @@ interface MediaViewerEntryPoint : FeatureEntryPoint { val mediaSource: MediaSource, val thumbnailSource: MediaSource?, val canShowInfo: Boolean, - val canDownload: Boolean, - val canShare: Boolean, ) : NodeInputs } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt index 59a7f423e6..19ac8718d5 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt @@ -59,8 +59,6 @@ class DefaultMediaViewerEntryPoint @Inject constructor() : MediaViewerEntryPoint mediaSource = MediaSource(url = avatarUrl), thumbnailSource = null, canShowInfo = false, - canDownload = false, - canShare = false, ) ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt index 42127db229..74d47797a2 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDetailsBottomSheet.kt @@ -48,6 +48,8 @@ import io.element.android.libraries.ui.strings.CommonStrings fun MediaDetailsBottomSheet( state: MediaBottomSheetState.MediaDetailsBottomSheetState, onViewInTimeline: (EventId) -> Unit, + onShare: (EventId) -> Unit, + onDownload: (EventId) -> Unit, onDelete: (EventId) -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier, @@ -92,6 +94,22 @@ fun MediaDetailsBottomSheet( onViewInTimeline(state.eventId) } ) + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.ShareAndroid())), + headlineContent = { Text(stringResource(CommonStrings.action_share)) }, + style = ListItemStyle.Primary, + onClick = { + onShare(state.eventId) + } + ) + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Download())), + headlineContent = { Text(stringResource(CommonStrings.action_save)) }, + style = ListItemStyle.Primary, + onClick = { + onDownload(state.eventId) + } + ) if (state.canDelete) { HorizontalDivider() ListItem( @@ -196,6 +214,8 @@ internal fun MediaDetailsBottomSheetPreview() = ElementPreview { MediaDetailsBottomSheet( state = aMediaDetailsBottomSheetState(), onViewInTimeline = {}, + onShare = {}, + onDownload = {}, onDelete = {}, onDismiss = {}, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryEvents.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryEvents.kt index 717ba4edbb..e4199e6d5e 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryEvents.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryEvents.kt @@ -15,8 +15,8 @@ import io.element.android.libraries.mediaviewer.api.MediaInfo sealed interface MediaGalleryEvents { data class ChangeMode(val mode: MediaGalleryMode) : MediaGalleryEvents data class LoadMore(val direction: Timeline.PaginationDirection) : MediaGalleryEvents - data class Share(val mediaItem: MediaItem.Event) : MediaGalleryEvents - data class SaveOnDisk(val mediaItem: MediaItem.Event) : MediaGalleryEvents + data class Share(val eventId: EventId?) : MediaGalleryEvents + data class SaveOnDisk(val eventId: EventId?) : MediaGalleryEvents data class OpenInfo(val mediaItem: MediaItem.Event) : MediaGalleryEvents data class ViewInTimeline(val eventId: EventId) : MediaGalleryEvents diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt index 905ba2c770..adedd83599 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenter.kt @@ -117,8 +117,16 @@ class MediaGalleryPresenter @AssistedInject constructor( timeline.dataOrNull()?.paginate(event.direction) } is MediaGalleryEvents.Delete -> coroutineScope.delete(timeline, event.eventId) - is MediaGalleryEvents.SaveOnDisk -> coroutineScope.saveOnDisk(event.mediaItem) - is MediaGalleryEvents.Share -> coroutineScope.share(event.mediaItem) + is MediaGalleryEvents.SaveOnDisk -> coroutineScope.launch { + mediaItems.dataOrNull().find(event.eventId)?.let { + saveOnDisk(it) + } + } + is MediaGalleryEvents.Share -> coroutineScope.launch { + mediaItems.dataOrNull().find(event.eventId)?.let { + share(it) + } + } is MediaGalleryEvents.ViewInTimeline -> { mediaBottomSheetState = MediaBottomSheetState.Hidden navigator.onViewInTimelineClick(event.eventId) @@ -221,7 +229,7 @@ class MediaGalleryPresenter @AssistedInject constructor( } } - private fun CoroutineScope.saveOnDisk(mediaItem: MediaItem.Event) = launch { + private suspend fun saveOnDisk(mediaItem: MediaItem.Event) { downloadMedia(mediaItem) .mapCatching { localMedia -> localMediaActions.saveOnDisk(localMedia) @@ -236,7 +244,7 @@ class MediaGalleryPresenter @AssistedInject constructor( } } - private fun CoroutineScope.share(mediaItem: MediaItem.Event) = launch { + private suspend fun share(mediaItem: MediaItem.Event) { downloadMedia(mediaItem) .mapCatching { localMedia -> localMediaActions.share(localMedia) @@ -255,3 +263,11 @@ class MediaGalleryPresenter @AssistedInject constructor( } } } + +private fun List?.find(eventId: EventId?): MediaItem.Event? { + if (this == null || eventId == null) { + return null + } + return filterIsInstance() + .firstOrNull { it.eventId() == eventId } +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt index 41dfdb1baa..eec45a0b22 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryView.kt @@ -153,6 +153,12 @@ fun MediaGalleryView( onViewInTimeline = { eventId -> state.eventSink(MediaGalleryEvents.ViewInTimeline(eventId)) }, + onShare = { eventId -> + state.eventSink(MediaGalleryEvents.Share(eventId)) + }, + onDownload = { eventId -> + state.eventSink(MediaGalleryEvents.SaveOnDisk(eventId)) + }, onDelete = { eventId -> state.eventSink( MediaGalleryEvents.ConfirmDelete( @@ -276,11 +282,17 @@ private fun MediaGalleryFilesList( modifier = Modifier.animateItem(), file = item, onClick = { onItemClick(item) }, + onLongClick = { + eventSink(MediaGalleryEvents.OpenInfo(item)) + }, ) is MediaItem.Audio -> AudioItemView( modifier = Modifier.animateItem(), audio = item, onClick = { onItemClick(item) }, + onLongClick = { + eventSink(MediaGalleryEvents.OpenInfo(item)) + }, ) is MediaItem.Voice -> { val presenter: Presenter = presenterFactories.rememberPresenter(item) @@ -288,9 +300,9 @@ private fun MediaGalleryFilesList( modifier = Modifier.animateItem(), state = presenter.present(), voice = item, - onShareClick = { eventSink(MediaGalleryEvents.Share(item)) }, - onDownloadClick = { eventSink(MediaGalleryEvents.SaveOnDisk(item)) }, - onInfoClick = { eventSink(MediaGalleryEvents.OpenInfo(item)) }, + onLongClick = { + eventSink(MediaGalleryEvents.OpenInfo(item)) + }, ) } is MediaItem.DateSeparator -> DateItemView( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryRootNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryRootNode.kt index 4f5272b01b..caee363d51 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryRootNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryRootNode.kt @@ -122,8 +122,6 @@ class MediaGalleryRootNode @AssistedInject constructor( mediaSource = navTarget.mediaSource, thumbnailSource = navTarget.thumbnailSource, canShowInfo = true, - canDownload = true, - canShare = true, ) ) .callback(callback) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt index dd06fa1bf8..d3511fcaed 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/AudioItemView.kt @@ -7,8 +7,9 @@ package io.element.android.libraries.mediaviewer.impl.gallery.ui +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -41,6 +42,7 @@ import io.element.android.libraries.mediaviewer.impl.gallery.MediaItem fun AudioItemView( audio: MediaItem.Audio, onClick: () -> Unit, + onLongClick: () -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -52,6 +54,7 @@ fun AudioItemView( FilenameRow( audio = audio, onClick = onClick, + onLongClick = onLongClick, ) val caption = audio.mediaInfo.caption if (caption != null) { @@ -63,10 +66,12 @@ fun AudioItemView( } } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun FilenameRow( audio: MediaItem.Audio, onClick: () -> Unit, + onLongClick: () -> Unit, ) { Row( modifier = Modifier @@ -75,7 +80,7 @@ private fun FilenameRow( color = ElementTheme.colors.bgSubtleSecondary, shape = RoundedCornerShape(12.dp), ) - .clickable { onClick() } + .combinedClickable(onClick = onClick, onLongClick = onLongClick) .fillMaxWidth() .padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, @@ -119,5 +124,6 @@ internal fun AudioItemViewPreview( AudioItemView( audio = audio, onClick = {}, + onLongClick = {}, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt index 6618815c21..5ad2234900 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/FileItemView.kt @@ -7,8 +7,9 @@ package io.element.android.libraries.mediaviewer.impl.gallery.ui +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -40,6 +41,7 @@ import io.element.android.libraries.mediaviewer.impl.gallery.MediaItem fun FileItemView( file: MediaItem.File, onClick: () -> Unit, + onLongClick: () -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -51,6 +53,7 @@ fun FileItemView( FilenameRow( file = file, onClick = onClick, + onLongClick = onLongClick, ) val caption = file.mediaInfo.caption if (caption != null) { @@ -62,10 +65,12 @@ fun FileItemView( } } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun FilenameRow( file: MediaItem.File, onClick: () -> Unit, + onLongClick: () -> Unit, ) { Row( modifier = Modifier @@ -74,7 +79,7 @@ private fun FilenameRow( color = ElementTheme.colors.bgSubtleSecondary, shape = RoundedCornerShape(12.dp), ) - .clickable { onClick() } + .combinedClickable(onClick = onClick, onLongClick = onLongClick) .fillMaxWidth() .padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, @@ -118,5 +123,6 @@ internal fun FileItemViewPreview( FileItemView( file = file, onClick = {}, + onLongClick = {}, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt index 5864914dda..ab322427ee 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/ui/VoiceItemView.kt @@ -7,9 +7,10 @@ package io.element.android.libraries.mediaviewer.impl.gallery.ui +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -58,9 +59,7 @@ import kotlinx.coroutines.delay fun VoiceItemView( state: VoiceMessageState, voice: MediaItem.Voice, - onShareClick: () -> Unit, - onDownloadClick: () -> Unit, - onInfoClick: () -> Unit, + onLongClick: () -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -72,6 +71,7 @@ fun VoiceItemView( VoiceInfoRow( state = state, voice = voice, + onLongClick = onLongClick, ) val caption = voice.mediaInfo.caption if (caption != null) { @@ -79,19 +79,16 @@ fun VoiceItemView( } else { Spacer(modifier = Modifier.height(16.dp)) } - ActionIconsRow( - onShareClick = onShareClick, - onDownloadClick = onDownloadClick, - onInfoClick = onInfoClick, - ) HorizontalDivider() } } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun VoiceInfoRow( state: VoiceMessageState, voice: MediaItem.Voice, + onLongClick: () -> Unit, ) { fun playPause() { state.eventSink(VoiceMessageEvents.PlayPause) @@ -104,6 +101,7 @@ private fun VoiceInfoRow( color = ElementTheme.colors.bgSubtleSecondary, shape = RoundedCornerShape(12.dp), ) + .combinedClickable(onClick = {}, onLongClick = onLongClick) .fillMaxWidth() .padding(start = 12.dp, end = 36.dp, top = 8.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, @@ -257,43 +255,6 @@ private fun CustomIconButton( ) } -@Composable -private fun ActionIconsRow( - onShareClick: () -> Unit, - onDownloadClick: () -> Unit, - onInfoClick: () -> Unit, -) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End - ) { - IconButton( - onClick = onShareClick, - ) { - Icon( - imageVector = CompoundIcons.ShareAndroid(), - contentDescription = null, - ) - } - IconButton( - onClick = onDownloadClick, - ) { - Icon( - imageVector = CompoundIcons.Download(), - contentDescription = null, - ) - } - IconButton( - onClick = onInfoClick, - ) { - Icon( - imageVector = CompoundIcons.Info(), - contentDescription = null, - ) - } - } -} - @PreviewsDayNight @Composable internal fun VoiceItemViewPreview( @@ -302,9 +263,7 @@ internal fun VoiceItemViewPreview( VoiceItemView( state = aVoiceMessageState(), voice = voice, - onShareClick = {}, - onDownloadClick = {}, - onInfoClick = {}, + onLongClick = {}, ) } @@ -316,8 +275,6 @@ internal fun VoiceItemViewPlayPreview( VoiceItemView( state = state, voice = aMediaItemVoice(), - onShareClick = {}, - onDownloadClick = {}, - onInfoClick = {}, + onLongClick = {}, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index a480d1ba7c..a907934724 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -83,9 +83,18 @@ class MediaViewerPresenter @AssistedInject constructor( when (mediaViewerEvents) { MediaViewerEvents.RetryLoading -> loadMediaTrigger++ MediaViewerEvents.ClearLoadingError -> localMedia.value = AsyncData.Uninitialized - MediaViewerEvents.SaveOnDisk -> coroutineScope.saveOnDisk(localMedia.value) - MediaViewerEvents.Share -> coroutineScope.share(localMedia.value) - MediaViewerEvents.OpenWith -> coroutineScope.open(localMedia.value) + MediaViewerEvents.SaveOnDisk -> { + mediaBottomSheetState = MediaBottomSheetState.Hidden + coroutineScope.saveOnDisk(localMedia.value) + } + MediaViewerEvents.Share -> { + mediaBottomSheetState = MediaBottomSheetState.Hidden + coroutineScope.share(localMedia.value) + } + MediaViewerEvents.OpenWith -> { + mediaBottomSheetState = MediaBottomSheetState.Hidden + coroutineScope.open(localMedia.value) + } is MediaViewerEvents.Delete -> { mediaBottomSheetState = MediaBottomSheetState.Hidden coroutineScope.delete(mediaViewerEvents.eventId) @@ -126,8 +135,6 @@ class MediaViewerPresenter @AssistedInject constructor( downloadedMedia = localMedia.value, snackbarMessage = snackbarMessage, canShowInfo = inputs.canShowInfo, - canDownload = inputs.canDownload, - canShare = inputs.canShare, mediaBottomSheetState = mediaBottomSheetState, eventSink = ::handleEvents ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt index 6ae8554b06..3e0deaf9b3 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt @@ -22,8 +22,6 @@ data class MediaViewerState( val downloadedMedia: AsyncData, val snackbarMessage: SnackbarMessage?, val canShowInfo: Boolean, - val canDownload: Boolean, - val canShare: Boolean, val mediaBottomSheetState: MediaBottomSheetState, val eventSink: (MediaViewerEvents) -> Unit, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt index 61e4f78176..63c82be7ba 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt @@ -91,8 +91,6 @@ open class MediaViewerStateProvider : PreviewParameterProvider ), mediaInfo = it, canShowInfo = false, - canDownload = false, - canShare = false, ) }, aMediaViewerState( @@ -118,8 +116,6 @@ fun aMediaViewerState( downloadedMedia: AsyncData = AsyncData.Uninitialized, mediaInfo: MediaInfo = anImageMediaInfo(), canShowInfo: Boolean = true, - canDownload: Boolean = true, - canShare: Boolean = true, mediaBottomSheetState: MediaBottomSheetState = MediaBottomSheetState.Hidden, eventSink: (MediaViewerEvents) -> Unit = {}, ) = MediaViewerState( @@ -129,8 +125,6 @@ fun aMediaViewerState( downloadedMedia = downloadedMedia, snackbarMessage = null, canShowInfo = canShowInfo, - canDownload = canDownload, - canShare = canShare, mediaBottomSheetState = mediaBottomSheetState, eventSink = eventSink, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 6ca9ebec9c..d3347b9812 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -119,8 +119,6 @@ fun MediaViewerView( ) { MediaViewerTopBar( actionsEnabled = state.downloadedMedia is AsyncData.Success, - canDownload = state.canDownload, - canShare = state.canShare, mimeType = state.mediaInfo.mimeType, senderName = state.mediaInfo.senderName, dateSent = state.mediaInfo.dateSent, @@ -148,6 +146,12 @@ fun MediaViewerView( onViewInTimeline = { state.eventSink(MediaViewerEvents.ViewInTimeline(it)) }, + onShare = { + state.eventSink(MediaViewerEvents.Share) + }, + onDownload = { + state.eventSink(MediaViewerEvents.SaveOnDisk) + }, onDelete = { eventId -> state.eventSink(MediaViewerEvents.ConfirmDelete(eventId)) }, @@ -313,8 +317,6 @@ private fun rememberShowProgress(downloadedMedia: AsyncData): Boolea @Composable private fun MediaViewerTopBar( actionsEnabled: Boolean, - canDownload: Boolean, - canShare: Boolean, mimeType: String, senderName: String?, dateSent: String?, @@ -348,19 +350,6 @@ private fun MediaViewerTopBar( ), navigationIcon = { BackButton(onClick = onBackClick) }, actions = { - if (canShare) { - IconButton( - enabled = actionsEnabled, - onClick = { - eventSink(MediaViewerEvents.Share) - }, - ) { - Icon( - imageVector = CompoundIcons.ShareAndroid(), - contentDescription = stringResource(id = CommonStrings.action_share) - ) - } - } IconButton( enabled = actionsEnabled, onClick = { @@ -378,19 +367,6 @@ private fun MediaViewerTopBar( ) } } - if (canDownload) { - IconButton( - enabled = actionsEnabled, - onClick = { - eventSink(MediaViewerEvents.SaveOnDisk) - }, - ) { - Icon( - imageVector = CompoundIcons.Download(), - contentDescription = stringResource(id = CommonStrings.action_save), - ) - } - } if (canShowInfo) { IconButton( onClick = onInfoClick, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index 43835e71e5..1e5f6120cc 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -66,8 +66,6 @@ class MediaViewerPresenterTest { assertThat(initialState.downloadedMedia).isInstanceOf(AsyncData.Success::class.java) assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isTrue() - assertThat(initialState.canDownload).isTrue() - assertThat(initialState.canShare).isTrue() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -86,48 +84,6 @@ class MediaViewerPresenterTest { assertThat(initialState.downloadedMedia).isInstanceOf(AsyncData.Success::class.java) assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isFalse() - assertThat(initialState.canDownload).isTrue() - assertThat(initialState.canShare).isTrue() - assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) - } - } - - @Test - fun `present - initial state cannot share`() = runTest { - val presenter = createMediaViewerPresenter( - canShare = false, - room = FakeMatrixRoom( - canRedactOwnResult = { Result.success(true) }, - ) - ) - presenter.test { - skipItems(2) - val initialState = awaitItem() - assertThat(initialState.downloadedMedia).isInstanceOf(AsyncData.Success::class.java) - assertThat(initialState.snackbarMessage).isNull() - assertThat(initialState.canShowInfo).isTrue() - assertThat(initialState.canDownload).isTrue() - assertThat(initialState.canShare).isFalse() - assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) - } - } - - @Test - fun `present - initial state cannot download`() = runTest { - val presenter = createMediaViewerPresenter( - canDownload = false, - room = FakeMatrixRoom( - canRedactOwnResult = { Result.success(true) }, - ) - ) - presenter.test { - skipItems(2) - val initialState = awaitItem() - assertThat(initialState.downloadedMedia).isInstanceOf(AsyncData.Success::class.java) - assertThat(initialState.snackbarMessage).isNull() - assertThat(initialState.canShowInfo).isTrue() - assertThat(initialState.canDownload).isFalse() - assertThat(initialState.canShare).isTrue() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -146,8 +102,6 @@ class MediaViewerPresenterTest { assertThat(initialState.downloadedMedia).isInstanceOf(AsyncData.Success::class.java) assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isTrue() - assertThat(initialState.canDownload).isTrue() - assertThat(initialState.canShare).isTrue() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -167,8 +121,6 @@ class MediaViewerPresenterTest { assertThat(initialState.downloadedMedia).isInstanceOf(AsyncData.Success::class.java) assertThat(initialState.snackbarMessage).isNull() assertThat(initialState.canShowInfo).isTrue() - assertThat(initialState.canDownload).isTrue() - assertThat(initialState.canShare).isTrue() assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -350,8 +302,6 @@ class MediaViewerPresenterTest { localMediaActions: FakeLocalMediaActions = FakeLocalMediaActions(), snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(), canShowInfo: Boolean = true, - canShare: Boolean = true, - canDownload: Boolean = true, mediaViewerNavigator: MediaViewerNavigator = FakeMediaViewerNavigator(), room: MatrixRoom = FakeMatrixRoom( liveTimeline = FakeTimeline(), @@ -364,8 +314,6 @@ class MediaViewerPresenterTest { mediaSource = aMediaSource(), thumbnailSource = null, canShowInfo = canShowInfo, - canShare = canShare, - canDownload = canDownload, ), localMediaFactory = localMediaFactory, mediaLoader = matrixMediaLoader, diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt index acbfb57619..83fada0e54 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt @@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.mediaviewer.api.anImageMediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.impl.details.aMediaDetailsBottomSheetState import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EventsRecorder @@ -54,16 +55,6 @@ class MediaViewerViewTest { testMenuAction(CommonStrings.action_open_with, MediaViewerEvents.OpenWith) } - @Test - fun `clicking on save emit expected Event`() { - testMenuAction(CommonStrings.action_save, MediaViewerEvents.SaveOnDisk) - } - - @Test - fun `clicking on share emit expected Event`() { - testMenuAction(CommonStrings.action_share, MediaViewerEvents.Share) - } - private fun testMenuAction(contentDescriptionRes: Int, expectedEvent: MediaViewerEvents) { val eventsRecorder = EventsRecorder() rule.setMediaViewerView( @@ -80,6 +71,32 @@ class MediaViewerViewTest { eventsRecorder.assertSingle(expectedEvent) } + @Test + fun `clicking on save emit expected Event`() { + testBottomSheetAction(CommonStrings.action_save, MediaViewerEvents.SaveOnDisk) + } + + @Test + fun `clicking on share emit expected Event`() { + testBottomSheetAction(CommonStrings.action_share, MediaViewerEvents.Share) + } + + private fun testBottomSheetAction(contentDescriptionRes: Int, expectedEvent: MediaViewerEvents) { + val eventsRecorder = EventsRecorder() + rule.setMediaViewerView( + aMediaViewerState( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, anImageMediaInfo()) + ), + mediaInfo = anImageMediaInfo(), + mediaBottomSheetState = aMediaDetailsBottomSheetState(), + eventSink = eventsRecorder + ), + ) + rule.clickOn(contentDescriptionRes) + eventsRecorder.assertSingle(expectedEvent) + } + @Test fun `clicking on image hides the overlay`() { val eventsRecorder = EventsRecorder(expectEvents = false)