From c8c2cb8ff3ec514d2958aa003288d0f5be88784a Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 22 May 2023 16:25:50 +0200 Subject: [PATCH] Media: send file without preview --- .../features/messages/impl/MessagesView.kt | 24 +++++-- .../MessageComposerPresenter.kt | 63 +++++++++++++++++-- .../messagecomposer/MessageComposerState.kt | 1 + 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 9e5c974949..d88a9199fd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -72,6 +72,7 @@ import io.element.android.features.messages.impl.timeline.TimelineView import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView import io.element.android.libraries.androidutils.ui.hideKeyboard +import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -87,6 +88,8 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.launch import timber.log.Timber +import io.element.android.libraries.ui.strings.R as StringsR + @Composable fun MessagesView( state: MessagesState, @@ -109,12 +112,7 @@ fun MessagesView( val bottomSheetState = rememberModalBottomSheetState(initialValue = initialBottomSheetState) val coroutineScope = rememberCoroutineScope() - val attachmentsState = state.composerState.attachmentsState - if (attachmentsState is AttachmentsState.Previewing) { - LaunchedEffect(attachmentsState) { - onPreviewAttachments(attachmentsState.attachments) - } - } + AttachmentStateView(state.composerState.attachmentsState, onPreviewAttachments) BackHandler(enabled = bottomSheetState.isVisible) { coroutineScope.launch { @@ -221,6 +219,20 @@ fun MessagesView( } } +@Composable +private fun AttachmentStateView( + state: AttachmentsState, + onPreviewAttachments: (ImmutableList) -> Unit +) { + when (state) { + AttachmentsState.None -> Unit + is AttachmentsState.Previewing -> LaunchedEffect(state) { + onPreviewAttachments(state.attachments) + } + is AttachmentsState.Sending -> ProgressDialog(text = stringResource(id = StringsR.string.common_loading)) + } +} + @Composable fun MessagesViewContent( state: MessagesState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 527d875f25..c2bf12228c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -27,23 +27,28 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.media3.common.MimeTypes import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.media.local.LocalMediaFactory import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.data.StableCharSequence import io.element.android.libraries.core.data.toStableCharSequence -import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.designsystem.utils.SnackbarDispatcher +import io.element.android.libraries.designsystem.utils.SnackbarMessage import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.mediapickers.api.PickerProvider +import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject +import io.element.android.libraries.core.mimetype.MimeTypes.Any as AnyMimeTypes @SingleIn(RoomScope::class) class MessageComposerPresenter @Inject constructor( @@ -52,6 +57,8 @@ class MessageComposerPresenter @Inject constructor( private val mediaPickerProvider: PickerProvider, private val featureFlagService: FeatureFlagService, private val localMediaFactory: LocalMediaFactory, + private val mediaSender: MediaSender, + private val snackbarDispatcher: SnackbarDispatcher, ) : Presenter { @SuppressLint("UnsafeOptInUsageError") @@ -69,16 +76,26 @@ class MessageComposerPresenter @Inject constructor( AttachmentsState.None } else { val mediaAttachment = Attachment.Media(localMedia) - AttachmentsState.Previewing(persistentListOf(mediaAttachment)) + val isPreviewable = when { + MimeTypes.isImage(mimeType) -> true + MimeTypes.isVideo(mimeType) -> true + MimeTypes.isAudio(mimeType) -> true + else -> false + } + if (isPreviewable) { + AttachmentsState.Previewing(persistentListOf(mediaAttachment)) + } else { + AttachmentsState.Sending(persistentListOf(mediaAttachment)) + } } } val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker(onResult = { uri, mimeType -> handlePickedMedia(uri, mimeType) }) - val filesPicker = mediaPickerProvider.registerFilePicker(MimeTypes.Any, onResult = { handlePickedMedia(it) }) - val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(onResult = { handlePickedMedia(it, MimeTypes.Jpeg) }) - val cameraVideoPicker = mediaPickerProvider.registerCameraVideoPicker(onResult = { handlePickedMedia(it, MimeTypes.Mp4) }) + val filesPicker = mediaPickerProvider.registerFilePicker(AnyMimeTypes, onResult = { handlePickedMedia(it) }) + val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(onResult = { handlePickedMedia(it, MimeTypes.IMAGE_JPEG) }) + val cameraVideoPicker = mediaPickerProvider.registerCameraVideoPicker(onResult = { handlePickedMedia(it, MimeTypes.VIDEO_MP4) }) val isFullScreen = rememberSaveable { mutableStateOf(false) @@ -99,6 +116,13 @@ class MessageComposerPresenter @Inject constructor( } } + LaunchedEffect(attachmentsState.value) { + when (val attachmentStateValue = attachmentsState.value) { + is AttachmentsState.Sending -> localCoroutineScope.sendAttachment(attachmentStateValue.attachments.first(), attachmentsState) + else -> Unit + } + } + fun handleEvents(event: MessageComposerEvents) { when (event) { MessageComposerEvents.ToggleFullScreenState -> isFullScreen.value = !isFullScreen.value @@ -177,4 +201,33 @@ class MessageComposerPresenter @Inject constructor( } } + private fun CoroutineScope.sendAttachment( + attachment: Attachment, + attachmentState: MutableState, + ) = launch { + when (attachment) { + is Attachment.Media -> { + sendMedia( + uri = attachment.localMedia.uri, + mimeType = attachment.localMedia.mimeType, + attachmentState = attachmentState + ) + } + } + } + + private suspend fun sendMedia( + uri: Uri, + mimeType: String, + attachmentState: MutableState, + ) { + mediaSender.sendMedia(uri, mimeType) + .onSuccess { + attachmentState.value = AttachmentsState.None + }.onFailure { + val snackbarMessage = SnackbarMessage(sendAttachmentError(it)) + snackbarDispatcher.post(snackbarMessage) + attachmentState.value = AttachmentsState.None + } + } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt index 9df3e2269a..12965b7e55 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt @@ -38,6 +38,7 @@ data class MessageComposerState( sealed interface AttachmentsState { object None : AttachmentsState data class Previewing(val attachments: ImmutableList) : AttachmentsState + data class Sending(val attachments: ImmutableList) : AttachmentsState } sealed interface AttachmentSourcePicker {