diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index 371285a602..3c0f1f7769 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -671,7 +671,7 @@ fun ChatLayout( }) { ChatItemsList( remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue, - useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, loadMessages, deleteMessage, deleteMessages, + useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, showChatInfo = info, loadMessages, deleteMessage, deleteMessages, receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem, updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember, setReaction, showItemDetails, markItemsRead, markChatRead, remember { { onComposed(it) } }, developerTools, showViaProxy, @@ -944,6 +944,7 @@ fun BoxScope.ChatItemsList( linkMode: SimplexLinkMode, selectedChatItems: MutableState?>, showMemberInfo: (GroupInfo, GroupMember) -> Unit, + showChatInfo: () -> Unit, loadMessages: suspend (ChatId, ChatPagination, ActiveChatState, visibleItemIndexesNonReversed: () -> IntRange) -> Unit, deleteMessage: (Long, CIDeleteMode) -> Unit, deleteMessages: (List) -> Unit, @@ -1071,7 +1072,7 @@ fun BoxScope.ChatItemsList( highlightedItems.value = setOf() } } - ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp) + ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, showChatInfo = showChatInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt index bf871ab626..22842eb350 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt @@ -24,12 +24,12 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.* import chat.simplex.common.model.* import chat.simplex.common.model.ChatModel.controller +import chat.simplex.common.model.ChatModel.currentUser import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chat.* import chat.simplex.common.views.helpers.* import chat.simplex.res.MR -import kotlinx.coroutines.launch import kotlinx.datetime.Clock import kotlin.math.* @@ -51,6 +51,12 @@ fun chatEventText(eventText: String, ts: String): AnnotatedString = withStyle(chatEventStyle) { append("$eventText $ts") } } +data class ChatItemReactionMenuItem ( + val name: String, + val image: String?, + val onClick: (() -> Unit)? +) + @Composable fun ChatItemView( rhId: Long?, @@ -87,6 +93,7 @@ fun ChatItemView( showItemDetails: (ChatInfo, ChatItem) -> Unit, reveal: (Boolean) -> Unit, showMemberInfo: (GroupInfo, GroupMember) -> Unit, + showChatInfo: () -> Unit, developerTools: Boolean, showViaProxy: Boolean, showTimestamp: Boolean, @@ -120,7 +127,7 @@ fun ChatItemView( Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.chatItemOffset(cItem, itemSeparation.largeGap, inverted = true, revealed = true)) { cItem.reactions.forEach { r -> val showReactionMenu = remember { mutableStateOf(false) } - val reactionMembers = remember { mutableStateOf(emptyList()) } + val reactionMenuItems = remember { mutableStateOf(emptyList()) } val interactionSource = remember { MutableInteractionSource() } val enterInteraction = remember { HoverInteraction.Enter() } KeyChangeEffect(highlighted.value) { @@ -134,18 +141,39 @@ fun ChatItemView( var modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp).clip(RoundedCornerShape(8.dp)) if (cInfo.featureEnabled(ChatFeature.Reactions)) { fun showReactionsMenu() { - if (cInfo is ChatInfo.Group) { - withBGApi { - try { - val members = controller.apiGetReactionMembers(rhId, cInfo.groupInfo.groupId, cItem.id, r.reaction) - if (members != null) { - showReactionMenu.value = true - reactionMembers.value = members + when (cInfo) { + is ChatInfo.Group -> { + withBGApi { + try { + val members = controller.apiGetReactionMembers(rhId, cInfo.groupInfo.groupId, cItem.id, r.reaction) + if (members != null) { + showReactionMenu.value = true + reactionMenuItems.value = members.map { + val enabled = cInfo.groupInfo.membership.groupMemberId != it.groupMember.groupMemberId + val click = if (enabled) ({ showMemberInfo(cInfo.groupInfo, it.groupMember) }) else null + ChatItemReactionMenuItem(it.groupMember.displayName, it.groupMember.image, click) + } + } + } catch (e: Exception) { + Log.d(TAG, "chatItemView ChatItemReactions onLongClick: unexpected exception: ${e.stackTraceToString()}") } - } catch (e: Exception) { - Log.d(TAG, "hatItemView ChatItemReactions onLongClick: unexpected exception: ${e.stackTraceToString()}") } } + is ChatInfo.Direct -> { + showReactionMenu.value = true + val reactions = mutableListOf() + + if (!r.userReacted || r.totalReacted > 1) { + val contact = cInfo.contact + reactions.add(ChatItemReactionMenuItem(contact.displayName, contact.image, showChatInfo)) + } + + if (r.userReacted) { + reactions.add(ChatItemReactionMenuItem(generalGetString(MR.strings.sender_you_pronoun), currentUser.value?.image, null)) + } + reactionMenuItems.value = reactions + } + else -> {} } } modifier = modifier @@ -166,19 +194,19 @@ fun ChatItemView( Row(modifier.padding(2.dp), verticalAlignment = Alignment.CenterVertically) { ReactionIcon(r.reaction.text, fontSize = 12.sp) DefaultDropdownMenu(showMenu = showReactionMenu) { - reactionMembers.value.forEach { m -> + reactionMenuItems.value.forEach { m -> ItemAction( - text = m.groupMember.displayName, - composable = { ProfileImage(44.dp, m.groupMember.image) }, + text = m.name, + composable = { ProfileImage(44.dp, m.image) }, onClick = { - if (cInfo is ChatInfo.Group && cInfo.groupInfo.membership.groupMemberId != m.groupMember.groupMemberId) { - showMemberInfo(cInfo.groupInfo, m.groupMember) - showReactionMenu.value = false - } else { + val click = m.onClick + if (click != null) { + click() showReactionMenu.value = false } }, - lineLimit = 1 + lineLimit = 1, + color = if (m.onClick == null) MaterialTheme.colors.secondary else MenuTextColor ) } } @@ -1188,6 +1216,7 @@ fun PreviewChatItemView( showItemDetails = { _, _ -> }, reveal = {}, showMemberInfo = { _, _ ->}, + showChatInfo = {}, developerTools = false, showViaProxy = false, showTimestamp = true, @@ -1233,6 +1262,7 @@ fun PreviewChatItemViewDeletedContent() { showItemDetails = { _, _ -> }, reveal = {}, showMemberInfo = { _, _ ->}, + showChatInfo = {}, developerTools = false, showViaProxy = false, preview = true,