Skip to content

Commit 5bbbb4b

Browse files
authored
Merge pull request #5830 from nextcloud/fix/5826/supportMarkdown
support markdown and make links clickable for scheduled messages
2 parents d5ab3d4 + 6e21704 commit 5bbbb4b

1 file changed

Lines changed: 117 additions & 53 deletions

File tree

app/src/main/java/com/nextcloud/talk/chat/ScheduledMessagesActivity.kt

Lines changed: 117 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ package com.nextcloud.talk.chat
99

1010
import android.content.Intent
1111
import android.os.Bundle
12+
import android.text.method.LinkMovementMethod
13+
import android.text.util.Linkify
14+
import android.widget.TextView
1215
import androidx.activity.compose.setContent
1316
import androidx.compose.foundation.background
1417
import androidx.compose.foundation.combinedClickable
@@ -38,12 +41,14 @@ import androidx.compose.material.icons.outlined.Check
3841
import androidx.compose.material.icons.outlined.Close
3942
import androidx.compose.material.icons.outlined.Delete
4043
import androidx.compose.material.icons.outlined.Edit
44+
import androidx.compose.material.icons.outlined.Forum
4145
import androidx.compose.material.icons.outlined.InsertEmoticon
4246
import androidx.compose.material.icons.outlined.Keyboard
4347
import androidx.compose.material.icons.outlined.NotificationsOff
4448
import androidx.compose.material3.ExperimentalMaterial3Api
4549
import androidx.compose.material3.Icon
4650
import androidx.compose.material3.IconButton
51+
import androidx.compose.material3.LocalContentColor
4752
import androidx.compose.material3.MaterialTheme
4853
import androidx.compose.material3.ModalBottomSheet
4954
import androidx.compose.material3.Scaffold
@@ -67,6 +72,7 @@ import androidx.compose.ui.draw.clip
6772
import androidx.compose.ui.draw.drawBehind
6873
import androidx.compose.ui.geometry.Offset
6974
import androidx.compose.ui.graphics.Color
75+
import androidx.compose.ui.graphics.toArgb
7076
import androidx.compose.ui.graphics.vector.ImageVector
7177
import androidx.compose.ui.platform.LocalContext
7278
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@@ -103,6 +109,7 @@ import com.nextcloud.talk.utils.DateUtils
103109
import com.nextcloud.talk.utils.bundle.BundleKeys
104110
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
105111
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_THREAD_ID
112+
import com.nextcloud.talk.utils.message.MessageUtils
106113
import com.vanniktech.emoji.EmojiEditText
107114
import com.vanniktech.emoji.EmojiPopup
108115
import java.time.Instant
@@ -166,15 +173,26 @@ class ScheduledMessagesActivity : BaseActivity() {
166173
edit(message, sendAt, user)
167174
},
168175
onDeleteScheduledMessage = { message -> deleteScheduledMessage(message, user) },
169-
onOpenParentMessage = { messageId, threadId ->
170-
openParentMessage(messageId, threadId)
176+
onOpenParentMessage = { messageId ->
177+
openParentMessage(messageId)
178+
},
179+
onOpenThread = { threadId ->
180+
openThread(threadId)
171181
}
172182
)
173183
}
174184
}
175185
}
176186
}
177187

