mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
refactor(commons): move the thread, badges, app-recommendations, connected-apps and home datasources to commons
The four families that read one relay-set state off `Account` now carry that set as a `StateFlow` on their key, the way `SearchQueryState` does: `ProfileAppRecommendationsQueryState(outboxRelays, defaultGlobalRelays)`, `ProfileBadgesQueryState(notificationRelays)`, `ConnectedAppsQueryState(homeRelays)` and `ThreadQueryState(defaultRelays)`. Thread also takes the cache: `ThreadFilterAssembler(cache, client)`, and its two sub-assemblers run `ThreadAssembler(cache)`. The home feed moves whole. Its `FilterHomePosts*` dispatchers were already import-clean; `HomeOutboxEventsEoseManager` becomes a `TopNavFeedSubAssembler<HomeQueryState>` whose key carries the new-threads and replies floors plus `enabledHomeFeedTypes`. The Settings › Home toggle stays an unsampled invalidator (still `drop(1)`) via `extraInvalidators`, and the disabled-kinds stripping is unchanged. Same deltas as the other feeds: watchers on the screen scope, follows sampler 500 ms (was 1000 ms). The commented-out alternative sub-assemblers in `HomeFilterAssembler` are dropped. The app keeps one `*Subscription.kt` per feature, building the key from `AccountViewModel`. The algo-feed filter test moves to commons commonTest on `kotlin.test`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J836W9ZjSUJUQ1d23TpiJo
This commit is contained in:
+6
-6
@@ -20,9 +20,11 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.relayClient.reqCommand
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.apps.recommendations.ProfileAppRecommendationsFilterAssembler
|
||||
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.badges.profile.ProfileBadgesFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.calendars.CalendarsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.channel.ChannelFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.chess.ChessFilterAssembler
|
||||
@@ -36,10 +38,12 @@ import com.vitorpamplona.amethyst.commons.relayClient.geohash.GeoHashFilterAssem
|
||||
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.home.HomeFilterAssembler
|
||||
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.ConnectedAppsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.napplets.NappletsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nests.NestsFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip47WalletConnect.NWCPaymentFilterAssembler
|
||||
@@ -58,6 +62,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.relay.RelayFeedFilterAssem
|
||||
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.thread.ThreadFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.url.UrlFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.user.UserFinderFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.video.VideoFilterAssembler
|
||||
@@ -68,8 +73,6 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.Account
|
||||
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.badges.profile.datasource.ProfileBadgesFilterAssembler
|
||||
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
|
||||
@@ -86,11 +89,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayG
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.datasource.ChatroomListFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.datasource.FollowPackFeedFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.datasource.HashtagFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeFilterAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource.ConnectedAppsFilterAssembler
|
||||
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.threadview.datasources.ThreadFilterAssembler
|
||||
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
|
||||
@@ -156,7 +156,7 @@ class RelaySubscriptionsCoordinator(
|
||||
val chatroom = ChatroomFilterAssembler(client)
|
||||
val community = CommunityFilterAssembler(cache, client)
|
||||
val gitRepository = RepositoryFilterAssembler(cache, client)
|
||||
val thread = ThreadFilterAssembler(client)
|
||||
val thread = ThreadFilterAssembler(cache, client)
|
||||
val profile = UserProfileFilterAssembler(cache, client)
|
||||
val hashtags = HashtagFilterAssembler(client)
|
||||
val geohashes = GeoHashFilterAssembler(client)
|
||||
|
||||
+7
-1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.apps.recommendations.datas
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.apps.recommendations.ProfileAppRecommendationsQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -29,7 +30,12 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
fun ProfileAppRecommendationsFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
ProfileAppRecommendationsQueryState(accountViewModel.account)
|
||||
val account = accountViewModel.account
|
||||
ProfileAppRecommendationsQueryState(
|
||||
account = account,
|
||||
outboxRelays = account.outboxRelays.flow,
|
||||
defaultGlobalRelays = account.defaultGlobalRelays.flow,
|
||||
)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().profileAppRecommendations)
|
||||
|
||||
+3
-1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.profile.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.badges.profile.ProfileBadgesQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -29,7 +30,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
fun ProfileBadgesFilterAssemblerSubscription(accountViewModel: AccountViewModel) {
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
ProfileBadgesQueryState(accountViewModel.account)
|
||||
val account = accountViewModel.account
|
||||
ProfileBadgesQueryState(account, account.notificationRelays.flow)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().profileBadges)
|
||||
|
||||
+1
-1
@@ -23,8 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.datasourc
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allUserFollows.AllUserFollowsByOutboxTopNavFilter
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allUserFollows.AllUserFollowsByProxyTopNavFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.SingleSubEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.filterHomePostsByAuthors
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip65Follows.filterHomePostsByAuthors
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
||||
|
||||
-61
@@ -1,61 +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.home.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.amethyst.ui.screen.loggedIn.AccountFeedContentStates
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip65Follows.HomeOutboxEventsEoseManager
|
||||
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
|
||||
|
||||
@Stable
|
||||
class HomeQueryState(
|
||||
override val account: Account,
|
||||
val feedState: AccountFeedContentStates,
|
||||
val scope: CoroutineScope,
|
||||
) : AccountScopedQuery
|
||||
|
||||
@Stable
|
||||
class HomeFilterAssembler(
|
||||
client: INostrClient,
|
||||
) : ComposeSubscriptionManager<HomeQueryState>() {
|
||||
val group =
|
||||
listOf(
|
||||
HomeOutboxEventsEoseManager(client, ::allKeys),
|
||||
// HomeOutboxUsersEoseManager(client, ::allKeys),
|
||||
// We can break it down, one for each sub if needed.
|
||||
// HashtagEventsFilterSubAssembler(client, ::allKeys),
|
||||
// GeohashEventsFilterSubAssembler(client, ::allKeys),
|
||||
// CommunityEventsFilterSubAssembler(client, ::allKeys),
|
||||
// MixGeohashHashtagsCommunityEoseManager(client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateKeys() = invalidateFilters()
|
||||
|
||||
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||
|
||||
override fun destroy() = group.forEach { it.destroy() }
|
||||
}
|
||||
+10
-3
@@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.HomeFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.HomeQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@@ -43,10 +45,15 @@ fun HomeFilterAssemblerSubscription(
|
||||
// even if they are tracking the same tag.
|
||||
val state =
|
||||
remember(accountViewModel.account) {
|
||||
val account = accountViewModel.account
|
||||
HomeQueryState(
|
||||
accountViewModel.account,
|
||||
accountViewModel.feedStates,
|
||||
accountViewModel.viewModelScope,
|
||||
account = account,
|
||||
listName = account.settings.defaultHomeFollowList,
|
||||
followsPerRelay = account.liveHomeFollowListsPerRelay,
|
||||
scope = accountViewModel.viewModelScope,
|
||||
newThreads = accountViewModel.feedStates.homeNewThreads,
|
||||
replies = accountViewModel.feedStates.homeReplies,
|
||||
enabledHomeFeedTypes = account.settings.enabledHomeFeedTypes,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.napplets.datasource
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.napplets.ConnectedAppsQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
@@ -33,7 +34,8 @@ fun ConnectedAppsFilterAssemblerSubscription(
|
||||
) {
|
||||
val state =
|
||||
remember(accountViewModel.account, authors) {
|
||||
ConnectedAppsQueryState(accountViewModel.account, authors)
|
||||
val account = accountViewModel.account
|
||||
ConnectedAppsQueryState(account, authors, account.homeRelays.flow)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().connectedApps)
|
||||
|
||||
+3
-1
@@ -24,6 +24,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.thread.ThreadFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.thread.ThreadQueryState
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
@@ -48,7 +50,7 @@ fun ThreadFilterAssemblerSubscription(
|
||||
// even if they are tracking the same tag.
|
||||
val state =
|
||||
remember(eventId) {
|
||||
ThreadQueryState(eventId, account)
|
||||
ThreadQueryState(eventId, account, account.followPlusAllMineWithSearch.flow)
|
||||
}
|
||||
|
||||
LifecycleAwareKeyDataSourceSubscription(state, filterAssembler)
|
||||
|
||||
@@ -693,6 +693,33 @@ change each); `followPacks/feed` (dispatches to the app-side home filters);
|
||||
`launchChatFeedToggleObserver`); `nests/NestRoom*`, `relayGroup/*`, `concord/*`
|
||||
and `home/` (`Account` + `LocalCache` live streams + `AccountViewModel`).
|
||||
|
||||
### Single-relay-read families and the home feed landed (2026-09-05, after #4059)
|
||||
|
||||
The four families that read one relay-set state off `Account` now carry that
|
||||
set as a `StateFlow` on their key, the way `SearchQueryState` does:
|
||||
`ProfileAppRecommendationsQueryState(outboxRelays, defaultGlobalRelays)`,
|
||||
`ProfileBadgesQueryState(notificationRelays)`,
|
||||
`ConnectedAppsQueryState(homeRelays)` and `ThreadQueryState(defaultRelays)`.
|
||||
Thread also takes the cache (`ThreadFilterAssembler(cache, client)`; its two
|
||||
sub-assemblers run `ThreadAssembler(cache)`). All live under
|
||||
`commons/relayClient/{apps/recommendations,badges/profile,napplets,thread}`.
|
||||
|
||||
The home feed moved whole: the `FilterHomePosts*` dispatchers under
|
||||
`commons/relayClient/home/<nip>/`, and `HomeOutboxEventsEoseManager` is now a
|
||||
`TopNavFeedSubAssembler<HomeQueryState>` whose key carries the new-threads and
|
||||
replies floors plus `enabledHomeFeedTypes`; the Settings › Home toggle is an
|
||||
`extraInvalidators` entry (still `drop(1)`), and the disabled-kinds stripping
|
||||
is unchanged. Deltas as for the other feeds: watchers on the screen scope,
|
||||
follows sampler 500 ms (was 1000). The commented-out alternative
|
||||
sub-assemblers in `HomeFilterAssembler` were dropped.
|
||||
|
||||
What remains in `ui/screen/**/datasource*/` (92 files) is the Compose glue —
|
||||
one `*Subscription.kt` per feature, which builds the key from
|
||||
`AccountViewModel` — plus the families that need Wave 4: `hashtag/`
|
||||
(`Account.followsPerRelay`), `followPacks/feed` (`Account.proxyRelayList`,
|
||||
`blockedRelayList`, `cache`), `chats/*`, `nests/NestRoom*`, `relayGroup/*`,
|
||||
`concord/*`, and `wallet`'s and `polls/results`' composables.
|
||||
|
||||
### Desktop `subscriptions/FilterBuilders.kt` — investigated, not migrated (2026-09-05)
|
||||
|
||||
The audit listed this 742-line file as a hand-rolled copy of ~34
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.apps.recommendations.datasource
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.apps.recommendations
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
+12
-3
@@ -18,16 +18,25 @@
|
||||
* 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.apps.recommendations.datasource
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.apps.recommendations
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.model.IAccount
|
||||
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 com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* @param outboxRelays the user's own outbox relays, where their kind-31989 recommendations live.
|
||||
* @param defaultGlobalRelays follows' outboxes plus the user's relays, where recent kind-31990
|
||||
* app definitions are discovered.
|
||||
*/
|
||||
class ProfileAppRecommendationsQueryState(
|
||||
override val account: Account,
|
||||
override val account: IAccount,
|
||||
val outboxRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||
val defaultGlobalRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||
) : AccountScopedQuery
|
||||
|
||||
@Stable
|
||||
+3
-3
@@ -18,7 +18,7 @@
|
||||
* 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.apps.recommendations.datasource
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.apps.recommendations
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
@@ -37,9 +37,9 @@ class ProfileAppRecommendationsSubAssembler(
|
||||
): List<RelayBasedFilter> =
|
||||
filterMyAppRecommendations(
|
||||
pubkey = user(key).pubkeyHex,
|
||||
relays = key.account.outboxRelays.flow.value,
|
||||
relays = key.outboxRelays.value,
|
||||
) +
|
||||
filterRecentAppDefinitions(
|
||||
relays = key.account.defaultGlobalRelays.flow.value,
|
||||
relays = key.defaultGlobalRelays.value,
|
||||
)
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.profile.datasource
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.badges.profile
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
+7
-3
@@ -18,16 +18,20 @@
|
||||
* 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.profile.datasource
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.badges.profile
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.model.IAccount
|
||||
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 com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/** @param notificationRelays the user's inbox relays, where badge awards addressed to them land. */
|
||||
class ProfileBadgesQueryState(
|
||||
override val account: Account,
|
||||
override val account: IAccount,
|
||||
val notificationRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||
) : AccountScopedQuery
|
||||
|
||||
@Stable
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* 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.profile.datasource
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.badges.profile
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
@@ -37,6 +37,6 @@ class ProfileBadgesSubAssembler(
|
||||
): List<RelayBasedFilter> =
|
||||
filterReceivedBadgeAwards(
|
||||
pubkey = user(key).pubkeyHex,
|
||||
relays = key.account.notificationRelays.flow.value,
|
||||
relays = key.notificationRelays.value,
|
||||
)
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.commons.relayClient.home
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.model.HomeFeedType
|
||||
import com.vitorpamplona.amethyst.commons.model.IAccount
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.IFeedTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.HomeOutboxEventsEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.topNavFeeds.TopNavFeedFilterAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.topNavFeeds.TopNavFeedQueryState
|
||||
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Key for the home feed: the top-nav selection, the new-threads and replies floors that bound
|
||||
* `since`, and the Settings › Home content-type toggles whose disabled groups are stripped from
|
||||
* every home REQ.
|
||||
*/
|
||||
@Stable
|
||||
class HomeQueryState(
|
||||
account: IAccount,
|
||||
listName: StateFlow<TopFilter>,
|
||||
followsPerRelay: StateFlow<IFeedTopNavPerRelayFilterSet>,
|
||||
scope: CoroutineScope,
|
||||
val newThreads: FeedContentState,
|
||||
val replies: FeedContentState,
|
||||
val enabledHomeFeedTypes: StateFlow<Set<HomeFeedType>>,
|
||||
) : TopNavFeedQueryState(account, listName, followsPerRelay, scope, listOf(newThreads, replies))
|
||||
|
||||
class HomeFilterAssembler(
|
||||
client: INostrClient,
|
||||
) : TopNavFeedFilterAssembler<HomeQueryState>({ keys -> listOf(HomeOutboxEventsEoseManager(client, keys)) })
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.home.datasource.nip01Core
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.aroundMe.LocationTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip22Comments.filterHomePostsByScopes
|
||||
+4
-4
@@ -18,15 +18,15 @@
|
||||
* 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.home.datasource.nip01Core
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.global.GlobalTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.HomePostsConversationKinds
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.HomePostsNewThreadKinds1
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.HomePostsNewThreadKinds2
|
||||
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.nip65Follows.HomePostsConversationKinds
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip65Follows.HomePostsNewThreadKinds1
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip65Follows.HomePostsNewThreadKinds2
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
fun filterHomePostsByGlobal(
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.home.datasource.nip01Core
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.hashtag.HashtagTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.nip22Comments.filterHomePostsByScopes
|
||||
+4
-4
@@ -18,15 +18,15 @@
|
||||
* 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.home.datasource.nip01Core
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.relay.RelayTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.HomePostsConversationKinds
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.HomePostsNewThreadKinds1
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.HomePostsNewThreadKinds2
|
||||
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.nip65Follows.HomePostsConversationKinds
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip65Follows.HomePostsNewThreadKinds1
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip65Follows.HomePostsNewThreadKinds2
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
fun filterHomePostsByRelay(
|
||||
+4
-4
@@ -18,14 +18,14 @@
|
||||
* 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.home.datasource.nip65Follows
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.allFollows.AllFollowsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core.filterHomePostsByGeohashes
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core.filterHomePostsByHashtags
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip72Communities.filterHomePostsFromAllCommunities
|
||||
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.nip72Communities.filterHomePostsFromAllCommunities
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
|
||||
fun filterHomePostsByAllFollows(
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.home.datasource.nip65Follows
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.author.AuthorsTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.muted.MutedAuthorsTopNavPerRelayFilterSet
|
||||
+25
-89
@@ -18,11 +18,9 @@
|
||||
* 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.home.datasource.nip65Follows
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.HomeFeedType
|
||||
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.favoriteAlgoFeeds.FavoriteAlgoFeedTopNavPerRelayFilterSet
|
||||
@@ -33,39 +31,38 @@ 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.eoseManagers.PerUserAndFollowListEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.HomeQueryState
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core.filterHomePostsByGeohashes
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core.filterHomePostsByGlobal
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core.filterHomePostsByHashtags
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip01Core.filterHomePostsByRelay
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip72Communities.filterHomePostsByAllCommunities
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip72Communities.filterHomePostsByCommunity
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.home.nip90AlgoFeeds.filterHomePostsByAlgoFeedIds
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.scopedTo
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.topNavFeeds.TopNavFeedSubAssembler
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.HomeQueryState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core.filterHomePostsByGeohashes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core.filterHomePostsByGlobal
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core.filterHomePostsByHashtags
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip01Core.filterHomePostsByRelay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip72Communities.filterHomePostsByAllCommunities
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip72Communities.filterHomePostsByCommunity
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.datasource.nip90AlgoFeeds.filterHomePostsByAlgoFeedIds
|
||||
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.Flow
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.sample
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* The home feed's REQ builder: dispatches the top-nav selection to the home filters, bounded by the
|
||||
* new-threads and replies floors, and drops the kinds the user turned off in Settings › Home.
|
||||
*/
|
||||
class HomeOutboxEventsEoseManager(
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<HomeQueryState>,
|
||||
) : PerUserAndFollowListEoseManager<HomeQueryState, TopFilter>(client, allKeys) {
|
||||
) : TopNavFeedSubAssembler<HomeQueryState>(client, allKeys) {
|
||||
override fun updateFilter(
|
||||
key: HomeQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
val feedSettings = key.followsPerRelay()
|
||||
val newThreadSince = key.feedState.homeNewThreads.lastNoteCreatedAtIfFilled()
|
||||
val repliesSince = key.feedState.homeReplies.lastNoteCreatedAtIfFilled()
|
||||
): List<RelayBasedFilter> {
|
||||
val feedSettings = key.followsPerRelay.value
|
||||
val newThreadSince = key.newThreads.lastNoteCreatedAtIfFilled()
|
||||
val repliesSince = key.replies.lastNoteCreatedAtIfFilled()
|
||||
|
||||
val base =
|
||||
when (feedSettings) {
|
||||
is AllCommunitiesTopNavPerRelayFilterSet -> filterHomePostsByAllCommunities(feedSettings, since, newThreadSince)
|
||||
@@ -83,75 +80,14 @@ class HomeOutboxEventsEoseManager(
|
||||
|
||||
// Drop the kinds the user turned off in Settings › Home from every home relay filter, so a
|
||||
// disabled group is never downloaded regardless of which top-nav strategy built the filters.
|
||||
return base.removeDisabledHomeKinds(HomeFeedType.disabledKinds(key.account.settings.enabledHomeFeedTypes.value))
|
||||
return base.removeDisabledHomeKinds(HomeFeedType.disabledKinds(key.enabledHomeFeedTypes.value))
|
||||
}
|
||||
|
||||
override fun user(key: HomeQueryState) = key.account.userProfile()
|
||||
|
||||
override fun list(key: HomeQueryState) = key.listName()
|
||||
|
||||
fun HomeQueryState.listNameFlow() = account.settings.defaultHomeFollowList
|
||||
|
||||
fun HomeQueryState.listName() = listNameFlow().value
|
||||
|
||||
fun HomeQueryState.followRelayFlow() = account.liveHomeFollowListsPerRelay
|
||||
|
||||
fun HomeQueryState.followsPerRelay() = followRelayFlow().value
|
||||
|
||||
val userJobMap = mutableMapOf<User, List<Job>>()
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override fun newSub(key: HomeQueryState): 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.followRelayFlow().sample(1000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.scope.launch(Dispatchers.IO) {
|
||||
// Re-arm the home subscriptions when a content-type toggle flips, so a disabled
|
||||
// group leaves the live REQ and a re-enabled one comes back without a restart.
|
||||
key.account.settings.enabledHomeFeedTypes
|
||||
.drop(1)
|
||||
.collectLatest { invalidateFilters() }
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedState.homeNewThreads.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.feedState.homeReplies.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() }
|
||||
}
|
||||
// Re-arm the home subscriptions when a content-type toggle flips, so a disabled group leaves the
|
||||
// live REQ and a re-enabled one comes back without a restart.
|
||||
override fun extraInvalidators(key: HomeQueryState): List<Flow<*>> = listOf(key.enabledHomeFeedTypes.drop(1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the [disabled] kinds from each home filter. A filter with no `kinds` (e.g. an
|
||||
* algo-feed id/address fetch) is left untouched; a filter whose kinds all become disabled is
|
||||
* dropped entirely, since sending it with an empty `kinds` would wrongly match every kind.
|
||||
*/
|
||||
private fun List<RelayBasedFilter>.removeDisabledHomeKinds(disabled: Set<Int>): List<RelayBasedFilter> {
|
||||
if (disabled.isEmpty()) return this
|
||||
return mapNotNull { relayFilter ->
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.home.datasource.nip72Communities
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip72Communities
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.allcommunities.AllCommunitiesTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.home.datasource.nip72Communities
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip72Communities
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.noteBased.community.SingleCommunityTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.home.datasource.nip90AlgoFeeds
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip90AlgoFeeds
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.favoriteAlgoFeeds.FavoriteAlgoFeedTopNavPerRelayFilter
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.favoriteAlgoFeeds.FavoriteAlgoFeedTopNavPerRelayFilterSet
|
||||
+8
-3
@@ -18,24 +18,29 @@
|
||||
* 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
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.napplets
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.model.IAccount
|
||||
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
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Keyspace for the connected-apps manifest subscription. Carries the account (for relay selection)
|
||||
* and the specific authors whose napplet manifests should be fetched — the set of pubkeys that have
|
||||
* been granted permissions in the user's ledger.
|
||||
*
|
||||
* @param homeRelays the user's home (read) relays, where the connected apps' manifests are fetched.
|
||||
*/
|
||||
class ConnectedAppsQueryState(
|
||||
override val account: Account,
|
||||
override val account: IAccount,
|
||||
val authors: Set<HexKey>,
|
||||
val homeRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||
) : AccountScopedQuery
|
||||
|
||||
/**
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
* 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
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.napplets
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.eoseManagers.PerUserEoseManager
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.napplets.filterNappletsByAuthors
|
||||
@@ -43,7 +43,7 @@ class ConnectedAppsSubAssembler(
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter> {
|
||||
if (key.authors.isEmpty()) return emptyList()
|
||||
val relays = key.account.homeRelays.flow.value
|
||||
val relays = key.homeRelays.value
|
||||
if (relays.isEmpty()) return emptyList()
|
||||
return relays.flatMap { relay ->
|
||||
filterNappletsByAuthors(relay, key.authors, since?.get(relay)?.time)
|
||||
+6
-6
@@ -18,14 +18,13 @@
|
||||
* 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.threadview.datasources.subassembies
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.thread
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.ThreadAssembler
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
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
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
@@ -40,6 +39,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
* saves the of the thread EOSEs in the long run to avoid re-downloading
|
||||
*/
|
||||
class ThreadEventLoaderSubAssembler(
|
||||
val cache: ICacheProvider,
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<ThreadQueryState>,
|
||||
) : PerUniqueIdEoseManager<ThreadQueryState, HexKey>(client, allKeys, invalidateAfterEose = true) {
|
||||
@@ -47,9 +47,9 @@ class ThreadEventLoaderSubAssembler(
|
||||
key: ThreadQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
val branches = ThreadAssembler(LocalCache).findThreadFor(key.eventId) ?: return null
|
||||
val defaultRelays = key.account.followPlusAllMineWithSearch.flow.value
|
||||
return filterMissingEventsForThread(LocalCache, branches, defaultRelays)
|
||||
val branches = ThreadAssembler(cache).findThreadFor(key.eventId) ?: return null
|
||||
val defaultRelays = key.defaultRelays.value
|
||||
return filterMissingEventsForThread(cache, branches, defaultRelays)
|
||||
}
|
||||
|
||||
override fun id(key: ThreadQueryState) = key.eventId
|
||||
+17
-8
@@ -18,32 +18,41 @@
|
||||
* 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.threadview.datasources
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.thread
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.commons.model.IAccount
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
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.threadview.datasources.subassembies.ThreadEventLoaderSubAssembler
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.subassembies.ThreadFilterSubAssembler
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
// This allows multiple screen to be listening to tags, even the same tag
|
||||
/**
|
||||
* One screen's interest in a thread. Several screens may open the same thread at once, each with
|
||||
* its own key.
|
||||
*
|
||||
* @param defaultRelays follows' outboxes plus the user's relays and search relays: the fallback set
|
||||
* asked for a thread event that no relay hint resolves.
|
||||
*/
|
||||
@Stable
|
||||
class ThreadQueryState(
|
||||
val eventId: HexKey,
|
||||
override val account: Account,
|
||||
override val account: IAccount,
|
||||
val defaultRelays: StateFlow<Set<NormalizedRelayUrl>>,
|
||||
) : AccountScopedQuery
|
||||
|
||||
@Stable
|
||||
class ThreadFilterAssembler(
|
||||
cache: ICacheProvider,
|
||||
client: INostrClient,
|
||||
) : ComposeSubscriptionManager<ThreadQueryState>() {
|
||||
val group =
|
||||
listOf(
|
||||
ThreadFilterSubAssembler(client, ::allKeys),
|
||||
ThreadEventLoaderSubAssembler(client, ::allKeys),
|
||||
ThreadFilterSubAssembler(cache, client, ::allKeys),
|
||||
ThreadEventLoaderSubAssembler(cache, client, ::allKeys),
|
||||
)
|
||||
|
||||
override fun invalidateKeys() = invalidateFilters()
|
||||
+4
-4
@@ -18,14 +18,13 @@
|
||||
* 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.threadview.datasources.subassembies
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.thread
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.ThreadAssembler
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||
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
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
@@ -37,6 +36,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
* saves the of the thread EOSEs in the long run to avoid re-downloading
|
||||
*/
|
||||
class ThreadFilterSubAssembler(
|
||||
val cache: ICacheProvider,
|
||||
client: INostrClient,
|
||||
allKeys: () -> Set<ThreadQueryState>,
|
||||
) : PerUniqueIdEoseManager<ThreadQueryState, HexKey>(client, allKeys) {
|
||||
@@ -44,7 +44,7 @@ class ThreadFilterSubAssembler(
|
||||
key: ThreadQueryState,
|
||||
since: SincePerRelayMap?,
|
||||
): List<RelayBasedFilter>? {
|
||||
val root = ThreadAssembler(LocalCache).findRoot(key.eventId) ?: return null
|
||||
val root = ThreadAssembler(cache).findRoot(key.eventId) ?: return null
|
||||
|
||||
return filterEventsInThreadForRoot(root, since)
|
||||
}
|
||||
+5
-5
@@ -18,17 +18,17 @@
|
||||
* 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.home.datasource.nip90AlgoFeeds
|
||||
package com.vitorpamplona.amethyst.commons.relayClient.home.nip90AlgoFeeds
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.favoriteAlgoFeeds.FavoriteAlgoFeedTopNavPerRelayFilter
|
||||
import com.vitorpamplona.amethyst.commons.model.topNavFeeds.favoriteAlgoFeeds.FavoriteAlgoFeedTopNavPerRelayFilterSet
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class FilterHomePostsByAlgoFeedIdsTest {
|
||||
private val userRelay = RelayUrlNormalizer.normalizeOrNull("wss://user.example/")!!
|
||||
Reference in New Issue
Block a user