mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-05-29 13:00:17 +02:00
feat(auto_mark_as_read): snap reply
This commit is contained in:
parent
6e7aa7c498
commit
785fb49aa9
@ -490,7 +490,7 @@
|
||||
},
|
||||
"auto_mark_as_read": {
|
||||
"name": "Auto Mark as Read",
|
||||
"description": "Automatically marks messages as read when sending a message to a conversation, even when Stealth Mode is enabled"
|
||||
"description": "Automatically marks messages/snaps as read even when Stealth Mode is enabled"
|
||||
},
|
||||
"loop_media_playback": {
|
||||
"name": "Loop Media Playback",
|
||||
@ -1092,6 +1092,10 @@
|
||||
"location_indicator": "Adds a \uD83D\uDCCD icon to snaps when they have been sent with location enabled",
|
||||
"ovf_editor_indicator": "Indicates if a snap has been sent using OVF Editor",
|
||||
"director_mode_indicator": "Adds a \u270F\uFE0F icon to snaps when they have been sent using Director Mode, which can be used to send gallery images as snaps"
|
||||
},
|
||||
"auto_mark_as_read": {
|
||||
"conversation_read": "Mark conversation as read when sending a message",
|
||||
"snap_reply": "Mark snaps as read when replying to them"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -55,7 +55,7 @@ class MessagingTweaks : ConfigContainer() {
|
||||
val hideBitmojiPresence = boolean("hide_bitmoji_presence")
|
||||
val hideTypingNotifications = boolean("hide_typing_notifications")
|
||||
val unlimitedSnapViewTime = boolean("unlimited_snap_view_time")
|
||||
val autoMarkAsRead = boolean("auto_mark_as_read") { requireRestart() }
|
||||
val autoMarkAsRead = multiple("auto_mark_as_read", "snap_reply", "conversation_read") { requireRestart() }
|
||||
val loopMediaPlayback = boolean("loop_media_playback") { requireRestart() }
|
||||
val disableReplayInFF = boolean("disable_replay_in_ff")
|
||||
val halfSwipeNotifier = container("half_swipe_notifier", HalfSwipeNotifierConfig()) { requireRestart()}
|
||||
|
@ -1,12 +1,26 @@
|
||||
package me.rhunk.snapenhance.core.features.impl.messaging
|
||||
|
||||
import android.widget.ProgressBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.WarningAmber
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.common.data.ContentType
|
||||
import me.rhunk.snapenhance.common.data.MessageUpdate
|
||||
import me.rhunk.snapenhance.core.event.events.impl.SendMessageWithContentEvent
|
||||
import me.rhunk.snapenhance.core.features.Feature
|
||||
import me.rhunk.snapenhance.core.features.FeatureLoadParams
|
||||
import me.rhunk.snapenhance.core.features.impl.spying.StealthMode
|
||||
import me.rhunk.snapenhance.core.ui.ViewAppearanceHelper
|
||||
import me.rhunk.snapenhance.core.util.ktx.getObjectFieldOrNull
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.random.Random
|
||||
|
||||
class AutoMarkAsRead : Feature("Auto Mark As Read", loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||
val isEnabled by lazy { context.config.messaging.autoMarkAsRead.get() }
|
||||
val canMarkConversationAsRead by lazy { context.config.messaging.autoMarkAsRead.get().contains("conversation_read") }
|
||||
|
||||
fun markConversationsAsRead(conversationIds: List<String>) {
|
||||
conversationIds.forEach { conversationId ->
|
||||
@ -20,12 +34,71 @@ class AutoMarkAsRead : Feature("Auto Mark As Read", loadParams = FeatureLoadPara
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun markSnapAsSeen(conversationId: String, clientMessageId: Long) {
|
||||
suspendCoroutine { continuation ->
|
||||
context.feature(Messaging::class).conversationManager?.updateMessage(conversationId, clientMessageId, MessageUpdate.READ) {
|
||||
continuation.resume(Unit)
|
||||
if (it != null && it != "DUPLICATEREQUEST") {
|
||||
context.log.error("Error marking message as read $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun markSnapsAsSeen(conversationId: String) {
|
||||
val messaging = context.feature(Messaging::class)
|
||||
val messageIds = messaging.getFeedCachedMessageIds(conversationId)?.takeIf { it.isNotEmpty() } ?: run {
|
||||
context.inAppOverlay.showStatusToast(
|
||||
Icons.Default.WarningAmber,
|
||||
context.translation["mark_as_seen.no_unseen_snaps_toast"]
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var job: Job? = null
|
||||
val dialog = ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity)
|
||||
.setTitle("Processing...")
|
||||
.setView(ProgressBar(context.mainActivity).apply {
|
||||
setPadding(10, 10, 10, 10)
|
||||
})
|
||||
.setOnDismissListener { job?.cancel() }
|
||||
.show()
|
||||
|
||||
context.coroutineScope.launch(Dispatchers.IO) {
|
||||
messageIds.forEach { messageId ->
|
||||
markSnapAsSeen(conversationId, messageId)
|
||||
delay(Random.nextLong(20, 60))
|
||||
context.runOnUiThread {
|
||||
dialog.setTitle("Processing... (${messageIds.indexOf(messageId) + 1}/${messageIds.size})")
|
||||
}
|
||||
}
|
||||
}.also { job = it }.invokeOnCompletion {
|
||||
context.runOnUiThread {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
if (!isEnabled) return
|
||||
val config by context.config.messaging.autoMarkAsRead
|
||||
if (config.isEmpty()) return
|
||||
|
||||
context.event.subscribe(SendMessageWithContentEvent::class) { event ->
|
||||
event.addCallbackResult("onSuccess") {
|
||||
markConversationsAsRead(event.destinations.conversations?.map { it.toString() } ?: return@addCallbackResult)
|
||||
if (canMarkConversationAsRead) {
|
||||
markConversationsAsRead(event.destinations.conversations?.map { it.toString() } ?: return@addCallbackResult)
|
||||
}
|
||||
|
||||
if (config.contains("snap_reply")) {
|
||||
val quotedMessageId = event.messageContent.instanceNonNull().getObjectFieldOrNull("mQuotedMessageId") as? Long ?: return@addCallbackResult
|
||||
val message = context.database.getConversationMessageFromId(quotedMessageId) ?: return@addCallbackResult
|
||||
|
||||
if (message.contentType == ContentType.SNAP.id) {
|
||||
context.coroutineScope.launch {
|
||||
markSnapAsSeen(event.destinations.conversations?.firstOrNull()?.toString() ?: return@launch, quotedMessageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN
|
||||
}, onSuccess = {
|
||||
context.coroutineScope.launch(coroutineDispatcher) {
|
||||
appendNotificationText("${myUser.displayName ?: myUser.mutableUsername}: $input")
|
||||
context.feature(AutoMarkAsRead::class).takeIf { it.isEnabled }?.markConversationsAsRead(listOf(conversationId))
|
||||
context.feature(AutoMarkAsRead::class).takeIf { it.canMarkConversationAsRead }?.markConversationsAsRead(listOf(conversationId))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -8,30 +8,21 @@ import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Switch
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircleOutline
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.NotInterested
|
||||
import androidx.compose.material.icons.filled.WarningAmber
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.common.data.ContentType
|
||||
import me.rhunk.snapenhance.common.data.FriendLinkType
|
||||
import me.rhunk.snapenhance.common.data.MessageUpdate
|
||||
import me.rhunk.snapenhance.common.database.impl.ConversationMessage
|
||||
import me.rhunk.snapenhance.common.database.impl.FriendInfo
|
||||
import me.rhunk.snapenhance.common.database.impl.UserConversationLink
|
||||
import me.rhunk.snapenhance.common.scripting.ui.EnumScriptInterface
|
||||
import me.rhunk.snapenhance.common.scripting.ui.InterfaceManager
|
||||
import me.rhunk.snapenhance.common.scripting.ui.ScriptInterface
|
||||
@ -39,6 +30,7 @@ import me.rhunk.snapenhance.common.ui.createComposeView
|
||||
import me.rhunk.snapenhance.common.util.protobuf.ProtoReader
|
||||
import me.rhunk.snapenhance.common.util.snap.BitmojiSelfie
|
||||
import me.rhunk.snapenhance.core.features.impl.experiments.EndToEndEncryption
|
||||
import me.rhunk.snapenhance.core.features.impl.messaging.AutoMarkAsRead
|
||||
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
|
||||
@ -53,9 +45,6 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.random.Random
|
||||
|
||||
class FriendFeedInfoMenu : AbstractMenu() {
|
||||
private fun getImageDrawable(url: String): Drawable {
|
||||
@ -131,47 +120,6 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun markAsSeen(conversationId: String) {
|
||||
val messaging = context.feature(Messaging::class)
|
||||
val messageIds = messaging.getFeedCachedMessageIds(conversationId)?.takeIf { it.isNotEmpty() } ?: run {
|
||||
context.inAppOverlay.showStatusToast(
|
||||
Icons.Default.WarningAmber,
|
||||
context.translation["mark_as_seen.no_unseen_snaps_toast"]
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var job: Job? = null
|
||||
val dialog = ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity)
|
||||
.setTitle("Processing...")
|
||||
.setView(ProgressBar(context.mainActivity).apply {
|
||||
setPadding(10, 10, 10, 10)
|
||||
})
|
||||
.setOnDismissListener { job?.cancel() }
|
||||
.show()
|
||||
|
||||
context.coroutineScope.launch(Dispatchers.IO) {
|
||||
messageIds.forEach { messageId ->
|
||||
suspendCoroutine { continuation ->
|
||||
messaging.conversationManager?.updateMessage(conversationId, messageId, MessageUpdate.READ) {
|
||||
continuation.resume(Unit)
|
||||
if (it != null && it != "DUPLICATEREQUEST") {
|
||||
context.log.error("Error marking message as read $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
delay(Random.nextLong(20, 60))
|
||||
context.runOnUiThread {
|
||||
dialog.setTitle("Processing... (${messageIds.indexOf(messageId) + 1}/${messageIds.size})")
|
||||
}
|
||||
}
|
||||
}.also { job = it }.invokeOnCompletion {
|
||||
context.runOnUiThread {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPreview(userId: String?, conversationId: String) {
|
||||
//query message
|
||||
val messageLogger = context.feature(MessageLogger::class)
|
||||
@ -324,8 +272,10 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
||||
isSoundEffectsEnabled = false
|
||||
applyTheme(view.width, hasRadius = true)
|
||||
setOnClickListener {
|
||||
this@FriendFeedInfoMenu.context.mainActivity?.triggerRootCloseTouchEvent()
|
||||
markAsSeen(conversationId)
|
||||
this@FriendFeedInfoMenu.context.apply {
|
||||
mainActivity?.triggerRootCloseTouchEvent()
|
||||
feature(AutoMarkAsRead::class).markSnapsAsSeen(conversationId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user