From ed5e5ba7e1d26d29922b1be1af9f06613bcace14 Mon Sep 17 00:00:00 2001 From: darken Date: Mon, 3 Aug 2026 19:56:42 +0200 Subject: [PATCH] fix(upgrade): Record the last known entitlement upstream of the flatMapLatest buffer The tracking onEach sat downstream of flatMapLatest, so its channel buffer could hold the Pro emission while the inner flow already threw: the catch then read a null last-known state and emitted a non-Pro error Info, kicking a supporter back to the pitch. Tracking now runs in the same coroutine as the throw. --- .../darken/capod/common/upgrade/core/UpgradeRepoFoss.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/foss/java/eu/darken/capod/common/upgrade/core/UpgradeRepoFoss.kt b/app/src/foss/java/eu/darken/capod/common/upgrade/core/UpgradeRepoFoss.kt index 46fe0f25..5f855314 100644 --- a/app/src/foss/java/eu/darken/capod/common/upgrade/core/UpgradeRepoFoss.kt +++ b/app/src/foss/java/eu/darken/capod/common/upgrade/core/UpgradeRepoFoss.kt @@ -37,6 +37,9 @@ class UpgradeRepoFoss @Inject constructor( private val refreshTrigger = MutableStateFlow(UUID.randomUUID()) // Written only from the sharing coroutine (single collector) — no synchronization needed. + // Recorded INSIDE the flatMapLatest block, upstream of its channel buffer: a downstream onEach + // can still be waiting on a buffered emission when the inner flow throws, and the catch below + // would then read a stale (null) value and revoke an entitlement we already saw. private var lastKnownInfo: Info? = null override val upgradeInfo: Flow = refreshTrigger @@ -53,6 +56,10 @@ class UpgradeRepoFoss @Inject constructor( ) } } + // Same coroutine as the throw below, so the ordering is guaranteed. Only + // successfully mapped elements pass here — catch emissions go straight downstream + // and never record themselves as a last known state. + .onEach { lastKnownInfo = it } .catch { e -> // A SharedFlow cannot fail: without this, a thrown cache read dies inside // shareIn's sharing coroutine and every collector hangs forever (VM state stuck @@ -68,7 +75,6 @@ class UpgradeRepoFoss @Inject constructor( emit((lastKnownInfo ?: Info()).copy(error = e)) } } - .onEach { if (it.error == null) lastKnownInfo = it } .setupCommonEventHandlers(TAG) { "upgradeInfo" } .shareIn(appScope, SharingStarted.WhileSubscribed(3000L, 0L), replay = 1)