mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-15 02:36:12 -04:00
fix(upgrade): Show supporter status on every upgrade screen route
The FOSS upgrade screen's view mapping only surfaced STATUS_UPGRADED on the manage route. Forced routes (the Pro-locked settings entry) deliberately don't auto-close, so a supporter completing the sponsor flow from there stayed on the sales pitch with a live sponsor button -- reading as if sponsoring didn't work, with only the transient thanks toast saying otherwise. The gplay flavour already renders ownership state route-independently on this path. The isPro branch now wins on every route, matching the adjacent comment's stated intent and gplay's behaviour. Forced routes keep their don't-auto-close semantics; the durable status view is what acknowledges the upgrade.
This commit is contained in:
@@ -42,8 +42,10 @@ class UpgradeViewModel @Inject constructor(
|
||||
|
||||
// Which presentation the screen shows. The manage route (settings "upgrade status" entry)
|
||||
// gets a status view first; the pitch only appears once a free user asks for the upgrade
|
||||
// options. Upgrading wins over that choice — completing the sponsor flow from the pitch must
|
||||
// land on the upgraded status, not back on the ask. null until the route is bound.
|
||||
// options. Upgrading wins on EVERY route, not just manage: forced routes (Pro-locked settings)
|
||||
// stay open after the sponsor flow completes, and the pitch with its live sponsor button reads
|
||||
// as "sponsoring didn't work" to a fresh supporter — the toast alone is too transient for a
|
||||
// money moment without a receipt behind it. null until the route is bound.
|
||||
internal val state: StateFlow<State?> = combine(
|
||||
routeFlow,
|
||||
upgradeRepo.upgradeInfo,
|
||||
@@ -51,7 +53,7 @@ class UpgradeViewModel @Inject constructor(
|
||||
) { route, info, showOptions ->
|
||||
val view = when {
|
||||
route == null -> null
|
||||
route.manage && info.isPro -> FossUpgradeView.STATUS_UPGRADED
|
||||
info.isPro -> FossUpgradeView.STATUS_UPGRADED
|
||||
route.manage && !showOptions -> FossUpgradeView.STATUS_FREE
|
||||
else -> FossUpgradeView.PITCH
|
||||
}
|
||||
|
||||
@@ -191,6 +191,35 @@ class FossUpgradeViewModelTest : BaseTest() {
|
||||
upgradedView.await().view shouldBe FossUpgradeView.STATUS_UPGRADED
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `forced route lands on the upgraded status when the sponsor flow completes`() = runTest2(
|
||||
context = testDispatcher,
|
||||
) {
|
||||
// Forced routes (Pro-locked settings entry) deliberately don't auto-close, so the screen is
|
||||
// still up when the unlock lands — it must flip to the supporter status instead of keeping
|
||||
// the sales pitch, which reads as "sponsoring didn't work".
|
||||
val info = MutableStateFlow(UpgradeRepoFoss.Info())
|
||||
val vm = buildVm(repo = mockRepo(info))
|
||||
|
||||
val navEvents = mutableListOf<NavEvent>()
|
||||
val collector = launch(start = CoroutineStart.UNDISPATCHED) { vm.navEvents.collect { navEvents.add(it) } }
|
||||
|
||||
val pitchView = async { vm.state.first { it != null }!! }
|
||||
vm.bindRoute(Nav.Main.Upgrade(forced = true))
|
||||
advanceUntilIdle()
|
||||
pitchView.await().view shouldBe FossUpgradeView.PITCH
|
||||
|
||||
val upgradedView = async { vm.state.first { it?.view == FossUpgradeView.STATUS_UPGRADED }!! }
|
||||
info.value = upgradedInfo()
|
||||
advanceUntilIdle()
|
||||
|
||||
upgradedView.await().view shouldBe FossUpgradeView.STATUS_UPGRADED
|
||||
// The don't-auto-close semantics of forced routes are unchanged — status, not navigation,
|
||||
// is what acknowledges the upgrade here.
|
||||
navEvents.shouldBeEmpty()
|
||||
collector.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `default route bounces an upgraded user out of the screen`() = runTest2(context = testDispatcher) {
|
||||
val vm = buildVm(repo = mockRepo(MutableStateFlow(upgradedInfo())))
|
||||
|
||||
Reference in New Issue
Block a user