Migrates contact list management to addressable notes

Creates new observable flows for LocalCache.
This commit is contained in:
Vitor Pamplona
2026-02-06 16:12:11 -05:00
parent 130a83f0b9
commit ec629ff081
25 changed files with 563 additions and 590 deletions
@@ -21,13 +21,16 @@
package com.vitorpamplona.quartz.nip02FollowList
import androidx.compose.runtime.Stable
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.any
import com.vitorpamplona.quartz.nip01Core.hints.PubKeyHintProvider
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
import com.vitorpamplona.quartz.nip31Alts.AltTag
@@ -56,7 +59,11 @@ class ContactListEvent(
fun follows() = tags.mapNotNull(ContactTag::parseValid)
fun relays(): Map<NormalizedRelayUrl, ReadWrite>? {
fun isFollowing(pubKey: HexKey) = tags.any(ContactTag::isTagged, pubKey)
fun followCount() = tags.count(ContactTag::isTagged)
fun relays(): Map<NormalizedRelayUrl, ReadWrite> {
val regular = RelaySet.parse(content)
val normalized = mutableMapOf<NormalizedRelayUrl, ReadWrite>()
@@ -75,6 +82,12 @@ class ContactListEvent(
const val KIND = 3
const val ALT = "Follow List"
fun createAddress(pubKey: HexKey): Address = Address(KIND, pubKey)
fun createAddressATag(pubKey: HexKey): ATag = ATag(KIND, pubKey)
fun createAddressTag(pubKey: HexKey): String = Address.assemble(KIND, pubKey)
fun blockListFor(pubKeyHex: HexKey): String = "3:$pubKeyHex:"
suspend fun createFromScratch(
@@ -54,7 +54,12 @@ data class ContactTag(
companion object {
const val TAG_NAME = "p"
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64
fun isTagged(
tag: Array<String>,
pubkey: HexKey,
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == pubkey
fun parse(tag: Array<String>): ContactTag? {
ensure(tag.has(1)) { return null }