Add push notification deep linking and sharing subscription checks

- Add deep link navigation from push notifications to specific task column on kanban board
- Fix subscription check in push notification handler to allow navigation when limitations disabled
- Add pendingNavigationTaskId to handle notifications when app isn't ready
- Add ScrollViewReader to AllTasksView for programmatic scrolling to task column
- Add canShareResidence() and canShareContractor() subscription checks (iOS & Android)
- Add test APNS file for simulator push notification testing

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-10 23:17:28 -06:00
parent ed14a1c69e
commit cbe073aa21
21 changed files with 723 additions and 127 deletions

View File

@@ -9,6 +9,9 @@ class PushNotificationManager: NSObject, ObservableObject {
@Published var deviceToken: String?
@Published var notificationPermissionGranted = false
/// Pending task ID to navigate to (set when notification is tapped before app is ready)
@Published var pendingNavigationTaskId: Int?
private let registeredTokenKey = "com.casera.registeredDeviceToken"
/// The last token that was successfully registered with the backend
@@ -175,18 +178,23 @@ class PushNotificationManager: NSObject, ObservableObject {
}
// Check subscription status to determine navigation
let isPremium = SubscriptionCacheWrapper.shared.currentTier == "pro" ||
!(SubscriptionCacheWrapper.shared.currentSubscription?.limitationsEnabled ?? true)
// Allow navigation if: Pro user, OR limitations are disabled, OR subscription hasn't loaded yet
let subscription = SubscriptionCacheWrapper.shared.currentSubscription
let isPremium = SubscriptionCacheWrapper.shared.currentTier == "pro"
let limitationsEnabled = subscription?.limitationsEnabled ?? false // Default to false (allow) if not loaded
let canNavigateToTask = isPremium || !limitationsEnabled
if isPremium {
// Premium user - navigate to task detail
print("📬 Push nav check: isPremium=\(isPremium), limitationsEnabled=\(limitationsEnabled), canNavigate=\(canNavigateToTask), subscription=\(subscription != nil ? "loaded" : "nil")")
if canNavigateToTask {
// Navigate to task detail
if let taskIdStr = userInfo["task_id"] as? String, let taskId = Int(taskIdStr) {
navigateToTask(taskId: taskId)
} else if let taskId = userInfo["task_id"] as? Int {
navigateToTask(taskId: taskId)
}
} else {
// Free user - navigate to home
// Free user with limitations enabled - navigate to home
navigateToHome()
}
}
@@ -203,11 +211,14 @@ class PushNotificationManager: NSObject, ObservableObject {
}
// Check subscription status
let isPremium = SubscriptionCacheWrapper.shared.currentTier == "pro" ||
!(SubscriptionCacheWrapper.shared.currentSubscription?.limitationsEnabled ?? true)
// Allow actions if: Pro user, OR limitations are disabled, OR subscription hasn't loaded yet
let subscription = SubscriptionCacheWrapper.shared.currentSubscription
let isPremium = SubscriptionCacheWrapper.shared.currentTier == "pro"
let limitationsEnabled = subscription?.limitationsEnabled ?? false // Default to false (allow) if not loaded
let canPerformAction = isPremium || !limitationsEnabled
guard isPremium else {
// Free user shouldn't see actions, but if they somehow do, go to home
guard canPerformAction else {
// Free user with limitations enabled shouldn't see actions, but if they somehow do, go to home
navigateToHome()
return
}
@@ -375,6 +386,8 @@ class PushNotificationManager: NSObject, ObservableObject {
private func navigateToTask(taskId: Int) {
print("📱 Navigating to task \(taskId)")
// Store pending navigation in case MainTabView isn't ready yet
pendingNavigationTaskId = taskId
NotificationCenter.default.post(
name: .navigateToTask,
object: nil,
@@ -382,6 +395,11 @@ class PushNotificationManager: NSObject, ObservableObject {
)
}
/// Clear pending navigation after it has been handled
func clearPendingNavigation() {
pendingNavigationTaskId = nil
}
private func navigateToEditTask(taskId: Int) {
print("✏️ Navigating to edit task \(taskId)")
NotificationCenter.default.post(