feat(subscriptions): split foreground-only account loaders into AccountForegroundFilterAssembler
Move AccountFollowsLoaderSubAssembler and AccountNotificationsEoseFromRandomRelaysManager out of the always-on AccountFilterAssembler and into a new AccountForegroundFilterAssembler that is mounted via LifecycleAwareKeyDataSourceSubscription. These two scan-heavy loaders now pause on ON_STOP and resume on ON_START, while the lightweight always-on account work (metadata, gift wraps, drafts, inbox-relay notifications, marmot groups) keeps running in background. https://claude.ai/code/session_01RUmRxmzUAcVXezF9PEETGz
This commit is contained in:
+7
-2
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand
|
|||||||
|
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssembler
|
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.reqCommand.channel.ChannelFinderFilterAssemblyGroup
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssembler
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderFilterAssembler
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCPaymentFilterAssembler
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCPaymentFilterAssembler
|
||||||
@@ -70,8 +71,11 @@ class RelaySubscriptionsCoordinator(
|
|||||||
failureTracker: RelayOfflineTracker,
|
failureTracker: RelayOfflineTracker,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
// main one: notifications, dms and account settings
|
// main one: notifications, dms and account settings (always-on while logged in)
|
||||||
val account = AccountFilterAssembler(client, cache, authenticator, failureTracker, scope)
|
val account = AccountFilterAssembler(client)
|
||||||
|
|
||||||
|
// foreground-only account loaders (follows-outbox finder, random-relay notifications)
|
||||||
|
val accountForeground = AccountForegroundFilterAssembler(client, cache, authenticator, failureTracker, scope)
|
||||||
|
|
||||||
// always running, feed assemblers.
|
// always running, feed assemblers.
|
||||||
val home = HomeFilterAssembler(client)
|
val home = HomeFilterAssembler(client)
|
||||||
@@ -124,6 +128,7 @@ class RelaySubscriptionsCoordinator(
|
|||||||
val all =
|
val all =
|
||||||
listOf(
|
listOf(
|
||||||
account,
|
account,
|
||||||
|
accountForeground,
|
||||||
home,
|
home,
|
||||||
chatroomList,
|
chatroomList,
|
||||||
video,
|
video,
|
||||||
|
|||||||
+3
-13
@@ -23,20 +23,14 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.account
|
|||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.drafts.AccountDraftsEoseManager
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.drafts.AccountDraftsEoseManager
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.follows.AccountFollowsLoaderSubAssembler
|
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.marmot.MarmotGroupEventsEoseManager
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.marmot.MarmotGroupEventsEoseManager
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.metadata.AccountMetadataEoseManager
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.metadata.AccountMetadataEoseManager
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.nip01Notifications.AccountNotificationsEoseFromInboxRelaysManager
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.nip01Notifications.AccountNotificationsEoseFromInboxRelaysManager
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.nip01Notifications.AccountNotificationsEoseFromRandomRelaysManager
|
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.nip59GiftWraps.AccountGiftWrapsEoseManager
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.nip59GiftWraps.AccountGiftWrapsEoseManager
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountFeedContentStates
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountFeedContentStates
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
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
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
|
|
||||||
// This allows multiple screen to be listening to logged-in accounts.
|
// This allows multiple screen to be listening to logged-in accounts.
|
||||||
@Stable
|
@Stable
|
||||||
@@ -47,24 +41,20 @@ class AccountQueryState(
|
|||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This assembler loads everything eech account needs.
|
* Always-on account loaders: metadata, gift wraps, drafts, inbox-relay
|
||||||
|
* notifications, marmot group events. Foreground-only loaders live in
|
||||||
|
* [AccountForegroundFilterAssembler].
|
||||||
*/
|
*/
|
||||||
@Stable
|
@Stable
|
||||||
class AccountFilterAssembler(
|
class AccountFilterAssembler(
|
||||||
client: INostrClient,
|
client: INostrClient,
|
||||||
cache: LocalCache,
|
|
||||||
authenticator: IAuthStatus,
|
|
||||||
failureTracker: RelayOfflineTracker,
|
|
||||||
scope: CoroutineScope,
|
|
||||||
) : ComposeSubscriptionManager<AccountQueryState>() {
|
) : ComposeSubscriptionManager<AccountQueryState>() {
|
||||||
val group =
|
val group =
|
||||||
listOf(
|
listOf(
|
||||||
AccountMetadataEoseManager(client, ::allKeys),
|
AccountMetadataEoseManager(client, ::allKeys),
|
||||||
AccountFollowsLoaderSubAssembler(client, cache, scope, authenticator, failureTracker, ::allKeys),
|
|
||||||
AccountGiftWrapsEoseManager(client, ::allKeys),
|
AccountGiftWrapsEoseManager(client, ::allKeys),
|
||||||
AccountDraftsEoseManager(client, ::allKeys),
|
AccountDraftsEoseManager(client, ::allKeys),
|
||||||
AccountNotificationsEoseFromInboxRelaysManager(client, ::allKeys),
|
AccountNotificationsEoseFromInboxRelaysManager(client, ::allKeys),
|
||||||
AccountNotificationsEoseFromRandomRelaysManager(client, ::allKeys),
|
|
||||||
MarmotGroupEventsEoseManager(client, ::allKeys),
|
MarmotGroupEventsEoseManager(client, ::allKeys),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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.service.relayClient.reqCommand.account
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager
|
||||||
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.follows.AccountFollowsLoaderSubAssembler
|
||||||
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.nip01Notifications.AccountNotificationsEoseFromRandomRelaysManager
|
||||||
|
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
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account-scoped loaders that only need to run while the app has a screen
|
||||||
|
* in the foreground. Mounted by a lifecycle-aware subscription so they pause
|
||||||
|
* on ON_STOP and resume on ON_START.
|
||||||
|
*
|
||||||
|
* Lightweight always-on account work (metadata, gift wraps, drafts, inbox
|
||||||
|
* notifications, marmot groups) stays in [AccountFilterAssembler].
|
||||||
|
*/
|
||||||
|
@Stable
|
||||||
|
class AccountForegroundFilterAssembler(
|
||||||
|
client: INostrClient,
|
||||||
|
cache: LocalCache,
|
||||||
|
authenticator: IAuthStatus,
|
||||||
|
failureTracker: RelayOfflineTracker,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
) : ComposeSubscriptionManager<AccountQueryState>() {
|
||||||
|
val group =
|
||||||
|
listOf(
|
||||||
|
AccountFollowsLoaderSubAssembler(client, cache, scope, authenticator, failureTracker, ::allKeys),
|
||||||
|
AccountNotificationsEoseFromRandomRelaysManager(client, ::allKeys),
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun invalidateKeys() = invalidateFilters()
|
||||||
|
|
||||||
|
override fun invalidateFilters() = group.forEach { it.invalidateFilters() }
|
||||||
|
|
||||||
|
override fun destroy() = group.forEach { it.destroy() }
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.service.relayClient.reqCommand.account
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AccountForegroundFilterAssemblerSubscription(accountViewModel: AccountViewModel) =
|
||||||
|
AccountForegroundFilterAssemblerSubscription(
|
||||||
|
accountViewModel,
|
||||||
|
accountViewModel.dataSources().accountForeground,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AccountForegroundFilterAssemblerSubscription(
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
dataSource: AccountForegroundFilterAssembler,
|
||||||
|
) {
|
||||||
|
val state =
|
||||||
|
remember(accountViewModel) {
|
||||||
|
AccountQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.trustedAccounts.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
LifecycleAwareKeyDataSourceSubscription(state, dataSource)
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.model.Account
|
|||||||
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.RelayAuthSubscription
|
import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.RelayAuthSubscription
|
||||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssemblerSubscription
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountFilterAssemblerSubscription
|
||||||
|
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.AccountForegroundFilterAssemblerSubscription
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
|
||||||
@@ -82,6 +83,10 @@ fun LoggedInPage(
|
|||||||
// Loads account information + DMs and Notifications from Relays.
|
// Loads account information + DMs and Notifications from Relays.
|
||||||
AccountFilterAssemblerSubscription(accountViewModel)
|
AccountFilterAssemblerSubscription(accountViewModel)
|
||||||
|
|
||||||
|
// Foreground-only loaders: follows-outbox finder + random-relay notifications.
|
||||||
|
// Pauses on ON_STOP, resumes on ON_START.
|
||||||
|
AccountForegroundFilterAssemblerSubscription(accountViewModel)
|
||||||
|
|
||||||
// Pre-loads the feed for every icon the user has pinned to the bottom bar.
|
// Pre-loads the feed for every icon the user has pinned to the bottom bar.
|
||||||
// Subscriptions follow the user's chosen list reactively, not the default 5.
|
// Subscriptions follow the user's chosen list reactively, not the default 5.
|
||||||
BottomBarFeedPreloaders(accountViewModel)
|
BottomBarFeedPreloaders(accountViewModel)
|
||||||
|
|||||||
Reference in New Issue
Block a user