99
1010package com.nextcloud.client.assistant.chat
1111
12+ import android.content.ClipData
1213import androidx.compose.animation.core.LinearEasing
1314import androidx.compose.animation.core.RepeatMode
1415import androidx.compose.animation.core.animateFloat
@@ -17,6 +18,7 @@ import androidx.compose.animation.core.rememberInfiniteTransition
1718import androidx.compose.animation.core.tween
1819import androidx.compose.foundation.Image
1920import androidx.compose.foundation.background
21+ import androidx.compose.foundation.combinedClickable
2022import androidx.compose.foundation.layout.Arrangement
2123import androidx.compose.foundation.layout.Box
2224import androidx.compose.foundation.layout.Column
@@ -36,18 +38,26 @@ import androidx.compose.foundation.shape.CircleShape
3638import androidx.compose.foundation.shape.RoundedCornerShape
3739import androidx.compose.material3.Button
3840import androidx.compose.material3.CircularProgressIndicator
41+ import androidx.compose.material3.DropdownMenu
42+ import androidx.compose.material3.DropdownMenuItem
3943import androidx.compose.material3.Icon
4044import androidx.compose.material3.MaterialTheme
4145import androidx.compose.material3.Text
4246import androidx.compose.runtime.Composable
4347import androidx.compose.runtime.LaunchedEffect
4448import androidx.compose.runtime.collectAsState
4549import androidx.compose.runtime.getValue
50+ import androidx.compose.runtime.mutableStateOf
51+ import androidx.compose.runtime.remember
52+ import androidx.compose.runtime.rememberCoroutineScope
53+ import androidx.compose.runtime.setValue
4654import androidx.compose.ui.Alignment
4755import androidx.compose.ui.Modifier
4856import androidx.compose.ui.draw.clip
4957import androidx.compose.ui.draw.scale
5058import androidx.compose.ui.layout.ContentScale
59+ import androidx.compose.ui.platform.LocalClipboard
60+ import androidx.compose.ui.platform.toClipEntry
5161import androidx.compose.ui.res.colorResource
5262import androidx.compose.ui.res.painterResource
5363import androidx.compose.ui.res.stringResource
@@ -60,6 +70,7 @@ import com.nextcloud.utils.TimeConstants
6070import com.nextcloud.utils.extensions.time
6171import com.owncloud.android.R
6272import com.owncloud.android.lib.resources.assistant.chat.model.ChatMessage
73+ import kotlinx.coroutines.launch
6374import java.time.Instant
6475
6576private val MIN_CHAT_HEIGHT = 60 .dp
@@ -340,6 +351,32 @@ private fun TypingAnimation() {
340351 }
341352}
342353
354+ @Composable
355+ private fun CopyableMessageBubble (text : String , modifier : Modifier = Modifier , content : @Composable () -> Unit ) {
356+ var showMenu by remember { mutableStateOf(false ) }
357+ val clipboard = LocalClipboard .current
358+ val scope = rememberCoroutineScope()
359+
360+ Box (
361+ modifier = modifier
362+ .combinedClickable(onClick = {}, onLongClick = { showMenu = true })
363+ ) {
364+ content()
365+ DropdownMenu (expanded = showMenu, onDismissRequest = { showMenu = false }) {
366+ DropdownMenuItem (
367+ text = { Text (stringResource(R .string.common_copy)) },
368+ onClick = {
369+ scope.launch {
370+ val plainText = ClipData .newPlainText(null , text).toClipEntry()
371+ clipboard.setClipEntry(plainText)
372+ }
373+ showMenu = false
374+ }
375+ )
376+ }
377+ }
378+ }
379+
343380@Composable
344381private fun AssistantMessageItem (message : ChatMessage ) {
345382 Box (
@@ -363,7 +400,8 @@ private fun AssistantMessageItem(message: ChatMessage) {
363400 alignment = Alignment .Center
364401 )
365402 }
366- Box (
403+ CopyableMessageBubble (
404+ text = message.content,
367405 modifier = Modifier
368406 .padding(start = 8 .dp, end = 16 .dp)
369407 .defaultMinSize(minHeight = MIN_CHAT_HEIGHT )
@@ -390,7 +428,8 @@ private fun UserMessageItem(message: ChatMessage) {
390428 .fillMaxWidth(),
391429 contentAlignment = Alignment .CenterEnd
392430 ) {
393- Box (
431+ CopyableMessageBubble (
432+ text = message.content,
394433 modifier = Modifier
395434 .padding(start = 16 .dp, end = 8 .dp)
396435 .defaultMinSize(minHeight = MIN_CHAT_HEIGHT )
0 commit comments