android: add quoted message to chat item info (#2688)

This commit is contained in:
spaced4ndy
2023-07-13 11:10:34 +04:00
committed by GitHub
parent 2e5a0fca1a
commit 43ceb184c4
4 changed files with 190 additions and 32 deletions
@@ -1832,7 +1832,7 @@ class CIQuote (
fun sender(membership: GroupMember?): String? = when (chatDir) {
is CIDirection.DirectSnd -> generalGetString(MR.strings.sender_you_pronoun)
is CIDirection.DirectRcv -> null
is CIDirection.GroupSnd -> membership?.displayName
is CIDirection.GroupSnd -> membership?.displayName ?: generalGetString(MR.strings.sender_you_pronoun)
is CIDirection.GroupRcv -> chatDir.groupMember.displayName
null -> null
}
@@ -7,18 +7,18 @@ import SectionView
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.CurrentColors
import chat.simplex.app.ui.theme.DEFAULT_PADDING
@@ -26,34 +26,42 @@ import chat.simplex.app.views.chat.item.ItemAction
import chat.simplex.app.views.chat.item.MarkdownText
import chat.simplex.app.views.helpers.*
import chat.simplex.res.MR
import dev.icerock.moko.resources.ImageResource
enum class CIInfoTab {
History, Quote
}
@Composable
fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
val sent = ci.chatDir.sent
val appColors = CurrentColors.collectAsState().value.appColors
val itemColor = if (sent) appColors.sentMessage else appColors.receivedMessage
val uriHandler = LocalUriHandler.current
val selection = remember { mutableStateOf(CIInfoTab.History) }
@Composable
fun TextBubble(text: String, formattedText: List<FormattedText>?, sender: String?, showMenu: MutableState<Boolean>) {
if (text != "") {
MarkdownText(
text, if (text.isEmpty()) emptyList() else formattedText,
sender = sender,
senderBold = true,
linkMode = SimplexLinkMode.DESCRIPTION, uriHandler = uriHandler,
onLinkLongClick = { showMenu.value = true }
)
} else {
Text(
generalGetString(MR.strings.item_info_no_text),
style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.secondary, lineHeight = 22.sp, fontStyle = FontStyle.Italic)
)
}
}
@Composable
fun ItemVersionView(ciVersion: ChatItemVersion, current: Boolean) {
val showMenu = remember { mutableStateOf(false) }
val text = ciVersion.msgContent.text
@Composable
fun VersionText() {
if (text != "") {
MarkdownText(
text, if (text.isEmpty()) emptyList() else ciVersion.formattedText,
linkMode = SimplexLinkMode.DESCRIPTION, uriHandler = uriHandler,
onLinkLongClick = { showMenu.value = true }
)
} else {
Text(
generalGetString(MR.strings.item_info_no_text),
style = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.secondary, lineHeight = 22.sp, fontStyle = FontStyle.Italic)
)
}
}
val itemColor = if (sent) appColors.sentMessage else appColors.receivedMessage
Column {
Box(
@@ -61,7 +69,7 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = {})
) {
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
VersionText()
TextBubble(text, ciVersion.formattedText, sender = null, showMenu)
}
}
Row(Modifier.padding(start = 12.dp, top = 3.dp, bottom = 16.dp)) {
@@ -94,7 +102,46 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
}
}
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
@Composable
fun QuotedMsgView(qi: CIQuote) {
val showMenu = remember { mutableStateOf(false) }
val text = qi.text
val quoteColor = if (qi.chatDir?.sent == true) appColors.sentMessage else appColors.receivedMessage
Column {
Box(
Modifier.clip(RoundedCornerShape(18.dp)).background(quoteColor).padding(bottom = 3.dp)
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = {})
) {
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
TextBubble(text, qi.formattedText, sender = qi.sender(null), showMenu)
}
}
Row(Modifier.padding(start = 12.dp, top = 3.dp, bottom = 16.dp)) {
Text(
localTimestamp(qi.sentAt),
fontSize = 12.sp,
color = MaterialTheme.colors.secondary,
modifier = Modifier.padding(end = 6.dp)
)
}
if (text != "") {
DefaultDropdownMenu(showMenu) {
ItemAction(stringResource(MR.strings.share_verb), painterResource(MR.images.ic_share), onClick = {
shareText(text)
showMenu.value = false
})
ItemAction(stringResource(MR.strings.copy_verb), painterResource(MR.images.ic_content_copy), onClick = {
copyText(text)
showMenu.value = false
})
}
}
}
}
@Composable
fun Details() {
AppBarTitle(stringResource(if (sent) MR.strings.sent_message else MR.strings.received_message))
SectionView {
InfoRow(stringResource(MR.strings.info_row_sent_at), localTimestamp(ci.meta.itemTs))
@@ -106,10 +153,12 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
if (itemDeleted.deletedTs != null) {
InfoRow(stringResource(MR.strings.info_row_deleted_at), localTimestamp(itemDeleted.deletedTs))
}
is CIDeleted.Moderated ->
if (itemDeleted.deletedTs != null) {
InfoRow(stringResource(MR.strings.info_row_moderated_at), localTimestamp(itemDeleted.deletedTs))
}
else -> {}
}
val deleteAt = ci.meta.itemTimed?.deleteAt
@@ -121,17 +170,105 @@ fun ChatItemInfoView(ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
InfoRow(stringResource(MR.strings.info_row_updated_at), localTimestamp(ci.meta.updatedAt))
}
}
val versions = ciInfo.itemVersions
if (versions.isNotEmpty()) {
}
@Composable
fun HistoryTab() {
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
Details()
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
Text(stringResource(MR.strings.edit_history), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = DEFAULT_PADDING))
versions.forEachIndexed { i, ciVersion ->
ItemVersionView(ciVersion, current = i == 0)
val versions = ciInfo.itemVersions
if (versions.isNotEmpty()) {
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
Text(stringResource(MR.strings.edit_history), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = DEFAULT_PADDING))
versions.forEachIndexed { i, ciVersion ->
ItemVersionView(ciVersion, current = i == 0)
}
}
} else {
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(MR.strings.no_history), color = MaterialTheme.colors.secondary)
}
}
}
SectionBottomSpacer()
}
}
@Composable
fun QuoteTab(qi: CIQuote) {
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
Details()
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
Text(stringResource(MR.strings.in_reply_to), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = DEFAULT_PADDING))
QuotedMsgView(qi)
}
SectionBottomSpacer()
}
}
@Composable
fun tabTitle(tab: CIInfoTab): String {
return when (tab) {
CIInfoTab.History -> stringResource(MR.strings.edit_history)
CIInfoTab.Quote -> stringResource(MR.strings.in_reply_to)
}
}
fun tabIcon(tab: CIInfoTab): ImageResource {
return when (tab) {
CIInfoTab.History -> MR.images.ic_history
CIInfoTab.Quote -> MR.images.ic_reply
}
}
Column {
if (ci.quotedItem != null) {
Column(
Modifier
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween
) {
Column(Modifier.weight(1f)) {
when (selection.value) {
CIInfoTab.History -> {
HistoryTab()
}
CIInfoTab.Quote -> {
QuoteTab(ci.quotedItem)
}
}
}
TabRow(
selectedTabIndex = selection.value.ordinal,
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.primary,
) {
CIInfoTab.values().forEachIndexed { index, it ->
Tab(
selected = selection.value.ordinal == index,
onClick = {
selection.value = CIInfoTab.values()[index]
},
text = { Text(tabTitle(it), fontSize = 13.sp) },
icon = {
Icon(
painterResource(tabIcon(it)),
tabTitle(it)
)
},
selectedContentColor = MaterialTheme.colors.primary,
unselectedContentColor = MaterialTheme.colors.secondary,
)
}
}
}
} else {
HistoryTab()
}
SectionBottomSpacer()
}
}
@@ -149,10 +286,12 @@ fun itemInfoShareText(ci: ChatItem, chatItemInfo: ChatItemInfo, devTools: Boolea
if (itemDeleted.deletedTs != null) {
shareText.add(String.format(generalGetString(MR.strings.share_text_deleted_at), localTimestamp(itemDeleted.deletedTs)))
}
is CIDeleted.Moderated ->
if (itemDeleted.deletedTs != null) {
shareText.add(String.format(generalGetString(MR.strings.share_text_moderated_at), localTimestamp(itemDeleted.deletedTs)))
}
else -> {}
}
val deleteAt = ci.meta.itemTimed?.deleteAt
@@ -163,6 +302,21 @@ fun itemInfoShareText(ci: ChatItem, chatItemInfo: ChatItemInfo, devTools: Boolea
shareText.add(String.format(generalGetString(MR.strings.share_text_database_id), meta.itemId))
shareText.add(String.format(generalGetString(MR.strings.share_text_updated_at), meta.updatedAt))
}
val qi = ci.quotedItem
if (qi != null) {
shareText.add("")
shareText.add(generalGetString(MR.strings.in_reply_to))
shareText.add("")
val ts = localTimestamp(qi.sentAt)
val sender = qi.sender(null)
if (sender != null) {
shareText.add(String.format(generalGetString(MR.strings.sender_at_ts), sender, ts))
} else {
shareText.add(ts)
}
val t = qi.text
shareText.add(if (t != "") t else generalGetString(MR.strings.item_info_no_text))
}
val versions = chatItemInfo.itemVersions
if (versions.isNotEmpty()) {
shareText.add("")
@@ -174,7 +328,7 @@ fun itemInfoShareText(ci: ChatItem, chatItemInfo: ChatItemInfo, devTools: Boolea
if (index == 0 && ci.meta.itemDeleted == null) {
String.format(generalGetString(MR.strings.current_version_timestamp), ts)
} else {
localTimestamp(itemVersion.itemVersionTs)
ts
}
)
val t = itemVersion.msgContent.text
@@ -220,6 +220,8 @@
<string name="sent_message">Sent message</string>
<string name="received_message">Received message</string>
<string name="edit_history">History</string>
<string name="no_history">No history</string>
<string name="in_reply_to">In reply to</string>
<string name="delete_verb">Delete</string>
<string name="reveal_verb">Reveal</string>
<string name="hide_verb">Hide</string>
@@ -1161,7 +1163,8 @@
<string name="share_text_moderated_at">Moderated at: %s</string>
<string name="share_text_disappears_at">Disappears at: %s</string>
<string name="item_info_current">(current)</string>
<string name="current_version_timestamp">"%s (current)"</string>
<string name="sender_at_ts">%s at %s</string>
<string name="current_version_timestamp">%s (current)</string>
<string name="item_info_no_text">no text</string>
<!-- GroupMemberInfoView.kt -->
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M476.898-124.5q-140.398 0-240.648-94.75T125-452.332q-1-11.668 7.283-20.634 8.283-8.965 20.764-9Q164.5-482 172.75-473.5t9.836 20.575Q193.5-338 276.664-260q83.165 78 200.19 78 125.646 0 213.396-88.513Q778-359.026 778-484.763 778-608 689.313-693q-88.686-85-212.813-85-67.5 0-126 30.5t-102 80.5H323q12.5 0 20.75 8.287Q352-650.425 352-638.5q0 12.5-8.25 20.75T323-609.5H177q-12.5 0-20.5-8.25t-8-20.75v-145.2q0-11.84 8-20.07T177-812q12.5 0 20.75 8.287Q206-795.425 206-783.5v77q51-60 121.194-94.5 70.195-34.5 149.306-34.5 74 0 139.325 27.528 65.324 27.527 114.25 75.5Q779-684.5 807.25-620.318q28.25 64.183 28.25 138.25 0 74.068-28.25 139.318Q779-277.5 730.25-229t-113.986 76.5q-65.237 28-139.366 28ZM510.5-493.653l113.95 112.088q9.05 9.065 9.05 21.065 0 12-9 20.75T604.25-331q-11.25 0-20.335-9.043L461.547-460.977Q457-465.5 455-470.739q-2-5.239-2-11.261v-170q0-11.925 8-20.213 8-8.287 20.5-8.287t20.75 8.287q8.25 8.288 8.25 20.213v158.347Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB