diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 8042abcb0..ad8e1faf6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -108,7 +108,6 @@ import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSignerExternal import com.vitorpamplona.quartz.signers.NostrSignerInternal import com.vitorpamplona.quartz.utils.DualCase -import com.vitorpamplona.quartz.utils.RelayListRecommendationProcessor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers @@ -504,6 +503,8 @@ class Account( userList: Map>, hasOnionConnection: Boolean = false, ): Map> { + checkNotInMainThread() + val authorsPerRelayUrl = mutableMapOf>() val relayUrlsPerAuthor = mutableMapOf>() @@ -585,53 +586,6 @@ class Account( liveHomeListAuthorsPerRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyMap()) } - fun relaysFromPeopleListFlows( - currentFollowList: LiveFollowLists, - relayUrlsToIgnore: Set, - ): Flow> = - 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> 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> by lazy { - liveHomeFollowRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyList()) - } - @OptIn(ExperimentalCoroutinesApi::class) private val liveNotificationList: Flow by lazy { defaultNotificationFollowList.flatMapLatest { listName -> @@ -656,6 +610,23 @@ class Account( .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex))) } + @OptIn(ExperimentalCoroutinesApi::class) + val liveStoriesListAuthorsPerRelayFlow: Flow>?> 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>?> by lazy { + liveStoriesListAuthorsPerRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyMap()) + } + @OptIn(ExperimentalCoroutinesApi::class) private val liveDiscoveryList: Flow by lazy { defaultDiscoveryFollowList.flatMapLatest { listName -> @@ -668,6 +639,23 @@ class Account( .stateIn(scope, SharingStarted.Eagerly, LiveFollowLists(usersPlusMe = setOf(keyPair.pubKeyHex))) } + @OptIn(ExperimentalCoroutinesApi::class) + val liveDiscoveryListAuthorsPerRelayFlow: Flow>?> 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>?> by lazy { + liveDiscoveryListAuthorsPerRelayFlow.stateIn(scope, SharingStarted.Eagerly, emptyMap()) + } + private fun decryptLiveFollows( listEvent: GeneralListEvent, onReady: (LiveFollowLists) -> Unit, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrDiscoveryDataSource.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrDiscoveryDataSource.kt index b1bb432be..9b63a070a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrDiscoveryDataSource.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrDiscoveryDataSource.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.relays.EOSEAccount import com.vitorpamplona.ammolite.relays.FeedType import com.vitorpamplona.ammolite.relays.TypedFilter +import com.vitorpamplona.ammolite.relays.filters.SinceAuthorPerRelayFilter import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter import com.vitorpamplona.quartz.events.AppDefinitionEvent import com.vitorpamplona.quartz.events.ChannelCreateEvent @@ -66,10 +67,7 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") { } fun createMarketplaceFilter(): List { - val follows = - account.liveDiscoveryFollowLists.value - ?.users - ?.toList() + val follows = account.liveDiscoveryListAuthorsPerRelay.value val hashToLoad = account.liveDiscoveryFollowLists.value ?.hashtags @@ -81,9 +79,9 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") { return listOfNotNull( TypedFilter( - types = setOf(FeedType.GLOBAL), + types = if (follows == null) setOf(FeedType.GLOBAL) else setOf(FeedType.FOLLOWS), filter = - SincePerRelayFilter( + SinceAuthorPerRelayFilter( authors = follows, kinds = listOf(ClassifiedsEvent.KIND), limit = 300, @@ -165,12 +163,14 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") { ?.users ?.toList() + val followsRelays = account.liveDiscoveryListAuthorsPerRelay.value + return listOfNotNull( TypedFilter( types = setOf(FeedType.GLOBAL), filter = - SincePerRelayFilter( - authors = follows, + SinceAuthorPerRelayFilter( + authors = followsRelays, kinds = listOf(LiveActivitiesChatMessageEvent.KIND, LiveActivitiesEvent.KIND), limit = 300, since = @@ -200,17 +200,14 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") { } fun createPublicChatFilter(): List { - val follows = - account.liveDiscoveryFollowLists.value - ?.users - ?.toList() + val follows = account.liveDiscoveryListAuthorsPerRelay.value val followChats = account.selectedChatsFollowList().toList() return listOfNotNull( TypedFilter( types = setOf(FeedType.PUBLIC_CHATS), filter = - SincePerRelayFilter( + SinceAuthorPerRelayFilter( authors = follows, kinds = listOf(ChannelMessageEvent.KIND), limit = 500, @@ -243,15 +240,12 @@ object NostrDiscoveryDataSource : AmethystNostrDataSource("DiscoveryFeed") { } fun createCommunitiesFilter(): TypedFilter { - val follows = - account.liveDiscoveryFollowLists.value - ?.users - ?.toList() + val follows = account.liveDiscoveryListAuthorsPerRelay.value return TypedFilter( types = setOf(FeedType.GLOBAL), filter = - SincePerRelayFilter( + SinceAuthorPerRelayFilter( authors = follows, kinds = listOf(CommunityDefinitionEvent.KIND, CommunityPostApprovalEvent.KIND), limit = 300, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrVideoDataSource.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrVideoDataSource.kt index a2394e030..df3ae87a9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrVideoDataSource.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrVideoDataSource.kt @@ -25,6 +25,7 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.relays.EOSEAccount import com.vitorpamplona.ammolite.relays.FeedType import com.vitorpamplona.ammolite.relays.TypedFilter +import com.vitorpamplona.ammolite.relays.filters.SinceAuthorPerRelayFilter import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileStorageHeaderEvent @@ -64,15 +65,12 @@ object NostrVideoDataSource : AmethystNostrDataSource("VideoFeed") { } fun createContextualFilter(): TypedFilter { - val follows = - account.liveStoriesFollowLists.value - ?.users - ?.toList() + val follows = account.liveStoriesListAuthorsPerRelay.value return TypedFilter( - types = setOf(FeedType.GLOBAL), + types = if (follows == null) setOf(FeedType.GLOBAL) else setOf(FeedType.FOLLOWS), filter = - SincePerRelayFilter( + SinceAuthorPerRelayFilter( authors = follows, kinds = listOf(FileHeaderEvent.KIND, FileStorageHeaderEvent.KIND, VideoHorizontalEvent.KIND, VideoVerticalEvent.KIND), limit = 200,