diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt index 9265d889d..93a556a01 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/NotificationRelayService.kt @@ -89,7 +89,20 @@ class NotificationRelayService : Service() { try { ContextCompat.startForegroundService(context, intent) } catch (e: Exception) { - Log.e(TAG, "Failed to start foreground service", e) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && + e is ForegroundServiceStartNotAllowedException + ) { + // Android 12+ blocks startForegroundService() from the background unless + // the caller has a temporary FGS exemption (e.g. broadcast receiver, + // exact alarm, high-priority FCM). Cold-start triggered by a flow emission + // from AlwaysOnNotificationServiceManager hits this path. The other + // notification layers (BootCompletedReceiver, ServiceWatchdogManager, + // NotificationCatchUpWorker) plus MainActivity.onResume retry from + // contexts that are allowed. + Log.w(TAG) { "Foreground service start not allowed from background; will retry from another layer" } + } else { + Log.e(TAG, "Failed to start foreground service", e) + } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt index 0859ef4b3..1694849b0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand 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.reqCommand.event.EventFinderFilterAssembler import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCPaymentFilterAssembler @@ -70,8 +71,11 @@ class RelaySubscriptionsCoordinator( failureTracker: RelayOfflineTracker, scope: CoroutineScope, ) { - // main one: notifications, dms and account settings - val account = AccountFilterAssembler(client, cache, authenticator, failureTracker, scope) + // main one: notifications, dms and account settings (always-on while logged in) + 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. val home = HomeFilterAssembler(client) @@ -124,6 +128,7 @@ class RelaySubscriptionsCoordinator( val all = listOf( account, + accountForeground, home, chatroomList, video, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountFilterAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountFilterAssembler.kt index bbf37f88c..6b28e608b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountFilterAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountFilterAssembler.kt @@ -23,20 +23,14 @@ 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.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.follows.AccountFollowsLoaderSubAssembler 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.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.ui.screen.loggedIn.AccountFeedContentStates import com.vitorpamplona.quartz.nip01Core.core.HexKey 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. @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 class AccountFilterAssembler( client: INostrClient, - cache: LocalCache, - authenticator: IAuthStatus, - failureTracker: RelayOfflineTracker, - scope: CoroutineScope, ) : ComposeSubscriptionManager() { val group = listOf( AccountMetadataEoseManager(client, ::allKeys), - AccountFollowsLoaderSubAssembler(client, cache, scope, authenticator, failureTracker, ::allKeys), AccountGiftWrapsEoseManager(client, ::allKeys), AccountDraftsEoseManager(client, ::allKeys), AccountNotificationsEoseFromInboxRelaysManager(client, ::allKeys), - AccountNotificationsEoseFromRandomRelaysManager(client, ::allKeys), MarmotGroupEventsEoseManager(client, ::allKeys), ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountForegroundFilterAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountForegroundFilterAssembler.kt new file mode 100644 index 000000000..0678be343 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountForegroundFilterAssembler.kt @@ -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() { + 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() } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountForegroundFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountForegroundFilterAssemblerSubscription.kt new file mode 100644 index 000000000..8938fa0ea --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/AccountForegroundFilterAssemblerSubscription.kt @@ -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) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/channel/ChannelFinderFilterAssemblySubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/channel/ChannelFinderFilterAssemblySubscription.kt index 75b2b8d96..8d5f6828a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/channel/ChannelFinderFilterAssemblySubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/channel/ChannelFinderFilterAssemblySubscription.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.channel import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import com.vitorpamplona.amethyst.commons.model.Channel -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun ChannelFinderFilterAssemblerSubscription( ChannelFinderQueryState(channel) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/EventFinderFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/EventFinderFilterAssemblerSubscription.kt index f24b48b75..005f4f203 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/EventFinderFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/event/EventFinderFilterAssemblerSubscription.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.event import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -46,5 +46,5 @@ fun EventFinderFilterAssemblerSubscription( EventFinderQueryState(note, account) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt index 8890e3698..04dd4f2df 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/user/UserFinderFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.service.relayClient.reqCommand.user import android.annotation.SuppressLint import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -52,5 +52,5 @@ fun UserFinderFilterAssemblerSubscription( UserFinderQueryState(user, forAccount) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/TextSearchDataSourceSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/TextSearchDataSourceSubscription.kt index 80b916cec..87cb9880c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/TextSearchDataSourceSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/TextSearchDataSourceSubscription.kt @@ -21,7 +21,7 @@ package com.vitorpamplona.amethyst.service.relayClient.searchCommand import androidx.compose.runtime.Composable -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchBarViewModel @@ -36,5 +36,5 @@ fun TextSearchDataSourceSubscription( searchBarViewModel: SearchBarViewModel, dataSource: SearchFilterAssembler, ) { - KeyDataSourceSubscription(searchBarViewModel.searchDataSourceState, dataSource) + LifecycleAwareKeyDataSourceSubscription(searchBarViewModel.searchDataSourceState, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/UserSearchDataSourceSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/UserSearchDataSourceSubscription.kt index 21d5e22df..f0cf9f248 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/UserSearchDataSourceSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/searchCommand/UserSearchDataSourceSubscription.kt @@ -21,7 +21,7 @@ package com.vitorpamplona.amethyst.service.relayClient.searchCommand import androidx.compose.runtime.Composable -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -36,5 +36,5 @@ fun UserSearchDataSourceSubscription( userSuggestions: UserSuggestionState, dataSource: SearchFilterAssembler, ) { - KeyDataSourceSubscription(userSuggestions.searchDataSourceState, dataSource) + LifecycleAwareKeyDataSourceSubscription(userSuggestions.searchDataSourceState, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt index 4b1cd5789..40a464cec 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt @@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.debugState import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService +import com.vitorpamplona.amethyst.service.notifications.NotificationRelayService import com.vitorpamplona.amethyst.service.playback.composable.DEFAULT_MUTED_SETTING import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia import com.vitorpamplona.amethyst.ui.navigation.findParameterValue @@ -89,6 +90,13 @@ class MainActivity : AppCompatActivity() { // starts muted every time DEFAULT_MUTED_SETTING.value = true + + // If always-on notifications are enabled but the foreground service couldn't be + // started from the background during cold-start (Android 12+ restriction), retry + // now that the activity is in the foreground. + if (NotificationRelayService.isEnabled(this)) { + NotificationRelayService.start(this) + } } override fun onPause() { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoggedInPage.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoggedInPage.kt index d78c48991..3e1843906 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoggedInPage.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoggedInPage.kt @@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils 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.AccountForegroundFilterAssemblerSubscription import com.vitorpamplona.amethyst.ui.navigation.AppNavigation import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager @@ -82,6 +83,10 @@ fun LoggedInPage( // Loads account information + DMs and Notifications from Relays. 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. // Subscriptions follow the user's chosen list reactively, not the default 5. BottomBarFeedPreloaders(accountViewModel) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/datasource/ArticlesFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/datasource/ArticlesFilterAssemblerSubscription.kt index d0791c6a0..8be1a112b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/datasource/ArticlesFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/articles/datasource/ArticlesFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun ArticlesFilterAssemblerSubscription( ArticlesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/datasource/BadgesFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/datasource/BadgesFilterAssemblerSubscription.kt index 3d4b818ea..5ce4d48f7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/datasource/BadgesFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/datasource/BadgesFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun BadgesFilterAssemblerSubscription( BadgesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/datasource/ProfileBadgesFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/datasource/ProfileBadgesFilterAssemblerSubscription.kt index c0ecc05a1..cfb44d7e4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/datasource/ProfileBadgesFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/profile/datasource/ProfileBadgesFilterAssemblerSubscription.kt @@ -22,7 +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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -32,5 +32,5 @@ fun ProfileBadgesFilterAssemblerSubscription(accountViewModel: AccountViewModel) ProfileBadgesQueryState(accountViewModel.account) } - KeyDataSourceSubscription(state, accountViewModel.dataSources().profileBadges) + LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().profileBadges) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/datasource/ChatroomFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/datasource/ChatroomFilterAssemblerSubscription.kt index 9ceed3838..c666272fc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/datasource/ChatroomFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/datasource/ChatroomFilterAssemblerSubscription.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.datasource import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey @@ -39,5 +39,5 @@ fun ChatroomFilterAssemblerSubscription( ChatroomQueryState(room, accountViewModel.account) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/datasource/ChannelFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/datasource/ChannelFilterAssemblerSubscription.kt index e8231e71b..7c748382b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/datasource/ChannelFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/datasource/ChannelFilterAssemblerSubscription.kt @@ -27,7 +27,7 @@ import androidx.compose.runtime.remember import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.commons.model.Channel import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -43,7 +43,7 @@ fun ChannelFilterAssemblerSubscription( ChannelQueryState(channel, accountViewModel.account) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) // Live streams need the 30311 event to populate `channel.info` before we can read its // `goal` tag and add the goal+zap subscriptions. Re-invalidate when metadata changes so diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt index acbe21fc5..97e17fb04 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chess/datasource/ChessSubscription.kt @@ -25,7 +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.subscriptions.KeyDataSourceSubscription +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 @@ -78,14 +78,14 @@ fun ChessSubscription( } // Register subscription with Amethyst's subscription system - KeyDataSourceSubscription(state, chessAssembler) + LifecycleAwareKeyDataSourceSubscription(state, chessAssembler) // Trigger ViewModel refresh when subscription state changes // This fetches challenges from LocalCache after events arrive DisposableEffect(state) { chessViewModel.forceRefresh() onDispose { - // Subscription cleanup handled by KeyDataSourceSubscription + // Subscription cleanup handled by LifecycleAwareKeyDataSourceSubscription } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/datasource/CommunityFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/datasource/CommunityFilterAssemblerSubscription.kt index becd02331..8f6c9210f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/datasource/CommunityFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/datasource/CommunityFilterAssemblerSubscription.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.datasource import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.AddressableNote @Composable @@ -37,5 +37,5 @@ fun CommunityFilterAssemblerSubscription( CommunityQueryState(channel) } - KeyDataSourceSubscription(state, filterAssembler) + LifecycleAwareKeyDataSourceSubscription(state, filterAssembler) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/datasource/CommunitiesListFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/datasource/CommunitiesListFilterAssemblerSubscription.kt index a1db05935..c04be6c0c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/datasource/CommunitiesListFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/communities/list/datasource/CommunitiesListFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun CommunitiesListFilterAssemblerSubscription( CommunitiesListQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/datasource/BrowseEmojiSetsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/datasource/BrowseEmojiSetsFilterAssemblerSubscription.kt index 4d801f915..9fb3e7d30 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/datasource/BrowseEmojiSetsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/emojipacks/browse/datasource/BrowseEmojiSetsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun BrowseEmojiSetsFilterAssemblerSubscription( BrowseEmojiSetsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/datasource/FollowPackFeedFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/datasource/FollowPackFeedFilterAssemblerSubscription.kt index cdf6c6e27..3b9f99ce7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/datasource/FollowPackFeedFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/feed/datasource/FollowPackFeedFilterAssemblerSubscription.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.datasourc import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -38,5 +38,5 @@ fun FollowPackFeedFilterAssemblerSubscription( FollowPackFeedQueryState(pack, accountViewModel.account) } - KeyDataSourceSubscription(state, accountViewModel.dataSources().followPacks) + LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().followPacks) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/datasource/FollowPacksFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/datasource/FollowPacksFilterAssemblerSubscription.kt index 67eb7a6a7..8b8256ff1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/datasource/FollowPacksFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/followPacks/list/datasource/FollowPacksFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun FollowPacksFilterAssemblerSubscription( FollowPacksQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/datasource/GeoHashFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/datasource/GeoHashFilterAssemblerSubscription.kt index 3ae5253b9..349d5502c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/datasource/GeoHashFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/geohash/datasource/GeoHashFilterAssemblerSubscription.kt @@ -23,7 +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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -40,5 +40,5 @@ fun GeoHashFilterAssemblerSubscription( GeohashQueryState(tag.geohash, accountViewModel.account.followOutboxesOrProxy.flow.value) } - KeyDataSourceSubscription(state, accountViewModel.dataSources().geohashes) + LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().geohashes) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFilterAssemblerSubscription.kt index 6b5753126..0075f5d49 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/hashtag/datasource/HashtagFilterAssemblerSubscription.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.datasource import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -38,5 +38,5 @@ fun HashtagFilterAssemblerSubscription( HashtagQueryState(tag.hashtag, accountViewModel.account.followOutboxesOrProxy.flow.value) } - KeyDataSourceSubscription(state, accountViewModel.dataSources().hashtags) + LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().hashtags) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/datasource/LiveStreamsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/datasource/LiveStreamsFilterAssemblerSubscription.kt index d890b6403..76111d119 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/datasource/LiveStreamsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/livestreams/datasource/LiveStreamsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun LiveStreamsFilterAssemblerSubscription( LiveStreamsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/datasource/LongsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/datasource/LongsFilterAssemblerSubscription.kt index 10b999082..669ce58cc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/datasource/LongsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/longs/datasource/LongsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun LongsFilterAssemblerSubscription( LongsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomFilterAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomFilterAssembler.kt index a4abe1c97..bb7db2368 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomFilterAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomFilterAssembler.kt @@ -24,7 +24,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUniqueIdEoseManager @@ -156,5 +156,5 @@ fun NestRoomFilterAssemblerSubscription( remember(note, account.pubKey) { NestRoomQueryState(note, account) } - KeyDataSourceSubscription(state, filterAssembler) + LifecycleAwareKeyDataSourceSubscription(state, filterAssembler) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomLivenessProbeAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomLivenessProbeAssembler.kt index 9c45989ea..5ebe3d216 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomLivenessProbeAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestRoomLivenessProbeAssembler.kt @@ -24,7 +24,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.AddressableNote import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUniqueIdEoseManager @@ -137,5 +137,5 @@ fun NestRoomLivenessProbeSubscription( remember(note, account.pubKey) { NestRoomLivenessQueryState(note, account) } - KeyDataSourceSubscription(state, filterAssembler) + LifecycleAwareKeyDataSourceSubscription(state, filterAssembler) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestsFilterAssemblerSubscription.kt index 6aef84671..458e3a1e5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/datasource/NestsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun NestsFilterAssemblerSubscription( NestsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt index 0e8efdefb..0091834e1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/stage/ParticipantsGrid.kt @@ -58,6 +58,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp @@ -93,6 +94,16 @@ private val MAX_RING_WIDTH = 7.dp // fades to 0 when not speaking. private val MAX_GLOW_RADIUS = 12.dp +// Detached green ring drawn around a speaking avatar with a clear +// gap to the profile picture, so the "is talking" signal reads from +// across the room without overpowering the face. Only rendered while +// `isSpeaking` is true; pulses with the live audio level. Drawn via +// drawBehind to avoid affecting layout bounds — the gap + ring fit +// inside the GRID_SPACING between cells. +private val OUTER_RING_GAP = 5.dp +private val OUTER_RING_WIDTH = 2.5.dp +private val OUTER_RING_MAX_WIDTH = 4.dp + // Cap badge sizes so they stay legible without dominating the avatar // at 100.dp. The 0.42 ratio was tuned for ~48.dp avatars (giving // ~20.dp badges); without a cap, scaling to 100.dp produces 42.dp @@ -392,16 +403,44 @@ private fun MemberCell( targetValue = if (isSpeaking) (clampedLevel * 0.45f) else 0f, label = "speaker-ring-glow-alpha", ) + // Detached outer ring: stays at full opacity while speaking so the + // signal is unmistakable, fades out when speech stops. Stroke width + // tracks audio level for an extra liveness cue on top of the inner + // border ring. + val animatedOuterRingAlpha by animateFloatAsState( + targetValue = if (isSpeaking) 1f else 0f, + label = "speaker-outer-ring-alpha", + ) + val animatedOuterRingWidth by animateDpAsState( + targetValue = + if (isSpeaking) { + OUTER_RING_WIDTH + (clampedLevel * (OUTER_RING_MAX_WIDTH - OUTER_RING_WIDTH).value).dp + } else { + 0.dp + }, + label = "speaker-outer-ring-width", + ) val avatarModifier = Modifier .drawBehind { - if (animatedGlowAlpha <= 0.001f) return@drawBehind - val baseRadius = size.minDimension / 2f - val extra = MAX_GLOW_RADIUS.toPx() * clampedLevel - drawCircle( - color = NEST_SPEAKING_COLOR.copy(alpha = animatedGlowAlpha), - radius = baseRadius + extra, - ) + if (animatedGlowAlpha > 0.001f) { + val baseRadius = size.minDimension / 2f + val extra = MAX_GLOW_RADIUS.toPx() * clampedLevel + drawCircle( + color = NEST_SPEAKING_COLOR.copy(alpha = animatedGlowAlpha), + radius = baseRadius + extra, + ) + } + if (animatedOuterRingAlpha > 0.001f && animatedOuterRingWidth > 0.dp) { + val baseRadius = size.minDimension / 2f + val strokePx = animatedOuterRingWidth.toPx() + val ringRadius = baseRadius + OUTER_RING_GAP.toPx() + strokePx / 2f + drawCircle( + color = NEST_SPEAKING_COLOR.copy(alpha = animatedOuterRingAlpha), + radius = ringRadius, + style = Stroke(width = strokePx), + ) + } }.border(animatedRingWidth, animatedRingColor, CircleShape) .let { if (member.absent) it.alpha(0.5f) else it } val user = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/datasource/PicturesFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/datasource/PicturesFilterAssemblerSubscription.kt index 90c0c0fc9..e91f3959c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/datasource/PicturesFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/pictures/datasource/PicturesFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun PicturesFilterAssemblerSubscription( PicturesQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/datasource/PollsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/datasource/PollsFilterAssemblerSubscription.kt index 52e489c57..ed74767bf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/datasource/PollsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/polls/datasource/PollsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun PollsFilterAssemblerSubscription( PollsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/datasource/ProductsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/datasource/ProductsFilterAssemblerSubscription.kt index 96f0123fa..a722f7ac9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/datasource/ProductsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/products/datasource/ProductsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun ProductsFilterAssemblerSubscription( ProductsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/datasource/UserProfileFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/datasource/UserProfileFilterAssemblerSubscription.kt index ddbc23006..9b86949c8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/datasource/UserProfileFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/datasource/UserProfileFilterAssemblerSubscription.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.datasource import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.User @Composable @@ -37,5 +37,5 @@ fun UserProfileFilterAssemblerSubscription( UserProfileQueryState(user) } - KeyDataSourceSubscription(state, assembler) + LifecycleAwareKeyDataSourceSubscription(state, assembler) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/datasource/PublicChatsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/datasource/PublicChatsFilterAssemblerSubscription.kt index 237080f80..a4f489a15 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/datasource/PublicChatsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/publicChats/datasource/PublicChatsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun PublicChatsFilterAssemblerSubscription( PublicChatsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relay/datasource/RelayFeedFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relay/datasource/RelayFeedFilterAssemblerSubscription.kt index 8780a35ef..628e191b2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relay/datasource/RelayFeedFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relay/datasource/RelayFeedFilterAssemblerSubscription.kt @@ -22,7 +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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer @@ -38,5 +38,5 @@ fun RelayFeedFilterAssemblerSubscription( RelayFeedQueryState(normalizedUrl) } ?: return - KeyDataSourceSubscription(state, accountViewModel.dataSources().relayFeed) + LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().relayFeed) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/datasource/RelayInfoNip66FilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/datasource/RelayInfoNip66FilterAssemblerSubscription.kt index 772278573..8fc8d3452 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/datasource/RelayInfoNip66FilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/datasource/RelayInfoNip66FilterAssemblerSubscription.kt @@ -22,7 +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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl @@ -36,5 +36,5 @@ fun RelayInfoNip66FilterAssemblerSubscription( RelayInfoNip66QueryState(relayUrl, accountViewModel.account.followOutboxesOrProxy.flow.value) } - KeyDataSourceSubscription(state, accountViewModel.dataSources().relayInfoNip66) + LifecycleAwareKeyDataSourceSubscription(state, accountViewModel.dataSources().relayInfoNip66) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/datasource/ShortsFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/datasource/ShortsFilterAssemblerSubscription.kt index df5f1d54b..3af1d674f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/datasource/ShortsFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/shorts/datasource/ShortsFilterAssemblerSubscription.kt @@ -23,7 +23,7 @@ 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.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @Composable @@ -44,5 +44,5 @@ fun ShortsFilterAssemblerSubscription( ShortsQueryState(accountViewModel.account, accountViewModel.feedStates, accountViewModel.viewModelScope) } - KeyDataSourceSubscription(state, dataSource) + LifecycleAwareKeyDataSourceSubscription(state, dataSource) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt index b3e317601..f30c0221c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt @@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription +import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -50,5 +50,5 @@ fun ThreadFilterAssemblerSubscription( ThreadQueryState(eventId, account) } - KeyDataSourceSubscription(state, filterAssembler) + LifecycleAwareKeyDataSourceSubscription(state, filterAssembler) } diff --git a/amethyst/src/main/res/values-sl-rSI/strings.xml b/amethyst/src/main/res/values-sl-rSI/strings.xml index 2f58f1582..f6c763eb1 100644 --- a/amethyst/src/main/res/values-sl-rSI/strings.xml +++ b/amethyst/src/main/res/values-sl-rSI/strings.xml @@ -176,6 +176,8 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Neuspešno shranjevanje slike Video shranjen v video galerijo telefona Neuspešno shranjevanje videa + PDF shranjen v Prenosi/Amethyst + PDF ni bil shranjen Naloži sliko Naloži datoteko Zajemi sliko @@ -502,6 +504,7 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Vklopi mikrofon Grem v živo… Oddajanje neuspešno: %1$s + Utišanje neuspešno: %1$s Za govor v tej sobi je potreben dostop do mikrofona. Odpri nastavitve Gnezda @@ -512,6 +515,10 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Stop Pridruži se gnezdu Gostitelj + Odpri + Nedavni klepeti + Trenutno v sobi ni nikogar + %1$d jih posluša Gostitelj Moderator Mikrofon je utišan @@ -522,11 +529,18 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem govori pridružen kot občinstvo zapusti gnezdo + Zapusti Prekini to gnezdo Ste gostitelj. Če zaprete sobo, boste prekinili povezavo vsem. Izberite »Zapusti«, če se želite vrniti pozneje – soba se samodejno zapre po 8 urah neaktivnosti. Zapri sobo Zapusti Sobe ni bilo mogoče zapreti. Vseeno zapuščam – soba se bo zaprla samodejno. + + %1$d poslušalec + %1$d poslušalcev + %1$d poslušalcev + %1$d poslušalci + V ŽIVO Pogovor Občinstvo @@ -561,7 +575,15 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Pomanjšaj Pomanjšaj za nadaljnje poslušanje Izpad zvoka + Nalagam sobo… + Te sobe ne morem odpreti + V prostoru manjka avdio storitev ali končna točka. Gostitelj mora posodobiti podatke, preden se poslušalci lahko povežejo. + Nazaj Začni sobo za klepet + V živo zdaj + Na urniku + Nedavno dodano + Končano %1$s nazaj Vzpostavi strežnik gnezda Niste še izbrali strežnika gnezda. Želite dodati %1$s na seznam strežnikov in nadaljevati?\n\nTo lahko kasneje spremenite v nastavitvah. Uporabi privzeto @@ -571,6 +593,7 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Ime sobe O čem teče beseda? URL naslov storitve MoQ + Stranska storitev za avtentikacijo — privzeto nostrnests.com Končna točka releja MoQ URL za WebTransport – običajno isti gostitelj URL naslovne slike (neobvezno) @@ -583,6 +606,11 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Vaši strežniki Shranjeno kot zamenljiv dogodek kind-10112, da lahko vaše nastavitve berejo tudi drugi odjemalci. Dodaj strežnik gnezda + Rele (WebTransport) URL + URL za avtentikacijo (JWT mint) + Dodaj + Rele + Avtentikacija Priporočeni strežniki Vgrajeni predlogi, ki jih lahko na seznam dodate z enim dotikom. uporabi Amethyst-ove privzete nastavitve @@ -1313,6 +1341,10 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Ni mogoče pridobiti fakture s strežnikov prejemnika Vaš \"Wallet Connect\" ponudnik je vrnil naslednjo napako: %1$s Ni bilo mogoče povezati s Tor + Tor se ne povezuje + Amethystu ni uspelo zagnati povezave Tor. Želite uporabiti običajno povezavo za nalaganje vsebine? To izbiro bomo ohranili eno uro, nato bomo ponovno poskusili s Torom. + Uporabi običajno povezavo + Nadaljuj s čakanjem Prenos rele dokumentacije ni na voljo Ni bilo mogoče sestaviti LNUrl iz \"lightning\" naslova \'%1$s\'. Preverite uporabnikove nastavitve Prejemnikova \"lightning\" storitev na %1$s ni na voljo. Izračunana je bila iz \"lightning\" naslova \'%2$s\'. Napaka: %3$s. Preverite, ali strežnik deluje in ali je \"lightning\" naslov pravilen @@ -1510,6 +1542,7 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Odpri vse reakcije na to objavo Zapri vse reakcije na to objavo Odgovori + Skoči na izvorno sporočilo Posreduj ali citiraj Všečkaj Zap @@ -1518,6 +1551,10 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Povlecite za spremembo vrstnega reda. Preklopite za dodajanje ali odstranjevanje elementov s spodnje vrstice. Če ni izbran noben element, je spodnja vrstica skrita. Dostopno Razporedi + Obnovi prevzete + Domači zavihki + Izberite zavihke, ki se prikažejo na začetnem zaslonu. Če je aktiven le en zavihek, je vrstica z zavihki skrita. + Vse Vrstica odzivnih ikon Nastavite prikaza gumbov za odzive, njihov vrstni red in prikaz števcev. Omogočeno @@ -2123,6 +2160,42 @@ Za podpisovanje se je potrebno prijaviti s privatnim ključem Nazadje viden na %1$s (%2$s nazaj) Nazadnje viden prav zdaj Nikoli viden + + %1$d minuta + %1$dminuti + %1$d minut + %1$d minute + + + %1$d Ura + %1$d uri + %1$d ure + %1$d ur + + + %1$d dan + %1$d dni + %1$d dni + %1$d dni + + + %1$d teden + %1$d tedna + %1$d tedni + %1$d tednov + + + %1$d mesec + %1$d meseca + %1$d meseci + %1$d mesecev + + + %1$d leto + %1$d leti + %1$d let + %1$d let + <%1$s Povezujem Prenašam diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/subscriptions/LifecycleAwareKeyDataSourceSubscription.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/subscriptions/LifecycleAwareKeyDataSourceSubscription.kt index bbd9bc1c1..6a5df9fa9 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/subscriptions/LifecycleAwareKeyDataSourceSubscription.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relayClient/subscriptions/LifecycleAwareKeyDataSourceSubscription.kt @@ -22,23 +22,35 @@ package com.vitorpamplona.amethyst.commons.relayClient.subscriptions import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.LocalLifecycleOwner import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.ComposeSubscriptionManager +import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.MutableComposeSubscriptionManager +import com.vitorpamplona.amethyst.commons.relayClient.composeSubscriptionManagers.MutableQueryState +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +private const val UNSUBSCRIBE_GRACE_MILLIS = 30_000L /** * A lifecycle-aware version of [KeyDataSourceSubscription] that subscribes - * when the lifecycle reaches STARTED and unsubscribes when it reaches STOPPED. + * when the lifecycle reaches STARTED and unsubscribes 30 seconds after it + * reaches STOPPED. If the lifecycle returns to STARTED before the grace + * period elapses, the pending unsubscribe is cancelled and the subscription + * keeps running uninterrupted. + * + * The grace window absorbs short app switches (copy a snippet from another + * app, dismiss a notification, glance at recents) without tearing down and + * rebuilding the relay REQ — which would otherwise lose EOSE state and + * trigger a refetch on return. * * Use this for heavy feed subscriptions (home, video, discovery, chatroom list) - * that should NOT run when the app is in the background. When an always-on - * notification service keeps the relay client connected, these subscriptions - * would otherwise leak bandwidth on feeds nobody is viewing. + * that should NOT run when the app is truly in the background. When an + * always-on notification service keeps the relay client connected, these + * subscriptions would otherwise leak bandwidth on feeds nobody is viewing. * * Lightweight subscriptions that should always run (account metadata, notifications, * gift wraps) should continue using the regular [KeyDataSourceSubscription]. @@ -49,24 +61,43 @@ fun LifecycleAwareKeyDataSourceSubscription( dataSource: ComposeSubscriptionManager, ) { val lifecycleOwner = LocalLifecycleOwner.current - var isStarted by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() DisposableEffect(state, lifecycleOwner) { + var isSubscribed = false + var pendingUnsubscribe: Job? = null + + fun subscribeNow() { + pendingUnsubscribe?.cancel() + pendingUnsubscribe = null + if (!isSubscribed) { + dataSource.subscribe(state) + isSubscribed = true + } + } + + fun scheduleUnsubscribe() { + if (!isSubscribed || pendingUnsubscribe != null) return + pendingUnsubscribe = + scope.launch { + delay(UNSUBSCRIBE_GRACE_MILLIS) + if (isSubscribed) { + dataSource.unsubscribe(state) + isSubscribed = false + } + pendingUnsubscribe = null + } + } + val observer = LifecycleEventObserver { _, event -> when (event) { Lifecycle.Event.ON_START -> { - if (!isStarted) { - dataSource.subscribe(state) - isStarted = true - } + subscribeNow() } Lifecycle.Event.ON_STOP -> { - if (isStarted) { - dataSource.unsubscribe(state) - isStarted = false - } + scheduleUnsubscribe() } else -> {} @@ -75,17 +106,84 @@ fun LifecycleAwareKeyDataSourceSubscription( lifecycleOwner.lifecycle.addObserver(observer) - // If already started (e.g., recomposition while visible), subscribe immediately if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { - dataSource.subscribe(state) - isStarted = true + subscribeNow() } onDispose { lifecycleOwner.lifecycle.removeObserver(observer) - if (isStarted) { + pendingUnsubscribe?.cancel() + pendingUnsubscribe = null + if (isSubscribed) { dataSource.unsubscribe(state) - isStarted = false + isSubscribed = false + } + } + } +} + +@Composable +fun LifecycleAwareKeyDataSourceSubscription( + state: T, + dataSource: MutableComposeSubscriptionManager, +) { + val lifecycleOwner = LocalLifecycleOwner.current + val scope = rememberCoroutineScope() + + DisposableEffect(state, lifecycleOwner) { + var isSubscribed = false + var pendingUnsubscribe: Job? = null + + fun subscribeNow() { + pendingUnsubscribe?.cancel() + pendingUnsubscribe = null + if (!isSubscribed) { + dataSource.subscribe(state) + isSubscribed = true + } + } + + fun scheduleUnsubscribe() { + if (!isSubscribed || pendingUnsubscribe != null) return + pendingUnsubscribe = + scope.launch { + delay(UNSUBSCRIBE_GRACE_MILLIS) + if (isSubscribed) { + dataSource.unsubscribe(state) + isSubscribed = false + } + pendingUnsubscribe = null + } + } + + val observer = + LifecycleEventObserver { _, event -> + when (event) { + Lifecycle.Event.ON_START -> { + subscribeNow() + } + + Lifecycle.Event.ON_STOP -> { + scheduleUnsubscribe() + } + + else -> {} + } + } + + lifecycleOwner.lifecycle.addObserver(observer) + + if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { + subscribeNow() + } + + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + pendingUnsubscribe?.cancel() + pendingUnsubscribe = null + if (isSubscribed) { + dataSource.unsubscribe(state) + isSubscribed = false } } }