Moves discovery and video lists to Outbox when Follows or relay lists are selected

This commit is contained in:
Vitor Pamplona
2024-08-07 16:39:24 -04:00
parent a1aaec0b7d
commit b026bffe4a
3 changed files with 52 additions and 72 deletions

View File

@@ -108,7 +108,6 @@ import com.vitorpamplona.quartz.signers.NostrSigner
import com.vitorpamplona.quartz.signers.NostrSignerExternal import com.vitorpamplona.quartz.signers.NostrSignerExternal
import com.vitorpamplona.quartz.signers.NostrSignerInternal import com.vitorpamplona.quartz.signers.NostrSignerInternal
import com.vitorpamplona.quartz.utils.DualCase import com.vitorpamplona.quartz.utils.DualCase
import com.vitorpamplona.quartz.utils.RelayListRecommendationProcessor
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -504,6 +503,8 @@ class Account(
userList: Map<HexKey, List<String>>, userList: Map<HexKey, List<String>>,
hasOnionConnection: Boolean = false, hasOnionConnection: Boolean = false,
): Map<String, List<HexKey>> { ): Map<String, List<HexKey>> {
checkNotInMainThread()
val authorsPerRelayUrl = mutableMapOf<String, MutableSet<HexKey>>() val authorsPerRelayUrl = mutableMapOf<String, MutableSet<HexKey>>()
val relayUrlsPerAuthor = mutableMapOf<HexKey, MutableSet<String>>() val relayUrlsPerAuthor = mutableMapOf<HexKey, MutableSet<String>>()
@@ -585,53 +586,6 @@ class Account(
liveHomeListAuthorsPerRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyMap()) liveHomeListAuthorsPerRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyMap())
} }
fun relaysFromPeopleListFlows(
currentFollowList: LiveFollowLists,
relayUrlsToIgnore: Set<String>,
): Flow<List<RelayListRecommendationProcessor.RelayRecommendation>> =
combine(
currentFollowList.users.map {
getNIP65RelayListFlow(it)
},
) { followsNIP65RelayLists ->
RelayListRecommendationProcessor
.reliableRelaySetFor(
followsNIP65RelayLists.mapNotNull {
(it.note.event as? AdvertisedRelayListEvent)
},
relayUrlsToIgnore,
hasOnionConnection = proxy != null,
).sortedByDescending { it.users.size }
}
@OptIn(ExperimentalCoroutinesApi::class)
val liveHomeFollowRelayFlow: Flow<List<RelayListRecommendationProcessor.RelayRecommendation>> by lazy {
combineTransform(liveHomeFollowListFlow, connectToRelaysFlow) { followList, existing ->
if (followList != null) {
emit(
relaysFromPeopleListFlows(
followList,
existing.mapNotNullTo(HashSet()) {
if (it.read && FeedType.FOLLOWS in it.feedTypes) {
it.url
} else {
null
}
},
),
)
} else {
emit(MutableStateFlow(emptyList()))
}
}.flatMapLatest {
it
}
}
val liveHomeFollowRelays: StateFlow<List<RelayListRecommendationProcessor.RelayRecommendation>> by lazy {
liveHomeFollowRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyList())
}
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private val liveNotificationList: Flow<ListNameNotePair> by lazy { private val liveNotificationList: Flow<ListNameNotePair> by lazy {
defaultNotificationFollowList.flatMapLatest { listName -> defaultNotificationFollowList.flatMapLatest { listName ->
@@ -656,6 +610,23 @@ class Account(
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex))) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} }
@OptIn(ExperimentalCoroutinesApi::class)
val liveStoriesListAuthorsPerRelayFlow: Flow<Map<String, List<String>>?> by lazy {
combineTransform(liveStoriesFollowLists, connectToRelaysFlow) { followList, existing ->
if (followList != null) {
emit(authorsPerRelay(followList.usersPlusMe, existing.filter { it.feedTypes.contains(FeedType.FOLLOWS) && it.read }.map { it.url }))
} else {
emit(MutableStateFlow(null))
}
}.flatMapLatest {
it
}
}
val liveStoriesListAuthorsPerRelay: StateFlow<Map<String, List<String>>?> by lazy {
liveStoriesListAuthorsPerRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyMap())
}
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private val liveDiscoveryList: Flow<ListNameNotePair> by lazy { private val liveDiscoveryList: Flow<ListNameNotePair> by lazy {
defaultDiscoveryFollowList.flatMapLatest { listName -> defaultDiscoveryFollowList.flatMapLatest { listName ->
@@ -668,6 +639,23 @@ class Account(
.stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex))) .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex)))
} }
@OptIn(ExperimentalCoroutinesApi::class)
val liveDiscoveryListAuthorsPerRelayFlow: Flow<Map<String, List<String>>?> by lazy {
combineTransform(liveDiscoveryFollowLists, connectToRelaysFlow) { followList, existing ->
if (followList != null) {
emit(authorsPerRelay(followList.usersPlusMe, existing.filter { it.read }.map { it.url }))
} else {
emit(MutableStateFlow(null))
}
}.flatMapLatest {
it
}
}
val liveDiscoveryListAuthorsPerRelay: StateFlow<Map<String, List<String>>?> by lazy {
liveDiscoveryListAuthorsPerRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyMap())
}
private fun decryptLiveFollows( private fun decryptLiveFollows(
listEvent: GeneralListEvent, listEvent: GeneralListEvent,
onReady: (LiveFollowLists) -> Unit, onReady: (LiveFollowLists) -> Unit,

View File

@@ -25,6 +25,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.relays.EOSEAccount import com.vitorpamplona.amethyst.service.relays.EOSEAccount
import com.vitorpamplona.ammolite.relays.FeedType import com.vitorpamplona.ammolite.relays.FeedType
import com.vitorpamplona.ammolite.relays.TypedFilter import com.vitorpamplona.ammolite.relays.TypedFilter
import com.vitorpamplona.ammolite.relays.filters.SinceAuthorPerRelayFilter
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
import com.vitorpamplona.quartz.events.AppDefinitionEvent import com.vitorpamplona.quartz.events.AppDefinitionEvent
import com.vitorpamplona.quartz.events.ChannelCreateEvent import com.vitorpamplona.quartz.events.ChannelCreateEvent
@@ -66,10 +67,7 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") {
} }
fun createMarketplaceFilter(): List<TypedFilter> { fun createMarketplaceFilter(): List<TypedFilter> {
val follows = val follows = account.liveDiscoveryListAuthorsPerRelay.value
account.liveDiscoveryFollowLists.value
?.users
?.toList()
val hashToLoad = val hashToLoad =
account.liveDiscoveryFollowLists.value account.liveDiscoveryFollowLists.value
?.hashtags ?.hashtags
@@ -81,9 +79,9 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") {
return listOfNotNull( return listOfNotNull(
TypedFilter( TypedFilter(
types = setOf(FeedType.GLOBAL), types = if (follows == null) setOf(FeedType.GLOBAL) else setOf(FeedType.FOLLOWS),
filter = filter =
SincePerRelayFilter( SinceAuthorPerRelayFilter(
authors = follows, authors = follows,
kinds = listOf(ClassifiedsEvent.KIND), kinds = listOf(ClassifiedsEvent.KIND),
limit = 300, limit = 300,
@@ -165,12 +163,14 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") {
?.users ?.users
?.toList() ?.toList()
val followsRelays = account.liveDiscoveryListAuthorsPerRelay.value
return listOfNotNull( return listOfNotNull(
TypedFilter( TypedFilter(
types = setOf(FeedType.GLOBAL), types = setOf(FeedType.GLOBAL),
filter = filter =
SincePerRelayFilter( SinceAuthorPerRelayFilter(
authors = follows, authors = followsRelays,
kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND), kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND),
limit = 300, limit = 300,
since = since =
@@ -200,17 +200,14 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") {
} }
fun createPublicChatFilter(): List<TypedFilter> { fun createPublicChatFilter(): List<TypedFilter> {
val follows = val follows = account.liveDiscoveryListAuthorsPerRelay.value
account.liveDiscoveryFollowLists.value
?.users
?.toList()
val followChats = account.selectedChatsFollowList().toList() val followChats = account.selectedChatsFollowList().toList()
return listOfNotNull( return listOfNotNull(
TypedFilter( TypedFilter(
types = setOf(FeedType.PUBLIC_CHATS), types = setOf(FeedType.PUBLIC_CHATS),
filter = filter =
SincePerRelayFilter( SinceAuthorPerRelayFilter(
authors = follows, authors = follows,
kinds = listOf(ChannelMessageEvent.KIND), kinds = listOf(ChannelMessageEvent.KIND),
limit = 500, limit = 500,
@@ -243,15 +240,12 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") {
} }
fun createCommunitiesFilter(): TypedFilter { fun createCommunitiesFilter(): TypedFilter {
val follows = val follows = account.liveDiscoveryListAuthorsPerRelay.value
account.liveDiscoveryFollowLists.value
?.users
?.toList()
return TypedFilter( return TypedFilter(
types = setOf(FeedType.GLOBAL), types = setOf(FeedType.GLOBAL),
filter = filter =
SincePerRelayFilter( SinceAuthorPerRelayFilter(
authors = follows, authors = follows,
kinds = listOf(CommunityDefinitionEvent.KIND, CommunityPostApprovalEvent.KIND), kinds = listOf(CommunityDefinitionEvent.KIND, CommunityPostApprovalEvent.KIND),
limit = 300, limit = 300,

View File

@@ -25,6 +25,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.relays.EOSEAccount import com.vitorpamplona.amethyst.service.relays.EOSEAccount
import com.vitorpamplona.ammolite.relays.FeedType import com.vitorpamplona.ammolite.relays.FeedType
import com.vitorpamplona.ammolite.relays.TypedFilter import com.vitorpamplona.ammolite.relays.TypedFilter
import com.vitorpamplona.ammolite.relays.filters.SinceAuthorPerRelayFilter
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
@@ -64,15 +65,12 @@ object NostrVideoDataSource : AmethystNostrDataSource("VideoFeed") {
} }
fun createContextualFilter(): TypedFilter { fun createContextualFilter(): TypedFilter {
val follows = val follows = account.liveStoriesListAuthorsPerRelay.value
account.liveStoriesFollowLists.value
?.users
?.toList()
return TypedFilter( return TypedFilter(
types = setOf(FeedType.GLOBAL), types = if (follows == null) setOf(FeedType.GLOBAL) else setOf(FeedType.FOLLOWS),
filter = filter =
SincePerRelayFilter( SinceAuthorPerRelayFilter(
authors = follows, authors = follows,
kinds = listOf(FileHeaderEvent.KIND, FileStorageHeaderEvent.KIND, VideoHorizontalEvent.KIND, VideoVerticalEvent.KIND), kinds = listOf(FileHeaderEvent.KIND, FileStorageHeaderEvent.KIND, VideoHorizontalEvent.KIND, VideoVerticalEvent.KIND),
limit = 200, limit = 200,