1.0.5: Eating out slots, rule violations panel, Crashlytics, search bar fix
- Eating out: tap any empty slot → "Mark as eating out"; shows teal indicator, skipped by auto-assign, counts as complete, tap again to unmark - Rule violations panel: access via wand long-press context menu when conflicts exist; shows all isRuleOverridden slots with Fix (clear) or Ignore (acknowledge) actions - Firebase Crashlytics integrated: CrashlyticsService + dSYM upload build phase, isPremium property tracked per session - PremiumSyncService: extracted premium state machine, StoreKit Transaction.updates listener, isPremium no longer synced via iCloud to avoid stale state - StoreManager: analytics on purchase/restore, bundle ID fallback for product ID lookup - Search bar contrast bug fixed: TextField now has explicit foreground color for dark mode - WelcomeStepView: redesigned onboarding welcome screen with week preview - Version bump: 1.0.5 build 22 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,7 @@ final class StoreManager: ObservableObject {
|
||||
if case .verified(let transaction) = verification {
|
||||
await transaction.finish()
|
||||
isPremium = true
|
||||
AnalyticsService.logPremiumPurchased()
|
||||
return .success
|
||||
}
|
||||
return .failed
|
||||
@@ -99,14 +100,18 @@ final class StoreManager: ObservableObject {
|
||||
|
||||
do {
|
||||
try await AppStore.sync()
|
||||
let wasNotPremium = !isPremium
|
||||
await checkPremiumStatus()
|
||||
if wasNotPremium && isPremium {
|
||||
AnalyticsService.logPremiumRestored()
|
||||
}
|
||||
} catch {
|
||||
print("Restore failed: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func checkPremiumStatus() async {
|
||||
isPremium = await Self.hasActiveSubscription(productIds: productIds)
|
||||
isPremium = await Self.hasActiveSubscription()
|
||||
}
|
||||
|
||||
var monthlyProduct: Product? {
|
||||
@@ -133,12 +138,18 @@ final class StoreManager: ObservableObject {
|
||||
|
||||
var debugProductIds: [String] { productIds }
|
||||
|
||||
static func hasActiveSubscription(productIds: [String] = [
|
||||
StoreManager.monthlyProductId
|
||||
]) async -> Bool {
|
||||
static func hasActiveSubscription() async -> Bool {
|
||||
// Check all known product ID variants to handle both the canonical ID
|
||||
// and any bundle-prefixed IDs that may have been used at purchase time.
|
||||
var knownIds: [String] = [monthlyProductId]
|
||||
if let bundleId = Bundle.main.bundleIdentifier {
|
||||
knownIds.append("\(bundleId).premium.monthly")
|
||||
}
|
||||
let ids = Set(knownIds)
|
||||
|
||||
for await result in Transaction.currentEntitlements {
|
||||
if case .verified(let transaction) = result,
|
||||
productIds.contains(transaction.productID),
|
||||
ids.contains(transaction.productID),
|
||||
transaction.revocationDate == nil {
|
||||
if let expirationDate = transaction.expirationDate, expirationDate < Date() {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user