Merge d74fb7d82cf2d4511c9f0c91402ca0db3e37ca39 into 1f24973b06ee77492742ba522002e028f38f4381

This commit is contained in:
Taksh Kothari 2026-11-08 11:44:59 +05:30 committed by GitHub
commit dbb62069ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 37 additions and 14 deletions

View File

@ -23,11 +23,15 @@ data class NostrFilter(
* Create filter for NIP-17 gift wraps
*/
fun giftWrapsFor(pubkey: String, since: Long? = null): NostrFilter {
// Each delivered+read DM is typically three gift wraps (message, delivery
// ack, read receipt). A limit of 100 therefore covers only ~33 messages
// inside the lookback, and NIP-01 returns the newest-by-created_at which
// is randomized — so loss is permanent. Match the geohash fetch budget.
return NostrFilter(
kinds = listOf(NostrKind.GIFT_WRAP),
since = since?.let { (it / 1000).toInt() },
tagFilters = mapOf("p" to listOf(pubkey)),
limit = 100
limit = 1000
)
}

View File

@ -289,18 +289,33 @@ class MessageManager(private val state: ChatState) {
}
}
/**
* A replayed receipt is the same state even when `Read`/`Delivered` carry
* a new timestamp. Rewriting the chat list for that copies every thread
* onto main during a cold-start backlog.
*/
private fun isUnchangedDelivery(current: DeliveryStatus?, updated: DeliveryStatus?): Boolean {
if (current === updated || current == updated) return true
return when {
current is DeliveryStatus.Read && updated is DeliveryStatus.Read -> true
current is DeliveryStatus.Delivered && updated is DeliveryStatus.Delivered -> true
else -> false
}
}
fun updateMessageDeliveryStatus(messageID: String, status: DeliveryStatus) {
// Update in private chats
// Update in private chats. Copy a thread only when one of its messages
// actually changes, so a no-op receipt does not rebuild the map.
val updatedPrivateChats = state.getPrivateChatsValue().toMutableMap()
var updated = false
updatedPrivateChats.forEach { (peerID, messages) ->
val updatedMessages = messages.toMutableList()
val messageIndex = updatedMessages.indexOfFirst { it.id == messageID }
val messageIndex = messages.indexOfFirst { it.id == messageID }
if (messageIndex >= 0) {
val current = updatedMessages[messageIndex].deliveryStatus
val current = messages[messageIndex].deliveryStatus
val finalStatus = chooseStatus(current, status)
if (finalStatus !== current) {
if (!isUnchangedDelivery(current, finalStatus)) {
val updatedMessages = messages.toMutableList()
updatedMessages[messageIndex] = updatedMessages[messageIndex].copy(deliveryStatus = finalStatus)
updatedPrivateChats[peerID] = updatedMessages
updated = true
@ -320,27 +335,31 @@ class MessageManager(private val state: ChatState) {
if (messageIndex >= 0) {
val current = updatedMessages[messageIndex].deliveryStatus
val finalStatus = chooseStatus(current, status)
if (finalStatus !== current) {
if (!isUnchangedDelivery(current, finalStatus)) {
updatedMessages[messageIndex] = updatedMessages[messageIndex].copy(deliveryStatus = finalStatus)
state.setMessages(updatedMessages)
}
}
// Update in channel messages
val updatedChannelMessages = state.getChannelMessagesValue().toMutableMap()
var channelUpdated = false
updatedChannelMessages.forEach { (channel, messages) ->
val channelMessagesList = messages.toMutableList()
val channelMessageIndex = channelMessagesList.indexOfFirst { it.id == messageID }
val channelMessageIndex = messages.indexOfFirst { it.id == messageID }
if (channelMessageIndex >= 0) {
val current = channelMessagesList[channelMessageIndex].deliveryStatus
val current = messages[channelMessageIndex].deliveryStatus
val finalStatus = chooseStatus(current, status)
if (finalStatus !== current) {
if (!isUnchangedDelivery(current, finalStatus)) {
val channelMessagesList = messages.toMutableList()
channelMessagesList[channelMessageIndex] = channelMessagesList[channelMessageIndex].copy(deliveryStatus = finalStatus)
updatedChannelMessages[channel] = channelMessagesList
channelUpdated = true
}
}
}
state.setChannelMessages(updatedChannelMessages)
if (channelUpdated) {
state.setChannelMessages(updatedChannelMessages)
}
}
// Drop a blocked sender's messages from every geohash timeline