feat(experimental): edit messages
This commit is contained in:
@@ -704,6 +704,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"edit_message": {
|
||||
"name": "Edit Messages",
|
||||
"description": "Allows you to edit messages in conversations"
|
||||
},
|
||||
"app_passcode": {
|
||||
"name": "App Passcode",
|
||||
"description": "Sets a passcode to lock the app"
|
||||
|
||||
@@ -31,6 +31,7 @@ class Experimental : ConfigContainer() {
|
||||
val storyLogger = boolean("story_logger") { requireRestart(); addNotices(FeatureNotice.UNSTABLE); }
|
||||
val callRecorder = boolean("call_recorder") { requireRestart(); addNotices(FeatureNotice.UNSTABLE); }
|
||||
val accountSwitcher = container("account_switcher", AccountSwitcherConfig()) { requireRestart(); addNotices(FeatureNotice.UNSTABLE) }
|
||||
val editMessage = boolean("edit_message") { requireRestart(); addNotices(FeatureNotice.BAN_RISK) }
|
||||
val appPasscode = string("app_passcode")
|
||||
val appLockOnResume = boolean("app_lock_on_resume")
|
||||
val infiniteStoryBoost = boolean("infinite_story_boost")
|
||||
|
||||
@@ -20,6 +20,7 @@ import me.rhunk.snapenhance.core.wrapper.impl.Snapchatter
|
||||
import me.rhunk.snapenhance.core.wrapper.impl.toSnapUUID
|
||||
import me.rhunk.snapenhance.mapper.impl.CallbackMapper
|
||||
import me.rhunk.snapenhance.mapper.impl.FriendsFeedEventDispatcherMapper
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.Future
|
||||
|
||||
class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_ASYNC or FeatureLoadParams.INIT_SYNC) {
|
||||
@@ -93,7 +94,12 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C
|
||||
})
|
||||
}
|
||||
|
||||
fun localUpdateMessage(conversationId: String, message: Message) {
|
||||
fun localUpdateMessage(conversationId: String, message: Message, forceUpdate: Boolean = false) {
|
||||
if (forceUpdate) {
|
||||
message.messageMetadata?.screenRecordedBy = ArrayList<SnapUUID>(message.messageMetadata?.screenRecordedBy ?: emptyList()).apply {
|
||||
add(SnapUUID(UUID.randomUUID().toString()))
|
||||
}
|
||||
}
|
||||
conversationManagerDelegate?.let {
|
||||
it::class.java.methods.first { method ->
|
||||
method.name == "onConversationUpdated"
|
||||
|
||||
@@ -4,11 +4,17 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoWriter
|
||||
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
|
||||
import me.rhunk.snapenhance.core.features.impl.spying.MessageLogger
|
||||
import me.rhunk.snapenhance.core.ui.ViewAppearanceHelper
|
||||
import me.rhunk.snapenhance.core.ui.ViewTagState
|
||||
import me.rhunk.snapenhance.core.ui.applyTheme
|
||||
import me.rhunk.snapenhance.core.ui.menu.AbstractMenu
|
||||
@@ -58,7 +64,11 @@ class ChatActionMenu : AbstractMenu() {
|
||||
val viewGroup = parent.parent.parent as? ViewGroup ?: return
|
||||
if (viewTagState[viewGroup]) return
|
||||
//close the action menu using a touch event
|
||||
val closeActionMenu = { parent.triggerCloseTouchEvent() }
|
||||
val closeActionMenu = {
|
||||
context.runOnUiThread {
|
||||
parent.triggerCloseTouchEvent()
|
||||
}
|
||||
}
|
||||
|
||||
val messaging = context.feature(Messaging::class)
|
||||
val messageLogger = context.feature(MessageLogger::class)
|
||||
@@ -129,6 +139,72 @@ class ChatActionMenu : AbstractMenu() {
|
||||
})
|
||||
}
|
||||
|
||||
if (context.config.experimental.editMessage.get() && messaging.conversationManager?.isEditMessageSupported() == true) {
|
||||
injectButton(Button(viewGroup.context).apply button@{
|
||||
text = "Edit Message"
|
||||
setOnClickListener {
|
||||
messaging.conversationManager?.fetchMessage(
|
||||
messaging.openedConversationUUID.toString(),
|
||||
messaging.lastFocusedMessageId,
|
||||
onSuccess = onSuccess@{ message ->
|
||||
closeActionMenu()
|
||||
if (message.senderId.toString() != this@ChatActionMenu.context.database.myUserId) {
|
||||
this@ChatActionMenu.context.shortToast("You can only edit your own messages")
|
||||
return@onSuccess
|
||||
}
|
||||
|
||||
val editText = EditText(viewGroup.context).apply {
|
||||
setText(ProtoReader(message.messageContent?.content ?: return@apply).getString(2, 1) ?: run {
|
||||
this@ChatActionMenu.context.shortToast("You can only edit text messages")
|
||||
return@onSuccess
|
||||
})
|
||||
setTextColor(resources.getColor(android.R.color.white, context.theme))
|
||||
postDelayed({
|
||||
requestFocus()
|
||||
setSelection(text.length)
|
||||
context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE)
|
||||
.let { it as android.view.inputmethod.InputMethodManager }
|
||||
.showSoftInput(this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT)
|
||||
}, 200)
|
||||
}
|
||||
|
||||
this@ChatActionMenu.context.runOnUiThread {
|
||||
ViewAppearanceHelper.newAlertDialogBuilder(this@ChatActionMenu.context.mainActivity!!)
|
||||
.setPositiveButton("Save") { _, _ ->
|
||||
val newMessageContent = ProtoWriter().apply {
|
||||
from(2) { addString(1, editText.text.toString()) }
|
||||
}.toByteArray()
|
||||
message.messageContent?.content = newMessageContent
|
||||
messaging.conversationManager?.editMessage(
|
||||
message.messageDescriptor?.conversationId.toString(),
|
||||
message.messageDescriptor?.messageId ?: return@setPositiveButton,
|
||||
newMessageContent,
|
||||
onSuccess = {
|
||||
this@ChatActionMenu.context.coroutineScope.launch(Dispatchers.Main) {
|
||||
message.messageMetadata?.isEdited = true
|
||||
messaging.localUpdateMessage(
|
||||
message.messageDescriptor?.conversationId.toString(),
|
||||
message,
|
||||
forceUpdate = true
|
||||
)
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
this@ChatActionMenu.context.shortToast("Failed to edit message: $it")
|
||||
}
|
||||
)
|
||||
}
|
||||
.setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() }
|
||||
.setView(editText)
|
||||
.setTitle("Edit message content")
|
||||
.show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (context.config.experimental.convertMessageLocally.get()) {
|
||||
injectButton(Button(viewGroup.context).apply {
|
||||
text = this@ChatActionMenu.context.translation["chat_action_menu.convert_message"]
|
||||
|
||||
@@ -80,7 +80,7 @@ class NewChatActionMenu : AbstractMenu() {
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(5.dp),
|
||||
.padding(2.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Button(onClick = {
|
||||
|
||||
@@ -159,4 +159,20 @@ class ConversationManager(
|
||||
.override("onError") { onError(it.arg<Any>(0).toString()) }.build()
|
||||
getOneOnOneConversationIds.invoke(instanceNonNull(), userIds.map { it.toSnapUUID().instanceNonNull() }.toMutableList(), callback)
|
||||
}
|
||||
|
||||
fun editMessage(conversationId: String, messageId: Long, content: ByteArray, onSuccess: () -> Unit, onError: (error: String) -> Unit) {
|
||||
val editMessageMethod = instanceNonNull()::class.java.methods.first { it.name == "editMessage" }
|
||||
editMessageMethod.invoke(instanceNonNull(), editMessageMethod.parameterTypes[0].dataBuilder {
|
||||
set("mConversationId", conversationId.toSnapUUID().instanceNonNull())
|
||||
set("mMessageId", messageId)
|
||||
}, editMessageMethod.parameterTypes[1].dataBuilder {
|
||||
set("mContent", content)
|
||||
set("mMentionInfo", null)
|
||||
}, CallbackBuilder(getCallbackClass("Callback"))
|
||||
.override("onSuccess") { onSuccess() }
|
||||
.override("onError") { onError(it.arg<Any>(0).toString()) }.build()
|
||||
)
|
||||
}
|
||||
|
||||
fun isEditMessageSupported() = instanceNonNull()::class.java.methods.any { it.name == "editMessage" }
|
||||
}
|
||||
@@ -20,9 +20,23 @@ class MessageMetadata(obj: Any?) : AbstractWrapper(obj){
|
||||
@get:JSGetter @set:JSSetter
|
||||
var seenBy by field("mSeenBy", uuidArrayListMapper)
|
||||
@get:JSGetter @set:JSSetter
|
||||
var screenRecordedBy by field("mScreenRecordedBy", uuidArrayListMapper)
|
||||
@get:JSGetter @set:JSSetter
|
||||
var screenShottedBy by field("mScreenShottedBy", uuidArrayListMapper)
|
||||
@get:JSGetter @set:JSSetter
|
||||
var reactions by field("mReactions") {
|
||||
(it as ArrayList<*>).map { i -> UserIdToReaction(i) }.toMutableList()
|
||||
}
|
||||
@get:JSGetter @set:JSSetter
|
||||
var isSaveable by field<Boolean>("mIsSaveable")
|
||||
@get:JSGetter @set:JSSetter
|
||||
var isEditable by field<Boolean>("mIsEditable")
|
||||
@get:JSGetter @set:JSSetter
|
||||
var isEdited by field<Boolean>("mIsEdited")
|
||||
@get:JSGetter @set:JSSetter
|
||||
var isErasable by field<Boolean>("mIsErasable")
|
||||
@get:JSGetter @set:JSSetter
|
||||
var isFriendLinkPending by field<Boolean>("mIsFriendLinkPending")
|
||||
@get:JSGetter @set:JSSetter
|
||||
var isReactable by field<Boolean>("mIsReactable")
|
||||
}
|
||||
Reference in New Issue
Block a user