fix: record contact-card EOSEs from the d-tag so card syncs are incremental

UserCardsSubAssembler.newEose read the filter's p-tags to stamp each target
user's card EOSE, but kind:30382 filters address targets in the d-tag — so
no per-user EOSE was ever recorded, groupByRelayPresence always classified
users as never-checked, and every filter update re-downloaded the visible
users' cards with since = null. Read the d-tag instead, and use DTag.TAG_NAME
on both the filter builder and the EOSE reader so the two keys cannot drift
apart again. Pre-existing on main; surfaced while verifying that card syncs
are incremental from the account's outbox relays.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QdvE4LvgkSewJXyFzyyAUY
This commit is contained in:
Claude
2026-07-13 21:52:29 +00:00
parent e8ecf429a2
commit a6da2ee69d
2 changed files with 8 additions and 2 deletions
@@ -33,6 +33,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.tags.dTag.DTag
import com.vitorpamplona.quartz.utils.mapOfSet
class UserCardsSubAssembler(
@@ -46,7 +47,10 @@ class UserCardsSubAssembler(
filters: List<Filter>?,
) {
filters?.forEach { filter ->
filter.tags?.get("p")?.forEach {
// kind:30382 addresses the target user in the d-tag (the key the
// filter builder uses). Reading any other tag leaves the per-user
// EOSEs unset and forces full re-downloads with since = null.
filter.tags?.get(DTag.TAG_NAME)?.forEach {
val targetUser = cache.getUserIfExists(it)
targetUser?.cardsOrNull()?.latestEOSEs?.newEose(relay, time)
}
@@ -24,6 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.tags.dTag.DTag
import com.vitorpamplona.quartz.nip85TrustedAssertions.users.ContactCardEvent
val ContactCardKindList = listOf(ContactCardEvent.KIND)
@@ -46,7 +47,8 @@ fun filterContactCardsToTargetKeysFromTrustedAccountsInTheRelay(
Filter(
kinds = ContactCardKindList,
authors = trustedAccounts,
tags = mapOf("d" to targets.sorted()),
// kind:30382 addresses the target user in the d-tag
tags = mapOf(DTag.TAG_NAME to targets.sorted()),
since = since,
),
)