mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
Merge pull request #4059 from vitorpamplona/claude/amethyst-commons-migration-ua8ma8
Move the relay-filter subassemblies and datasource assemblers to commons/relayClient
This commit is contained in:
@@ -221,15 +221,6 @@ import com.vitorpamplona.quartz.nip18Reposts.BaseRepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.quotes.taggedQuoteIds
|
||||
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
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
||||
import com.vitorpamplona.quartz.nip19Bech32.isATag
|
||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||
@@ -743,7 +734,9 @@ object LocalCache : ILocalCache, ICacheProvider, Dao {
|
||||
fun getRelayGroupChannelIfExists(key: GroupId): RelayGroupChannel? = relayGroupChannels.get(key)
|
||||
|
||||
/** Every relay group we know of that is hosted on [relay] (its channel directory). */
|
||||
fun getRelayGroupChannelsOnRelay(relay: NormalizedRelayUrl): List<RelayGroupChannel> = relayGroupChannels.filter { key, _ -> key.relayUrl == relay }
|
||||
override fun getRelayGroupChannelsOnRelay(relay: NormalizedRelayUrl): List<RelayGroupChannel> = relayGroupChannels.filter { key, _ -> key.relayUrl == relay }
|
||||
|
||||
override fun allRelayGroupChannels(): List<RelayGroupChannel> = relayGroupChannels.values().toList()
|
||||
|
||||
/**
|
||||
* The [RelayGroupChannel] a group-scoped content [note] belongs to, resolved the same way
|
||||
@@ -3300,53 +3293,8 @@ object LocalCache : ILocalCache, ICacheProvider, Dao {
|
||||
wasVerified: Boolean,
|
||||
): Boolean = !event.isDeleted() && consumeBaseReplaceable(event, relay, wasVerified)
|
||||
|
||||
fun consume(nip19: Entity) {
|
||||
when (nip19) {
|
||||
is NSec -> {
|
||||
getOrCreateUser(nip19.toPubKeyHex())
|
||||
}
|
||||
|
||||
is NPub -> {
|
||||
getOrCreateUser(nip19.hex)
|
||||
}
|
||||
|
||||
is NProfile -> {
|
||||
nip19.relay.forEach { relayHint ->
|
||||
relayHints.addKey(nip19.hex, relayHint)
|
||||
}
|
||||
getOrCreateUser(nip19.hex)
|
||||
}
|
||||
|
||||
is NNote -> {
|
||||
getOrCreateNote(nip19.hex)
|
||||
}
|
||||
|
||||
is NEvent -> {
|
||||
nip19.relay.forEach { relayHint ->
|
||||
relayHints.addEvent(nip19.hex, relayHint)
|
||||
}
|
||||
val note = getOrCreateNote(nip19.hex)
|
||||
if (note.author == null) {
|
||||
nip19.author?.let { note.author = checkGetOrCreateUser(it) }
|
||||
}
|
||||
}
|
||||
|
||||
is NEmbed -> {
|
||||
justConsume(nip19.event, null, false)
|
||||
}
|
||||
|
||||
is NRelay -> {}
|
||||
|
||||
is NAddress -> {
|
||||
val aTag = nip19.aTag()
|
||||
nip19.relay.forEach { relayHint ->
|
||||
relayHints.addAddress(aTag, relayHint)
|
||||
}
|
||||
getOrCreateAddressableNote(nip19.address())
|
||||
}
|
||||
|
||||
else -> { }
|
||||
}
|
||||
override fun consumeEmbedded(event: Event) {
|
||||
justConsume(event, null, false)
|
||||
}
|
||||
|
||||
override fun justConsumeMyOwnEvent(event: Event) = justConsumeAndUpdateIndexes(event, null, true)
|
||||
|
||||
+46
-46
@@ -20,24 +20,59 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.relayClient.reqCommand
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.articles.ArticlesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.assemblers.CashuMintDirectoryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.badges.BadgesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.calendars.CalendarsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.ChannelFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.chess.ChessFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.communities.CommunityFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.communities.list.CommunitiesListFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.discover.DiscoveryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.emojipacks.BrowseEmojiSetsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.event.EventFinderFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.followPacks.FollowPacksFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.geohash.GeoHashFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.gitRepo.RepositoryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.gitRepositories.GitRepositoriesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.highlights.HighlightsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.livestreams.LiveStreamsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.longs.LongsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.music.MusicPlaylistsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.music.MusicTracksFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.napplets.NappletsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nests.NestsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip47WalletConnect.NWCPaymentFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nsites.NsitesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.pictures.PicturesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.MyPodcastFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.OnePodcastFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.PodcastEpisodesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.PodcastsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.polls.PollsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.polls.results.PollResponsesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.products.ProductsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.profile.UserProfileFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.publicChats.PublicChatsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.relay.RelayFeedFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.relays.RelayInfoNip66FilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.shorts.ShortsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.softwareapps.SoftwareAppsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.url.UrlFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.UserFinderFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.video.VideoFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.wallet.OnchainZapsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.workouts.WorkoutsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssembler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountForegroundFilterAssembler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel.ChannelFinderFilterAssemblyGroup
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.SearchFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.apps.recommendations.datasource.ProfileAppRecommendationsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.articles.datasource.ArticlesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.datasource.BadgesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.profile.datasource.ProfileBadgesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.datasource.CalendarsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.datasource.ChatroomFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.datasource.ConcordChannelFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.concord.datasource.ConcordChannelHistoryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.datasource.ChannelFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.BuzzDmJoinedChatTailFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupCardWarmupFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupJoinedChatTailFilterAssembler
|
||||
@@ -49,48 +84,13 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayG
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsDiscoveryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsOnRelayFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.datasource.ChessFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.datasource.CommunityFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.list.datasource.CommunitiesListFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource.DiscoveryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.emojipacks.browse.datasource.BrowseEmojiSetsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.datasource.FollowPackFeedFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.datasource.FollowPacksFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.datasource.GeoHashFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.datasource.RepositoryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.GitRepositoriesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.datasource.HashtagFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.highlights.datasource.HighlightsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.livestreams.datasource.LiveStreamsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.longs.datasource.LongsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.music.datasource.MusicPlaylistsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.music.datasource.MusicTracksFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource.ConnectedAppsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource.NappletsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.datasource.NestRoomFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.datasource.NestRoomLivenessAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.datasource.NestsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.nsites.datasource.NsitesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.datasource.PicturesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource.MyPodcastFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource.OnePodcastFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource.PodcastEpisodesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource.PodcastsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.datasource.PollsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.results.datasources.PollResponsesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.products.datasource.ProductsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.datasource.UserProfileFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.publicChats.datasource.PublicChatsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relay.datasource.RelayFeedFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.datasource.RelayInfoNip66FilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts.datasource.ShortsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.softwareapps.datasource.SoftwareAppsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.ThreadFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.url.datasource.UrlFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.VideoFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.datasource.OnchainZapsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.datasource.WorkoutsFilterAssembler
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.RelayOfflineTracker
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.auth.IAuthStatus
|
||||
@@ -154,10 +154,10 @@ class RelaySubscriptionsCoordinator(
|
||||
val concordChannelHistory = ConcordChannelHistoryFilterAssembler(client)
|
||||
|
||||
val chatroom = ChatroomFilterAssembler(client)
|
||||
val community = CommunityFilterAssembler(client)
|
||||
val gitRepository = RepositoryFilterAssembler(client)
|
||||
val community = CommunityFilterAssembler(cache, client)
|
||||
val gitRepository = RepositoryFilterAssembler(cache, client)
|
||||
val thread = ThreadFilterAssembler(client)
|
||||
val profile = UserProfileFilterAssembler(client)
|
||||
val profile = UserProfileFilterAssembler(cache, client)
|
||||
val hashtags = HashtagFilterAssembler(client)
|
||||
val geohashes = GeoHashFilterAssembler(client)
|
||||
val urls = UrlFilterAssembler(client)
|
||||
@@ -170,7 +170,7 @@ class RelaySubscriptionsCoordinator(
|
||||
val polls = PollsFilterAssembler(client)
|
||||
|
||||
// Votes for the poll whose results screen is open.
|
||||
val pollResponses = PollResponsesFilterAssembler(client)
|
||||
val pollResponses = PollResponsesFilterAssembler(cache, client)
|
||||
val pictures = PicturesFilterAssembler(client)
|
||||
val workouts = WorkoutsFilterAssembler(client)
|
||||
val gitRepositories = GitRepositoriesFilterAssembler(client)
|
||||
@@ -189,8 +189,8 @@ class RelaySubscriptionsCoordinator(
|
||||
val musicPlaylists = MusicPlaylistsFilterAssembler(client)
|
||||
val podcastEpisodes = PodcastEpisodesFilterAssembler(client)
|
||||
val podcasts = PodcastsFilterAssembler(client)
|
||||
val onePodcast = OnePodcastFilterAssembler(client)
|
||||
val myPodcast = MyPodcastFilterAssembler(client)
|
||||
val onePodcast = OnePodcastFilterAssembler(cache, client)
|
||||
val myPodcast = MyPodcastFilterAssembler(cache, client)
|
||||
val softwareApps = SoftwareAppsFilterAssembler(client)
|
||||
val napplets = NappletsFilterAssembler(client)
|
||||
val connectedApps = ConnectedAppsFilterAssembler(client)
|
||||
@@ -205,7 +205,7 @@ class RelaySubscriptionsCoordinator(
|
||||
val nwc = NWCPaymentFilterAssembler(client)
|
||||
|
||||
// active when the wallet's on-chain transactions screen is on top.
|
||||
val onchainZaps = OnchainZapsFilterAssembler(client)
|
||||
val onchainZaps = OnchainZapsFilterAssembler(cache, client)
|
||||
|
||||
// active while the user is browsing the NIP-87 mint picker. Subscribes to
|
||||
// kind:38172 cashu mint announcements + kind:38000 cashu-scoped
|
||||
|
||||
+5
-18
@@ -21,32 +21,19 @@
|
||||
package com.vitorpamplona.amethyst.service.relayClient.searchCommand
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.MutableComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.MutableQueryState
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.subassemblies.SearchPostWatcherSubAssembler
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.subassemblies.SearchUserWatcherSubAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.search.SearchPostWatcherSubAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.search.SearchQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.search.SearchUserWatcherSubAssembler
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@Stable
|
||||
class SearchQueryState(
|
||||
val searchQuery: MutableStateFlow<String>,
|
||||
override val account: Account,
|
||||
) : MutableQueryState,
|
||||
AccountScopedQuery {
|
||||
override fun flow(): Flow<String> = searchQuery
|
||||
}
|
||||
|
||||
@Stable
|
||||
class SearchFilterAssembler(
|
||||
client: INostrClient,
|
||||
scope: CoroutineScope,
|
||||
val cache: LocalCache,
|
||||
val cache: ICacheProvider,
|
||||
) : MutableComposeSubscriptionManager<SearchQueryState>(scope) {
|
||||
val group =
|
||||
listOf(
|
||||
|
||||
+9
-2
@@ -25,9 +25,9 @@ import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.search.SearchQueryState
|
||||
import com.vitorpamplona.amethyst.logTime
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.SearchQueryState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrlOrNull
|
||||
@@ -92,7 +92,14 @@ class UserSuggestionState(
|
||||
) {
|
||||
val invalidations = MutableStateFlow(0)
|
||||
val currentWord = MutableStateFlow("")
|
||||
val searchDataSourceState = SearchQueryState(MutableStateFlow(""), account)
|
||||
val searchDataSourceState =
|
||||
SearchQueryState(
|
||||
searchQuery = MutableStateFlow(""),
|
||||
account = account,
|
||||
searchRelays = account.searchRelayList.flow,
|
||||
indexerRelays = account.indexerRelayList.flow,
|
||||
followPlusAllMineWithSearchRelays = account.followPlusAllMineWithSearch.flow,
|
||||
)
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
val searchTerm =
|
||||
|
||||
+1
-1
@@ -67,6 +67,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.nip30CustomEmojis.ui.ShowEmojiSuggestionList
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.communities.CommunityFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.geo_post_change_place
|
||||
import com.vitorpamplona.amethyst.commons.resources.geo_post_posting_to
|
||||
@@ -115,7 +116,6 @@ import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapTo
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.zapsplits.ForwardZapToButton
|
||||
import com.vitorpamplona.amethyst.ui.note.types.ReplyRenderType
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.datasource.CommunityFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.topNavFeeds.TopNavFeedQueryState
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Builds the key for a top-nav-driven feed subscription out of this view model's account and feed
|
||||
* states: the selected list, its per-relay resolution, and the feed(s) whose floor bounds `since`.
|
||||
*/
|
||||
fun AccountViewModel.topNavFeedQueryState(
|
||||
listName: StateFlow<TopFilter>,
|
||||
followsPerRelay: StateFlow<IFeedTopNavPerRelayFilterSet>,
|
||||
vararg feeds: FeedContentState,
|
||||
) = TopNavFeedQueryState(account, listName, followsPerRelay, viewModelScope, feeds.toList())
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.articles.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.articles.ArticlesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun ArticlesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun ArticlesFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
ArticlesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultArticlesFollowList,
|
||||
account.liveArticlesFollowListsPerRelay,
|
||||
accountViewModel.feedStates.articlesFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.articles.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip23LongForm.makeLongFormFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ArticlesSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<ArticlesQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<ArticlesQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: ArticlesQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeLongFormFilter(feedSettings, since, key.feedStates.articlesFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: ArticlesQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: ArticlesQueryState) = key.listName()
|
||||
|
||||
fun ArticlesQueryState.listNameFlow() = account.settings.defaultArticlesFollowList
|
||||
|
||||
fun ArticlesQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun ArticlesQueryState.followsPerRelayFlow() = account.liveArticlesFollowListsPerRelay
|
||||
|
||||
fun ArticlesQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: ArticlesQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.articlesFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.badges.BadgesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun BadgesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun BadgesFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
BadgesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultBadgesFollowList,
|
||||
account.liveBadgesFollowListsPerRelay,
|
||||
accountViewModel.feedStates.badgesFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class BadgesSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<BadgesQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<BadgesQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: BadgesQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow now resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me].
|
||||
val defaultSince = key.feedStates.badgesFeed.lastNoteCreatedAtIfFilled()
|
||||
|
||||
return makeBadgesFilter(key.followsPerRelay(), since, defaultSince)
|
||||
}
|
||||
|
||||
override fun user(key: BadgesQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: BadgesQueryState) = key.listName()
|
||||
|
||||
fun BadgesQueryState.listNameFlow() = account.settings.defaultBadgesFollowList
|
||||
|
||||
fun BadgesQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun BadgesQueryState.followsPerRelayFlow() = account.liveBadgesFollowListsPerRelay
|
||||
|
||||
fun BadgesQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: BadgesQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.badgesFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -25,6 +25,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import com.vitorpamplona.amethyst.commons.model.buzz.BuzzChannelInvites
|
||||
import com.vitorpamplona.amethyst.commons.model.buzz.BuzzDmChannels
|
||||
import com.vitorpamplona.amethyst.commons.model.buzz.ChannelClassification
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.RELAY_GROUP_METADATA_KINDS
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.buzz.buzzChannelTypes
|
||||
@@ -32,7 +33,6 @@ import com.vitorpamplona.amethyst.model.buzz.classifyBuzzChannel
|
||||
import com.vitorpamplona.amethyst.model.buzz.membershipNoticeFilter
|
||||
import com.vitorpamplona.amethyst.model.buzz.membershipNotices
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RELAY_GROUP_METADATA_KINDS
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.fetchAllWithHooks
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
|
||||
+1
-1
@@ -28,12 +28,12 @@ import com.vitorpamplona.amethyst.commons.model.buzz.BuzzDmChannels
|
||||
import com.vitorpamplona.amethyst.commons.model.buzz.BuzzDmRegistry
|
||||
import com.vitorpamplona.amethyst.commons.model.buzz.BuzzRelayDialect
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.filter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.RELAY_GROUP_METADATA_KINDS
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.RelayAuthDecision
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.buzz.membershipNoticeFilter
|
||||
import com.vitorpamplona.amethyst.model.buzz.membershipNotices
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RELAY_GROUP_METADATA_KINDS
|
||||
import com.vitorpamplona.quartz.buzz.dvDmVisibility.DmVisibilityEvent
|
||||
import com.vitorpamplona.quartz.buzz.notifications.MemberAddedNotificationEvent
|
||||
import com.vitorpamplona.quartz.buzz.stream.StreamMessageV2Event
|
||||
|
||||
+1
-1
@@ -23,10 +23,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.buzz
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupDeletions
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.RELAY_GROUP_METADATA_KINDS
|
||||
import com.vitorpamplona.amethyst.commons.relayauth.RelayAuthDecision
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RELAY_GROUP_METADATA_KINDS
|
||||
import com.vitorpamplona.quartz.buzz.notifications.MemberAddedNotificationEvent
|
||||
import com.vitorpamplona.quartz.buzz.stream.SystemMessageEvent
|
||||
import com.vitorpamplona.quartz.buzz.workspace.isBuzzDm
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.calendars.CalendarsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun CalendarsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun CalendarsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
CalendarsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultCalendarsFollowList,
|
||||
account.liveCalendarsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.calendarAppointmentsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.calendars.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CalendarsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<CalendarsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<CalendarsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: CalendarsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeCalendarsFilter(feedSettings, since, key.feedStates.calendarAppointmentsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: CalendarsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: CalendarsQueryState) = key.listName()
|
||||
|
||||
fun CalendarsQueryState.listNameFlow() = account.settings.defaultCalendarsFollowList
|
||||
|
||||
fun CalendarsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun CalendarsQueryState.followsPerRelayFlow() = account.liveCalendarsFollowListsPerRelay
|
||||
|
||||
fun CalendarsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: CalendarsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
// Calendar events are addressables stored in LocalCache's WeakReference
|
||||
// map: while the user is viewing list B the strong refs from list A's UI
|
||||
// are gone and the GC may reclaim those notes. If the EOSE cursor for the
|
||||
// list the user is switching back to still says "you have everything up
|
||||
// to T", the relay won't re-send the now-evicted events. Clearing the
|
||||
// cursor here forces a fresh fetch so the feed comes back whole.
|
||||
clearEoseFor(key)
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.calendarAppointmentsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+2
@@ -28,6 +28,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.model.Channel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.ChannelFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.ChannelQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
|
||||
+3
@@ -25,6 +25,9 @@ import com.vitorpamplona.amethyst.commons.model.buzz.BuzzDmRegistry
|
||||
import com.vitorpamplona.amethyst.commons.model.privateChats.DmHistoryTuning
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.account.nip01Notifications.filterGroupNotificationsToPubkey
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupAuxFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupJoinedChatTailFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupPreviewFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.paging.WindowLoadTracker
|
||||
|
||||
+3
-1
@@ -22,13 +22,15 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relay
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.filterRelayGroupState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.RELAY_GROUP_CARD_WARMUP_KINDS
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.isRelayGroupJoined
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.datasource.subassemblies.filterRelayGroupState
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip29RelayGroups.GroupId
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.model.buzz.BuzzDmChannels
|
||||
import com.vitorpamplona.amethyst.commons.model.buzz.BuzzDmRegistry
|
||||
import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupJoinedChatTailFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
+4
@@ -25,6 +25,10 @@ import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType
|
||||
import com.vitorpamplona.amethyst.commons.model.privateChats.DmHistoryTuning
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.account.nip01Notifications.filterGroupNotificationsToPubkey
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupAuxFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupJoinedChatTailFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupLiveStateFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupPreviewFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.paging.WindowLoadTracker
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relay
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.chats.ChatFeedType
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupStateFilters
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
|
||||
+1
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupHistoryFilters
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.paging.BackwardRelayPager
|
||||
|
||||
+2
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relay
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.privateChats.DmHistoryTuning
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupAuxFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupOpenChatTailFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.paging.WindowLoadTracker
|
||||
|
||||
+1
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.nip29RelayGroups.RelayGroupChannel
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupThreadsFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
|
||||
+1
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupThreadsHistoryFilters
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.paging.BackwardRelayPager
|
||||
|
||||
+14
-12
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.aroundMe.LocationTopNavPerRelayFilterSet
|
||||
@@ -30,32 +31,33 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.Aut
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community.SingleCommunityTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.relay.RelayTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByAllCommunities
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByAuthors
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByCommunity
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByFollows
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByGeohashes
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByHashtag
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByMutedAuthors
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByRelay
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsGlobal
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.scopedTo
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByAllCommunities
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByAuthors
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByCommunity
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByFollows
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByGeohashes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByHashtag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByMutedAuthors
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByRelay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsGlobal
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
fun filterRelayGroupsDiscovery(
|
||||
cache: ICacheProvider,
|
||||
feedSettings: IFeedTopNavPerRelayFilterSet,
|
||||
since: SincePerRelayMap?,
|
||||
defaultSince: Long? = null,
|
||||
): List<RelayBasedFilter> =
|
||||
when (feedSettings) {
|
||||
is AllCommunitiesTopNavPerRelayFilterSet -> filterRelayGroupsByAllCommunities(feedSettings, since, defaultSince)
|
||||
is AllFollowsTopNavPerRelayFilterSet -> filterRelayGroupsByFollows(feedSettings, since, defaultSince)
|
||||
is AuthorsTopNavPerRelayFilterSet -> filterRelayGroupsByAuthors(feedSettings, since, defaultSince)
|
||||
is AllFollowsTopNavPerRelayFilterSet -> filterRelayGroupsByFollows(cache, feedSettings, since, defaultSince)
|
||||
is AuthorsTopNavPerRelayFilterSet -> filterRelayGroupsByAuthors(cache, feedSettings, since, defaultSince)
|
||||
is GlobalTopNavPerRelayFilterSet -> filterRelayGroupsGlobal(feedSettings, since, defaultSince)
|
||||
is HashtagTopNavPerRelayFilterSet -> filterRelayGroupsByHashtag(feedSettings, since, defaultSince)
|
||||
is LocationTopNavPerRelayFilterSet -> filterRelayGroupsByGeohashes(feedSettings, since, defaultSince)
|
||||
is MutedAuthorsTopNavPerRelayFilterSet -> filterRelayGroupsByMutedAuthors(feedSettings, since, defaultSince)
|
||||
is MutedAuthorsTopNavPerRelayFilterSet -> filterRelayGroupsByMutedAuthors(cache, feedSettings, since, defaultSince)
|
||||
is RelayTopNavPerRelayFilterSet -> filterRelayGroupsByRelay(feedSettings, since, defaultSince)
|
||||
is SingleCommunityTopNavPerRelayFilterSet -> filterRelayGroupsByCommunity(feedSettings, since, defaultSince)
|
||||
else -> emptyList()
|
||||
|
||||
+4
-4
@@ -24,12 +24,12 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.filterRelayGroupsByAuthors
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.relayGroupChannelsByRelay
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.scopedTo
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.filterRelayGroupsByAuthors
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.subassemblies.relayGroupChannelsByRelay
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
@@ -56,7 +56,7 @@ class RelayGroupsDiscoverySubAssembler(
|
||||
val feedSettings = key.followsPerRelay()
|
||||
val defaultSince = key.feedStates.relayGroupsDiscoveryFeed.lastNoteCreatedAtIfFilled()
|
||||
|
||||
val base = filterRelayGroupsDiscovery(feedSettings, since, defaultSince)
|
||||
val base = filterRelayGroupsDiscovery(LocalCache, feedSettings, since, defaultSince)
|
||||
|
||||
// The follow-list filter sets resolve their relays via the outbox model (a follow's own
|
||||
// publish relays), but a NIP-29 roster (39001/39002) lives ONLY on the group's host relay.
|
||||
@@ -78,7 +78,7 @@ class RelayGroupsDiscoverySubAssembler(
|
||||
val hostRelays = key.groupHostRelays() - alreadyQueried
|
||||
if (hostRelays.isEmpty()) return base
|
||||
|
||||
val channelsByRelay = relayGroupChannelsByRelay()
|
||||
val channelsByRelay = relayGroupChannelsByRelay(LocalCache)
|
||||
val extra =
|
||||
hostRelays.flatMap { relay ->
|
||||
filterRelayGroupsByAuthors(
|
||||
|
||||
+1
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.relayGroup.buildRelayGroupDirectoryFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.chess.ChessQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelNew
|
||||
|
||||
+1
-1
@@ -44,6 +44,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.communities.CommunityFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.feed
|
||||
import com.vitorpamplona.amethyst.commons.resources.mod_queue
|
||||
@@ -64,7 +65,6 @@ import com.vitorpamplona.amethyst.ui.screen.RefresheableFeedView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.dal.CommunityFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.dal.CommunityModerationFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.datasource.CommunityFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.TabRowHeight
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.list.datasourc
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.communities.list.CommunitiesListFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun CommunitiesListFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun CommunitiesListFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
CommunitiesListQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultCommunitiesFollowList,
|
||||
account.liveCommunitiesFollowListsPerRelay,
|
||||
accountViewModel.feedStates.communitiesList,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.list.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip72Communities.makeCommunitiesFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CommunitiesListSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<CommunitiesListQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<CommunitiesListQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: CommunitiesListQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow now resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me].
|
||||
val defaultSince = key.feedStates.communitiesList.lastNoteCreatedAtIfFilled()
|
||||
|
||||
return makeCommunitiesFilter(key.followsPerRelay(), since, defaultSince)
|
||||
}
|
||||
|
||||
override fun user(key: CommunitiesListQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: CommunitiesListQueryState) = key.listName()
|
||||
|
||||
fun CommunitiesListQueryState.listNameFlow() = account.settings.defaultCommunitiesFollowList
|
||||
|
||||
fun CommunitiesListQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun CommunitiesListQueryState.followsPerRelayFlow() = account.liveCommunitiesFollowListsPerRelay
|
||||
|
||||
fun CommunitiesListQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: CommunitiesListQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.communitiesList.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+17
-1
@@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.discover.DiscoveryFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.discover.DiscoveryQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -43,7 +45,21 @@ fun DiscoveryFilterAssemblerSubscription(
|
||||
// even if they are tracking the same tag.
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
DiscoveryQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
val feeds = accountViewModel.feedStates
|
||||
DiscoveryQueryState(
|
||||
account = account,
|
||||
listName = account.settings.defaultDiscoveryFollowList,
|
||||
followsPerRelay = account.liveDiscoveryFollowListsPerRelay,
|
||||
scope = accountViewModel.viewModelScope,
|
||||
reads = feeds.discoverReads,
|
||||
marketplace = feeds.discoverMarketplace,
|
||||
dvms = feeds.discoverDVMs,
|
||||
followSets = feeds.discoverFollowSets,
|
||||
live = feeds.discoverLive,
|
||||
publicChats = feeds.discoverPublicChats,
|
||||
communities = feeds.discoverCommunities,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip51FollowSets.makeFollowSetsFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip53LiveActivities.makeLiveActivitiesFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DiscoveryFollowsSetsAndLiveStreamsSubAssembler2(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<DiscoveryQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<DiscoveryQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: DiscoveryQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeFollowSetsFilter(feedSettings, since, key.feedStates.discoverFollowSets.lastNoteCreatedAtIfFilled()) +
|
||||
makeLiveActivitiesFilter(feedSettings, since, key.feedStates.discoverLive.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: DiscoveryQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: DiscoveryQueryState) = key.listName()
|
||||
|
||||
fun DiscoveryQueryState.listNameFlow() = account.settings.defaultDiscoveryFollowList
|
||||
|
||||
fun DiscoveryQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun DiscoveryQueryState.followsPerRelayFlow() = account.liveDiscoveryFollowListsPerRelay
|
||||
|
||||
fun DiscoveryQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: DiscoveryQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(1000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
combine(
|
||||
key.feedStates.discoverFollowSets.lastNoteCreatedAtWhenFullyLoaded,
|
||||
key.feedStates.discoverLive.lastNoteCreatedAtWhenFullyLoaded,
|
||||
) {
|
||||
Any()
|
||||
}.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip23LongForm.makeLongFormFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip90DVMs.makeContentDVMsFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip99Classifieds.makeClassifiedsFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DiscoveryLongFormClassifiedsAndDVMSubAssembler1(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<DiscoveryQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<DiscoveryQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: DiscoveryQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeLongFormFilter(feedSettings, since, key.feedStates.discoverReads.lastNoteCreatedAtIfFilled()) +
|
||||
makeClassifiedsFilter(feedSettings, since, key.feedStates.discoverMarketplace.lastNoteCreatedAtIfFilled()) +
|
||||
makeContentDVMsFilter(feedSettings, since, key.feedStates.discoverDVMs.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: DiscoveryQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: DiscoveryQueryState) = key.listName()
|
||||
|
||||
fun DiscoveryQueryState.listNameFlow() = account.settings.defaultDiscoveryFollowList
|
||||
|
||||
fun DiscoveryQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun DiscoveryQueryState.followsPerRelayFlow() = account.liveDiscoveryFollowListsPerRelay
|
||||
|
||||
fun DiscoveryQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: DiscoveryQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
combine(
|
||||
key.feedStates.discoverReads.lastNoteCreatedAtWhenFullyLoaded,
|
||||
key.feedStates.discoverDVMs.lastNoteCreatedAtWhenFullyLoaded,
|
||||
key.feedStates.discoverMarketplace.lastNoteCreatedAtWhenFullyLoaded,
|
||||
) {
|
||||
Any()
|
||||
}.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip28Chats.makePublicChatsFilter
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip72Communities.makeCommunitiesFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DiscoveryPublicChatsAndCommunitiesSubAssembler3(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<DiscoveryQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<DiscoveryQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: DiscoveryQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
return makePublicChatsFilter(feedSettings, since, key.feedStates.discoverPublicChats.lastNoteCreatedAtIfFilled()) +
|
||||
makeCommunitiesFilter(feedSettings, since, key.feedStates.discoverCommunities.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: DiscoveryQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: DiscoveryQueryState) = key.listName()
|
||||
|
||||
fun DiscoveryQueryState.listNameFlow() = account.settings.defaultDiscoveryFollowList
|
||||
|
||||
fun DiscoveryQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun DiscoveryQueryState.followsPerRelayFlow() = account.liveDiscoveryFollowListsPerRelay
|
||||
|
||||
fun DiscoveryQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: DiscoveryQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
combine(
|
||||
key.feedStates.discoverPublicChats.lastNoteCreatedAtWhenFullyLoaded,
|
||||
key.feedStates.discoverCommunities.lastNoteCreatedAtWhenFullyLoaded,
|
||||
) {
|
||||
Any()
|
||||
}.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.emojipacks.browse.datasour
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.emojipacks.BrowseEmojiSetsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun BrowseEmojiSetsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun BrowseEmojiSetsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
BrowseEmojiSetsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultBrowseEmojiSetsFollowList,
|
||||
account.liveBrowseEmojiSetsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.browseEmojiSetsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.emojipacks.browse.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class BrowseEmojiSetsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<BrowseEmojiSetsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<BrowseEmojiSetsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: BrowseEmojiSetsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
val defaultSince = key.feedStates.browseEmojiSetsFeed.lastNoteCreatedAtIfFilled()
|
||||
|
||||
return makeBrowseEmojiSetsFilter(feedSettings, since, defaultSince)
|
||||
}
|
||||
|
||||
override fun user(key: BrowseEmojiSetsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: BrowseEmojiSetsQueryState) = key.listName()
|
||||
|
||||
fun BrowseEmojiSetsQueryState.listNameFlow() = account.settings.defaultBrowseEmojiSetsFollowList
|
||||
|
||||
fun BrowseEmojiSetsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun BrowseEmojiSetsQueryState.followsPerRelayFlow() = account.liveBrowseEmojiSetsFollowListsPerRelay
|
||||
|
||||
fun BrowseEmojiSetsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: BrowseEmojiSetsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.browseEmojiSetsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.datasourc
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.followPacks.FollowPacksFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun FollowPacksFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun FollowPacksFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
FollowPacksQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultFollowPacksFollowList,
|
||||
account.liveFollowPacksFollowListsPerRelay,
|
||||
accountViewModel.feedStates.followPacksFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.list.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip51FollowSets.makeFollowSetsFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FollowPacksSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<FollowPacksQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<FollowPacksQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: FollowPacksQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeFollowSetsFilter(feedSettings, since, key.feedStates.followPacksFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: FollowPacksQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: FollowPacksQueryState) = key.listName()
|
||||
|
||||
fun FollowPacksQueryState.listNameFlow() = account.settings.defaultFollowPacksFollowList
|
||||
|
||||
fun FollowPacksQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun FollowPacksQueryState.followsPerRelayFlow() = account.liveFollowPacksFollowListsPerRelay
|
||||
|
||||
fun FollowPacksQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: FollowPacksQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.followPacksFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.datasource
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.geohash.GeohashQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
+1
-1
@@ -65,6 +65,7 @@ import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.nip34Git.GitBrowseState
|
||||
import com.vitorpamplona.amethyst.commons.nip34Git.GitRepoSnapshotCache
|
||||
import com.vitorpamplona.amethyst.commons.nip34Git.GitRepositoryBrowserViewModel
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.gitRepo.RepositoryFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_new_issue_button
|
||||
import com.vitorpamplona.amethyst.commons.resources.git_repo_bookmark
|
||||
@@ -96,7 +97,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.code.GitCodeTab
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.code.GitReadmeSection
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.dal.RepositoryIssuesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.dal.RepositoryPatchesFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.datasource.RepositoryFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.gitRepositories.GitRepositoriesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun GitRepositoriesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun GitRepositoriesFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
GitRepositoriesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultGitRepositoriesFollowList,
|
||||
account.liveGitRepositoriesFollowListsPerRelay,
|
||||
accountViewModel.feedStates.gitRepositoriesFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GitRepositoriesSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<GitRepositoriesQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<GitRepositoriesQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: GitRepositoriesQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow now resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me] against the
|
||||
// user's own outbox.
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeGitRepositoriesFilter(feedSettings, since, key.feedStates.gitRepositoriesFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: GitRepositoriesQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: GitRepositoriesQueryState) = key.listName()
|
||||
|
||||
fun GitRepositoriesQueryState.listNameFlow() = account.settings.defaultGitRepositoriesFollowList
|
||||
|
||||
fun GitRepositoriesQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun GitRepositoriesQueryState.followsPerRelayFlow() = account.liveGitRepositoriesFollowListsPerRelay
|
||||
|
||||
fun GitRepositoriesQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: GitRepositoriesQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.gitRepositoriesFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,10 +20,10 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip22Comments.CommentKinds
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip22Comments.CommentKinds
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStorySceneEvent
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.highlights.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.highlights.HighlightsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun HighlightsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun HighlightsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
HighlightsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultHighlightsFollowList,
|
||||
account.liveHighlightsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.highlightsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.highlights.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HighlightsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<HighlightsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<HighlightsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: HighlightsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me] against the
|
||||
// user's own outbox.
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeHighlightsFilter(feedSettings, since, key.feedStates.highlightsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: HighlightsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: HighlightsQueryState) = key.listName()
|
||||
|
||||
fun HighlightsQueryState.listNameFlow() = account.settings.defaultHighlightsFollowList
|
||||
|
||||
fun HighlightsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun HighlightsQueryState.followsPerRelayFlow() = account.liveHighlightsFollowListsPerRelay
|
||||
|
||||
fun HighlightsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: HighlightsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.highlightsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,10 +21,10 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.aroundMe.LocationTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip22Comments.filterHomePostsByScopes
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip22Comments.filterHomePostsByScopes
|
||||
import com.vitorpamplona.quartz.experimental.attestations.attestation.AttestationEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
+1
-1
@@ -21,10 +21,10 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.hashtag.HashtagTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip22Comments.filterHomePostsByScopes
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip22Comments.filterHomePostsByScopes
|
||||
import com.vitorpamplona.quartz.experimental.attestations.attestation.AttestationEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||
|
||||
+1
-1
@@ -21,10 +21,10 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip65Follows
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip22Comments.filterHomePostsByScopes
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core.filterHomePostsByGeohashes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core.filterHomePostsByHashtags
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip22Comments.filterHomePostsByScopes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip72Communities.filterHomePostsFromAllCommunities
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.livestreams.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.livestreams.LiveStreamsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun LiveStreamsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun LiveStreamsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
LiveStreamsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultLiveStreamsFollowList,
|
||||
account.liveLiveStreamsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.liveStreamsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.livestreams.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip53LiveActivities.makeLiveActivitiesFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LiveStreamsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<LiveStreamsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<LiveStreamsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: LiveStreamsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeLiveActivitiesFilter(feedSettings, since, key.feedStates.liveStreamsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: LiveStreamsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: LiveStreamsQueryState) = key.listName()
|
||||
|
||||
fun LiveStreamsQueryState.listNameFlow() = account.settings.defaultLiveStreamsFollowList
|
||||
|
||||
fun LiveStreamsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun LiveStreamsQueryState.followsPerRelayFlow() = account.liveLiveStreamsFollowListsPerRelay
|
||||
|
||||
fun LiveStreamsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: LiveStreamsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.liveStreamsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.video.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
@@ -31,7 +32,6 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
|
||||
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.dal.SupportedContent
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET
|
||||
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoMeta
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.longs.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.longs.LongsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun LongsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun LongsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
LongsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultLongsFollowList,
|
||||
account.liveLongsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.longsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.longs.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LongsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<LongsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<LongsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: LongsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeLongsFilter(feedSettings, since, key.feedStates.longsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: LongsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: LongsQueryState) = key.listName()
|
||||
|
||||
fun LongsQueryState.listNameFlow() = account.settings.defaultLongsFollowList
|
||||
|
||||
fun LongsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun LongsQueryState.followsPerRelayFlow() = account.liveLongsFollowListsPerRelay
|
||||
|
||||
fun LongsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: LongsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.longsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.music.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.music.MusicPlaylistsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun MusicPlaylistsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun MusicPlaylistsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
MusicPlaylistsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultMusicPlaylistsFollowList,
|
||||
account.liveMusicPlaylistsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.musicPlaylistsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.music.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Mirror of [MusicTracksSubAssembler] but keyed on the user's *playlists* follow list
|
||||
* ([com.vitorpamplona.amethyst.model.AccountSettings.defaultMusicPlaylistsFollowList]).
|
||||
* The playlists screen exposes its own top-bar spinner, and the previous setup reused the
|
||||
* tracks subscription — so if the user picked, e.g., Global for playlists but Following for
|
||||
* tracks, the REQ only asked Following's authors and the playlists feed showed empty.
|
||||
*
|
||||
* Asks for kind 34139 only — tracks referenced by playlists are loaded on demand by each
|
||||
* playlist row's own per-track observer (see MusicPlaylist.kt), not bundled into this REQ.
|
||||
*/
|
||||
class MusicPlaylistsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<MusicPlaylistsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<MusicPlaylistsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: MusicPlaylistsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow now resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me].
|
||||
val feedSettings = key.followsPerRelay()
|
||||
// REQ now only asks for kind 34139 (playlists), keyed to this screen's follow
|
||||
// list selector — no cross-feed cursor min needed.
|
||||
return makeMusicPlaylistsFilter(feedSettings, since, key.feedStates.musicPlaylistsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: MusicPlaylistsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: MusicPlaylistsQueryState) = key.listName()
|
||||
|
||||
fun MusicPlaylistsQueryState.listNameFlow() = account.settings.defaultMusicPlaylistsFollowList
|
||||
|
||||
fun MusicPlaylistsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun MusicPlaylistsQueryState.followsPerRelayFlow() = account.liveMusicPlaylistsFollowListsPerRelay
|
||||
|
||||
fun MusicPlaylistsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
private val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: MusicPlaylistsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.musicPlaylistsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.music.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.music.MusicTracksFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun MusicTracksFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun MusicTracksFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
MusicTracksQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultMusicTracksFollowList,
|
||||
account.liveMusicTracksFollowListsPerRelay,
|
||||
accountViewModel.feedStates.musicTracksFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.music.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MusicTracksSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<MusicTracksQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<MusicTracksQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: MusicTracksQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow now resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me].
|
||||
val feedSettings = key.followsPerRelay()
|
||||
// REQ now only asks for kind 36787 (tracks), so the `since` cursor lines up with
|
||||
// the tracks feed alone — no cross-feed min needed.
|
||||
return makeMusicTracksFilter(feedSettings, since, key.feedStates.musicTracksFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: MusicTracksQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: MusicTracksQueryState) = key.listName()
|
||||
|
||||
fun MusicTracksQueryState.listNameFlow() = account.settings.defaultMusicTracksFollowList
|
||||
|
||||
fun MusicTracksQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun MusicTracksQueryState.followsPerRelayFlow() = account.liveMusicTracksFollowListsPerRelay
|
||||
|
||||
fun MusicTracksQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
private val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: MusicTracksQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.musicTracksFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.napplets.NappletsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
|
||||
+1
-1
@@ -21,8 +21,8 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.napplets.filterNappletsByAuthors
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource.subassemblies.filterNappletsByAuthors
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
/**
|
||||
* Keyspace for the napplet-discovery subscription. Unlike a feed datasource it carries no
|
||||
* `FeedContentState` — [com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.NappletsScreen]
|
||||
* reads the manifests straight out of `LocalCache`, so this only needs the account (for relay
|
||||
* selection) and a scope.
|
||||
*/
|
||||
class NappletsQueryState(
|
||||
override val account: Account,
|
||||
val scope: CoroutineScope,
|
||||
) : AccountScopedQuery
|
||||
|
||||
/**
|
||||
* Subscribes to NIP-5D napplet manifests (kinds 15129/35129) while a napplet screen is open,
|
||||
* honoring the top-nav follow-list selection at the relay level (see [NappletsFilterSubAssembler])
|
||||
* and dumping the results into `LocalCache` for the screen to observe. Registered as an app-lifetime
|
||||
* singleton in `RelaySubscriptionsCoordinator` (the underlying EOSE manager opens its relay
|
||||
* subscription at construction, so it must not be created per screen).
|
||||
*/
|
||||
@Stable
|
||||
class NappletsFilterAssembler(
|
||||
client: INostrClient,
|
||||
) : ComposeSubscriptionManager<NappletsQueryState>() {
|
||||
val group =
|
||||
listOf(
|
||||
NappletsFilterSubAssembler(client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateKeys() = invalidateFilters()
|
||||
|
||||
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||
|
||||
override fun destroy() = group.forEach { it.destroy() }
|
||||
}
|
||||
+6
-2
@@ -22,15 +22,19 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun NappletsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
NappletsQueryState(accountViewModel.account, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultNappletsFollowList,
|
||||
account.liveNappletsFollowListsPerRelay,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().napplets)
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Subscribes to NIP-5D napplet manifests (kinds 15129/35129), honoring the top-nav follow-list
|
||||
* selection at the relay level: one subscription per logged-in user + selected list, with the authors
|
||||
* resolved to each author's outbox relays (via [com.vitorpamplona.amethyst.model.Account.liveNappletsFollowListsPerRelay]).
|
||||
* Mirrors [com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.datasource.PicturesSubAssembler]; the
|
||||
* only spinner options are author-based (see `authorOnlyRoutes`), so [makeNappletsFilter] needs no
|
||||
* tag-based branches.
|
||||
*/
|
||||
class NappletsFilterSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<NappletsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<NappletsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: NappletsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow now resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me].
|
||||
return makeNappletsFilter(key.followsPerRelay(), since)
|
||||
}
|
||||
|
||||
override fun user(key: NappletsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: NappletsQueryState) = key.listName()
|
||||
|
||||
fun NappletsQueryState.listNameFlow() = account.settings.defaultNappletsFollowList
|
||||
|
||||
fun NappletsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun NappletsQueryState.followsPerRelayFlow() = account.liveNappletsFollowListsPerRelay
|
||||
|
||||
fun NappletsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: NappletsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nests.NestsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun NestsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun NestsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
NestsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultNestsFollowList,
|
||||
account.liveNestsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.nestsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Reuses `makeLiveActivitiesFilter`, which already subscribes to kinds
|
||||
* 30311/30312/30313/1311. The feed filter narrows to 30312/30313 client-side;
|
||||
* sharing the wire filter avoids duplicate REQs on relays when both the Live
|
||||
* Streams and Audio Rooms screens are open for the same user.
|
||||
*/
|
||||
class NestsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<NestsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<NestsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: NestsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
return makeNestsFilter(feedSettings, since, key.feedStates.nestsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: NestsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: NestsQueryState) = key.listName()
|
||||
|
||||
fun NestsQueryState.listNameFlow() = account.settings.defaultNestsFollowList
|
||||
|
||||
fun NestsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun NestsQueryState.followsPerRelayFlow() = account.liveNestsFollowListsPerRelay
|
||||
|
||||
fun NestsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
private val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: NestsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.nestsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.nsites.datasource
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
/**
|
||||
* Keyspace for the nSite-discovery subscription. Like its nApplet sibling it carries no
|
||||
* `FeedContentState` — [com.vitorpamplona.amethyst.ui.screen.loggedIn.nsites.NsitesScreen] reads the
|
||||
* manifests straight out of `LocalCache`, so this only needs the account (for relay selection) and a
|
||||
* scope.
|
||||
*/
|
||||
class NsitesQueryState(
|
||||
override val account: Account,
|
||||
val scope: CoroutineScope,
|
||||
) : AccountScopedQuery
|
||||
|
||||
/**
|
||||
* Subscribes to NIP-5A static-site manifests (kinds 15128/35128) while an nSite screen is open,
|
||||
* honoring the top-nav follow-list selection at the relay level (see [NsitesFilterSubAssembler]) and
|
||||
* dumping the results into `LocalCache` for the screen to observe. Registered as an app-lifetime
|
||||
* singleton in `RelaySubscriptionsCoordinator` (the underlying EOSE manager opens its relay
|
||||
* subscription at construction, so it must not be created per screen).
|
||||
*/
|
||||
@Stable
|
||||
class NsitesFilterAssembler(
|
||||
client: INostrClient,
|
||||
) : ComposeSubscriptionManager<NsitesQueryState>() {
|
||||
val group =
|
||||
listOf(
|
||||
NsitesFilterSubAssembler(client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateKeys() = invalidateFilters()
|
||||
|
||||
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||
|
||||
override fun destroy() = group.forEach { it.destroy() }
|
||||
}
|
||||
+6
-2
@@ -22,15 +22,19 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.nsites.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun NsitesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
NsitesQueryState(accountViewModel.account, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultNsitesFollowList,
|
||||
account.liveNsitesFollowListsPerRelay,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().nsites)
|
||||
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.nsites.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Subscribes to NIP-5A static-site manifests (kinds 15128/35128), honoring the top-nav follow-list
|
||||
* selection at the relay level: one subscription per logged-in user + selected list, with the authors
|
||||
* resolved to each author's outbox relays (via [com.vitorpamplona.amethyst.model.Account.liveNsitesFollowListsPerRelay]).
|
||||
* Mirrors [com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.datasource.PicturesSubAssembler]; the
|
||||
* only spinner options are author-based (see `authorOnlyRoutes`), so [makeNsitesFilter] needs no
|
||||
* tag-based branches.
|
||||
*/
|
||||
class NsitesFilterSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<NsitesQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<NsitesQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: NsitesQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
// "Mine" needs no special-case: the shared TopFilter.Mine flow now resolves to an author
|
||||
// filter scoped to the user, so followsPerRelay() already carries authors=[me].
|
||||
return makeNsitesFilter(key.followsPerRelay(), since)
|
||||
}
|
||||
|
||||
override fun user(key: NsitesQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: NsitesQueryState) = key.listName()
|
||||
|
||||
fun NsitesQueryState.listNameFlow() = account.settings.defaultNsitesFollowList
|
||||
|
||||
fun NsitesQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun NsitesQueryState.followsPerRelayFlow() = account.liveNsitesFollowListsPerRelay
|
||||
|
||||
fun NsitesQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: NsitesQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.pictures.PicturesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun PicturesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun PicturesFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
PicturesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultPicturesFollowList,
|
||||
account.livePicturesFollowListsPerRelay,
|
||||
accountViewModel.feedStates.picturesFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class PicturesSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<PicturesQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<PicturesQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: PicturesQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makePicturesFilter(feedSettings, since, key.feedStates.picturesFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: PicturesQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: PicturesQueryState) = key.listName()
|
||||
|
||||
fun PicturesQueryState.listNameFlow() = account.settings.defaultPicturesFollowList
|
||||
|
||||
fun PicturesQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun PicturesQueryState.followsPerRelayFlow() = account.livePicturesFollowListsPerRelay
|
||||
|
||||
fun PicturesQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: PicturesQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.picturesFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.MyPodcastQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.OnePodcastQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.PodcastEpisodesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun PodcastEpisodesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun PodcastEpisodesFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
PodcastEpisodesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultPodcastEpisodesFollowList,
|
||||
account.livePodcastEpisodesFollowListsPerRelay,
|
||||
accountViewModel.feedStates.podcastEpisodesFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class PodcastEpisodesSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<PodcastEpisodesQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<PodcastEpisodesQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: PodcastEpisodesQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
return makePodcastEpisodesFilter(feedSettings, since, key.feedStates.podcastEpisodesFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: PodcastEpisodesQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: PodcastEpisodesQueryState) = key.listName()
|
||||
|
||||
fun PodcastEpisodesQueryState.listNameFlow() = account.settings.defaultPodcastEpisodesFollowList
|
||||
|
||||
fun PodcastEpisodesQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun PodcastEpisodesQueryState.followsPerRelayFlow() = account.livePodcastEpisodesFollowListsPerRelay
|
||||
|
||||
fun PodcastEpisodesQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
private val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: PodcastEpisodesQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.podcastEpisodesFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.podcasts.PodcastsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun PodcastsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun PodcastsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
PodcastsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultPodcastsFollowList,
|
||||
account.livePodcastsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.podcastsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.podcasts.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class PodcastsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<PodcastsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<PodcastsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: PodcastsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
return makePodcastsFilter(feedSettings, since, key.feedStates.podcastsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: PodcastsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: PodcastsQueryState) = key.listName()
|
||||
|
||||
fun PodcastsQueryState.listNameFlow() = account.settings.defaultPodcastsFollowList
|
||||
|
||||
fun PodcastsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun PodcastsQueryState.followsPerRelayFlow() = account.livePodcastsFollowListsPerRelay
|
||||
|
||||
fun PodcastsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
private val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: PodcastsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.podcastsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.polls.PollsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun PollsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun PollsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
PollsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultPollsFollowList,
|
||||
account.livePollsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.pollsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class PollsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<PollsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<PollsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: PollsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makePollsFilter(feedSettings, since, key.feedStates.pollsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: PollsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: PollsQueryState) = key.listName()
|
||||
|
||||
fun PollsQueryState.listNameFlow() = account.settings.defaultPollsFollowList
|
||||
|
||||
fun PollsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun PollsQueryState.followsPerRelayFlow() = account.livePollsFollowListsPerRelay
|
||||
|
||||
fun PollsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: PollsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.pollsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+2
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.polls.results.datasources
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.polls.results.PollResponsesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.polls.results.PollResponsesQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.products.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.products.ProductsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun ProductsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun ProductsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
ProductsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultProductsFollowList,
|
||||
account.liveProductsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.productsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.products.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip99Classifieds.makeClassifiedsFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ProductsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<ProductsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<ProductsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: ProductsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeClassifiedsFilter(feedSettings, since, key.feedStates.productsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: ProductsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: ProductsQueryState) = key.listName()
|
||||
|
||||
fun ProductsQueryState.listNameFlow() = account.settings.defaultProductsFollowList
|
||||
|
||||
fun ProductsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun ProductsQueryState.followsPerRelayFlow() = account.liveProductsFollowListsPerRelay
|
||||
|
||||
fun ProductsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: ProductsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.productsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -65,6 +65,7 @@ import androidx.compose.ui.unit.IntSize
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.profile.UserProfileFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.gallery
|
||||
import com.vitorpamplona.amethyst.commons.resources.mutual
|
||||
@@ -86,7 +87,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.TabBookma
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.dal.UserProfileBookmarksFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.TabNotesConversations
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.conversations.dal.UserProfileConversationsFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.datasource.UserProfileFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.FollowersTabHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.TabFollowers
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.publicChats.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.publicChats.PublicChatsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun PublicChatsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun PublicChatsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
PublicChatsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultPublicChatsFollowList,
|
||||
account.livePublicChatsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.publicChatsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.publicChats.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip28Chats.makePublicChatsFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class PublicChatsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<PublicChatsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<PublicChatsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: PublicChatsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makePublicChatsFilter(feedSettings, since, key.feedStates.publicChatsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: PublicChatsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: PublicChatsQueryState) = key.listName()
|
||||
|
||||
fun PublicChatsQueryState.listNameFlow() = account.settings.defaultPublicChatsFollowList
|
||||
|
||||
fun PublicChatsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun PublicChatsQueryState.followsPerRelayFlow() = account.livePublicChatsFollowListsPerRelay
|
||||
|
||||
fun PublicChatsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: PublicChatsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.publicChatsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relay.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.relay.RelayFeedQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.relays.RelayInfoNip66QueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
|
||||
+9
-2
@@ -32,13 +32,13 @@ import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.actions.ConcordActions
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.search.SearchQueryState
|
||||
import com.vitorpamplona.amethyst.commons.search.SearchScope
|
||||
import com.vitorpamplona.amethyst.commons.search.SearchSortOrder
|
||||
import com.vitorpamplona.amethyst.commons.search.SearchSource
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.InvalidatableContent
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.SearchQueryState
|
||||
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
@@ -104,7 +104,14 @@ class SearchBarViewModel(
|
||||
.onEach(::updateDataSource)
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, searchValue)
|
||||
|
||||
val searchDataSourceState = SearchQueryState(MutableStateFlow(searchValue), account)
|
||||
val searchDataSourceState =
|
||||
SearchQueryState(
|
||||
searchQuery = MutableStateFlow(searchValue),
|
||||
account = account,
|
||||
searchRelays = account.searchRelayList.flow,
|
||||
indexerRelays = account.indexerRelayList.flow,
|
||||
followPlusAllMineWithSearchRelays = account.followPlusAllMineWithSearch.flow,
|
||||
)
|
||||
|
||||
@Suppress("unused")
|
||||
val sourceWatcher =
|
||||
|
||||
+1
-1
@@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.video.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
@@ -31,7 +32,6 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
|
||||
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.dal.SupportedContent
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET
|
||||
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoMeta
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoShortEvent
|
||||
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.shorts.ShortsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun ShortsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -41,7 +42,12 @@ fun ShortsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
ShortsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultShortsFollowList,
|
||||
account.liveShortsFollowListsPerRelay,
|
||||
accountViewModel.feedStates.shortsFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.shorts.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ShortsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<ShortsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<ShortsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: ShortsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeShortsFilter(feedSettings, since, key.feedStates.shortsFeed.lastNoteCreatedAtIfFilled())
|
||||
}
|
||||
|
||||
override fun user(key: ShortsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: ShortsQueryState) = key.listName()
|
||||
|
||||
fun ShortsQueryState.listNameFlow() = account.settings.defaultShortsFollowList
|
||||
|
||||
fun ShortsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun ShortsQueryState.followsPerRelayFlow() = account.liveShortsFollowListsPerRelay
|
||||
|
||||
fun ShortsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: ShortsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.shortsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.softwareapps.datasource
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.softwareapps.SoftwareAppsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.softwareapps.SoftwareAppsQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -41,7 +43,15 @@ fun SoftwareAppsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
SoftwareAppsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
SoftwareAppsQueryState(
|
||||
account = account,
|
||||
listName = account.settings.defaultSoftwareAppsFollowList,
|
||||
followsPerRelay = account.liveSoftwareAppsFollowListsPerRelay,
|
||||
scope = accountViewModel.viewModelScope,
|
||||
feed = accountViewModel.feedStates.softwareAppsFeed,
|
||||
blockedRelays = account.blockedRelayList.flow,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.softwareapps.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SoftwareAppsSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<SoftwareAppsQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<SoftwareAppsQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: SoftwareAppsQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
|
||||
return makeSoftwareAppsFilter(
|
||||
feedSettings,
|
||||
since,
|
||||
key.feedStates.softwareAppsFeed.lastNoteCreatedAtIfFilled(),
|
||||
key.account.blockedRelayList.flow.value,
|
||||
)
|
||||
}
|
||||
|
||||
override fun user(key: SoftwareAppsQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: SoftwareAppsQueryState) = key.listName()
|
||||
|
||||
fun SoftwareAppsQueryState.listNameFlow() = account.settings.defaultSoftwareAppsFollowList
|
||||
|
||||
fun SoftwareAppsQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun SoftwareAppsQueryState.followsPerRelayFlow() = account.liveSoftwareAppsFollowListsPerRelay
|
||||
|
||||
fun SoftwareAppsQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: SoftwareAppsQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.listNameFlow().collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
// Re-evaluate when the user blocks/unblocks zapstore's relay so the
|
||||
// global Apps feed stops/starts querying it without a restart.
|
||||
key.account.blockedRelayList.flow.collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedStates.softwareAppsFeed.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.sub
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.ThreadAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.thread.filterMissingEventsForThread
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.ThreadQueryState
|
||||
@@ -48,7 +49,7 @@ class ThreadEventLoaderSubAssembler(
|
||||
): List<RelayBasedFilter>? {
|
||||
val branches = ThreadAssembler(LocalCache).findThreadFor(key.eventId) ?: return null
|
||||
val defaultRelays = key.account.followPlusAllMineWithSearch.flow.value
|
||||
return filterMissingEventsForThread(branches, defaultRelays)
|
||||
return filterMissingEventsForThread(LocalCache, branches, defaultRelays)
|
||||
}
|
||||
|
||||
override fun id(key: ThreadQueryState) = key.eventId
|
||||
|
||||
+1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.sub
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.ThreadAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUniqueIdEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.thread.filterEventsInThreadForRoot
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.ThreadQueryState
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ import android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.url.UrlQueryState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@SuppressLint("StateFlowValueCalledInComposition")
|
||||
|
||||
+1
-1
@@ -24,13 +24,13 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.filterIntoSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.video.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
|
||||
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.SUPPORTED_VIDEO_FEED_MIME_TYPES_SET
|
||||
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
||||
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
|
||||
import com.vitorpamplona.quartz.nip68Picture.PictureMeta
|
||||
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.AccountScopedQuery
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountFeedContentStates
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.VideoOutboxEventsFilterSubAssembler
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
// This allows multiple screen to be listening to tags, even the same tag
|
||||
class VideoQueryState(
|
||||
override val account: Account,
|
||||
val feedState: AccountFeedContentStates,
|
||||
val scope: CoroutineScope,
|
||||
) : AccountScopedQuery
|
||||
|
||||
class VideoFilterAssembler(
|
||||
client: INostrClient,
|
||||
) : ComposeSubscriptionManager<VideoQueryState>() {
|
||||
val group =
|
||||
listOf(
|
||||
VideoOutboxEventsFilterSubAssembler(client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateKeys() = invalidateFilters()
|
||||
|
||||
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||
|
||||
override fun destroy() = group.forEach { it.destroy() }
|
||||
}
|
||||
+8
-2
@@ -22,9 +22,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.video.VideoFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.topNavFeedQueryState
|
||||
|
||||
@Composable
|
||||
fun VideoFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
@@ -43,7 +44,12 @@ fun VideoFilterAssemblerSubscription(
|
||||
// even if they are tracking the same tag.
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
VideoQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope)
|
||||
val account = accountViewModel.account
|
||||
accountViewModel.topNavFeedQueryState(
|
||||
account.settings.defaultStoriesFollowList,
|
||||
account.liveStoriesFollowListsPerRelay,
|
||||
accountViewModel.feedStates.videoFeed,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, filterAssembler)
|
||||
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.aroundMe.LocationTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.global.GlobalTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.hashtag.HashtagTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.allcommunities.AllCommunitiesTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community.SingleCommunityTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.VideoQueryState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.nip01Core.filterPictureAndVideoByGeohash
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.nip01Core.filterPictureAndVideoByHashtag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.nip01Core.filterPictureAndVideoGlobal
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.nip65Follows.filterPictureAndVideoByAuthors
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.nip65Follows.filterPictureAndVideoByFollows
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.nip72Communities.filterPictureAndVideoByAllCommunities
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.datasource.subassemblies.nip72Communities.filterPictureAndVideoByCommunity
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class VideoOutboxEventsFilterSubAssembler(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<VideoQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<VideoQueryState, TopFilter>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: VideoQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
val defaultSince = key.feedState.videoFeed.lastNoteCreatedAtWhenFullyLoaded.value
|
||||
return when (feedSettings) {
|
||||
is AllCommunitiesTopNavPerRelayFilterSet -> filterPictureAndVideoByAllCommunities(feedSettings, since, defaultSince)
|
||||
is AllFollowsTopNavPerRelayFilterSet -> filterPictureAndVideoByFollows(feedSettings, since, defaultSince)
|
||||
is AuthorsTopNavPerRelayFilterSet -> filterPictureAndVideoByAuthors(feedSettings, since, defaultSince)
|
||||
is GlobalTopNavPerRelayFilterSet -> filterPictureAndVideoGlobal(feedSettings, since, defaultSince ?: TimeUtils.oneWeekAgo())
|
||||
is HashtagTopNavPerRelayFilterSet -> filterPictureAndVideoByHashtag(feedSettings, since, defaultSince ?: TimeUtils.oneMonthAgo())
|
||||
is LocationTopNavPerRelayFilterSet -> filterPictureAndVideoByGeohash(feedSettings, since, defaultSince)
|
||||
is MutedAuthorsTopNavPerRelayFilterSet -> filterPictureAndVideoByAuthors(feedSettings, since, defaultSince)
|
||||
is SingleCommunityTopNavPerRelayFilterSet -> filterPictureAndVideoByCommunity(feedSettings, since, defaultSince)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun user(key: VideoQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: VideoQueryState) = key.listName()
|
||||
|
||||
fun VideoQueryState.listNameFlow() = account.settings.defaultStoriesFollowList
|
||||
|
||||
fun VideoQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun VideoQueryState.followsPerRelayFlow() = account.liveStoriesFollowListsPerRelay
|
||||
|
||||
fun VideoQueryState.followsPerRelay() = followsPerRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: VideoQueryState): Subscription {
|
||||
val user = user(key)
|
||||
userJobMap[user]?.forEach { it.cancel() }
|
||||
userJobMap[user] =
|
||||
listOf(
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.followsPerRelayFlow().sample(1000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
key.feedState.videoFeed.lastNoteCreatedAtWhenFullyLoaded.sample(1000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return super.newSub(key)
|
||||
}
|
||||
|
||||
override fun endSub(
|
||||
key: User,
|
||||
subId: String,
|
||||
) {
|
||||
super.endSub(key, subId)
|
||||
userJobMap[key]?.forEach { it.cancel() }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user