mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-24 10:55:33 +00:00
android: add quoted message to chat item info (#2688)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
+184
-30
@@ -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 |
Reference in New Issue
Block a user