Brute forces the finding of events.
This commit is contained in:
@@ -95,6 +95,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.Entity
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NNote
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
||||
@@ -2928,7 +2929,7 @@ object LocalCache : ILocalCache {
|
||||
}
|
||||
getOrCreateUser(nip19.hex)
|
||||
}
|
||||
is com.vitorpamplona.quartz.nip19Bech32.entities.NNote -> {
|
||||
is NNote -> {
|
||||
getOrCreateNote(nip19.hex)
|
||||
}
|
||||
is NEvent -> {
|
||||
|
||||
+2
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.relayClient.reqCommand.event
|
||||
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.NoteEventLoaderSubAssembler
|
||||
@@ -29,6 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
// This allows multiple screen to be listening to tags, even the same tag
|
||||
class EventFinderQueryState(
|
||||
val note: Note,
|
||||
val account: Account,
|
||||
)
|
||||
|
||||
class EventFinderFilterAssembler(
|
||||
|
||||
+5
-3
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.event
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.relayClient.KeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -30,18 +31,19 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
fun EventFinderFilterAssemblerSubscription(
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
) = EventFinderFilterAssemblerSubscription(note, accountViewModel.dataSources().eventFinder)
|
||||
) = EventFinderFilterAssemblerSubscription(note, accountViewModel.account, accountViewModel.dataSources().eventFinder)
|
||||
|
||||
@Composable
|
||||
fun EventFinderFilterAssemblerSubscription(
|
||||
note: Note,
|
||||
account: Account,
|
||||
dataSource: EventFinderFilterAssembler,
|
||||
) {
|
||||
// different screens get different states
|
||||
// even if they are tracking the same tag.
|
||||
val state =
|
||||
remember(note) {
|
||||
EventFinderQueryState(note)
|
||||
remember(note, account) {
|
||||
EventFinderQueryState(note, account)
|
||||
}
|
||||
|
||||
KeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
+80
-31
@@ -22,64 +22,113 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders
|
||||
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.LocalCache.relayHints
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderQueryState
|
||||
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.addressables.Address
|
||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
|
||||
fun potentialRelaysToFindAddress(note: AddressableNote): Set<NormalizedRelayUrl> {
|
||||
val set = mutableSetOf<NormalizedRelayUrl>()
|
||||
|
||||
set.addAll(LocalCache.relayHints.hintsForAddress(note.idHex))
|
||||
|
||||
val isInChannel = note.channelHex()
|
||||
if (isInChannel != null) {
|
||||
LocalCache.checkGetOrCreateChannel(isInChannel)?.relays()?.forEach {
|
||||
set.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
note.replyTo?.map { parentNote ->
|
||||
set.addAll(parentNote.relays)
|
||||
|
||||
parentNote
|
||||
.channelHex()
|
||||
?.let { LocalCache.checkGetOrCreateChannel(it) }
|
||||
?.relays()
|
||||
?.forEach { set.add(it) }
|
||||
|
||||
parentNote.author?.inboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
note.replies.map { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
|
||||
childNote
|
||||
.channelHex()
|
||||
?.let { LocalCache.checkGetOrCreateChannel(it) }
|
||||
?.relays()
|
||||
?.forEach { set.add(it) }
|
||||
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
note.reactions.map { reactionType ->
|
||||
reactionType.value.forEach { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
}
|
||||
|
||||
note.boosts.map { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
fun filterMissingAddressables(keys: List<EventFinderQueryState>): List<RelayBasedFilter>? {
|
||||
val missingAddressables = mutableSetOf<Address>()
|
||||
|
||||
keys.forEach {
|
||||
if (it.note is AddressableNote && it.note.event == null) {
|
||||
missingAddressables.add(it.note.address())
|
||||
val addressesPerRelay =
|
||||
mapOfSet {
|
||||
keys.forEach { key ->
|
||||
val default = key.account.followPlusAllMine.flow.value
|
||||
if (key.note is AddressableNote && key.note.event == null) {
|
||||
potentialRelaysToFindAddress(key.note).ifEmpty { default }.forEach { relayUrl ->
|
||||
add(relayUrl, key.note.address)
|
||||
}
|
||||
}
|
||||
|
||||
// loads threading that is event-based
|
||||
it.note.replyTo?.forEach {
|
||||
if (it is AddressableNote && it.event == null) {
|
||||
missingAddressables.add(it.address())
|
||||
key.note.replyTo?.forEach { note ->
|
||||
if (note is AddressableNote && note.event == null) {
|
||||
potentialRelaysToFindAddress(note).ifEmpty { default }.forEach { relayUrl ->
|
||||
add(relayUrl, note.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filterMissingAddressables(missingAddressables)
|
||||
return filterMissingAddressables(addressesPerRelay)
|
||||
}
|
||||
|
||||
fun filterMissingAddressables(missingAddressables: Set<Address>): List<RelayBasedFilter> {
|
||||
fun filterMissingAddressables(missingAddressables: Map<NormalizedRelayUrl, Set<Address>>): List<RelayBasedFilter> {
|
||||
if (missingAddressables.isEmpty()) return emptyList()
|
||||
|
||||
return missingAddressables.flatMap { aTag ->
|
||||
val authorHomeRelayEventAddress = AdvertisedRelayListEvent.createAddressTag(aTag.pubKeyHex)
|
||||
val authorHomeRelayEvent = (LocalCache.getAddressableNoteIfExists(authorHomeRelayEventAddress)?.event as? AdvertisedRelayListEvent)
|
||||
|
||||
val authorHomeRelays =
|
||||
authorHomeRelayEvent?.writeRelaysNorm()?.ifEmpty { null }
|
||||
?: LocalCache.relayHints.hintsForKey(aTag.pubKeyHex).ifEmpty { null }
|
||||
?: listOfNotNull(LocalCache.getUserIfExists(aTag.pubKeyHex)?.latestMetadataRelay)
|
||||
|
||||
val relayHints = LocalCache.relayHints.hintsForAddress(aTag.toValue())
|
||||
|
||||
(authorHomeRelays + relayHints).toSet().map {
|
||||
if (aTag.kind < 25000 && aTag.dTag.isBlank()) {
|
||||
return missingAddressables.flatMap { relayEntry ->
|
||||
relayEntry.value.map { address ->
|
||||
if (address.kind < 25000 && address.dTag.isBlank()) {
|
||||
RelayBasedFilter(
|
||||
relay = it,
|
||||
relay = relayEntry.key,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(aTag.kind),
|
||||
authors = listOf(aTag.pubKeyHex),
|
||||
kinds = listOf(address.kind),
|
||||
authors = listOf(address.pubKeyHex),
|
||||
limit = 1,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
RelayBasedFilter(
|
||||
relay = it,
|
||||
relay = relayEntry.key,
|
||||
filter =
|
||||
Filter(
|
||||
kinds = listOf(aTag.kind),
|
||||
tags = mapOf("d" to listOf(aTag.dTag)),
|
||||
authors = listOf(aTag.pubKeyHex),
|
||||
kinds = listOf(address.kind),
|
||||
tags = mapOf("d" to listOf(address.dTag)),
|
||||
authors = listOf(address.pubKeyHex),
|
||||
limit = 1,
|
||||
),
|
||||
)
|
||||
|
||||
+75
-22
@@ -22,48 +22,101 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders
|
||||
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.LocalCache.relayHints
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderQueryState
|
||||
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.utils.mapOfSet
|
||||
|
||||
fun filterMissingEvents(keys: List<EventFinderQueryState>): List<RelayBasedFilter>? {
|
||||
val missingEvents = mutableSetOf<String>()
|
||||
fun potentialRelaysToFindEvent(note: Note): Set<NormalizedRelayUrl> {
|
||||
val set = mutableSetOf<NormalizedRelayUrl>()
|
||||
|
||||
keys.forEach {
|
||||
if (it.note !is AddressableNote && it.note.event == null) {
|
||||
missingEvents.add(it.note.idHex)
|
||||
set.addAll(LocalCache.relayHints.hintsForEvent(note.idHex))
|
||||
|
||||
val isInChannel = note.channelHex()
|
||||
if (isInChannel != null) {
|
||||
LocalCache.checkGetOrCreateChannel(isInChannel)?.relays()?.forEach {
|
||||
set.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
note.replyTo?.map { parentNote ->
|
||||
set.addAll(parentNote.relays)
|
||||
|
||||
parentNote
|
||||
.channelHex()
|
||||
?.let { LocalCache.checkGetOrCreateChannel(it) }
|
||||
?.relays()
|
||||
?.forEach { set.add(it) }
|
||||
|
||||
parentNote.author?.inboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
note.replies.map { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
|
||||
childNote
|
||||
.channelHex()
|
||||
?.let { LocalCache.checkGetOrCreateChannel(it) }
|
||||
?.relays()
|
||||
?.forEach { set.add(it) }
|
||||
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
note.reactions.map { reactionType ->
|
||||
reactionType.value.forEach { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
}
|
||||
|
||||
note.boosts.map { childNote ->
|
||||
set.addAll(childNote.relays)
|
||||
childNote.author?.outboxRelays()?.let { set.addAll(it) }
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
fun filterMissingEvents(keys: List<EventFinderQueryState>): List<RelayBasedFilter>? {
|
||||
val eventsPerRelay =
|
||||
mapOfSet {
|
||||
keys.forEach { key ->
|
||||
val default = key.account.followPlusAllMine.flow.value
|
||||
|
||||
if (key.note !is AddressableNote && key.note.event == null) {
|
||||
potentialRelaysToFindEvent(key.note).ifEmpty { default }.forEach { relayUrl ->
|
||||
add(relayUrl, key.note.idHex)
|
||||
}
|
||||
}
|
||||
|
||||
// loads threading that is event-based
|
||||
it.note.replyTo?.forEach {
|
||||
if (it !is AddressableNote && it.event == null) {
|
||||
missingEvents.add(it.idHex)
|
||||
key.note.replyTo?.forEach { note ->
|
||||
if (note !is AddressableNote && note.event == null) {
|
||||
potentialRelaysToFindEvent(note).ifEmpty { default }.forEach { relayUrl ->
|
||||
add(relayUrl, note.idHex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filterMissingEvents(missingEvents)
|
||||
return filterMissingEvents(eventsPerRelay)
|
||||
}
|
||||
|
||||
fun filterMissingEvents(missingEventIds: Set<HexKey>): List<RelayBasedFilter> {
|
||||
fun filterMissingEvents(missingEventIds: Map<NormalizedRelayUrl, Set<String>>): List<RelayBasedFilter> {
|
||||
if (missingEventIds.isEmpty()) return emptyList()
|
||||
|
||||
val relayHints =
|
||||
mapOfSet {
|
||||
missingEventIds.forEach { eventId ->
|
||||
LocalCache.relayHints.hintsForEvent(eventId).forEach { relayUrl ->
|
||||
add(relayUrl, eventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return relayHints.map {
|
||||
return missingEventIds.mapNotNull {
|
||||
if (it.value.isNotEmpty()) {
|
||||
RelayBasedFilter(
|
||||
relay = it.key,
|
||||
filter = Filter(ids = it.value.sorted()),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-7
@@ -26,6 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
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.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
import kotlin.collections.forEach
|
||||
@@ -36,28 +37,34 @@ val MetadataAndRelayListKinds =
|
||||
AdvertisedRelayListEvent.KIND,
|
||||
)
|
||||
|
||||
fun filterFindUserMetadataForKey(author: HexKey): List<RelayBasedFilter> =
|
||||
fun filterFindUserMetadataForKey(
|
||||
author: HexKey,
|
||||
defaultRelays: Set<NormalizedRelayUrl>,
|
||||
): List<RelayBasedFilter> =
|
||||
LocalCache.checkGetOrCreateUser(author)?.let {
|
||||
filterFindUserMetadataForKey(setOf(it))
|
||||
filterFindUserMetadataForKey(setOf(it), defaultRelays)
|
||||
} ?: emptyList()
|
||||
|
||||
fun filterFindUserMetadataForKey(authors: Set<User>): List<RelayBasedFilter> {
|
||||
fun filterFindUserMetadataForKey(
|
||||
authors: Set<User>,
|
||||
defaultRelays: Set<NormalizedRelayUrl>,
|
||||
): List<RelayBasedFilter> {
|
||||
val perRelayKeys =
|
||||
mapOfSet {
|
||||
authors.forEach { key ->
|
||||
val relays =
|
||||
key.authorRelayList()?.writeRelaysNorm()
|
||||
key.authorRelayList()?.writeRelaysNorm()?.ifEmpty { null }
|
||||
?: LocalCache.relayHints.hintsForKey(key.pubkeyHex).ifEmpty { null }
|
||||
?: key.relaysBeingUsed.keys
|
||||
?: (key.relaysBeingUsed.keys + defaultRelays).toList()
|
||||
|
||||
relays.forEach {
|
||||
add(it, key.pubkeyHex)
|
||||
}
|
||||
// TODO, DESPERATION: TRY ALL THE CONNECTED RELAYS.
|
||||
}
|
||||
}
|
||||
|
||||
return perRelayKeys.map {
|
||||
return perRelayKeys.mapNotNull {
|
||||
if (it.value.isNotEmpty()) {
|
||||
RelayBasedFilter(
|
||||
relay = it.key,
|
||||
filter =
|
||||
@@ -66,5 +73,8 @@ fun filterFindUserMetadataForKey(authors: Set<User>): List<RelayBasedFilter> {
|
||||
authors = it.value.sorted(),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -26,6 +26,7 @@ import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.SingleSubNoEo
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderQueryState
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
|
||||
class UserLoaderSubAssembler(
|
||||
client: NostrClient,
|
||||
@@ -42,7 +43,11 @@ class UserLoaderSubAssembler(
|
||||
}
|
||||
}
|
||||
|
||||
val defaultRelays = mutableSetOf<NormalizedRelayUrl>()
|
||||
|
||||
keys.mapTo(mutableSetOf()) { it.account }.forEach {
|
||||
defaultRelays.addAll(it.followPlusAllMine.flow.value)
|
||||
|
||||
it.kind3FollowList.flow.value.authors.forEach {
|
||||
val user = LocalCache.getOrCreateUser(it)
|
||||
if (user.latestMetadata == null) {
|
||||
@@ -55,7 +60,7 @@ class UserLoaderSubAssembler(
|
||||
|
||||
if (firstTimers.isEmpty()) return null
|
||||
|
||||
return filterFindUserMetadataForKey(firstTimers)
|
||||
return filterFindUserMetadataForKey(firstTimers, defaultRelays)
|
||||
}
|
||||
|
||||
override fun distinct(key: UserFinderQueryState) = key.user
|
||||
|
||||
+24
-11
@@ -20,17 +20,30 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.relayClient.searchCommand.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.filterMissingAddressables
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.potentialRelaysToFindAddress
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.ifEmpty
|
||||
|
||||
fun filterByAddress(address: NAddress) =
|
||||
filterMissingAddressables(
|
||||
setOf(
|
||||
Address(
|
||||
address.kind,
|
||||
address.author,
|
||||
address.dTag,
|
||||
),
|
||||
),
|
||||
)
|
||||
fun filterByAddress(
|
||||
address: NAddress,
|
||||
default: Set<NormalizedRelayUrl>,
|
||||
): List<RelayBasedFilter> {
|
||||
val note = LocalCache.getOrCreateAddressableNote(address.address())
|
||||
|
||||
val list =
|
||||
mapOfSet {
|
||||
if (note.event == null) {
|
||||
potentialRelaysToFindAddress(note).ifEmpty { default }.forEach { relayUrl ->
|
||||
add(relayUrl, note.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filterMissingAddressables(list)
|
||||
}
|
||||
|
||||
+5
-1
@@ -22,5 +22,9 @@ package com.vitorpamplona.amethyst.service.relayClient.searchCommand.subassembli
|
||||
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.loaders.filterFindUserMetadataForKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
|
||||
fun filterByAuthor(pubKey: HexKey) = filterFindUserMetadataForKey(pubKey)
|
||||
fun filterByAuthor(
|
||||
pubKey: HexKey,
|
||||
defaultRelays: Set<NormalizedRelayUrl>,
|
||||
) = filterFindUserMetadataForKey(pubKey, defaultRelays)
|
||||
|
||||
+34
-1
@@ -20,7 +20,40 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.relayClient.searchCommand.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.filterMissingEvents
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.potentialRelaysToFindEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.ifEmpty
|
||||
|
||||
fun filterByEvent(eventId: HexKey) = filterMissingEvents(setOf(eventId))
|
||||
fun filterByEvent(
|
||||
eventId: HexKey,
|
||||
default: Set<NormalizedRelayUrl>,
|
||||
): List<RelayBasedFilter> {
|
||||
val note = LocalCache.checkGetOrCreateNote(eventId) ?: return emptyList()
|
||||
|
||||
val list =
|
||||
mapOfSet {
|
||||
if (note !is AddressableNote && note.event == null) {
|
||||
potentialRelaysToFindEvent(note).ifEmpty { default }.forEach { relayUrl ->
|
||||
add(relayUrl, note.idHex)
|
||||
}
|
||||
}
|
||||
|
||||
// loads threading that is event-based
|
||||
note.replyTo?.forEach { parentNote ->
|
||||
if (parentNote !is AddressableNote && note.event == null) {
|
||||
potentialRelaysToFindEvent(note).ifEmpty { default }.forEach { relayUrl ->
|
||||
add(relayUrl, note.idHex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filterMissingEvents(list)
|
||||
}
|
||||
|
||||
+10
-8
@@ -55,25 +55,27 @@ class SearchWatcherSubAssembler(
|
||||
|
||||
if (mySearchString.isBlank()) return null
|
||||
|
||||
val defaultRelays = key.account.followPlusAllMine.flow.value
|
||||
|
||||
val directFilters =
|
||||
runCatching {
|
||||
if (Hex.isHex(mySearchString)) {
|
||||
val key = Hex.decode(mySearchString).toHexKey()
|
||||
filterByAuthor(key) + filterByEvent(key)
|
||||
val hexKey = Hex.decode(mySearchString).toHexKey()
|
||||
filterByAuthor(hexKey, defaultRelays) + filterByEvent(hexKey, defaultRelays)
|
||||
} else {
|
||||
val parsed = Nip19Parser.uriToRoute(mySearchString)?.entity
|
||||
if (parsed != null) {
|
||||
cache.consume(parsed)
|
||||
|
||||
when (parsed) {
|
||||
is NSec -> filterByAuthor(parsed.toPubKeyHex())
|
||||
is NPub -> filterByAuthor(parsed.hex)
|
||||
is NProfile -> filterByAuthor(parsed.hex)
|
||||
is NNote -> filterByEvent(parsed.hex)
|
||||
is NEvent -> filterByEvent(parsed.hex)
|
||||
is NSec -> filterByAuthor(parsed.toPubKeyHex(), defaultRelays)
|
||||
is NPub -> filterByAuthor(parsed.hex, defaultRelays)
|
||||
is NProfile -> filterByAuthor(parsed.hex, defaultRelays)
|
||||
is NNote -> filterByEvent(parsed.hex, defaultRelays)
|
||||
is NEvent -> filterByEvent(parsed.hex, defaultRelays)
|
||||
is NEmbed -> emptyList()
|
||||
is NRelay -> emptyList()
|
||||
is NAddress -> filterByAddress(parsed)
|
||||
is NAddress -> filterByAddress(parsed, defaultRelays)
|
||||
else -> emptyList()
|
||||
}
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ fun ThreadScreen(
|
||||
)
|
||||
|
||||
WatchLifecycleAndUpdateModel(feedViewModel)
|
||||
ThreadFilterAssemblerSubscription(noteId, accountViewModel.dataSources().thread)
|
||||
ThreadFilterAssemblerSubscription(noteId, accountViewModel)
|
||||
|
||||
LoadNote(noteId, accountViewModel) {
|
||||
if (it != null) {
|
||||
|
||||
+2
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources
|
||||
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.subassembies.ThreadEventLoaderSubAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.subassembies.ThreadFilterSubAssembler
|
||||
@@ -29,6 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
|
||||
// This allows multiple screen to be listening to tags, even the same tag
|
||||
class ThreadQueryState(
|
||||
val eventId: HexKey,
|
||||
val account: Account,
|
||||
)
|
||||
|
||||
class ThreadFilterAssembler(
|
||||
|
||||
+14
-1
@@ -22,19 +22,32 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.relayClient.KeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
|
||||
@Composable
|
||||
fun ThreadFilterAssemblerSubscription(
|
||||
eventId: HexKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
) = ThreadFilterAssemblerSubscription(
|
||||
eventId,
|
||||
accountViewModel.account,
|
||||
accountViewModel.dataSources().thread,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ThreadFilterAssemblerSubscription(
|
||||
eventId: HexKey,
|
||||
account: Account,
|
||||
filterAssembler: ThreadFilterAssembler,
|
||||
) {
|
||||
// different screens get different states
|
||||
// even if they are tracking the same tag.
|
||||
val state =
|
||||
remember(eventId) {
|
||||
ThreadQueryState(eventId)
|
||||
ThreadQueryState(eventId, account)
|
||||
}
|
||||
|
||||
KeyDataSourceSubscription(state, filterAssembler)
|
||||
|
||||
+38
-18
@@ -24,34 +24,54 @@ import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.ThreadAssembler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.filterMissingAddressables
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.filterMissingEvents
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.potentialRelaysToFindAddress
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.loaders.potentialRelaysToFindEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.utils.mapOfSet
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.ifEmpty
|
||||
|
||||
fun filterMissingEventsForThread(threadInfo: ThreadAssembler.ThreadInfo): List<RelayBasedFilter> {
|
||||
val missingEvents = mutableSetOf<HexKey>()
|
||||
val missingAddresses = mutableSetOf<Address>()
|
||||
|
||||
if (threadInfo.root.event == null) {
|
||||
if (threadInfo.root is AddressableNote) {
|
||||
missingAddresses.add(threadInfo.root.address)
|
||||
} else {
|
||||
missingEvents.add(threadInfo.root.idHex)
|
||||
fun filterMissingEventsForThread(
|
||||
threadInfo: ThreadAssembler.ThreadInfo,
|
||||
defaultRelays: Set<NormalizedRelayUrl>,
|
||||
): List<RelayBasedFilter> {
|
||||
val missingEvents =
|
||||
mapOfSet {
|
||||
if (threadInfo.root.event == null && threadInfo.root !is AddressableNote) {
|
||||
potentialRelaysToFindEvent(threadInfo.root).ifEmpty { defaultRelays }.forEach { relayUrl ->
|
||||
add(relayUrl, threadInfo.root.idHex)
|
||||
}
|
||||
}
|
||||
|
||||
threadInfo.allNotes.forEach {
|
||||
if (it.event == null) {
|
||||
if (it is AddressableNote) {
|
||||
missingAddresses.add(it.address)
|
||||
} else {
|
||||
missingEvents.add(it.idHex)
|
||||
if (it !is AddressableNote && it.event == null) {
|
||||
potentialRelaysToFindEvent(it).ifEmpty { defaultRelays }.forEach { relayUrl ->
|
||||
add(relayUrl, it.idHex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val missingEventsFilter = filterMissingEvents(missingEvents) ?: emptyList()
|
||||
val missingAddressFilter = filterMissingAddressables(missingAddresses) ?: emptyList()
|
||||
val missingAddresses =
|
||||
mapOfSet {
|
||||
if (threadInfo.root.event == null && threadInfo.root is AddressableNote) {
|
||||
potentialRelaysToFindEvent(threadInfo.root).ifEmpty { defaultRelays }.forEach { relayUrl ->
|
||||
add(relayUrl, threadInfo.root.address)
|
||||
}
|
||||
}
|
||||
|
||||
threadInfo.allNotes.forEach {
|
||||
if (it is AddressableNote && it.event == null) {
|
||||
potentialRelaysToFindAddress(it).ifEmpty { defaultRelays }.forEach { relayUrl ->
|
||||
add(relayUrl, it.address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val missingEventsFilter = filterMissingEvents(missingEvents)
|
||||
val missingAddressFilter = filterMissingAddressables(missingAddresses)
|
||||
|
||||
return missingEventsFilter + missingAddressFilter
|
||||
}
|
||||
|
||||
+2
-1
@@ -45,7 +45,8 @@ class ThreadEventLoaderSubAssembler(
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
val branches = ThreadAssembler().findThreadFor(key.eventId) ?: return null
|
||||
return filterMissingEventsForThread(branches)
|
||||
val defaultRelays = key.account.followPlusAllMine.flow.value
|
||||
return filterMissingEventsForThread(branches, defaultRelays)
|
||||
}
|
||||
|
||||
override fun id(key: ThreadQueryState) = key.eventId
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.quartz.nip01Core.relay.filters
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
||||
|
||||
class Filter(
|
||||
val ids: List<String>? = null,
|
||||
@@ -48,22 +47,13 @@ class Filter(
|
||||
search: String? = this.search,
|
||||
) = Filter(ids, authors, kinds, tags, since, until, limit, search)
|
||||
|
||||
init {
|
||||
// TODO: Remove this in production
|
||||
ids?.forEach {
|
||||
if (it.length != 64) throw IllegalArgumentException("Invalid id length $it on ${toJson()}")
|
||||
}
|
||||
authors?.forEach {
|
||||
if (it.length != 64) throw IllegalArgumentException("Invalid author length $it on ${toJson()}")
|
||||
}
|
||||
tags?.get("#p")?.forEach {
|
||||
if (it.length != 64) throw IllegalArgumentException("Invalid p-tag length $it on ${toJson()}")
|
||||
}
|
||||
tags?.get("#e")?.forEach {
|
||||
if (it.length != 64) throw IllegalArgumentException("Invalid e-tag length $it on ${toJson()}")
|
||||
}
|
||||
tags?.get("#a")?.forEach {
|
||||
if (Address.parse(it) != null) throw IllegalArgumentException("Invalid a-tag length $it on ${toJson()}")
|
||||
}
|
||||
}
|
||||
fun isFilledFilter() =
|
||||
(ids != null && ids.isNotEmpty()) ||
|
||||
(authors != null && authors.isNotEmpty()) ||
|
||||
(kinds != null && kinds.isNotEmpty()) ||
|
||||
(tags != null && tags.isNotEmpty()) ||
|
||||
(since != null) ||
|
||||
(until != null) ||
|
||||
(limit != null) ||
|
||||
(search != null && search.isNotEmpty())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user