From 85f79daa3be7d2dfb7f4f41fd12c4ee934c4d02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BBerko?= Date: Wed, 10 Apr 2024 12:33:39 +0200 Subject: [PATCH] feat: image preview [WPB-6733] (#2850) --- .../home/conversations/ConversationScreen.kt | 22 +- .../media/preview/ImagesPreviewNavArgs.kt | 27 +++ .../media/preview/ImagesPreviewScreen.kt | 211 ++++++++++++++++++ .../media/preview/ImagesPreviewState.kt | 23 ++ .../media/preview/ImagesPreviewViewModel.kt | 41 ++++ .../sendmessage/SendMessageState.kt | 20 ++ .../sendmessage/SendMessageViewModel.kt | 55 ++++- .../home/messagecomposer/AdditionalOptions.kt | 2 + .../home/messagecomposer/AttachmentOptions.kt | 21 +- .../messagecomposer/EnabledMessageComposer.kt | 2 + .../home/messagecomposer/MessageComposer.kt | 7 +- 11 files changed, 409 insertions(+), 22 deletions(-) create mode 100644 app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewNavArgs.kt create mode 100644 app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewScreen.kt create mode 100644 app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewState.kt create mode 100644 app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewViewModel.kt create mode 100644 app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageState.kt diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt index 3b193f2d9bd..c6ebb742ae8 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt @@ -101,6 +101,7 @@ import com.wire.android.ui.common.snackbar.SwipeableSnackbar import com.wire.android.ui.common.visbility.rememberVisibilityState import com.wire.android.ui.destinations.ConversationScreenDestination import com.wire.android.ui.destinations.GroupConversationDetailsScreenDestination +import com.wire.android.ui.destinations.ImagesPreviewScreenDestination import com.wire.android.ui.destinations.InitiatingCallScreenDestination import com.wire.android.ui.destinations.MediaGalleryScreenDestination import com.wire.android.ui.destinations.MessageDetailsScreenDestination @@ -199,6 +200,7 @@ fun ConversationScreen( ) { val coroutineScope = rememberCoroutineScope() val uriHandler = LocalUriHandler.current + val resources = LocalContext.current.resources val showDialog = remember { mutableStateOf(ConversationScreenDialogType.NONE) } val conversationScreenState = rememberConversationScreenState() val messageComposerViewState = messageComposerViewModel.messageComposerViewState @@ -338,6 +340,17 @@ fun ConversationScreen( ) }, onSendMessage = sendMessageViewModel::trySendMessage, + onImagePicked = { + navigator.navigate( + NavigationCommand( + ImagesPreviewScreenDestination( + conversationId = conversationInfoViewModel.conversationInfoViewState.conversationId, + conversationName = conversationInfoViewModel.conversationInfoViewState.conversationName.asString(resources), + assetUri = it + ) + ) + ) + }, onDeleteMessage = conversationMessagesViewModel::showDeleteMessageDialog, onAssetItemClicked = conversationMessagesViewModel::downloadOrFetchAssetAndShowDialog, onImageFullScreenMode = { message, isSelfMessage -> @@ -626,6 +639,7 @@ private fun ConversationScreen( onOpenProfile: (String) -> Unit, onMessageDetailsClick: (messageId: String, isSelfMessage: Boolean) -> Unit, onSendMessage: (MessageBundle) -> Unit, + onImagePicked: (Uri) -> Unit, onDeleteMessage: (String, Boolean) -> Unit, onAudioClick: (String) -> Unit, onChangeAudioPosition: (String, Int) -> Unit, @@ -742,6 +756,7 @@ private fun ConversationScreen( messageComposerStateHolder = messageComposerStateHolder, messages = conversationMessagesViewState.messages, onSendMessage = onSendMessage, + onImagePicked = onImagePicked, onAssetItemClicked = onAssetItemClicked, onAudioItemClicked = onAudioClick, onChangeAudioPosition = onChangeAudioPosition, @@ -789,6 +804,7 @@ private fun ConversationScreenContent( messageComposerStateHolder: MessageComposerStateHolder, messages: Flow>, onSendMessage: (MessageBundle) -> Unit, + onImagePicked: (Uri) -> Unit, onAssetItemClicked: (String) -> Unit, onAudioItemClicked: (String) -> Unit, onChangeAudioPosition: (String, Int) -> Unit, @@ -856,7 +872,8 @@ private fun ConversationScreenContent( onCaptureVideoPermissionPermanentlyDenied = onCaptureVideoPermissionPermanentlyDenied, tempWritableVideoUri = tempWritableVideoUri, tempWritableImageUri = tempWritableImageUri, - onTypingEvent = onTypingEvent + onTypingEvent = onTypingEvent, + onImagePicked = onImagePicked ) } @@ -1129,6 +1146,7 @@ fun PreviewConversationScreen() { conversationScreenState = conversationScreenState, messageComposerStateHolder = messageComposerStateHolder, onLinkClick = { _ -> }, - onTypingEvent = {} + onTypingEvent = {}, + onImagePicked = {} ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewNavArgs.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewNavArgs.kt new file mode 100644 index 00000000000..9bc17cfa753 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewNavArgs.kt @@ -0,0 +1,27 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.home.conversations.media.preview + +import android.net.Uri +import com.wire.kalium.logic.data.id.ConversationId + +data class ImagesPreviewNavArgs( + val conversationId: ConversationId, + val conversationName: String, + val assetUri: Uri +) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewScreen.kt new file mode 100644 index 00000000000..3088fb2234c --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewScreen.kt @@ -0,0 +1,211 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.home.conversations.media.preview + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.wire.android.R +import com.wire.android.model.SnackBarMessage +import com.wire.android.navigation.Navigator +import com.wire.android.navigation.style.PopUpNavigationAnimation +import com.wire.android.ui.common.button.WirePrimaryButton +import com.wire.android.ui.common.button.WireSecondaryButton +import com.wire.android.ui.common.colorsScheme +import com.wire.android.ui.common.dialogs.SureAboutMessagingInDegradedConversationDialog +import com.wire.android.ui.common.dimensions +import com.wire.android.ui.common.divider.WireDivider +import com.wire.android.ui.common.scaffold.WireScaffold +import com.wire.android.ui.common.snackbar.LocalSnackbarHostState +import com.wire.android.ui.common.spacers.HorizontalSpace +import com.wire.android.ui.common.topappbar.NavigationIconType +import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar +import com.wire.android.ui.home.conversations.AssetTooLargeDialog +import com.wire.android.ui.home.conversations.SureAboutMessagingDialogState +import com.wire.android.ui.home.conversations.model.UriAsset +import com.wire.android.ui.home.conversations.sendmessage.SendMessageState +import com.wire.android.ui.home.conversations.sendmessage.SendMessageViewModel +import com.wire.android.ui.home.messagecomposer.model.ComposableMessageBundle +import com.wire.android.ui.home.messagecomposer.model.MessageBundle +import com.wire.android.ui.legalhold.dialog.subject.LegalHoldSubjectMessageDialog +import com.wire.android.ui.theme.wireColorScheme +import kotlinx.coroutines.flow.SharedFlow + +@RootNavGraph +@Destination( + navArgsDelegate = ImagesPreviewNavArgs::class, + style = PopUpNavigationAnimation::class +) +@Composable +fun ImagesPreviewScreen( + navigator: Navigator, + imagesPreviewViewModel: ImagesPreviewViewModel = hiltViewModel(), + sendMessageViewModel: SendMessageViewModel = hiltViewModel() +) { + LaunchedEffect(sendMessageViewModel.viewState.messageSent) { + if (sendMessageViewModel.viewState.messageSent) { + navigator.navigateBack() + } + } + + Content( + previewState = imagesPreviewViewModel.viewState, + sendState = sendMessageViewModel.viewState, + onNavigationPressed = { navigator.navigateBack() }, + onSendMessage = sendMessageViewModel::trySendMessage + ) + + AssetTooLargeDialog( + dialogState = sendMessageViewModel.assetTooLargeDialogState, + hideDialog = sendMessageViewModel::hideAssetTooLargeError + ) + + SureAboutMessagingInDegradedConversationDialog( + dialogState = sendMessageViewModel.sureAboutMessagingDialogState, + sendAnyway = sendMessageViewModel::acceptSureAboutSendingMessage, + hideDialog = sendMessageViewModel::dismissSureAboutSendingMessage + ) + + (sendMessageViewModel.sureAboutMessagingDialogState as? SureAboutMessagingDialogState.Visible.ConversationUnderLegalHold)?.let { + LegalHoldSubjectMessageDialog( + dialogDismissed = sendMessageViewModel::dismissSureAboutSendingMessage, + sendAnywayClicked = sendMessageViewModel::acceptSureAboutSendingMessage, + ) + } + + SnackBarMessage(sendMessageViewModel.infoMessage) +} + +@Composable +private fun Content( + previewState: ImagesPreviewState, + sendState: SendMessageState, + onNavigationPressed: () -> Unit = {}, + onSendMessage: (MessageBundle) -> Unit +) { + val configuration = LocalConfiguration.current + WireScaffold( + topBar = { + WireCenterAlignedTopAppBar( + title = previewState.conversationName, + navigationIconType = NavigationIconType.Back, + onNavigationPressed = onNavigationPressed, + ) + }, + bottomBar = { + Column( + modifier = Modifier + .background(MaterialTheme.wireColorScheme.background) + .height(dimensions().spacing80x) + ) { + WireDivider(color = MaterialTheme.wireColorScheme.outline) + Row( + modifier = Modifier + .weight(1F) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + HorizontalSpace.x16() + WireSecondaryButton( + modifier = Modifier.weight(1F), + text = stringResource(id = R.string.label_cancel), + onClick = onNavigationPressed + ) + HorizontalSpace.x16() + WirePrimaryButton( + loading = sendState.inProgress, + modifier = Modifier.weight(1F), + text = stringResource(id = R.string.import_media_send_button_title), + leadingIcon = { + Image( + painter = painterResource(id = R.drawable.ic_send), + contentDescription = null, + modifier = Modifier.padding(end = dimensions().spacing12x), + colorFilter = ColorFilter.tint(colorsScheme().onPrimaryButtonEnabled) + ) + }, + onClick = { + onSendMessage( + ComposableMessageBundle.AttachmentPickedBundle( + previewState.conversationId, + UriAsset(previewState.assetUri) + ) + ) + } + ) + HorizontalSpace.x16() + } + } + } + ) { padding -> + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .padding(padding) + .fillMaxHeight() + .fillMaxWidth() + ) { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(previewState.assetUri) + .build(), + contentDescription = "preview_asset_image", + contentScale = ContentScale.FillWidth, + modifier = Modifier.width(configuration.screenWidthDp.dp) + ) + } + } +} + +@Composable +private fun SnackBarMessage(infoMessages: SharedFlow) { + val context = LocalContext.current + val snackbarHostState = LocalSnackbarHostState.current + + LaunchedEffect(Unit) { + infoMessages.collect { + snackbarHostState.showSnackbar( + message = it.uiText.asString(context.resources) + ) + } + } +} diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewState.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewState.kt new file mode 100644 index 00000000000..6ca3b29ac6c --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewState.kt @@ -0,0 +1,23 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.home.conversations.media.preview + +import android.net.Uri +import com.wire.kalium.logic.data.id.ConversationId + +data class ImagesPreviewState(val conversationId: ConversationId, val conversationName: String, val assetUri: Uri) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewViewModel.kt new file mode 100644 index 00000000000..ef1f3c8e228 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/media/preview/ImagesPreviewViewModel.kt @@ -0,0 +1,41 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.home.conversations.media.preview + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle +import com.wire.android.navigation.SavedStateViewModel +import com.wire.android.ui.navArgs +import javax.inject.Inject + +class ImagesPreviewViewModel @Inject constructor( + override val savedStateHandle: SavedStateHandle, +) : SavedStateViewModel(savedStateHandle) { + + private val navArgs: ImagesPreviewNavArgs = savedStateHandle.navArgs() + var viewState by mutableStateOf( + ImagesPreviewState( + conversationId = navArgs.conversationId, + conversationName = navArgs.conversationName, + assetUri = navArgs.assetUri + ) + ) + private set +} diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageState.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageState.kt new file mode 100644 index 00000000000..e2a8262caa6 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageState.kt @@ -0,0 +1,20 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.home.conversations.sendmessage + +data class SendMessageState(val messageSent: Boolean, val inProgress: Boolean) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageViewModel.kt index fc1304616aa..7205fd09f69 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/sendmessage/SendMessageViewModel.kt @@ -59,6 +59,7 @@ import com.wire.kalium.logic.feature.message.SendLocationUseCase import com.wire.kalium.logic.feature.message.SendTextMessageUseCase import com.wire.kalium.logic.feature.message.draft.RemoveMessageDraftUseCase import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.isRight import com.wire.kalium.logic.functional.onFailure import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -103,6 +104,13 @@ class SendMessageViewModel @Inject constructor( SureAboutMessagingDialogState.Hidden ) + var viewState: SendMessageState by mutableStateOf( + SendMessageState( + messageSent = false, + inProgress = false + ) + ) + private fun onSnackbarMessage(type: SnackBarMessage) = viewModelScope.launch { _infoMessage.emit(type) } @@ -128,19 +136,23 @@ class SendMessageViewModel @Inject constructor( } } + @Suppress("LongMethod") private suspend fun sendMessage(messageBundle: MessageBundle) { when (messageBundle) { is ComposableMessageBundle.EditMessageBundle -> { + beforeSendingMessage() + removeMessageDraft(messageBundle.conversationId) + sendTypingEvent(messageBundle.conversationId, TypingIndicatorMode.STOPPED) with(messageBundle) { sendEditTextMessage( conversationId = conversationId, originalMessageId = originalMessageId, text = newContent, mentions = newMentions.map { it.intoMessageMention() }, - ).handleLegalHoldFailureAfterSendingMessage(conversationId) + ) + .handleLegalHoldFailureAfterSendingMessage(conversationId) + .handleAfterMessageResult() } - removeMessageDraft(messageBundle.conversationId) - sendTypingEvent(messageBundle.conversationId, TypingIndicatorMode.STOPPED) } is ComposableMessageBundle.AttachmentPickedBundle -> { @@ -159,28 +171,36 @@ class SendMessageViewModel @Inject constructor( } is ComposableMessageBundle.SendTextMessageBundle -> { + beforeSendingMessage() + removeMessageDraft(messageBundle.conversationId) + sendTypingEvent(messageBundle.conversationId, TypingIndicatorMode.STOPPED) with(messageBundle) { sendTextMessage( conversationId = conversationId, text = message, mentions = mentions.map { it.intoMessageMention() }, quotedMessageId = quotedMessageId - ).handleLegalHoldFailureAfterSendingMessage(conversationId) + ) + .handleLegalHoldFailureAfterSendingMessage(conversationId) + .handleAfterMessageResult() } - removeMessageDraft(messageBundle.conversationId) - sendTypingEvent(messageBundle.conversationId, TypingIndicatorMode.STOPPED) } is ComposableMessageBundle.LocationBundle -> { + beforeSendingMessage() with(messageBundle) { sendLocation(conversationId, location.latitude.toFloat(), location.longitude.toFloat(), locationName, zoom) .handleLegalHoldFailureAfterSendingMessage(conversationId) + .handleAfterMessageResult() } } is Ping -> { + beforeSendingMessage() pingRinger.ping(R.raw.ping_from_me, isReceivingPing = false) sendKnockUseCase(conversationId = messageBundle.conversationId, hotKnock = false) + .handleLegalHoldFailureAfterSendingMessage(messageBundle.conversationId) + .handleAfterMessageResult() } } } @@ -214,6 +234,7 @@ class SendMessageViewModel @Inject constructor( } internal fun sendAttachment(attachmentBundle: AssetBundle?, conversationId: ConversationId) { + beforeSendingMessage() viewModelScope.launch { withContext(dispatchers.io()) { attachmentBundle?.run { @@ -232,7 +253,9 @@ class SendMessageViewModel @Inject constructor( assetDataSize = dataSize, assetMimeType = mimeType, audioLengthInMs = 0L - ).handleLegalHoldFailureAfterSendingMessage(conversationId) + ) + .handleLegalHoldFailureAfterSendingMessage(conversationId) + .handleAfterMessageResult() } AttachmentType.VIDEO, @@ -251,7 +274,9 @@ class SendMessageViewModel @Inject constructor( dataPath = dataPath, mimeType = mimeType ) - ).handleLegalHoldFailureAfterSendingMessage(conversationId) + ) + .handleLegalHoldFailureAfterSendingMessage(conversationId) + .handleAfterMessageResult() } catch (e: OutOfMemoryError) { appLogger.e("There was an OutOfMemory error while uploading the asset") onSnackbarMessage(ConversationSnackbarMessages.ErrorSendingAsset) @@ -273,10 +298,14 @@ class SendMessageViewModel @Inject constructor( private fun Either.handleLegalHoldFailureAfterSendingMessage(conversationId: ConversationId) = onFailure { it.handleLegalHoldFailureAfterSendingMessage(conversationId) } - private fun ScheduleNewAssetMessageResult.handleLegalHoldFailureAfterSendingMessage(conversationId: ConversationId) = also { + private fun ScheduleNewAssetMessageResult.handleLegalHoldFailureAfterSendingMessage(conversationId: ConversationId) = let { if (it is ScheduleNewAssetMessageResult.Failure) { it.coreFailure.handleLegalHoldFailureAfterSendingMessage(conversationId) } + when (this) { + is ScheduleNewAssetMessageResult.Failure -> Either.Left(coreFailure) + is ScheduleNewAssetMessageResult.Success -> Either.Right(Unit) + } } fun retrySendingMessage(messageId: String, conversationId: ConversationId) { @@ -325,7 +354,11 @@ class SendMessageViewModel @Inject constructor( sureAboutMessagingDialogState = SureAboutMessagingDialogState.Hidden } - companion object { - private const val sizeOf1MB = 1024 * 1024 + private fun beforeSendingMessage() { + viewState = viewState.copy(messageSent = false, inProgress = true) + } + + private fun Either.handleAfterMessageResult() { + viewState = viewState.copy(messageSent = this.isRight(), inProgress = false) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt index 16277e3aac9..6ebcf5aa40f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AdditionalOptions.kt @@ -99,6 +99,7 @@ fun AdditionalOptionSubMenu( onCloseAdditionalAttachment: () -> Unit, onRecordAudioMessageClicked: () -> Unit, additionalOptionsState: AdditionalOptionSubMenuState, + onImagePicked: (Uri) -> Unit, onAttachmentPicked: (UriAsset) -> Unit, onAudioRecorded: (UriAsset) -> Unit, onLocationPicked: (GeoLocatedAddress) -> Unit, @@ -108,6 +109,7 @@ fun AdditionalOptionSubMenu( ) { Box(modifier = modifier) { AttachmentOptionsComponent( + onImagePicked = onImagePicked, onAttachmentPicked = onAttachmentPicked, tempWritableImageUri = tempWritableImageUri, tempWritableVideoUri = tempWritableVideoUri, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt index 6148d7bafa4..9069d2468f5 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/AttachmentOptions.kt @@ -60,6 +60,7 @@ import com.wire.android.util.ui.KeyboardHeight @Composable fun AttachmentOptionsComponent( + onImagePicked: (Uri) -> Unit, onAttachmentPicked: (UriAsset) -> Unit, onRecordAudioMessageClicked: () -> Unit, tempWritableImageUri: Uri?, @@ -75,6 +76,7 @@ fun AttachmentOptionsComponent( isFileSharingEnabled, tempWritableImageUri, tempWritableVideoUri, + onImagePicked, onAttachmentPicked, onRecordAudioMessageClicked, onLocationPickerClicked, @@ -219,6 +221,7 @@ private fun buildAttachmentOptionItems( isFileSharingEnabled: Boolean, tempWritableImageUri: Uri?, tempWritableVideoUri: Uri?, + onImagePicked: (Uri) -> Unit, onFilePicked: (UriAsset) -> Unit, onRecordAudioMessageClicked: () -> Unit, onLocationPickerClicked: () -> Unit, @@ -229,7 +232,7 @@ private fun buildAttachmentOptionItems( onPermissionPermanentlyDenied ) val galleryFlow = GalleryFlow( - remember { { onFilePicked(UriAsset(it, false)) } }, + remember { { onImagePicked(it) } }, onPermissionPermanentlyDenied ) val cameraFlow = TakePictureFlow( @@ -310,7 +313,8 @@ private data class AttachmentOptionItem( @Composable fun PreviewAttachmentComponents() { AttachmentOptionsComponent( - {}, + onImagePicked = {}, + onAttachmentPicked = {}, isFileSharingEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, @@ -329,7 +333,8 @@ fun PreviewAttachmentOptionsComponentSmallScreen() { contentAlignment = Alignment.BottomCenter ) { AttachmentOptionsComponent( - {}, + onAttachmentPicked = {}, + onImagePicked = {}, isFileSharingEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, @@ -350,13 +355,14 @@ fun PreviewAttachmentOptionsComponentNormalScreen() { contentAlignment = Alignment.BottomCenter ) { AttachmentOptionsComponent( - {}, + onAttachmentPicked = {}, + onImagePicked = {}, isFileSharingEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, onRecordAudioMessageClicked = {}, onLocationPickerClicked = {}, - onCaptureVideoPermissionPermanentlyDenied = {}, + onCaptureVideoPermissionPermanentlyDenied = {} ) } } @@ -371,13 +377,14 @@ fun PreviewAttachmentOptionsComponentTabledScreen() { contentAlignment = Alignment.BottomCenter ) { AttachmentOptionsComponent( - {}, + onAttachmentPicked = {}, + onImagePicked = {}, isFileSharingEnabled = true, tempWritableImageUri = null, tempWritableVideoUri = null, onRecordAudioMessageClicked = {}, onLocationPickerClicked = {}, - onCaptureVideoPermissionPermanentlyDenied = {}, + onCaptureVideoPermissionPermanentlyDenied = {} ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt index dc4ec8bfc40..fdaec494e6a 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/EnabledMessageComposer.kt @@ -74,6 +74,7 @@ fun EnabledMessageComposer( onSearchMentionQueryChanged: (String) -> Unit, onTypingEvent: (Conversation.TypingIndicatorMode) -> Unit, onSendButtonClicked: () -> Unit, + onImagePicked: (Uri) -> Unit, onAttachmentPicked: (UriAsset) -> Unit, onAudioRecorded: (UriAsset) -> Unit, onLocationPicked: (GeoLocatedAddress) -> Unit, @@ -284,6 +285,7 @@ fun EnabledMessageComposer( onRecordAudioMessageClicked = ::toAudioRecording, onCloseAdditionalAttachment = ::toInitialAttachmentOptions, onLocationPickerClicked = ::toLocationPicker, + onImagePicked = onImagePicked, onAttachmentPicked = onAttachmentPicked, onAudioRecorded = onAudioRecorded, onLocationPicked = onLocationPicked, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt index 104569193b4..4d5fa9a7ffc 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt @@ -85,7 +85,8 @@ fun MessageComposer( onCaptureVideoPermissionPermanentlyDenied: (type: PermissionDenialType) -> Unit, tempWritableVideoUri: Uri?, tempWritableImageUri: Uri?, - onTypingEvent: (TypingIndicatorMode) -> Unit + onTypingEvent: (TypingIndicatorMode) -> Unit, + onImagePicked: (Uri) -> Unit ) { with(messageComposerStateHolder) { when (messageComposerViewState.value.interactionAvailability) { @@ -136,6 +137,7 @@ fun MessageComposer( clearMessage() }, onPingOptionClicked = { onSendMessageBundle(Ping(conversationId)) }, + onImagePicked = onImagePicked, onAttachmentPicked = { onSendMessageBundle(ComposableMessageBundle.AttachmentPickedBundle(conversationId, it)) }, onAudioRecorded = { onSendMessageBundle(ComposableMessageBundle.AudioMessageBundle(conversationId, it)) }, onLocationPicked = { @@ -279,7 +281,8 @@ private fun BaseComposerPreview( onSendMessageBundle = { }, tempWritableVideoUri = null, tempWritableImageUri = null, - onTypingEvent = { } + onTypingEvent = { }, + onImagePicked = {} ) }