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>
67 lines
2.4 KiB
Kotlin
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()
|
|
}
|