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: composable scoped view model and handle message buttom sheet menu #2002

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
package com.wire.android.ui.home.conversations

import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.android.navigation.EXTRA_CONVERSATION_ID
import com.wire.android.navigation.SavedStateViewModel
import com.wire.android.navigation.EXTRA_MESSAGE_ID
import com.wire.kalium.logic.data.id.MessageButtonId
import com.wire.kalium.logic.data.id.MessageId
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.id.QualifiedIdMapper
import com.wire.kalium.logic.feature.message.composite.SendButtonActionMessageUseCase
Expand All @@ -33,28 +35,34 @@ import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class CompositeMessagesViewModel @Inject constructor(
class CompositeMessageViewModel @Inject constructor(
private val sendButtonActionMessageUseCase: SendButtonActionMessageUseCase,
qualifiedIdMapper: QualifiedIdMapper,
savedStateHandle: SavedStateHandle,
) : SavedStateViewModel(savedStateHandle) {
) : ViewModel() {

val conversationId: QualifiedID = qualifiedIdMapper.fromStringToQualifiedID(
savedStateHandle.get<String>(EXTRA_CONVERSATION_ID)!!
)

var pendingButtons = mutableStateMapOf<MessageId, MessageButtonId>()
private val messageId: String = savedStateHandle.get<String>(EXTRA_MESSAGE_ID)!!

var pendingButtonId: MessageButtonId? by mutableStateOf(null)
@VisibleForTesting
set

fun onButtonClicked(messageId: String, buttonId: String) {
if (pendingButtons.containsKey(messageId)) return
fun onButtonClicked(buttonId: String) {
if (pendingButtonId != null) return

pendingButtons[messageId] = buttonId
pendingButtonId = buttonId
viewModelScope.launch {
sendButtonActionMessageUseCase(conversationId, messageId, buttonId)
}.invokeOnCompletion {
pendingButtons.remove(messageId)
pendingButtonId = null
}
}

companion object {
const val ARGS_KEY = "CompositeMessageViewModelKey"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ fun ConversationScreen(
conversationCallViewModel: ConversationCallViewModel = hiltSavedStateViewModel(backNavArgs = backNavArgs),
conversationMessagesViewModel: ConversationMessagesViewModel = hiltSavedStateViewModel(backNavArgs = backNavArgs),
messageComposerViewModel: MessageComposerViewModel = hiltSavedStateViewModel(backNavArgs = backNavArgs),
compositeMessagesViewModel: CompositeMessagesViewModel = hiltSavedStateViewModel(backNavArgs = backNavArgs)
) {
val coroutineScope = rememberCoroutineScope()
val showDialog = remember { mutableStateOf(ConversationScreenDialogType.NONE) }
Expand Down Expand Up @@ -220,7 +219,6 @@ fun ConversationScreen(
onFailedMessageRetryClicked = messageComposerViewModel::retrySendingMessage,
requestMentions = messageComposerViewModel::searchMembersToMention,
onClearMentionSearchResult = messageComposerViewModel::clearMentionSearchResult,
compositeMessagesViewModel = compositeMessagesViewModel
)
DeleteMessageDialog(
state = messageComposerViewModel.deleteMessageDialogsState,
Expand Down Expand Up @@ -307,7 +305,6 @@ private fun ConversationScreen(
composerMessages: SharedFlow<SnackBarMessage>,
conversationMessages: SharedFlow<SnackBarMessage>,
conversationMessagesViewModel: ConversationMessagesViewModel,
compositeMessagesViewModel: CompositeMessagesViewModel,
onSelfDeletingMessageRead: (UIMessage) -> Unit,
onNewSelfDeletingMessagesStatus: (SelfDeletionTimer) -> Unit,
tempWritableImageUri: Uri?,
Expand Down Expand Up @@ -425,8 +422,6 @@ private fun ConversationScreen(
tempWritableImageUri = tempWritableImageUri,
tempWritableVideoUri = tempWritableVideoUri,
snackBarHostState = conversationScreenState.snackBarHostState,
onMessageButtonClicked = compositeMessagesViewModel::onButtonClicked,
pendingButtonsMap = compositeMessagesViewModel.pendingButtons,
)
}
MenuModalSheetLayout(
Expand Down Expand Up @@ -465,8 +460,6 @@ private fun ConversationScreenContent(
onChangeSelfDeletionClicked: () -> Unit,
onSearchMentionQueryChanged: (String) -> Unit,
onClearMentionSearchResult: () -> Unit,
onMessageButtonClicked: (messageId: String, buttonId: String) -> Unit,
pendingButtonsMap: Map<String, String>,
tempWritableImageUri: Uri?,
tempWritableVideoUri: Uri?,
snackBarHostState: SnackbarHostState
Expand Down Expand Up @@ -499,8 +492,6 @@ private fun ConversationScreenContent(
conversationDetailsData = conversationDetailsData,
onFailedMessageCancelClicked = onFailedMessageCancelClicked,
onFailedMessageRetryClicked = onFailedMessageRetryClicked,
onMessageButtonClicked = onMessageButtonClicked,
pendingButtonsMap = pendingButtonsMap
)
},
onChangeSelfDeletionClicked = onChangeSelfDeletionClicked,
Expand Down Expand Up @@ -581,8 +572,6 @@ fun MessageList(
conversationDetailsData: ConversationDetailsData,
onFailedMessageRetryClicked: (String) -> Unit,
onFailedMessageCancelClicked: (String) -> Unit,
onMessageButtonClicked: (messageId: String, buttonId: String) -> Unit,
pendingButtonsMap: Map<String, String>
) {
val mostRecentMessage = lazyPagingMessages.itemCount.takeIf { it > 0 }?.let { lazyPagingMessages[0] }

Expand Down Expand Up @@ -639,8 +628,6 @@ fun MessageList(
onSelfDeletingMessageRead = onSelfDeletingMessageRead,
onFailedMessageCancelClicked = onFailedMessageCancelClicked,
onFailedMessageRetryClicked = onFailedMessageRetryClicked,
onMessageButtonClicked = onMessageButtonClicked,
pendingButtonsMap = pendingButtonsMap
)
}

Expand Down Expand Up @@ -713,7 +700,6 @@ fun PreviewConversationScreen() {
tempWritableVideoUri = null,
onFailedMessageRetryClicked = {},
requestMentions = {},
onClearMentionSearchResult = {},
compositeMessagesViewModel = hiltViewModel()
onClearMentionSearchResult = {}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,6 @@ fun MessageItem(
onSelfDeletingMessageRead: (UIMessage) -> Unit,
onFailedMessageRetryClicked: (String) -> Unit = {},
onFailedMessageCancelClicked: (String) -> Unit = {},
onMessageButtonClicked: (messageId: String, buttonId: String) -> Unit,
pendingButtonsMap: Map<String, String> = emptyMap()
) {
with(message) {
val selfDeletionTimerState = rememberSelfDeletionTimer(header.messageStatus.expirationStatus)
Expand Down Expand Up @@ -232,8 +230,6 @@ fun MessageItem(
onImageClick = currentOnImageClick,
onLongClick = onLongClick,
onOpenProfile = onOpenProfile,
onMessageButtonClicked = onMessageButtonClicked,
pendingButtonsMap = pendingButtonsMap,
)
}
if (isMyMessage) {
Expand Down Expand Up @@ -448,8 +444,6 @@ private fun MessageContent(
audioMessagesState: Map<String, AudioState>,
onAssetClick: Clickable,
onImageClick: Clickable,
onMessageButtonClicked: (messageId: String, buttonId: String) -> Unit,
pendingButtonsMap: Map<String, String>,
onAudioClick: (String) -> Unit,
onChangeAudioPosition: (String, Int) -> Unit,
onLongClick: (() -> Unit)? = null,
Expand Down Expand Up @@ -483,8 +477,6 @@ private fun MessageContent(
onLongClick = onLongClick,
onOpenProfile = onOpenProfile,
buttonList = null,
onButtonClick = null,
pendingButton = null,
messageId = message.header.messageId
)
PartialDeliveryInformation(messageContent.deliveryStatus)
Expand All @@ -507,9 +499,7 @@ private fun MessageContent(
onLongClick = onLongClick,
onOpenProfile = onOpenProfile,
buttonList = messageContent.buttonList,
onButtonClick = onMessageButtonClicked,
messageId = message.header.messageId,
pendingButton = pendingButtonsMap[message.header.messageId]
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.wire.android.ui.common.bottomsheet.MenuItemIcon
import com.wire.android.ui.home.conversations.model.ExpirationStatus
import com.wire.android.ui.home.conversations.model.UIMessage
import com.wire.android.ui.home.conversations.model.UIMessageContent
import com.wire.android.util.Copyable
import com.wire.android.util.ui.UIText
import com.wire.kalium.logic.data.message.mention.MessageMention

Expand All @@ -49,39 +50,40 @@ fun EditMessageMenuItems(
): List<@Composable () -> Unit> {
val localContext = LocalContext.current

val onCopyItemClick = remember(message) {
{
hideEditMessageMenu {
onCopyClick(
(message.messageContent as UIMessageContent.TextMessage).messageBody.message.asString(
localContext.resources
)
)
val isComposite = remember(message.header.messageId) {
message.messageContent is UIMessageContent.Composite
}

val onCopyItemClick: (() -> Unit)? = remember(message.header.messageId) {
(message.messageContent as? Copyable)?.textToCopy(localContext.resources)?.let {
{
hideEditMessageMenu { onCopyClick(it) }
}
}
}
Comment on lines +57 to 63
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea here is only Copyable messages will have the copy option displayed, and if we want to implement it to other messages we can do so by simply implementing the interface and we will be good to go
because before it will crash if it was called for anything that is not text message

val onDeleteItemClick = remember(message) {

val onDeleteItemClick = remember(message.header.messageId) {
{
hideEditMessageMenu {
onDeleteClick(message.header.messageId, message.isMyMessage)
}
}
}
val onReactionItemClick = remember(message) {
val onReactionItemClick = remember(message.header.messageId) {
{ emoji: String ->
hideEditMessageMenu {
onReactionClick(message.header.messageId, emoji)
}
}
}
val onReplyItemClick = remember(message) {
val onReplyItemClick = remember(message.header.messageId) {
{
hideEditMessageMenu {
onReplyClick(message)
}
}
}
val onDetailsItemClick = remember(message) {
val onDetailsItemClick = remember(message.header.messageId) {
{
hideEditMessageMenu {
onDetailsClick(message.header.messageId, message.isMyMessage)
Expand Down Expand Up @@ -132,6 +134,7 @@ fun EditMessageMenuItems(
TextMessageEditMenuItems(
isEphemeral = message.header.messageStatus.expirationStatus is ExpirationStatus.Expirable,
isUploading = message.isPending,
isComposite = isComposite,
onDeleteClick = onDeleteItemClick,
onDetailsClick = onDetailsItemClick,
onReactionClick = onReactionItemClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,20 @@ import com.wire.android.ui.edit.ReplyMessageOption
fun TextMessageEditMenuItems(
isEphemeral: Boolean,
isUploading: Boolean,
isComposite: Boolean,
onDeleteClick: () -> Unit,
onDetailsClick: () -> Unit,
onReplyClick: () -> Unit,
onCopyClick: () -> Unit,
onCopyClick: (() -> Unit)?,
onReactionClick: (String) -> Unit,
onEditClick: (() -> Unit)? = null
): List<@Composable () -> Unit> {
return buildList {
if (!isUploading) {
if (!isEphemeral) add { ReactionOption(onReactionClick) }
if (!isEphemeral && !isComposite) add { ReactionOption(onReactionClick) }
add { MessageDetailsMenuOption(onDetailsClick) }
if (!isEphemeral) add { CopyItemMenuOption(onCopyClick) }
if (!isEphemeral) add { ReplyMessageOption(onReplyClick) }
onCopyClick?.also { add { CopyItemMenuOption(it) } }
if (!isEphemeral && !isComposite) add { ReplyMessageOption(onReplyClick) }
if (!isEphemeral && onEditClick != null) add { EditMessageMenuOption(onEditClick) }
}
add { DeleteItemMenuOption(onDeleteClick) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import com.sebaslogen.resaca.hilt.hiltViewModelScoped
import com.wire.android.model.Clickable
import com.wire.android.model.ImageAsset
import com.wire.android.navigation.EXTRA_MESSAGE_ID
import com.wire.android.ui.common.button.WireButtonState
import com.wire.android.ui.common.button.WireSecondaryButton
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.home.conversations.CompositeMessageViewModel
import com.wire.android.ui.home.conversations.model.messagetypes.asset.MessageAsset
import com.wire.android.ui.home.conversations.model.messagetypes.image.DisplayableImageMessage
import com.wire.android.ui.home.conversations.model.messagetypes.image.ImageMessageFailed
Expand Down Expand Up @@ -80,9 +84,7 @@ internal fun MessageBody(
isAvailable: Boolean,
onLongClick: (() -> Unit)? = null,
onOpenProfile: (String) -> Unit,
buttonList: List<MessageButton>?,
pendingButton: String?,
onButtonClick: ((messageId: String, buttonId: String) -> Unit)?
buttonList: List<MessageButton>?
) {
val (displayMentions, text) = messageBody?.message?.let {
mapToDisplayMentions(it, LocalContext.current.resources)
Expand Down Expand Up @@ -110,19 +112,21 @@ internal fun MessageBody(
MessageButtonsContent(
messageId = messageId,
buttonList = it,
onClick = onButtonClick,
pendingButton = pendingButton
)
}
}

@Composable
fun MessageButtonsContent(
messageId: String,
pendingButton: String?,
buttonList: List<MessageButton>,
onClick: ((messageId: String, buttonId: String) -> Unit)?
) {
val viewModel = hiltViewModelScoped<CompositeMessageViewModel>(
key = "${CompositeMessageViewModel.ARGS_KEY}$messageId",
defaultArguments = bundleOf(
EXTRA_MESSAGE_ID to messageId,
)
)
Column(
modifier = Modifier
.wrapContentSize()
Expand All @@ -131,10 +135,9 @@ fun MessageButtonsContent(
for (index in buttonList.indices) {
val button = buttonList[index]
MessageButtonItem(
messageId = messageId,
pendingButtonId = pendingButton,
pendingButtonId = viewModel.pendingButtonId,
MohamadJaara marked this conversation as resolved.
Show resolved Hide resolved
button = button,
onClick = onClick
onClick = viewModel::onButtonClicked
MohamadJaara marked this conversation as resolved.
Show resolved Hide resolved
)
if (index != buttonList.lastIndex) {
Spacer(modifier = Modifier.padding(top = dimensions().spacing8x))
Expand All @@ -146,15 +149,14 @@ fun MessageButtonsContent(
@SuppressLint("RememberReturnType")
@Composable
fun MessageButtonItem(
messageId: String,
button: MessageButton,
pendingButtonId: MessageButtonId?,
onClick: ((messageId: String, buttonId: String) -> Unit)?
onClick: ((buttonId: String) -> Unit)?
MohamadJaara marked this conversation as resolved.
Show resolved Hide resolved
) {
val onCLick = remember(button.isSelected) {
onClick?.let {
if (!button.isSelected) {
{ onClick(messageId, button.id) }
{ onClick(button.id) }
} else {
{ }
}
Expand Down
Loading
Loading