feat(subscriptions): make screen subscriptions lifecycle-aware
Migrates 32 call sites from KeyDataSourceSubscription to LifecycleAwareKeyDataSourceSubscription so feed/screen REQs are paused when the app goes to background and resumed on foreground, saving relay bandwidth while the always-on notification service keeps the socket open. Adds a MutableComposeSubscriptionManager overload to LifecycleAwareKeyDataSourceSubscription so the search bars (which bind to a flow-driven query) can also opt in. Skips AccountFilterAssemblerSubscription (always-on account state) and NWCFinderFilterAssemblerSubscription (in-flight zap payments that must complete in the background). https://claude.ai/code/session_01RUmRxmzUAcVXezF9PEETGz
This commit is contained in:
+69
@@ -27,6 +27,8 @@ 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
|
||||
@@ -119,3 +121,70 @@ fun <T> LifecycleAwareKeyDataSourceSubscription(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T : MutableQueryState> LifecycleAwareKeyDataSourceSubscription(
|
||||
state: T,
|
||||
dataSource: MutableComposeSubscriptionManager<T>,
|
||||
) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user