Media: send file without preview

This commit is contained in:
ganfra
2023-05-22 16:25:50 +02:00
parent 727cf69a39
commit c8c2cb8ff3
3 changed files with 77 additions and 11 deletions

View File

@@ -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<Attachment>) -> 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,

View File

@@ -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<MessageComposerState> {
@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<AttachmentsState>,
) = 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<AttachmentsState>,
) {
mediaSender.sendMedia(uri, mimeType)
.onSuccess {
attachmentState.value = AttachmentsState.None
}.onFailure {
val snackbarMessage = SnackbarMessage(sendAttachmentError(it))
snackbarDispatcher.post(snackbarMessage)
attachmentState.value = AttachmentsState.None
}
}
}

View File

@@ -38,6 +38,7 @@ data class MessageComposerState(
sealed interface AttachmentsState {
object None : AttachmentsState
data class Previewing(val attachments: ImmutableList<Attachment>) : AttachmentsState
data class Sending(val attachments: ImmutableList<Attachment>) : AttachmentsState
}
sealed interface AttachmentSourcePicker {