Skip to content

Commit

Permalink
fix: sharing extension for text content (WPB-10466) (#3291)
Browse files Browse the repository at this point in the history
  • Loading branch information
yamilmedina authored Aug 6, 2024
1 parent cdbc996 commit f1d68ba
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ import kotlinx.serialization.Serializable
data class ConversationNavArgs(
val conversationId: ConversationId,
val searchedMessageId: String? = null,
val pendingBundles: ArrayList<AssetBundle>? = null
val pendingBundles: ArrayList<AssetBundle>? = null,
val pendingTextBundle: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ class SendMessageViewModel @Inject constructor(
)

init {
conversationNavArgs.pendingTextBundle?.let { text ->
trySendPendingMessageBundle(text)
}
conversationNavArgs.pendingBundles?.let { assetBundles ->
trySendMessages(
assetBundles.map { assetBundle ->
Expand All @@ -135,6 +138,12 @@ class SendMessageViewModel @Inject constructor(
private suspend fun shouldInformAboutUnderLegalHoldBeforeSendingMessage(conversationId: ConversationId) =
observeConversationUnderLegalHoldNotified(conversationId).first().let { !it }

private fun trySendPendingMessageBundle(pendingMessage: String) {
viewModelScope.launch {
sendMessage(ComposableMessageBundle.SendTextMessageBundle(conversationId, pendingMessage, emptyList()))
}
}

fun trySendMessage(messageBundle: MessageBundle) {
trySendMessages(listOf(messageBundle))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ import com.wire.android.mapper.toUIPreview
import com.wire.android.model.ImageAsset
import com.wire.android.model.SnackBarMessage
import com.wire.android.model.UserAvatarData
import com.wire.android.ui.home.conversations.ConversationSnackbarMessages
import com.wire.android.ui.common.textfield.textAsFlow
import com.wire.android.ui.home.conversations.ConversationSnackbarMessages
import com.wire.android.ui.home.conversations.search.DEFAULT_SEARCH_QUERY_DEBOUNCE
import com.wire.android.ui.home.conversations.usecase.HandleUriAssetUseCase
import com.wire.android.ui.home.conversationslist.model.BlockState
Expand Down Expand Up @@ -379,4 +379,9 @@ data class ImportMediaAuthenticatedState(
val shareableConversationListState: ShareableConversationListState = ShareableConversationListState(),
val selectedConversationItem: List<ConversationItem> = emptyList(),
val selfDeletingTimer: SelfDeletionTimer = SelfDeletionTimer.Enabled(null)
)
) {
@Stable
fun isImportingData() {
importedText?.isNotEmpty() == true || importedAssets.isNotEmpty()
}
}
180 changes: 120 additions & 60 deletions app/src/main/kotlin/com/wire/android/ui/sharing/ImportMediaScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ 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.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState
Expand Down Expand Up @@ -120,6 +123,7 @@ fun ImportMediaScreen(
navigateBack = navigator.finish
)
}

FeatureFlagState.FileSharingState.NoUser -> {
ImportMediaLoggedOutContent(
fileSharingRestrictedState = fileSharingRestrictedState,
Expand Down Expand Up @@ -192,23 +196,26 @@ private fun ImportMediaAuthenticatedContent(
searchQueryTextState = importMediaViewModel.searchQueryTextState,
onConversationClicked = importMediaViewModel::onConversationClicked,
checkRestrictionsAndSendImportedMedia = {
importMediaViewModel.importMediaState.selectedConversationItem.firstOrNull()?.let { conversationItem ->
checkAssetRestrictionsViewModel.checkRestrictions(
importedMediaList = importMediaViewModel.importMediaState.importedAssets,
onSuccess = {
navigator.navigate(
NavigationCommand(
ConversationScreenDestination(
ConversationNavArgs(
conversationId = conversationItem.conversationId,
pendingBundles = ArrayList(it)
)
with(importMediaViewModel.importMediaState) {
selectedConversationItem.firstOrNull()?.let { conversationItem ->
checkAssetRestrictionsViewModel.checkRestrictions(
importedMediaList = importedAssets,
onSuccess = {
navigator.navigate(
NavigationCommand(
ConversationScreenDestination(
ConversationNavArgs(
conversationId = conversationItem.conversationId,
pendingBundles = ArrayList(it),
pendingTextBundle = importedText
)
),
BackStackMode.REMOVE_CURRENT_AND_REPLACE
),
BackStackMode.REMOVE_CURRENT_AND_REPLACE
),
)
}
)
)
}
)
}
}
},
onNewSelfDeletionTimerPicked = importMediaViewModel::onNewSelfDeletionTimerPicked,
Expand All @@ -222,10 +229,12 @@ private fun ImportMediaAuthenticatedContent(
)

val context = LocalContext.current
LaunchedEffect(importMediaViewModel.importMediaState.importedAssets) {
if (importMediaViewModel.importMediaState.importedAssets.isEmpty()) {
context.getActivity()
?.let { importMediaViewModel.handleReceivedDataFromSharingIntent(it) }
with(importMediaViewModel.importMediaState) {
LaunchedEffect(isImportingData()) {
if (importedAssets.isEmpty() || importedText.isNullOrEmpty()) {
context.getActivity()
?.let { activity -> importMediaViewModel.handleReceivedDataFromSharingIntent(activity) }
}
}
}
}
Expand Down Expand Up @@ -479,46 +488,9 @@ private fun ImportMediaContent(
)
}
} else {
LazyRow(
modifier = Modifier
.fillMaxWidth()
.height(dimensions().spacing120x),
contentPadding = PaddingValues(start = dimensions().spacing8x, end = dimensions().spacing8x)
) {
items(
count = importedItemsList.size,
) { index ->
Box(
modifier = Modifier
.width(dimensions().spacing120x)
.fillMaxHeight()
) {
val assetSize = dimensions().spacing120x - dimensions().spacing16x
AssetTilePreview(
modifier = Modifier
.width(assetSize)
.height(assetSize)
.align(Alignment.Center),
assetBundle = importedItemsList[index].assetBundle,
showOnlyExtension = false,
onClick = {}
)

if (importedItemsList.size > 1) {
RemoveIcon(
modifier = Modifier.align(Alignment.TopEnd),
onClick = { onRemoveAsset(index) },
contentDescription = stringResource(id = R.string.remove_asset_description)
)
}
if (importedItemsList[index].assetSizeExceeded != null) {
ErrorIcon(
stringResource(id = R.string.asset_attention_description),
modifier = Modifier.align(Alignment.Center)
)
}
}
}
when (state.importedText.isNullOrBlank()) {
true -> ImportAssetsCarrousel(importedItemsList, onRemoveAsset)
false -> ImportText(state.importedText)
}
}
HorizontalDivider(
Expand Down Expand Up @@ -559,6 +531,73 @@ private fun ImportMediaContent(
}
}

@Composable
private fun ImportText(importedText: String) {
val scrollState = rememberScrollState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.wrapContentSize()
.height(dimensions().spacing120x)
.verticalScroll(scrollState)
.padding(vertical = dimensions().spacing8x, horizontal = dimensions().spacing16x),
) {
Text(
text = importedText,
textAlign = TextAlign.Start,
style = MaterialTheme.wireTypography.body01,
)
}
}

@Composable
private fun ImportAssetsCarrousel(
importedItemsList: PersistentList<ImportedMediaAsset>,
onRemoveAsset: (index: Int) -> Unit
) {
LazyRow(
modifier = Modifier
.fillMaxWidth()
.height(dimensions().spacing120x),
contentPadding = PaddingValues(start = dimensions().spacing8x, end = dimensions().spacing8x)
) {
items(
count = importedItemsList.size,
) { index ->
Box(
modifier = Modifier
.width(dimensions().spacing120x)
.fillMaxHeight()
) {
val assetSize = dimensions().spacing120x - dimensions().spacing16x
AssetTilePreview(
modifier = Modifier
.width(assetSize)
.height(assetSize)
.align(Alignment.Center),
assetBundle = importedItemsList[index].assetBundle,
showOnlyExtension = false,
onClick = {}
)

if (importedItemsList.size > 1) {
RemoveIcon(
modifier = Modifier.align(Alignment.TopEnd),
onClick = { onRemoveAsset(index) },
contentDescription = stringResource(id = R.string.remove_asset_description)
)
}
if (importedItemsList[index].assetSizeExceeded != null) {
ErrorIcon(
stringResource(id = R.string.asset_attention_description),
modifier = Modifier.align(Alignment.Center)
)
}
}
}
}
}

@Composable
private fun SnackBarMessage(
infoMessages: SharedFlow<SnackBarMessage>,
Expand Down Expand Up @@ -659,6 +698,27 @@ fun PreviewImportMediaScreenRegular() {
}
}

@PreviewMultipleThemes
@Composable
fun PreviewImportMediaTextScreenRegular() {
WireTheme {
ImportMediaRegularContent(
importMediaAuthenticatedState = ImportMediaAuthenticatedState(
importedAssets = persistentListOf(),
importedText = "This is a shared text message \n" +
"This is a second line with a veeeeeeeeeeeeeeeeeeeeeeeeeeery long shared text message"
),
searchQueryTextState = rememberTextFieldState(),
onConversationClicked = {},
checkRestrictionsAndSendImportedMedia = {},
onNewSelfDeletionTimerPicked = {},
infoMessage = MutableSharedFlow(),
onRemoveAsset = { _ -> },
navigateBack = {}
)
}
}

@PreviewMultipleThemes
@Composable
fun PreviewImportMediaBottomBar() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.wire.android.config.mockUri
import com.wire.android.framework.FakeKaliumFileSystem
import com.wire.android.media.PingRinger
import com.wire.android.ui.home.conversations.ConversationNavArgs
import com.wire.android.ui.home.conversations.model.AssetBundle
import com.wire.android.ui.home.conversations.usecase.HandleUriAssetUseCase
import com.wire.android.ui.navArgs
import com.wire.android.util.ImageUtil
Expand Down Expand Up @@ -250,5 +251,19 @@ internal class SendMessageViewModelArrangement {
coEvery { retryFailedMessageUseCase(any(), any()) } returns Either.Right(Unit)
}

fun withPendingTextBundle(textToShare: String = "some text") = apply {
every { savedStateHandle.navArgs<ConversationNavArgs>() } returns ConversationNavArgs(
conversationId = conversationId,
pendingTextBundle = textToShare
)
}

fun withPendingAssetBundle(vararg assetBundle: AssetBundle) = apply {
every { savedStateHandle.navArgs<ConversationNavArgs>() } returns ConversationNavArgs(
conversationId = conversationId,
pendingBundles = arrayListOf(*assetBundle)
)
}

fun arrange() = this to viewModel
}
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,61 @@ class SendMessageViewModelTest {
}
}

@Test
fun `given text is being shared, when initializing the viewmodel, then message is sent to use the case`() = runTest {
val textToShare = "my nice text to share"
val (arrangement, _) = SendMessageViewModelArrangement()
.withSuccessfulViewModelInit()
.withPendingTextBundle(textToShare)
.withSuccessfulSendTextMessage()
.arrange()

coVerify { arrangement.sendTextMessage(any(), eq(textToShare), any(), any()) }
}

@Test
fun `given an asset is being shared, when initializing the viewmodel, then message is sent to use the case`() = runTest {
val assetBundles = arrayOf(
AssetBundle(
"key1",
"application/pdf",
"some-data-path1".toPath(),
1L,
"mocked_file1.pdf",
AttachmentType.GENERIC_FILE
),
AssetBundle(
"key2",
"application/pdf",
"some-data-path2".toPath(),
1L,
"mocked_file2.pdf",
AttachmentType.GENERIC_FILE
)
)
val (arrangement, _) = SendMessageViewModelArrangement()
.withSuccessfulViewModelInit()
.withPendingAssetBundle(*assetBundles)
.withSendAttachmentMessageResult(ScheduleNewAssetMessageResult.Success("some-message-id1"))
.withSendAttachmentMessageResult(ScheduleNewAssetMessageResult.Success("some-message-id2"))
.arrange()

assetBundles.forEach { bundle ->
coVerify {
arrangement.sendAssetMessage(
any(),
eq(bundle.dataPath),
eq(bundle.dataSize),
eq(bundle.fileName),
eq(bundle.mimeType),
any(),
any(),
any()
)
}
}
}

companion object {
val conversationId: ConversationId = ConversationId("some-dummy-value", "some.dummy.domain")
}
Expand Down

0 comments on commit f1d68ba

Please sign in to comment.