Files
honeyDueKMP/composeApp/src/commonMain/kotlin/com/example/casera/cache/SubscriptionCache.kt
Trey t 7444f73b46 Close all 25 codex audit findings across KMP, iOS, and Android
Remediate all P0-S priority findings from cross-platform architecture audit:
- Harden token storage with EncryptedSharedPreferences (Android) and Keychain (iOS)
- Add SSL pinning and certificate validation to API clients
- Fix subscription cache race conditions and add thread-safe access
- Add input validation for document uploads and file type restrictions
- Refactor DocumentApi to use proper multipart upload flow
- Add rate limiting awareness and retry logic to API layer
- Harden subscription tier enforcement in SubscriptionHelper
- Add biometric prompt for sensitive actions (Login, Onboarding)
- Fix notification permission handling and device registration
- Add UI test infrastructure (page objects, fixtures, smoke tests)
- Add CI workflow for mobile builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 13:15:34 -06:00

67 lines
2.4 KiB
Kotlin

package com.example.casera.cache
import com.example.casera.data.DataManager
import com.example.casera.models.FeatureBenefit
import com.example.casera.models.Promotion
import com.example.casera.models.SubscriptionStatus
import com.example.casera.models.UpgradeTriggerData
/**
* Thin facade over DataManager for subscription data.
*
* All state is delegated to DataManager (single source of truth).
* This object exists for backwards compatibility with callers that
* read subscription state (e.g. iOS SubscriptionCacheWrapper polling via Kotlin interop).
*
* For Compose UI code, prefer using DataManager StateFlows directly with collectAsState().
*/
object SubscriptionCache {
/**
* Current subscription status, delegated to DataManager.
* For Compose callers, prefer: `val subscription by DataManager.subscription.collectAsState()`
*/
val currentSubscription: SubscriptionCacheAccessor<SubscriptionStatus?>
get() = SubscriptionCacheAccessor { DataManager.subscription.value }
val upgradeTriggers: SubscriptionCacheAccessor<Map<String, UpgradeTriggerData>>
get() = SubscriptionCacheAccessor { DataManager.upgradeTriggers.value }
val featureBenefits: SubscriptionCacheAccessor<List<FeatureBenefit>>
get() = SubscriptionCacheAccessor { DataManager.featureBenefits.value }
val promotions: SubscriptionCacheAccessor<List<Promotion>>
get() = SubscriptionCacheAccessor { DataManager.promotions.value }
fun updateSubscriptionStatus(subscription: SubscriptionStatus) {
DataManager.setSubscription(subscription)
}
fun updateUpgradeTriggers(triggers: Map<String, UpgradeTriggerData>) {
DataManager.setUpgradeTriggers(triggers)
}
fun updateFeatureBenefits(benefits: List<FeatureBenefit>) {
DataManager.setFeatureBenefits(benefits)
}
fun updatePromotions(promos: List<Promotion>) {
DataManager.setPromotions(promos)
}
fun clear() {
DataManager.setSubscription(null)
DataManager.setUpgradeTriggers(emptyMap())
DataManager.setFeatureBenefits(emptyList())
DataManager.setPromotions(emptyList())
}
}
/**
* Simple accessor that provides .value to read from DataManager.
* This preserves the `SubscriptionCache.currentSubscription.value` call pattern
* used by existing callers (Kotlin code and iOS interop polling).
*/
class SubscriptionCacheAccessor<T>(private val getter: () -> T) {
val value: T get() = getter()
}