mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
android: Live messages without sending an empty text (#1709)
* android: Live messages without sending an empty text * Better quoted messages handling * Do not add item into preview * Change * Changes
This commit is contained in:
committed by
GitHub
parent
cba24983e6
commit
5a7d61c964
@@ -180,7 +180,11 @@ class ChatModel(val controller: ChatController) {
|
||||
}
|
||||
// add to current chat
|
||||
if (chatId.value == cInfo.id) {
|
||||
chatItems.add(cItem)
|
||||
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
chatItems.add(kotlin.math.max(0, chatItems.lastIndex), cItem)
|
||||
} else {
|
||||
chatItems.add(cItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +259,21 @@ class ChatModel(val controller: ChatController) {
|
||||
}
|
||||
}
|
||||
|
||||
fun addLiveChatItemDummy(quotedCItem: ChatItem?, chatInfo: ChatInfo): ChatItem {
|
||||
val quoted = if (quotedCItem?.content?.msgContent != null) {
|
||||
CIQuote(chatDir = quotedCItem.chatDir, itemId = quotedCItem.id, sentAt = quotedCItem.meta.createdAt, content = quotedCItem.content.msgContent!!)
|
||||
} else null
|
||||
val cItem = ChatItem.liveChatItemDummy(chatInfo is ChatInfo.Direct, quoted)
|
||||
chatItems.add(cItem)
|
||||
return cItem
|
||||
}
|
||||
|
||||
fun removeLiveChatItemDummy() {
|
||||
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
chatItems.removeLast()
|
||||
}
|
||||
}
|
||||
|
||||
fun markChatItemsRead(cInfo: ChatInfo, range: CC.ItemRange? = null, unreadCountAfter: Int? = null) {
|
||||
val markedRead = markItemsReadInCurrentChat(cInfo, range)
|
||||
// update preview
|
||||
@@ -1278,7 +1297,8 @@ data class ChatItem (
|
||||
}
|
||||
|
||||
private const val TEMP_DELETED_CHAT_ITEM_ID = -1L
|
||||
|
||||
const val TEMP_LIVE_CHAT_ITEM_ID = -2L
|
||||
|
||||
val deletedItemDummy: ChatItem
|
||||
get() = ChatItem(
|
||||
chatDir = CIDirection.DirectRcv(),
|
||||
@@ -1300,6 +1320,26 @@ data class ChatItem (
|
||||
file = null
|
||||
)
|
||||
|
||||
fun liveChatItemDummy(direct: Boolean, quoted: CIQuote?): ChatItem = ChatItem(
|
||||
chatDir = if (direct) CIDirection.DirectSnd() else CIDirection.GroupSnd(),
|
||||
meta = CIMeta(
|
||||
itemId = TEMP_LIVE_CHAT_ITEM_ID,
|
||||
itemTs = Clock.System.now(),
|
||||
itemText = "",
|
||||
itemStatus = CIStatus.RcvRead(),
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now(),
|
||||
itemDeleted = false,
|
||||
itemEdited = false,
|
||||
itemTimed = null,
|
||||
itemLive = true,
|
||||
editable = false
|
||||
),
|
||||
content = CIContent.SndMsgContent(MsgContent.MCText("")),
|
||||
quotedItem = quoted,
|
||||
file = null
|
||||
)
|
||||
|
||||
fun invalidJSON(json: String): ChatItem =
|
||||
ChatItem(
|
||||
chatDir = CIDirection.DirectSnd(),
|
||||
|
||||
@@ -147,8 +147,8 @@ fun TerminalLayout(
|
||||
sendMessage = sendCommand,
|
||||
sendLiveMessage = null,
|
||||
updateLiveMessage = null,
|
||||
::onMessageChange,
|
||||
textStyle
|
||||
onMessageChange = ::onMessageChange,
|
||||
textStyle = textStyle
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -568,7 +568,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
scope.launch {
|
||||
if (composeState.value.editing) {
|
||||
composeState.value = ComposeState(contextItem = ComposeContextItem.QuotedItem(cItem), useLinkPreviews = useLinkPreviews)
|
||||
} else {
|
||||
} else if (cItem.id != ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
composeState.value = composeState.value.copy(contextItem = ComposeContextItem.QuotedItem(cItem))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.views.chat.item.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -67,7 +68,8 @@ sealed class ComposeContextItem {
|
||||
data class LiveMessage(
|
||||
val chatItem: ChatItem,
|
||||
val typedMsg: String,
|
||||
val sentMsg: String
|
||||
val sentMsg: String,
|
||||
val sent: Boolean
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -352,6 +354,7 @@ fun ComposeView(
|
||||
chosenContent.value = emptyList()
|
||||
chosenAudio.value = null
|
||||
chosenFile.value = null
|
||||
chatModel.removeLiveChatItemDummy()
|
||||
}
|
||||
|
||||
suspend fun send(cInfo: ChatInfo, mc: MsgContent, quoted: Long?, file: String? = null, live: Boolean = false): ChatItem? {
|
||||
@@ -430,7 +433,7 @@ fun ComposeView(
|
||||
if (cs.contextItem is ComposeContextItem.EditingItem) {
|
||||
val ei = cs.contextItem.chatItem
|
||||
sent = updateMessage(ei, cInfo, live)
|
||||
} else if (liveMessage != null) {
|
||||
} else if (liveMessage != null && liveMessage.sent) {
|
||||
sent = updateMessage(liveMessage.chatItem, cInfo, live)
|
||||
} else {
|
||||
val msgs: ArrayList<MsgContent> = ArrayList()
|
||||
@@ -571,11 +574,14 @@ fun ComposeView(
|
||||
suspend fun sendLiveMessage() {
|
||||
val typedMsg = composeState.value.message
|
||||
val sentMsg = truncateToWords(typedMsg)
|
||||
if (composeState.value.liveMessage == null) {
|
||||
if (sentMsg.isNotEmpty() && (composeState.value.liveMessage == null || composeState.value.liveMessage?.sent == false)) {
|
||||
val ci = sendMessageAsync(sentMsg, live = true)
|
||||
if (ci != null) {
|
||||
composeState.value = composeState.value.copy(liveMessage = LiveMessage(ci, typedMsg = typedMsg, sentMsg = sentMsg))
|
||||
composeState.value = composeState.value.copy(liveMessage = LiveMessage(ci, typedMsg = typedMsg, sentMsg = sentMsg, sent = true))
|
||||
}
|
||||
} else if (composeState.value.liveMessage == null) {
|
||||
val cItem = chatModel.addLiveChatItemDummy((composeState.value.contextItem as? ComposeContextItem.QuotedItem)?.chatItem, chat.chatInfo)
|
||||
composeState.value = composeState.value.copy(liveMessage = LiveMessage(cItem, typedMsg = typedMsg, sentMsg = sentMsg, sent = false))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,7 +598,7 @@ fun ComposeView(
|
||||
if (sentMsg != null) {
|
||||
val ci = sendMessageAsync(sentMsg, live = true)
|
||||
if (ci != null) {
|
||||
composeState.value = composeState.value.copy(liveMessage = LiveMessage(ci, typedMsg = typedMsg, sentMsg = sentMsg))
|
||||
composeState.value = composeState.value.copy(liveMessage = LiveMessage(ci, typedMsg = typedMsg, sentMsg = sentMsg, sent = true))
|
||||
}
|
||||
} else if (liveMessage.typedMsg != typedMsg) {
|
||||
composeState.value = composeState.value.copy(liveMessage = liveMessage.copy(typedMsg = typedMsg))
|
||||
@@ -696,14 +702,28 @@ fun ComposeView(
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { composeState.value.contextItem }
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
if (composeState.value.liveMessage?.sent == false) {
|
||||
chatModel.removeLiveChatItemDummy()
|
||||
chatModel.addLiveChatItemDummy((it as? ComposeContextItem.QuotedItem)?.chatItem, chat.chatInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val activity = LocalContext.current as Activity
|
||||
DisposableEffect(Unit) {
|
||||
val orientation = activity.resources.configuration.orientation
|
||||
onDispose {
|
||||
if (orientation == activity.resources.configuration.orientation && composeState.value.liveMessage != null) {
|
||||
sendMessage()
|
||||
resetLinkPreview()
|
||||
if (orientation == activity.resources.configuration.orientation) {
|
||||
val cs = composeState.value
|
||||
if (cs.liveMessage != null && (cs.message.isNotEmpty() || cs.liveMessage.sent)) {
|
||||
sendMessage()
|
||||
resetLinkPreview()
|
||||
}
|
||||
chatModel.removeLiveChatItemDummy()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,6 +743,10 @@ fun ComposeView(
|
||||
},
|
||||
sendLiveMessage = ::sendLiveMessage,
|
||||
updateLiveMessage = ::updateLiveMessage,
|
||||
cancelLiveMessage = {
|
||||
composeState.value = composeState.value.copy(liveMessage = null)
|
||||
chatModel.removeLiveChatItemDummy()
|
||||
},
|
||||
onMessageChange = ::onMessageChange,
|
||||
textStyle = textStyle
|
||||
)
|
||||
|
||||
@@ -37,7 +37,6 @@ import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.core.view.inputmethod.EditorInfoCompat
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat
|
||||
@@ -65,6 +64,7 @@ fun SendMsgView(
|
||||
sendMessage: () -> Unit,
|
||||
sendLiveMessage: ( suspend () -> Unit)? = null,
|
||||
updateLiveMessage: (suspend () -> Unit)? = null,
|
||||
cancelLiveMessage: (() -> Unit)? = null,
|
||||
onMessageChange: (String) -> Unit,
|
||||
textStyle: MutableState<TextStyle>
|
||||
) {
|
||||
@@ -119,9 +119,14 @@ fun SendMsgView(
|
||||
}
|
||||
}
|
||||
}
|
||||
cs.liveMessage?.sent == false && cs.message.isEmpty() -> {
|
||||
CancelLiveMessageButton {
|
||||
cancelLiveMessage?.invoke()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val icon = if (cs.editing || cs.liveMessage != null) Icons.Filled.Check else Icons.Outlined.ArrowUpward
|
||||
val color = if (cs.sendEnabled()) MaterialTheme.colors.primary else HighOrLowlight
|
||||
val color = if (cs.sendEnabled() && cs.message.isNotEmpty()) MaterialTheme.colors.primary else HighOrLowlight
|
||||
if (composeState.value.liveMessage == null &&
|
||||
cs.preview !is ComposePreview.VoicePreview && !cs.editing &&
|
||||
sendLiveMessage != null && updateLiveMessage != null
|
||||
@@ -144,7 +149,7 @@ fun SendMsgView(
|
||||
)
|
||||
}
|
||||
} else {
|
||||
SendTextButton(icon, color, sendButtonSize, sendButtonAlpha, cs.sendEnabled(), sendMessage)
|
||||
SendTextButton(icon, color, sendButtonSize, sendButtonAlpha, cs.sendEnabled() && cs.message.isNotEmpty(), sendMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -373,6 +378,22 @@ private fun ProgressIndicator() {
|
||||
CircularProgressIndicator(Modifier.size(36.dp).padding(4.dp), color = HighOrLowlight, strokeWidth = 3.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CancelLiveMessageButton(
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
IconButton(onClick, Modifier.size(36.dp)) {
|
||||
Icon(
|
||||
Icons.Filled.Close,
|
||||
stringResource(R.string.icon_descr_cancel_live_message),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SendTextButton(
|
||||
icon: ImageVector,
|
||||
|
||||
@@ -271,6 +271,7 @@
|
||||
<string name="live_message">Live message!</string>
|
||||
<string name="send_live_message_desc">Send a live message - it will update for the recipient(s) as you type it</string>
|
||||
<string name="send_verb">Send</string>
|
||||
<string name="icon_descr_cancel_live_message">Cancel live message</string>
|
||||
|
||||
<!-- General Actions / Responses -->
|
||||
<string name="back">Back</string>
|
||||
|
||||
Reference in New Issue
Block a user