Optimize subscription tier management and empty state logic

Changes:
- Make currentTier a computed property from StoreKit instead of stored value
- Initialize StoreKit at app launch and refresh on foreground for ready subscription status
- Fix empty state logic: show empty state when limit > 0, upgrade prompt when limit = 0
- Add subscription status display in Profile screen (iOS/Android)
- Add upgrade prompts to Residences and Tasks screens for free tier users
- Improve SubscriptionHelper with better tier checking logic

iOS:
- SubscriptionCache: currentTier now computed from StoreKit.purchasedProductIDs
- StoreKitManager: add refreshSubscriptionStatus() method
- AppDelegate: initialize StoreKit at launch and refresh on app active
- ContractorsListView: use shouldShowUpgradePrompt(currentCount:limitKey:)
- WarrantiesTabContent/DocumentsTabContent: same empty state fix
- ProfileTabView: display current tier and limitations status
- ResidencesListView/ResidenceDetailView: add upgrade prompts for free users
- AllTasksView: add upgrade prompt for free users

Android:
- ContractorsScreen/DocumentsScreen: fix empty state logic
- ProfileScreen: display subscription status and limits
- ResidencesScreen/ResidenceDetailScreen: add upgrade prompts
- UpgradeFeatureScreen: improve UI layout

Shared:
- APILayer: add getSubscriptionStatus() method
- SubscriptionHelper: add hasAccessToFeature() utility
- Remove duplicate subscription checking logic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-24 18:59:09 -06:00
parent d92a4fd4f1
commit ce1ca0f0ce
23 changed files with 728 additions and 120 deletions

View File

@@ -25,7 +25,9 @@ struct ResidenceDetailView: View {
@State private var showReportConfirmation = false
@State private var showDeleteConfirmation = false
@State private var isDeleting = false
@State private var showingUpgradePrompt = false
@StateObject private var subscriptionCache = SubscriptionCacheWrapper.shared
@Environment(\.dismiss) private var dismiss
var body: some View {
@@ -120,6 +122,9 @@ struct ResidenceDetailView: View {
Text("Are you sure you want to archive \"\(task.title)\"? You can unarchive it later from archived tasks.")
}
}
.sheet(isPresented: $showingUpgradePrompt) {
UpgradePromptView(triggerKey: "add_11th_task", isPresented: $showingUpgradePrompt)
}
// MARK: onChange & lifecycle
.onChange(of: viewModel.reportMessage) { message in
@@ -255,7 +260,13 @@ private extension ResidenceDetailView {
}
Button {
showAddTask = true
// Check LIVE task count before adding
let totalTasks = tasksResponse?.columns.reduce(0) { $0 + $1.tasks.count } ?? 0
if subscriptionCache.shouldShowUpgradePrompt(currentCount: totalTasks, limitKey: "tasks") {
showingUpgradePrompt = true
} else {
showAddTask = true
}
} label: {
Image(systemName: "plus")
}

View File

@@ -5,7 +5,9 @@ struct ResidencesListView: View {
@StateObject private var viewModel = ResidenceViewModel()
@State private var showingAddResidence = false
@State private var showingJoinResidence = false
@State private var showingUpgradePrompt = false
@StateObject private var authManager = AuthenticationManager.shared
@StateObject private var subscriptionCache = SubscriptionCacheWrapper.shared
var body: some View {
@@ -71,7 +73,13 @@ struct ResidencesListView: View {
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
showingJoinResidence = true
// Check if we should show upgrade prompt before joining
let currentCount = viewModel.myResidences?.residences.count ?? 0
if subscriptionCache.shouldShowUpgradePrompt(currentCount: currentCount, limitKey: "properties") {
showingUpgradePrompt = true
} else {
showingJoinResidence = true
}
}) {
Image(systemName: "person.badge.plus")
.font(.system(size: 18, weight: .semibold))
@@ -79,7 +87,13 @@ struct ResidencesListView: View {
}
Button(action: {
showingAddResidence = true
// Check if we should show upgrade prompt before adding
let currentCount = viewModel.myResidences?.residences.count ?? 0
if subscriptionCache.shouldShowUpgradePrompt(currentCount: currentCount, limitKey: "properties") {
showingUpgradePrompt = true
} else {
showingAddResidence = true
}
}) {
Image(systemName: "plus.circle.fill")
.font(.system(size: 22, weight: .semibold))
@@ -101,6 +115,9 @@ struct ResidencesListView: View {
viewModel.loadMyResidences()
})
}
.sheet(isPresented: $showingUpgradePrompt) {
UpgradePromptView(triggerKey: "add_second_property", isPresented: $showingUpgradePrompt)
}
.onAppear {
if authManager.isAuthenticated {
viewModel.loadMyResidences()