android: marked deleted / reveal ui (#1488)

* android: marked deleted / reveal ui

* marked deleted, reveal

* fix ios

* different alerts
This commit is contained in:
JRoberts
2022-12-03 22:21:32 +04:00
committed by GitHub
parent 6f59df4e33
commit f3630d934c
7 changed files with 201 additions and 39 deletions
@@ -26,6 +26,8 @@ import chat.simplex.app.views.chat.ComposeState
import chat.simplex.app.views.helpers.*
import kotlinx.datetime.Clock
// TODO refactor so that FramedItemView can show all CIContent items if they're deleted (see Swift code)
@Composable
fun ChatItemView(
cInfo: ChatInfo,
@@ -46,7 +48,11 @@ fun ChatItemView(
val sent = cItem.chatDir.sent
val alignment = if (sent) Alignment.CenterEnd else Alignment.CenterStart
val showMenu = remember { mutableStateOf(false) }
val revealed = remember { mutableStateOf(false) }
val fullDeleteAllowed = remember(cInfo) { cInfo.fullDeletionAllowed }
val saveFileLauncher = rememberSaveFileLauncher(cxt = context, ciFile = cItem.file)
val onLinkLongClick = { _: String -> showMenu.value = true }
Box(
modifier = Modifier
.padding(bottom = 4.dp)
@@ -69,29 +75,36 @@ fun ChatItemView(
.clip(RoundedCornerShape(18.dp))
.combinedClickable(onLongClick = { showMenu.value = true }, onClick = onClick),
) {
@Composable fun ContentItem() {
val mc = cItem.content.msgContent
val onLinkLongClick = { _: String -> showMenu.value = true }
if (cItem.file == null && cItem.quotedItem == null && isShortEmoji(cItem.content.text)) {
EmojiItemView(cItem)
} else if (mc is MsgContent.MCVoice && cItem.content.text.isEmpty() && cItem.quotedItem == null) {
CIVoiceView(mc.duration, cItem.file, cItem.meta.itemEdited, cItem.chatDir.sent, hasText = false, cItem, longClick = { onLinkLongClick("") })
@Composable
fun framedItemView() {
FramedItemView(cInfo, cItem, uriHandler, imageProvider, showMember = showMember, linkMode = linkMode, showMenu, receiveFile, onLinkLongClick, scrollToItem)
}
fun deleteMessageQuestionText(): String {
return if (fullDeleteAllowed) {
generalGetString(R.string.delete_message_cannot_be_undone_warning)
} else {
FramedItemView(cInfo, cItem, uriHandler, imageProvider, showMember = showMember, linkMode = linkMode, showMenu, receiveFile, onLinkLongClick, scrollToItem)
generalGetString(R.string.delete_message_mark_deleted_warning)
}
}
@Composable
fun MsgContentItemDropdownMenu() {
DropdownMenu(
expanded = showMenu.value,
onDismissRequest = { showMenu.value = false },
Modifier.width(220.dp)
) {
ItemAction(stringResource(R.string.reply_verb), Icons.Outlined.Reply, onClick = {
if (composeState.value.editing) {
composeState.value = ComposeState(contextItem = ComposeContextItem.QuotedItem(cItem), useLinkPreviews = useLinkPreviews)
} else {
composeState.value = composeState.value.copy(contextItem = ComposeContextItem.QuotedItem(cItem))
}
showMenu.value = false
})
if (!cItem.meta.itemDeleted) {
ItemAction(stringResource(R.string.reply_verb), Icons.Outlined.Reply, onClick = {
if (composeState.value.editing) {
composeState.value = ComposeState(contextItem = ComposeContextItem.QuotedItem(cItem), useLinkPreviews = useLinkPreviews)
} else {
composeState.value = composeState.value.copy(contextItem = ComposeContextItem.QuotedItem(cItem))
}
showMenu.value = false
})
}
ItemAction(stringResource(R.string.share_verb), Icons.Outlined.Share, onClick = {
val filePath = getLoadedFilePath(SimplexApp.context, cItem.file)
when {
@@ -124,15 +137,59 @@ fun ChatItemView(
showMenu.value = false
})
}
if (cItem.meta.itemDeleted && revealed.value) {
ItemAction(
stringResource(R.string.hide_verb),
Icons.Outlined.VisibilityOff,
onClick = {
revealed.value = false
showMenu.value = false
}
)
}
DeleteItemAction(cItem, showMenu, questionText = deleteMessageQuestionText(), deleteMessage)
}
}
@Composable
fun MarkedDeletedItemDropdownMenu() {
DropdownMenu(
expanded = showMenu.value,
onDismissRequest = { showMenu.value = false },
Modifier.width(220.dp)
) {
ItemAction(
stringResource(R.string.delete_verb),
Icons.Outlined.Delete,
stringResource(R.string.reveal_verb),
Icons.Outlined.Visibility,
onClick = {
revealed.value = true
showMenu.value = false
deleteMessageAlertDialog(cItem, deleteMessage = deleteMessage)
},
color = Color.Red
}
)
DeleteItemAction(cItem, showMenu, questionText = deleteMessageQuestionText(), deleteMessage)
}
}
@Composable
fun ContentItem() {
val mc = cItem.content.msgContent
if (cItem.meta.itemDeleted && !revealed.value) {
MarkedDeletedItemView(cItem, showMember = showMember)
MarkedDeletedItemDropdownMenu()
} else if (cItem.quotedItem == null && !cItem.meta.itemDeleted) {
if (mc is MsgContent.MCText && isShortEmoji(cItem.content.text)) {
EmojiItemView(cItem)
MsgContentItemDropdownMenu()
} else if (mc is MsgContent.MCVoice && cItem.content.text.isEmpty()) {
CIVoiceView(mc.duration, cItem.file, cItem.meta.itemEdited, cItem.chatDir.sent, hasText = false, cItem, longClick = { onLinkLongClick("") })
MsgContentItemDropdownMenu()
} else {
framedItemView()
MsgContentItemDropdownMenu()
}
} else {
framedItemView()
MsgContentItemDropdownMenu()
}
}
@@ -143,15 +200,7 @@ fun ChatItemView(
onDismissRequest = { showMenu.value = false },
Modifier.width(220.dp)
) {
ItemAction(
stringResource(R.string.delete_verb),
Icons.Outlined.Delete,
onClick = {
showMenu.value = false
deleteMessageAlertDialog(cItem, deleteMessage = deleteMessage)
},
color = Color.Red
)
DeleteItemAction(cItem, showMenu, questionText = deleteMessageQuestionText(), deleteMessage)
}
}
@@ -184,6 +233,24 @@ fun ChatItemView(
}
}
@Composable
fun DeleteItemAction(
cItem: ChatItem,
showMenu: MutableState<Boolean>,
questionText: String,
deleteMessage: (Long, CIDeleteMode) -> Unit
) {
ItemAction(
stringResource(R.string.delete_verb),
Icons.Outlined.Delete,
onClick = {
showMenu.value = false
deleteMessageAlertDialog(cItem, questionText, deleteMessage = deleteMessage)
},
color = Color.Red
)
}
@Composable
fun ItemAction(text: String, icon: ImageVector, onClick: () -> Unit, color: Color = MaterialTheme.colors.onBackground) {
DropdownMenuItem(onClick) {
@@ -201,10 +268,10 @@ fun ItemAction(text: String, icon: ImageVector, onClick: () -> Unit, color: Colo
}
}
fun deleteMessageAlertDialog(chatItem: ChatItem, deleteMessage: (Long, CIDeleteMode) -> Unit) {
fun deleteMessageAlertDialog(chatItem: ChatItem, questionText: String, deleteMessage: (Long, CIDeleteMode) -> Unit) {
AlertManager.shared.showAlertDialogButtons(
title = generalGetString(R.string.delete_message__question),
text = generalGetString(R.string.delete_message_cannot_be_undone_warning),
text = questionText,
buttons = {
Row(
Modifier
@@ -6,6 +6,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -16,15 +17,15 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.*
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.tooling.preview.*
import androidx.compose.ui.unit.*
import androidx.compose.ui.util.fastMap
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.ChatItemLinkView
import chat.simplex.app.views.helpers.base64ToBitmap
import chat.simplex.app.views.helpers.*
import kotlinx.datetime.Clock
val SentColorLight = Color(0x1E45B8FF)
@@ -65,6 +66,33 @@ fun FramedItemView(
}
}
@Composable
fun ciDeletedView() {
Row(
Modifier
.background(if (sent) SentQuoteColorLight else ReceivedQuoteColorLight)
.fillMaxWidth()
.padding(start = 8.dp)
.padding(end = 12.dp)
.padding(top = 6.dp)
.padding(bottom = if (ci.quotedItem == null) 6.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Icon(
Icons.Outlined.Delete,
stringResource(R.string.marked_deleted_description),
Modifier.size(18.dp),
tint = if (isInDarkTheme()) FileDark else FileLight
)
Text(
buildAnnotatedString {
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = FontStyle.Italic, color = HighOrLowlight)) { append(generalGetString(R.string.marked_deleted_description)) }
},
style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp),
)
}
}
@Composable
fun ciQuoteView(qi: CIQuote) {
Row(
@@ -130,6 +158,7 @@ fun FramedItemView(
Box(contentAlignment = Alignment.BottomEnd) {
Column(Modifier.width(IntrinsicSize.Max)) {
PriorityLayout(Modifier, CHAT_IMAGE_LAYOUT_ID) {
if (ci.meta.itemDeleted) { ciDeletedView() }
ci.quotedItem?.let { ciQuoteView(it) }
if (ci.file == null && ci.formattedText == null && isShortEmoji(ci.content.text)) {
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
@@ -0,0 +1,57 @@
package chat.simplex.app.views.chat.item
import android.content.res.Configuration
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.ChatItem
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.helpers.generalGetString
@Composable
fun MarkedDeletedItemView(ci: ChatItem, showMember: Boolean = false) {
Surface(
shape = RoundedCornerShape(18.dp),
color = if (ci.chatDir.sent) SentColorLight else ReceivedColorLight,
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
buildAnnotatedString {
// appendSender(this, if (showMember) ci.memberDisplayName else null, true) // TODO font size
withStyle(SpanStyle(fontSize = 12.sp, fontStyle = FontStyle.Italic, color = HighOrLowlight)) { append(generalGetString(R.string.marked_deleted_description)) }
},
style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp),
modifier = Modifier.padding(end = 8.dp)
)
CIMetaView(ci)
}
}
}
@Preview(showBackground = true)
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
name = "Dark Mode"
)
@Composable
fun PreviewMarkedDeletedItemView() {
SimpleXTheme {
DeletedItemView(
ChatItem.getSampleData(itemDeleted = true)
)
}
}
@@ -168,10 +168,13 @@
<string name="save_verb">Speichern</string>
<string name="edit_verb">Bearbeiten</string>
<string name="delete_verb">Löschen</string>
<string name="reveal_verb">***Reveal</string>
<string name="hide_verb">***Hide</string>
<string name="allow_verb">Erlauben</string>
<string name="delete_message__question">Die Nachricht löschen?</string>
<string name="delete_message_cannot_be_undone_warning">Nachricht wird gelöscht - dies kann nicht rückgängig gemacht werden!</string>
<string name="for_me_only">Nur für mich</string>
<string name="delete_message_mark_deleted_warning">***Message will be marked for deletion. The recipient(s) will be able to reveal this message.</string>
<string name="for_me_only">***Delete for me</string>
<string name="for_everybody">Für alle</string>
<!-- CIMetaView.kt -->
@@ -168,10 +168,13 @@
<string name="save_verb">Сохранить</string>
<string name="edit_verb">Редактировать</string>
<string name="delete_verb">Удалить</string>
<string name="reveal_verb">Показать</string>
<string name="hide_verb">Спрятать</string>
<string name="allow_verb">Разрешить</string>
<string name="delete_message__question">Удалить сообщение?</string>
<string name="delete_message_cannot_be_undone_warning">Сообщение будет удалено – это действие нельзя отменить!</string>
<string name="for_me_only">Только для меня</string>
<string name="delete_message_mark_deleted_warning">Сообщение будет помечено на удаление. Получатель(и) сможет(смогут) посмотреть это сообщение.</string>
<string name="for_me_only">Удалить для меня</string>
<string name="for_everybody">Для всех</string>
<!-- CIMetaView.kt -->
@@ -168,10 +168,13 @@
<string name="save_verb">Save</string>
<string name="edit_verb">Edit</string>
<string name="delete_verb">Delete</string>
<string name="reveal_verb">Reveal</string>
<string name="hide_verb">Hide</string>
<string name="allow_verb">Allow</string>
<string name="delete_message__question">Delete message?</string>
<string name="delete_message_cannot_be_undone_warning">Message will be deleted - this cannot be undone!</string>
<string name="for_me_only">For me only</string>
<string name="delete_message_mark_deleted_warning">Message will be marked for deletion. The recipient(s) will be able to reveal this message.</string>
<string name="for_me_only">Delete for me</string>
<string name="for_everybody">For everyone</string>
<!-- CIMetaView.kt -->
@@ -17,7 +17,7 @@ struct MarkedDeletedItemView: View {
var body: some View {
HStack(alignment: .bottom, spacing: 0) {
if showMember, let member = chatItem.memberDisplayName {
Text(member).fontWeight(.medium) + Text(": ")
Text(member).font(.caption).fontWeight(.medium) + Text(": ").font(.caption)
}
Text("marked deleted")
.font(.caption)