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>
This commit is contained in:
@@ -1,37 +1,66 @@
|
||||
package com.example.casera.cache
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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 {
|
||||
val currentSubscription = mutableStateOf<SubscriptionStatus?>(null)
|
||||
val upgradeTriggers = mutableStateOf<Map<String, UpgradeTriggerData>>(emptyMap())
|
||||
val featureBenefits = mutableStateOf<List<FeatureBenefit>>(emptyList())
|
||||
val promotions = mutableStateOf<List<Promotion>>(emptyList())
|
||||
/**
|
||||
* 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) {
|
||||
currentSubscription.value = subscription
|
||||
DataManager.setSubscription(subscription)
|
||||
}
|
||||
|
||||
fun updateUpgradeTriggers(triggers: Map<String, UpgradeTriggerData>) {
|
||||
upgradeTriggers.value = triggers
|
||||
DataManager.setUpgradeTriggers(triggers)
|
||||
}
|
||||
|
||||
fun updateFeatureBenefits(benefits: List<FeatureBenefit>) {
|
||||
featureBenefits.value = benefits
|
||||
DataManager.setFeatureBenefits(benefits)
|
||||
}
|
||||
|
||||
fun updatePromotions(promos: List<Promotion>) {
|
||||
promotions.value = promos
|
||||
DataManager.setPromotions(promos)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
currentSubscription.value = null
|
||||
upgradeTriggers.value = emptyMap()
|
||||
featureBenefits.value = emptyList()
|
||||
promotions.value = emptyList()
|
||||
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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user