feat(ui/chat_action_menu): show chat edit history
Signed-off-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
@@ -143,7 +143,7 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
})
|
||||
|
||||
val edits by rememberAsyncMutableState(defaultValue = emptyList()) {
|
||||
loggerWrapper.getMessageEdits(selectedConversation!!, message.messageId)
|
||||
loggerWrapper.getChatEdits(selectedConversation!!, message.messageId)
|
||||
}
|
||||
edits.forEach { messageEdit ->
|
||||
val date = remember {
|
||||
@@ -152,10 +152,10 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
Text(
|
||||
modifier = Modifier.pointerInput(Unit) {
|
||||
detectTapGestures(onLongPress = {
|
||||
context.androidContext.copyToClipboard(messageEdit.messageText)
|
||||
context.androidContext.copyToClipboard(messageEdit.message)
|
||||
})
|
||||
}.fillMaxWidth().padding(start = 4.dp),
|
||||
text = messageEdit.messageText + " (edited at $date)",
|
||||
text = messageEdit.message + " (edited at $date)",
|
||||
fontWeight = FontWeight.Light,
|
||||
fontStyle = FontStyle.Italic,
|
||||
fontSize = 12.sp
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package me.rhunk.snapenhance.bridge.logger;
|
||||
|
||||
parcelable LoggedChatEdit {
|
||||
long timestamp;
|
||||
String message;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.rhunk.snapenhance.bridge.logger;
|
||||
|
||||
import me.rhunk.snapenhance.bridge.logger.BridgeLoggedMessage;
|
||||
import me.rhunk.snapenhance.bridge.logger.LoggedChatEdit;
|
||||
|
||||
interface LoggerInterface {
|
||||
/**
|
||||
@@ -38,4 +39,6 @@ interface LoggerInterface {
|
||||
String eventType,
|
||||
String data
|
||||
);
|
||||
|
||||
List<LoggedChatEdit> getChatEdits(String conversationId, long messageId);
|
||||
}
|
||||
@@ -1407,6 +1407,7 @@
|
||||
"preview_button": "Preview",
|
||||
"download_button": "Download",
|
||||
"delete_logged_message_button": "Delete Logged Message",
|
||||
"show_chat_edit_history": "Show Chat Edit History",
|
||||
"convert_message": "Convert Message",
|
||||
"edit_message": "Edit Message"
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.coroutines.*
|
||||
import me.rhunk.snapenhance.bridge.logger.BridgeLoggedMessage
|
||||
import me.rhunk.snapenhance.bridge.logger.LoggedChatEdit
|
||||
import me.rhunk.snapenhance.bridge.logger.LoggerInterface
|
||||
import me.rhunk.snapenhance.common.bridge.InternalFileHandleType
|
||||
import me.rhunk.snapenhance.common.data.StoryData
|
||||
@@ -20,11 +21,6 @@ import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
class LoggedMessageEdit(
|
||||
val timestamp: Long,
|
||||
val messageText: String
|
||||
)
|
||||
|
||||
class LoggedMessage(
|
||||
val messageId: Long,
|
||||
val conversationId: String,
|
||||
@@ -422,17 +418,17 @@ class LoggerWrapper(
|
||||
return ConversationInfo(conversationId, participantSize, groupTitle, usernames)
|
||||
}
|
||||
|
||||
fun getMessageEdits(conversationId: String, messageId: Long): List<LoggedMessageEdit> {
|
||||
val edits = mutableListOf<LoggedMessageEdit>()
|
||||
override fun getChatEdits(conversationId: String, messageId: Long): List<LoggedChatEdit> {
|
||||
val edits = mutableListOf<LoggedChatEdit>()
|
||||
database.rawQuery(
|
||||
"SELECT added_timestamp, message_text FROM chat_edits WHERE conversation_id = ? AND message_id = ?",
|
||||
"SELECT added_timestamp, message_text FROM chat_edits WHERE conversation_id = ? AND message_id = ? ORDER BY added_timestamp ASC",
|
||||
arrayOf(conversationId, messageId.toString())
|
||||
).use {
|
||||
while (it.moveToNext()) {
|
||||
edits.add(LoggedMessageEdit(
|
||||
timestamp = it.getLongOrNull("added_timestamp") ?: continue,
|
||||
messageText = it.getStringOrNull("message_text") ?: continue
|
||||
))
|
||||
).use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
edits.add(LoggedChatEdit().apply {
|
||||
timestamp = cursor.getLongOrNull("added_timestamp") ?: return@apply
|
||||
message = cursor.getStringOrNull("message_text")
|
||||
}.takeIf { it.timestamp > 0L } ?: continue)
|
||||
}
|
||||
}
|
||||
return edits
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.os.DeadObjectException
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import me.rhunk.snapenhance.bridge.logger.BridgeLoggedMessage
|
||||
import me.rhunk.snapenhance.bridge.logger.LoggedChatEdit
|
||||
import me.rhunk.snapenhance.common.data.ContentType
|
||||
import me.rhunk.snapenhance.common.data.MessageState
|
||||
import me.rhunk.snapenhance.common.data.QuotedMessageContentStatus
|
||||
@@ -76,6 +77,11 @@ class MessageLogger : Feature("MessageLogger",
|
||||
}
|
||||
}
|
||||
|
||||
fun getChatEdits(conversationId: String, clientMessageId: Long): List<LoggedChatEdit> {
|
||||
val uniqueMessageId = makeUniqueIdentifier(conversationId, clientMessageId) ?: return emptyList()
|
||||
return loggerInterface.getChatEdits(conversationId, uniqueMessageId)
|
||||
}
|
||||
|
||||
private fun computeMessageIdentifier(conversationId: String, orderKey: Long) = (orderKey.toString() + conversationId).longHashCode()
|
||||
|
||||
private fun makeUniqueIdentifier(conversationId: String, clientMessageId: Long): Long? {
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import me.rhunk.snapenhance.bridge.logger.LoggedChatEdit
|
||||
import me.rhunk.snapenhance.core.features.impl.downloader.MediaDownloader
|
||||
import me.rhunk.snapenhance.core.features.impl.experiments.ConvertMessageLocally
|
||||
import me.rhunk.snapenhance.core.features.impl.messaging.Messaging
|
||||
@@ -131,6 +132,30 @@ class ChatActionMenu : AbstractMenu() {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
injectButton(Button(viewGroup.context).apply {
|
||||
var chatEdits = emptyList<LoggedChatEdit>()
|
||||
text = this@ChatActionMenu.context.translation["chat_action_menu.show_chat_edit_history"]
|
||||
setOnClickListener {
|
||||
menuViewInjector.menu(NewChatActionMenu::class)?.showChatEditHistory(chatEdits)
|
||||
}
|
||||
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
visibility = View.GONE
|
||||
chatEdits = this@ChatActionMenu.context.feature(MessageLogger::class).getChatEdits(
|
||||
messaging.openedConversationUUID.toString(),
|
||||
messaging.lastFocusedMessageId,
|
||||
)
|
||||
if (chatEdits.isEmpty()) return
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View) {
|
||||
visibility = View.GONE
|
||||
chatEdits = emptyList()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (context.config.experimental.editMessage.get() && messaging.conversationManager?.isEditMessageSupported() == true) {
|
||||
|
||||
@@ -10,29 +10,32 @@ import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.RemoveRedEye
|
||||
import androidx.compose.material.icons.outlined.Image
|
||||
import androidx.compose.material.icons.rounded.BookmarkRemove
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.bridge.logger.LoggedChatEdit
|
||||
import me.rhunk.snapenhance.common.data.ContentType
|
||||
import me.rhunk.snapenhance.common.ui.createComposeAlertDialog
|
||||
import me.rhunk.snapenhance.common.ui.createComposeView
|
||||
import me.rhunk.snapenhance.common.ui.rememberAsyncMutableState
|
||||
import me.rhunk.snapenhance.common.util.ktx.copyToClipboard
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoWriter
|
||||
@@ -48,9 +51,11 @@ import me.rhunk.snapenhance.core.ui.debugEditText
|
||||
import me.rhunk.snapenhance.core.ui.iterateParent
|
||||
import me.rhunk.snapenhance.core.ui.menu.AbstractMenu
|
||||
import me.rhunk.snapenhance.core.ui.triggerCloseTouchEvent
|
||||
import me.rhunk.snapenhance.core.util.ktx.getIdentifier
|
||||
import me.rhunk.snapenhance.core.util.ktx.isDarkTheme
|
||||
import me.rhunk.snapenhance.core.util.ktx.setObjectField
|
||||
import me.rhunk.snapenhance.core.util.ktx.vibrateLongPress
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import kotlin.io.encoding.Base64
|
||||
@@ -70,6 +75,32 @@ class NewChatActionMenu : AbstractMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
fun showChatEditHistory(
|
||||
edits: List<LoggedChatEdit>,
|
||||
) {
|
||||
createComposeAlertDialog(context.mainActivity!!) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
itemsIndexed(edits) { index, edit ->
|
||||
Column(
|
||||
modifier = Modifier.padding(8.dp).fillMaxWidth().pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
context.androidContext.copyToClipboard(edit.message)
|
||||
}
|
||||
)
|
||||
},
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Text(edit.message)
|
||||
Text(text = DateFormat.getDateTimeInstance().format(edit.timestamp) + " (${index + 1})", fontSize = 12.sp, fontWeight = FontWeight.Light)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
fun editCurrentMessage(
|
||||
localContext: Context,
|
||||
dismissActionMenu: () -> Unit,
|
||||
@@ -267,6 +298,11 @@ class NewChatActionMenu : AbstractMenu() {
|
||||
|
||||
val composeView = createComposeView(event.view.context) {
|
||||
val primaryColor = remember { if (event.view.context.isDarkTheme()) Color.White else Color.Black }
|
||||
val avenirNextMediumFont = remember {
|
||||
FontFamily(
|
||||
Font(context.resources.getIdentifier("avenir_next_medium", "font"), FontWeight.Medium)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ListButton(
|
||||
@@ -288,7 +324,7 @@ class NewChatActionMenu : AbstractMenu() {
|
||||
tint = primaryColor,
|
||||
contentDescription = text
|
||||
)
|
||||
Text(text, color = primaryColor)
|
||||
Text(text, color = primaryColor, fontFamily = avenirNextMediumFont, fontSize = 16.sp)
|
||||
}
|
||||
Spacer(modifier = Modifier
|
||||
.height(1.dp)
|
||||
@@ -300,11 +336,11 @@ class NewChatActionMenu : AbstractMenu() {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
if (context.config.downloader.downloadContextMenu.get()) {
|
||||
ListButton(icon = Icons.Default.RemoveRedEye, text = context.translation["chat_action_menu.preview_button"], modifier = Modifier.clickable {
|
||||
ListButton(icon = Icons.Outlined.RemoveRedEye, text = context.translation["chat_action_menu.preview_button"], modifier = Modifier.clickable {
|
||||
closeActionMenu()
|
||||
mediaDownloader.onMessageActionMenu(true)
|
||||
})
|
||||
ListButton(icon = Icons.Default.Download, text = context.translation["chat_action_menu.download_button"], modifier = Modifier.pointerInput(Unit) {
|
||||
ListButton(icon = Icons.Outlined.Download, text = context.translation["chat_action_menu.download_button"], modifier = Modifier.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onTap = {
|
||||
closeActionMenu()
|
||||
@@ -319,7 +355,7 @@ class NewChatActionMenu : AbstractMenu() {
|
||||
}
|
||||
|
||||
if (context.config.experimental.editMessage.get() && messaging.conversationManager?.isEditMessageSupported() == true) {
|
||||
ListButton(icon = Icons.Rounded.Edit, text = context.translation["chat_action_menu.edit_message"], modifier = Modifier.clickable {
|
||||
ListButton(icon = Icons.Outlined.Edit, text = context.translation["chat_action_menu.edit_message"], modifier = Modifier.clickable {
|
||||
editCurrentMessage(event.view.context) {
|
||||
context.runOnUiThread {
|
||||
closeActionMenu()
|
||||
@@ -329,7 +365,21 @@ class NewChatActionMenu : AbstractMenu() {
|
||||
}
|
||||
|
||||
if (context.config.messaging.messageLogger.globalState == true) {
|
||||
ListButton(icon = Icons.Rounded.BookmarkRemove, text = context.translation["chat_action_menu.delete_logged_message_button"], modifier = Modifier.clickable {
|
||||
val chatEdits by rememberAsyncMutableState(defaultValue = null) {
|
||||
context.feature(MessageLogger::class).getChatEdits(
|
||||
messaging.openedConversationUUID.toString(),
|
||||
messaging.lastFocusedMessageId
|
||||
)
|
||||
}
|
||||
|
||||
if (chatEdits != null && chatEdits?.isNotEmpty() == true) {
|
||||
ListButton(icon = Icons.Outlined.History, text = context.translation["chat_action_menu.show_chat_edit_history"], modifier = Modifier.clickable {
|
||||
closeActionMenu()
|
||||
showChatEditHistory(chatEdits!!)
|
||||
})
|
||||
}
|
||||
|
||||
ListButton(icon = Icons.Outlined.BookmarkRemove, text = context.translation["chat_action_menu.delete_logged_message_button"], modifier = Modifier.clickable {
|
||||
closeActionMenu()
|
||||
context.executeAsync {
|
||||
messageLogger.deleteMessage(messaging.openedConversationUUID.toString(), messaging.lastFocusedMessageId)
|
||||
|
||||
Reference in New Issue
Block a user