188+
private fun openThread(threadId: Long) {
189+
val intent = Intent(this, ChatActivity::class.java).apply {
190+
putExtra(KEY_ROOM_TOKEN, roomToken)
191+
putExtra(KEY_THREAD_ID, threadId)
192+
}
193+
startActivity(intent)
194+
}
195+
178196
private fun loadScheduledMessages(user: User) {
179197
scheduledMessagesViewModel.loadScheduledMessages(
180198
user.getCredentials(),
@@ -256,7 +274,8 @@ class ScheduledMessagesActivity : BaseActivity() {
256274
onReschedule: (ChatMessage, Int) -> Unit,
257275
onEdit: (ChatMessage, Int) -> Unit,
258276
onDeleteScheduledMessage: (ChatMessage) -> Unit,
259-
onOpenParentMessage: (Long?, Long?) -> Unit
277+
onOpenParentMessage: (Long?) -> Unit,
278+
onOpenThread: (Long) -> Unit
260279
) {
261280
val snackBarHostState = remember { SnackbarHostState() }
262281
val scheduledState by scheduledMessagesViewModel.getScheduledMessagesState.collectAsStateWithLifecycle()
@@ -424,20 +443,16 @@ class ScheduledMessagesActivity : BaseActivity() {
424443
)
425444
}
426445
}
427-
428446
val parentMessage = parentId?.let { parentMessages[it] }
429-
430447
ScheduledMessageBubble(
431448
message = message,
432449
parentMessage = parentMessage,
433450
dateUtils = dateUtils,
434451
viewThemeUtils = viewThemeUtils,
435452
onClick = {
436453
val parentId = message.parentMessageId
437-
if (message.threadId != null) {
438-
onOpenParentMessage(parentId, message.threadId)
439-
} else if (parentId != null) {
440-
onOpenParentMessage(parentId, message.threadId)
454+
if (parentId != null) {
455+
onOpenParentMessage(parentId)
441456
}
442457
},
443458
onLongPress = {
@@ -520,6 +535,12 @@ class ScheduledMessagesActivity : BaseActivity() {
520535
val message = selectedMessage ?: return@ScheduledMessageActionsSheet
521536
onDeleteScheduledMessage(message)
522537
showActionsSheet = false
538+
},
539+
showOpenThreadAction = selectedMessage?.threadId != null && selectedMessage?.threadId!! > 0,
540+
onOpenThread = {
541+
val threadId = selectedMessage?.threadId ?: return@ScheduledMessageActionsSheet
542+
onOpenThread(threadId)
543+
showActionsSheet = false
523544
}
524545
)
525546
}
@@ -622,15 +643,16 @@ class ScheduledMessagesActivity : BaseActivity() {
622643
val context = LocalContext.current
623644
val scheduledAt = message.sendAt?.toLong() ?: message.timestamp
624645
val timeText = dateUtils.getLocalTimeStringFromTimestamp(scheduledAt)
625-
val text = ChatUtils.getParsedMessage(message.message, message.messageParameters).orEmpty()
646+
val messageTextColor = LocalContentColor.current.toArgb()
626647

627648
val bubbleColor = remember(context, message.isDeleted, viewThemeUtils) {
628649
Color(viewThemeUtils.talk.getOutgoingMessageBubbleColor(context, message.isDeleted, false))
629650
}
630651

631-
val isClickable = remember(message.threadTitle, parentMessage) {
632-
!message.threadTitle.isNullOrBlank() || parentMessage != null
652+
val isClickable = remember(parentMessage) {
653+
parentMessage != null
633654
}
655+
634656
Row(
635657
modifier = Modifier
636658
.fillMaxWidth()
@@ -646,11 +668,13 @@ class ScheduledMessagesActivity : BaseActivity() {
646668
.then(
647669
if (isClickable) {
648670
Modifier.combinedClickable(
671+
enabled = true,
649672
onClick = onClick,
650673
onLongClick = onLongPress
651674
)
652675
} else {
653676
Modifier.combinedClickable(
677+
enabled = true,
654678
onClick = {},
655679
onLongClick = onLongPress
656680
)
@@ -660,7 +684,6 @@ class ScheduledMessagesActivity : BaseActivity() {
660684
val strokeColor = MaterialTheme.colorScheme.primary
661685
Column(modifier = Modifier.padding(8.dp)) {
662686
parentMessage?.let { parent ->
663-
664687
if (!message.threadTitle.isNullOrBlank()) {
665688
Row(
666689
verticalAlignment = Alignment.CenterVertically,
@@ -677,44 +700,79 @@ class ScheduledMessagesActivity : BaseActivity() {
677700
style = MaterialTheme.typography.labelSmall.copy(fontWeight = FontWeight.Bold)
678701
)
679702
}
680-
} else {
681-
Row(
682-
modifier = Modifier
683-
.fillMaxWidth()
684-
.padding(bottom = 4.dp)
685-
.clip(RoundedCornerShape(4.dp))
686-
.background(MaterialTheme.colorScheme.background, MaterialTheme.shapes.small)
687-
.drawBehind {
688-
val strokeWidth = 3.dp.toPx()
689-
drawLine(
690-
color = strokeColor,
691-
start = Offset(strokeWidth / 2, 0f),
692-
end = Offset(strokeWidth / 2, size.height),
693-
strokeWidth = strokeWidth
694-
)
695-
}
696-
.padding(start = 12.dp, top = 4.dp, bottom = 4.dp, end = 8.dp)
697-
) {
698-
Column(modifier = Modifier.weight(1f)) {
699-
Text(
700-
text = parent.actorDisplayName ?: "Unknown",
701-
style = MaterialTheme.typography.labelSmall.copy(
702-
fontWeight = FontWeight.Bold
703-
)
704-
)
705-
Text(
706-
text = ChatUtils.getParsedMessage(
707-
parent.message,
708-
parent.messageParameters
709-
).orEmpty(),
710-
style = MaterialTheme.typography.bodySmall
703+
}
704+
Row(
705+
modifier = Modifier
706+
.fillMaxWidth()
707+
.padding(bottom = 4.dp)
708+
.clip(RoundedCornerShape(4.dp))
709+
.background(MaterialTheme.colorScheme.background, MaterialTheme.shapes.small)
710+
.drawBehind {
711+
val strokeWidth = 3.dp.toPx()
712+
drawLine(
713+
color = strokeColor,
714+
start = Offset(strokeWidth / 2, 0f),
715+
end = Offset(strokeWidth / 2, size.height),
716+
strokeWidth = strokeWidth
711717
)
712718
}
719+
.padding(start = 12.dp, top = 4.dp, bottom = 4.dp, end = 8.dp)
720+
) {
721+
Column(modifier = Modifier.weight(1f)) {
722+
Text(
723+
text = parent.actorDisplayName ?: "Unknown",
724+
style = MaterialTheme.typography.labelSmall.copy(
725+
fontWeight = FontWeight.Bold
726+
)
727+
)
728+
val parentMessage = ChatUtils.getParsedMessage(
729+
parent.message,
730+
parent.messageParameters
731+
).orEmpty()
732+
733+
AndroidView(
734+
factory = { androidContext ->
735+
TextView(androidContext).apply {
736+
movementMethod = LinkMovementMethod.getInstance()
737+
linksClickable = true
738+
}
739+
},
740+
update = { textView ->
741+
textView.setTextColor(messageTextColor)
742+
textView.text = MessageUtils(context).getRenderedMarkdownText(
743+
context,
744+
parentMessage,
745+
messageTextColor
746+
)
747+
Linkify.addLinks(textView, 0)
748+
}
749+
)
713750
}
714751
}
715752
}
753+
val messageTextColor = LocalContentColor.current.toArgb()
754+
val chatMessage = ChatUtils.getParsedMessage(
755+
message.message,
756+
message.messageParameters
757+
).orEmpty()
716758

717-
Text(text = text, style = MaterialTheme.typography.bodyMedium)
759+
AndroidView(
760+
factory = { androidContext ->
761+
TextView(androidContext).apply {
762+
movementMethod = LinkMovementMethod.getInstance()
763+
linksClickable = true
764+
}
765+
},
766+
update = { textView ->
767+
textView.setTextColor(messageTextColor)
768+
textView.text = MessageUtils(context).getRenderedMarkdownText(
769+
context,
770+
chatMessage,
771+
messageTextColor
772+
)
773+
Linkify.addLinks(textView, Linkify.ALL)
774+
}
775+
)
718776
Spacer(Modifier.height(4.dp))
719777

720778
Row(
@@ -860,7 +918,6 @@ class ScheduledMessagesActivity : BaseActivity() {
860918
if (it.text.toString() != editValue.text) {
861919
it.setText(editValue.text)
862920
}
863-
it.setSelection(editValue.text.length)
864921
}
865922
)
866923

@@ -889,13 +946,16 @@ class ScheduledMessagesActivity : BaseActivity() {
889946
}
890947
}
891948

949+
@Suppress("LongParameterList")
892950
@Composable
893951
private fun ScheduledMessageActionsSheet(
894952
scheduledTime: String,
895953
onReschedule: () -> Unit,
896954
onSendNow: () -> Unit,
897955
onEdit: () -> Unit,
898-
onDelete: () -> Unit
956+
onDelete: () -> Unit,
957+
showOpenThreadAction: Boolean,
958+
onOpenThread: () -> Unit
899959
) {
900960
Column(modifier = Modifier.fillMaxWidth()) {
901961
Column(
@@ -925,19 +985,23 @@ class ScheduledMessagesActivity : BaseActivity() {
925985
text = stringResource(R.string.nc_send_now),
926986
onClick = onSendNow
927987
)
988+
if (showOpenThreadAction) {
989+
ActionRow(
990+
icon = Icons.Outlined.Forum,
991+
text = stringResource(R.string.open_thread),
992+
onClick = onOpenThread
993+
)
994+
}
928995
ActionRow(icon = Icons.Outlined.Edit, text = stringResource(R.string.nc_edit), onClick = onEdit)
929996
ActionRow(icon = Icons.Outlined.Delete, text = stringResource(R.string.nc_delete), onClick = onDelete)
930997
}
931998
}
932999

933-
private fun openParentMessage(messageId: Long?, threadId: Long?) {
1000+
private fun openParentMessage(messageId: Long?) {
9341001
val intent = Intent(this, ChatActivity::class.java).apply {
9351002
putExtra(KEY_ROOM_TOKEN, roomToken)
936-
if (threadId != null && threadId > 0) {
937-
putExtra(KEY_THREAD_ID, threadId)
938-
} else {
939-
messageId?.let { putExtra(BundleKeys.KEY_MESSAGE_ID, it.toString()) }
940-
}
1003+
1004+
messageId?.let { putExtra(BundleKeys.KEY_MESSAGE_ID, it.toString()) }
9411005
}
9421006
startActivity(intent)
9431007
}

0 commit comments

Comments
 (0)