Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multiple image preview [WPB-8801] #3004

Merged
merged 20 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.wire.android.ui.common.imagepreview

import android.net.Uri
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import com.wire.android.ui.userprofile.avatarpicker.ImageSource
Expand All @@ -30,7 +31,7 @@ import com.wire.android.util.permission.rememberTakePictureFlow

class AvatarPickerFlow(
private val takePictureFlow: UseCameraRequestFlow,
private val openGalleryFlow: UseStorageRequestFlow
private val openGalleryFlow: UseStorageRequestFlow<Uri?>
) {
fun launch(imageSource: ImageSource) {
when (imageSource) {
Expand All @@ -56,7 +57,8 @@ fun rememberPickPictureState(
)

val openGalleryFlow = rememberOpenGalleryFlow(
onGalleryItemPicked = { pickedPictureUri -> onImageSelected(pickedPictureUri) },
contract = ActivityResultContracts.GetContent(),
onGalleryItemPicked = { pickedPictureUri -> pickedPictureUri?.let { onImageSelected(it) } },
onPermissionDenied = { /* Nothing to do */ },
onPermissionPermanentlyDenied = onPermissionPermanentlyDenied
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,42 +29,61 @@ import com.wire.kalium.logic.data.asset.AttachmentType

@Composable
fun AssetTooLargeDialog(dialogState: AssetTooLargeDialogState, hideDialog: () -> Unit) {
if (dialogState is AssetTooLargeDialogState.Visible) {
WireDialog(
title = getTitle(dialogState),
text = getLabel(dialogState),
buttonsHorizontalAlignment = false,
onDismiss = hideDialog,
optionButton1Properties = WireDialogButtonProperties(
text = stringResource(R.string.label_ok),
type = WireDialogButtonType.Primary,
onClick = hideDialog
when (dialogState) {
is AssetTooLargeDialogState.SingleVisible,
is AssetTooLargeDialogState.MultipleVisible -> {
WireDialog(
title = getTitle(dialogState),
text = getLabel(dialogState),
buttonsHorizontalAlignment = false,
onDismiss = hideDialog,
optionButton1Properties = WireDialogButtonProperties(
text = stringResource(R.string.label_ok),
type = WireDialogButtonType.Primary,
onClick = hideDialog
)
)
)
}

AssetTooLargeDialogState.Hidden -> {}
}
}

@Composable
private fun getTitle(dialogState: AssetTooLargeDialogState.Visible) = when (dialogState.assetType) {
AttachmentType.IMAGE -> stringResource(R.string.title_image_could_not_be_sent)
AttachmentType.VIDEO -> stringResource(R.string.title_video_could_not_be_sent)
AttachmentType.AUDIO, // TODO
AttachmentType.GENERIC_FILE -> stringResource(R.string.title_file_could_not_be_sent)
private fun getTitle(dialogState: AssetTooLargeDialogState) = when (dialogState) {
AssetTooLargeDialogState.Hidden -> ""
is AssetTooLargeDialogState.MultipleVisible -> stringResource(id = R.string.title_assets_could_not_be_sent)
is AssetTooLargeDialogState.SingleVisible -> when (dialogState.assetType) {
AttachmentType.IMAGE -> stringResource(R.string.title_image_could_not_be_sent)
AttachmentType.VIDEO -> stringResource(R.string.title_video_could_not_be_sent)
AttachmentType.AUDIO, // TODO
AttachmentType.GENERIC_FILE -> stringResource(R.string.title_file_could_not_be_sent)
}
}

@Composable
private fun getLabel(dialogState: AssetTooLargeDialogState.Visible) = when (dialogState.assetType) {
AttachmentType.IMAGE -> stringResource(R.string.label_shared_image_too_large, dialogState.maxLimitInMB)
AttachmentType.VIDEO -> stringResource(R.string.label_shared_video_too_large, dialogState.maxLimitInMB)
AttachmentType.AUDIO, // TODO
AttachmentType.GENERIC_FILE -> stringResource(R.string.label_shared_file_too_large, dialogState.maxLimitInMB)
}.let {
if (dialogState.savedToDevice) it + "\n" + stringResource(R.string.label_file_saved_to_device)
else it
private fun getLabel(dialogState: AssetTooLargeDialogState) = when (dialogState) {
AssetTooLargeDialogState.Hidden -> ""
is AssetTooLargeDialogState.MultipleVisible -> stringResource(id = R.string.label_shared_asset_too_large, dialogState.maxLimitInMB)
is AssetTooLargeDialogState.SingleVisible -> when (dialogState.assetType) {
AttachmentType.IMAGE -> stringResource(R.string.label_shared_image_too_large, dialogState.maxLimitInMB)
AttachmentType.VIDEO -> stringResource(R.string.label_shared_video_too_large, dialogState.maxLimitInMB)
AttachmentType.AUDIO, // TODO
AttachmentType.GENERIC_FILE -> stringResource(R.string.label_shared_file_too_large, dialogState.maxLimitInMB)
}.let {
if (dialogState.savedToDevice) it + "\n" + stringResource(R.string.label_file_saved_to_device)
else it
}
}

@Preview
@Composable
fun PreviewAssetTooLargeDialog() {
AssetTooLargeDialog(AssetTooLargeDialogState.Visible(AttachmentType.VIDEO, 100, true)) {}
AssetTooLargeDialog(AssetTooLargeDialogState.SingleVisible(AttachmentType.VIDEO, 100, true)) {}
}

@Preview
@Composable
fun PreviewMultipleAssetTooLargeDialog() {
AssetTooLargeDialog(AssetTooLargeDialogState.MultipleVisible(20)) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -373,13 +373,13 @@ fun ConversationScreen(
)
},
onSendMessage = sendMessageViewModel::trySendMessage,
onImagePicked = {
onImagesPicked = {
navigator.navigate(
NavigationCommand(
ImagesPreviewScreenDestination(
conversationId = conversationInfoViewModel.conversationInfoViewState.conversationId,
conversationName = conversationInfoViewModel.conversationInfoViewState.conversationName.asString(resources),
assetUri = it
assetUriList = ArrayList(it)
)
)
)
Expand Down Expand Up @@ -682,7 +682,7 @@ private fun ConversationScreen(
onOpenProfile: (String) -> Unit,
onMessageDetailsClick: (messageId: String, isSelfMessage: Boolean) -> Unit,
onSendMessage: (MessageBundle) -> Unit,
onImagePicked: (Uri) -> Unit,
onImagesPicked: (List<Uri>) -> Unit,
onDeleteMessage: (String, Boolean) -> Unit,
onAudioClick: (String) -> Unit,
onChangeAudioPosition: (String, Int) -> Unit,
Expand Down Expand Up @@ -799,7 +799,7 @@ private fun ConversationScreen(
messageComposerStateHolder = messageComposerStateHolder,
messages = conversationMessagesViewState.messages,
onSendMessage = onSendMessage,
onImagePicked = onImagePicked,
onImagesPicked = onImagesPicked,
onAssetItemClicked = onAssetItemClicked,
onAudioItemClicked = onAudioClick,
onChangeAudioPosition = onChangeAudioPosition,
Expand Down Expand Up @@ -848,7 +848,7 @@ private fun ConversationScreenContent(
messageComposerStateHolder: MessageComposerStateHolder,
messages: Flow<PagingData<UIMessage>>,
onSendMessage: (MessageBundle) -> Unit,
onImagePicked: (Uri) -> Unit,
onImagesPicked: (List<Uri>) -> Unit,
onAssetItemClicked: (String) -> Unit,
onAudioItemClicked: (String) -> Unit,
onChangeAudioPosition: (String, Int) -> Unit,
Expand Down Expand Up @@ -919,7 +919,7 @@ private fun ConversationScreenContent(
tempWritableVideoUri = tempWritableVideoUri,
tempWritableImageUri = tempWritableImageUri,
onTypingEvent = onTypingEvent,
onImagePicked = onImagePicked
onImagesPicked = onImagesPicked
)
}

Expand Down Expand Up @@ -1316,6 +1316,6 @@ fun PreviewConversationScreen() {
messageComposerStateHolder = messageComposerStateHolder,
onLinkClick = { _ -> },
onTypingEvent = {},
onImagePicked = {}
onImagesPicked = {}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ data class MessageComposerViewState(

sealed class AssetTooLargeDialogState {
data object Hidden : AssetTooLargeDialogState()
data class Visible(val assetType: AttachmentType, val maxLimitInMB: Int, val savedToDevice: Boolean) : AssetTooLargeDialogState()
data class SingleVisible(val assetType: AttachmentType, val maxLimitInMB: Int, val savedToDevice: Boolean) : AssetTooLargeDialogState()
data class MultipleVisible(val maxLimitInMB: Int) : AssetTooLargeDialogState()
}

sealed class VisitLinkDialogState {
Expand All @@ -52,15 +53,21 @@ sealed class InvalidLinkDialogState {
sealed class SureAboutMessagingDialogState {
data object Hidden : SureAboutMessagingDialogState()
sealed class Visible(open val conversationId: ConversationId) : SureAboutMessagingDialogState() {
data class ConversationVerificationDegraded(val messageBundleToSend: MessageBundle) : Visible(messageBundleToSend.conversationId)
data class ConversationVerificationDegraded(
override val conversationId: ConversationId,
val messageBundleListToSend: List<MessageBundle>
) : Visible(conversationId)

sealed class ConversationUnderLegalHold(override val conversationId: ConversationId) : Visible(conversationId) {
data class BeforeSending(
val messageBundleToSend: MessageBundle
) : ConversationUnderLegalHold(messageBundleToSend.conversationId)
override val conversationId: ConversationId,
val messageBundleListToSend: List<MessageBundle>
) : ConversationUnderLegalHold(conversationId)

data class AfterSending(val messageId: MessageId, override val conversationId: ConversationId) :
ConversationUnderLegalHold(conversationId)
data class AfterSending(
override val conversationId: ConversationId,
val messageIdList: List<MessageId>
) : ConversationUnderLegalHold(conversationId)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.wire.android.R
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.preview.PreviewMultipleThemes
import com.wire.android.ui.common.spacers.VerticalSpace
import com.wire.android.ui.theme.WireTheme
import com.wire.android.ui.theme.wireColorScheme
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.DeviceUtil
import java.util.Locale

@Composable
fun AssetFilePreview(
modifier: Modifier = Modifier,
assetName: String,
sizeInBytes: Long
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = modifier
.fillMaxSize()
.padding(horizontal = dimensions().spacing32x)
) {
Box(contentAlignment = Alignment.BottomCenter) {
Icon(
modifier = Modifier.size(dimensions().spacing80x),
painter = painterResource(id = R.drawable.ic_file),
contentDescription = assetName,
tint = MaterialTheme.wireColorScheme.secondaryText
)
Text(
modifier = Modifier.padding(bottom = dimensions().spacing8x),
text = assetName.split(".").last().uppercase(Locale.getDefault()),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.wireTypography.title05
)
}
VerticalSpace.x16()
Text(
assetName,
style = MaterialTheme.wireTypography.title02,
color = MaterialTheme.colorScheme.onBackground,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
VerticalSpace.x8()
Text(
DeviceUtil.formatSize(sizeInBytes),
style = MaterialTheme.wireTypography.body01,
color = MaterialTheme.wireColorScheme.secondaryText
)
}
}

@PreviewMultipleThemes
@Composable
fun PreviewWireImage() {
Box(
modifier = Modifier
.width(400.dp)
.height(800.dp)
) {
WireTheme {
AssetFilePreview(
assetName = "very long file naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame.png",
sizeInBytes = 1500
)
}
}
}
Loading
Loading