feat: prevent message list auto scroll

This commit is contained in:
rhunk 2023-12-03 12:42:57 +01:00
parent bfe367efd0
commit 79be5da030
4 changed files with 87 additions and 0 deletions

View File

@ -259,6 +259,10 @@
"name": "Enhanced Friend Map Nametags",
"description": "Improves the Nametags of friends on the Snapmap"
},
"prevent_message_list_auto_scroll": {
"name": "Prevent Message List Auto Scroll",
"description": "Prevents the message list from scrolling to the bottom when sending/receiving a message"
},
"streak_expiration_info": {
"name": "Show Streak Expiration Info",
"description": "Shows a Streak Expiration timer next to the Streaks counter"

View File

@ -29,6 +29,7 @@ class UserInterfaceTweaks : ConfigContainer() {
val snapPreview = boolean("snap_preview") { addNotices(FeatureNotice.UNSTABLE); requireRestart() }
val bootstrapOverride = container("bootstrap_override", BootstrapOverride()) { requireRestart() }
val mapFriendNameTags = boolean("map_friend_nametags") { requireRestart() }
val preventMessageListAutoScroll = boolean("prevent_message_list_auto_scroll") { requireRestart(); addNotices(FeatureNotice.UNSTABLE) }
val streakExpirationInfo = boolean("streak_expiration_info") { requireRestart() }
val hideFriendFeedEntry = boolean("hide_friend_feed_entry") { requireRestart() }
val hideStreakRestore = boolean("hide_streak_restore") { requireRestart() }

View File

@ -0,0 +1,80 @@
package me.rhunk.snapenhance.core.features.impl.tweaks
import android.view.View
import me.rhunk.snapenhance.core.event.events.impl.BindViewEvent
import me.rhunk.snapenhance.core.features.Feature
import me.rhunk.snapenhance.core.features.FeatureLoadParams
import me.rhunk.snapenhance.core.util.hook.HookStage
import me.rhunk.snapenhance.core.util.hook.hook
import me.rhunk.snapenhance.core.wrapper.impl.Message
import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID
class PreventMessageListAutoScroll : Feature("PreventMessageListAutoScroll", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) {
private var openedConversationId: String? = null
private val focusedMessages = mutableMapOf<View, Long>()
private var firstFocusedMessageId: Long? = null
private val delayedMessageUpdates = mutableListOf<() -> Unit>()
override fun onActivityCreate() {
if (!context.config.userInterface.preventMessageListAutoScroll.get()) return
context.mappings.getMappedClass("callbacks", "ConversationManagerDelegate").hook("onConversationUpdated", HookStage.BEFORE) { param ->
val updatedMessage = param.arg<ArrayList<*>>(2).map { Message(it) }.firstOrNull() ?: return@hook
if (openedConversationId != updatedMessage.messageDescriptor?.conversationId.toString()) return@hook
// cancel if the message is already in focus
if (focusedMessages.entries.any { entry -> entry.value == updatedMessage.messageDescriptor?.messageId && entry.key.isAttachedToWindow }) return@hook
val conversationLastMessages = context.database.getMessagesFromConversationId(
openedConversationId.toString(),
4
) ?: return@hook
if (conversationLastMessages.none {
focusedMessages.entries.any { entry -> entry.value == it.clientMessageId.toLong() && entry.key.isAttachedToWindow }
}) {
synchronized(delayedMessageUpdates) {
if (firstFocusedMessageId == null) firstFocusedMessageId = conversationLastMessages.lastOrNull()?.clientMessageId?.toLong()
delayedMessageUpdates.add {
param.invokeOriginal()
}
}
param.setResult(null)
}
}
context.classCache.conversationManager.apply {
hook("enterConversation", HookStage.BEFORE) { param ->
openedConversationId = SnapUUID(param.arg(0)).toString()
}
hook("exitConversation", HookStage.BEFORE) {
openedConversationId = null
firstFocusedMessageId = null
synchronized(focusedMessages) {
focusedMessages.clear()
}
synchronized(delayedMessageUpdates) {
delayedMessageUpdates.clear()
}
}
}
context.event.subscribe(BindViewEvent::class) { event ->
event.chatMessage { conversationId, messageId ->
if (conversationId != openedConversationId) return@chatMessage
synchronized(focusedMessages) {
focusedMessages[event.view] = messageId.toLong()
}
if (delayedMessageUpdates.isNotEmpty() && focusedMessages.entries.any { entry -> entry.value == firstFocusedMessageId && entry.key.isAttachedToWindow }) {
delayedMessageUpdates.apply {
synchronized(this) {
removeIf { it(); true }
firstFocusedMessageId = null
}
}
}
}
}
}
}

View File

@ -20,6 +20,7 @@ import me.rhunk.snapenhance.core.features.impl.spying.MessageLogger
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
import me.rhunk.snapenhance.core.features.impl.tweaks.BypassScreenshotDetection
import me.rhunk.snapenhance.core.features.impl.tweaks.CameraTweaks
import me.rhunk.snapenhance.core.features.impl.tweaks.PreventMessageListAutoScroll
import me.rhunk.snapenhance.core.features.impl.ui.*
import me.rhunk.snapenhance.core.logger.CoreLogger
import me.rhunk.snapenhance.core.manager.Manager
@ -111,6 +112,7 @@ class FeatureManager(
Stories::class,
DisableComposerModules::class,
FideliusIndicator::class,
PreventMessageListAutoScroll::class,
)
initializeFeatures()