From bb944ce91bb7e3a14aaa1bd9bbab3ba754cd5959 Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Fri, 29 May 2026 13:07:32 -0500 Subject: [PATCH] Allowing Markdown in Pinned Messages Signed-off-by: rapterjet2004 --- .../talk/chat/ui/model/ChatMessageUi.kt | 32 +++ .../com/nextcloud/talk/ui/PinnedMessage.kt | 203 +++++++++--------- 2 files changed, 139 insertions(+), 96 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt index ea5f4ea6c8..7ae804d856 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ui/model/ChatMessageUi.kt @@ -192,6 +192,38 @@ fun ChatMessage.toScheduledMessageUiModel( ) } +fun ChatMessage.toIncompleteMessageUiModel(roomToken: String, baseUrl: String): ChatMessageUi { + val sendAtTimestamp = sendAt?.toLong() ?: timestamp + return ChatMessageUi( + id = token?.toIntOrNull() ?: token.hashCode(), + message = getRichText(), + plainMessage = message.orEmpty(), + renderMarkdown = renderMarkdown != false, + actorDisplayName = actorDisplayName.orEmpty(), + isThread = isThread, + threadTitle = threadTitle.orEmpty(), + threadReplies = threadReplies ?: 0, + incoming = false, + isDeleted = isDeleted, + avatarUrl = avatarUrl, + statusIcon = MessageStatusIcon.SCHEDULED, + timestamp = sendAtTimestamp, + date = Instant.ofEpochSecond(sendAtTimestamp).atZone(ZoneId.systemDefault()).toLocalDate(), + content = null, + roomToken = roomToken, + activeUserId = null, + activeUserBaseUrl = baseUrl, + messageParameters = normalizeMessageParameters(), + reactions = emptyList(), + isEdited = lastEditTimestamp != 0L, + parentMessage = null, + replyable = false, + isGrouped = false, + isGroupedWithNext = false, + isSilent = silent + ) +} + private fun ChatMessage.normalizeMessageParameters(): Map> = messageParameters .orEmpty() diff --git a/app/src/main/java/com/nextcloud/talk/ui/PinnedMessage.kt b/app/src/main/java/com/nextcloud/talk/ui/PinnedMessage.kt index 231cb7e6e6..79eda88dc7 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/PinnedMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/PinnedMessage.kt @@ -29,6 +29,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -46,11 +47,14 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.nextcloud.talk.R import com.nextcloud.talk.chat.data.model.ChatMessage +import com.nextcloud.talk.chat.ui.model.toIncompleteMessageUiModel import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.ConversationEnums import com.nextcloud.talk.models.json.participants.Participant +import com.nextcloud.talk.ui.chat.EnrichedText +import com.nextcloud.talk.ui.theme.LocalViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.preview.ComposePreviewUtils @@ -119,121 +123,128 @@ fun PinnedMessageView( val interactionSource = remember { MutableInteractionSource() } - Box( - modifier = Modifier - .fillMaxWidth() - .padding(4.dp) - .shadow( - elevation = ELEVATION, - shape = RoundedCornerShape(CORNER_RADIUS.dp), - clip = false - ) - .background( - incomingBubbleColor, - RoundedCornerShape(CORNER_RADIUS.dp) - ) - .padding(SPACE_16.dp, SPACE_0.dp, SPACE_0.dp, SPACE_16.dp) - .heightIn(max = MAX_HEIGHT.dp) - .clickable( - interactionSource = interactionSource, - indication = null - ) { - scrollToMessageWithIdWithOffset(message.jsonMessageId.toString()) - } + CompositionLocalProvider( + LocalViewThemeUtils provides viewThemeUtils ) { - var expanded by remember { mutableStateOf(false) } - val pinnedUntilStr = stringResource(R.string.pinned_until) - val untilUnpin = stringResource(R.string.until_unpin) - val pinnedText = remember(message.pinnedUntil) { - message.pinnedUntil?.let { - val format = if (DateFormat.is24HourFormat(context)) { - "MMM dd yyyy, HH:mm" - } else { - "MMM dd yyyy, hh:mm a" + Box( + modifier = Modifier + .fillMaxWidth() + .padding(4.dp) + .shadow( + elevation = ELEVATION, + shape = RoundedCornerShape(CORNER_RADIUS.dp), + clip = false + ) + .background( + incomingBubbleColor, + RoundedCornerShape(CORNER_RADIUS.dp) + ) + .padding(SPACE_16.dp, SPACE_0.dp, SPACE_0.dp, SPACE_16.dp) + .heightIn(max = MAX_HEIGHT.dp) + .clickable( + interactionSource = interactionSource, + indication = null + ) { + scrollToMessageWithIdWithOffset(message.jsonMessageId.toString()) } + ) { + var expanded by remember { mutableStateOf(false) } + val pinnedUntilStr = stringResource(R.string.pinned_until) + val untilUnpin = stringResource(R.string.until_unpin) + val pinnedText = remember(message.pinnedUntil) { + message.pinnedUntil?.let { + val format = if (DateFormat.is24HourFormat(context)) { + "MMM dd yyyy, HH:mm" + } else { + "MMM dd yyyy, hh:mm a" + } - val localDateTime = Instant.ofEpochSecond(it) - .atZone(ZoneId.systemDefault()) - .toLocalDateTime() + val localDateTime = Instant.ofEpochSecond(it) + .atZone(ZoneId.systemDefault()) + .toLocalDateTime() - val timeString = localDateTime.format(DateTimeFormatter.ofPattern(format)) + val timeString = localDateTime.format(DateTimeFormatter.ofPattern(format)) - "$pinnedUntilStr $timeString" - } ?: untilUnpin - } - - Column( - modifier = Modifier - .verticalScroll(scrollState) - .padding(top = SPACE_16.dp, end = 40.dp) - ) { - Text( - text = pinnedHeadline, - color = colorScheme.onSurfaceVariant, - style = MaterialTheme.typography.labelMedium - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = message.getRichText(), - color = colorScheme.onSurface - ) - } + "$pinnedUntilStr $timeString" + } ?: untilUnpin + } - Box( - modifier = Modifier - .align(Alignment.TopEnd) - .padding(top = 2.dp) - ) { - IconButton(onClick = { expanded = true }) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert_24px), - contentDescription = stringResource(R.string.pinned_message_options), - tint = highEmphasisColor + Column( + modifier = Modifier + .verticalScroll(scrollState) + .padding(top = SPACE_16.dp, end = 40.dp) + ) { + Text( + text = pinnedHeadline, + color = colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.labelMedium + ) + Spacer(modifier = Modifier.height(4.dp)) + EnrichedText( + message = message.toIncompleteMessageUiModel( + currentConversation!!.token, + currentConversation.remoteServer!! + ), + Modifier ) } - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.background(outgoingBubbleColor) + Box( + modifier = Modifier + .align(Alignment.TopEnd) + .padding(top = 2.dp) ) { - DropdownMenuItem( - text = { - Text( - text = pinnedText, - color = highEmphasisColor - ) - }, - onClick = {}, - enabled = false - ) + IconButton(onClick = { expanded = true }) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert_24px), + contentDescription = stringResource(R.string.pinned_message_options), + tint = highEmphasisColor + ) + } - HorizontalDivider() + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.background(outgoingBubbleColor) + ) { + DropdownMenuItem( + text = { + Text( + text = pinnedText, + color = highEmphasisColor + ) + }, + onClick = {}, + enabled = false + ) - DropdownMenuItem( - text = { Text(stringResource(R.string.pinned_go_to_message), color = highEmphasisColor) }, - onClick = { - expanded = false - scrollToMessageWithIdWithOffset(message.jsonMessageId.toString()) - } - ) + HorizontalDivider() - DropdownMenuItem( - text = { Text(stringResource(R.string.pinned_dismiss), color = highEmphasisColor) }, - onClick = { - expanded = false - hidePinnedMessage(message) - } - ) + DropdownMenuItem( + text = { Text(stringResource(R.string.pinned_go_to_message), color = highEmphasisColor) }, + onClick = { + expanded = false + scrollToMessageWithIdWithOffset(message.jsonMessageId.toString()) + } + ) - if (canPin) { DropdownMenuItem( - text = { Text(stringResource(R.string.unpin_message), color = highEmphasisColor) }, + text = { Text(stringResource(R.string.pinned_dismiss), color = highEmphasisColor) }, onClick = { expanded = false - unPinMessage(message) + hidePinnedMessage(message) } ) + + if (canPin) { + DropdownMenuItem( + text = { Text(stringResource(R.string.unpin_message), color = highEmphasisColor) }, + onClick = { + expanded = false + unPinMessage(message) + } + ) + } } } }