mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-15 02:36:12 -04:00
persistUpgrade() wrote an unconditional record, so any sponsor return that got past the ViewModel's isPro guard replaced an existing supporter's upgradedAt - the "supporter since" date the status screen shows - and, for the legacy records every existing supporter has, their stored reason too. That guard reads a shareIn replay and can be stale, so it is not a race-free answer on its own. The write now happens inside the store transaction: an existing record is kept and reported back, only an absent one is created. The ViewModel thanks the user only for an actual unlock and stays quiet otherwise. It also no longer eats the sponsor visit when something fails - a failed entitlement read or a failed write restores the pending-launch marker so the next return can retry, while the error still travels the normal path. FossCache gains the same constructor test seam BillingCache has, so the transaction can be exercised against a real DataStore on a temp file.