Fix push-навигация: stale pendingChatRoute вызывал переход в чужой чат при переключении табов

This commit is contained in:
2026-04-01 15:32:08 +05:00
parent 8f69781a66
commit 79c5635715
6 changed files with 455 additions and 68 deletions

View File

@@ -1,5 +1,6 @@
import Combine
import SwiftUI
import UIKit
// MARK: - Navigation State (survives parent re-renders)
@@ -104,23 +105,22 @@ struct ChatListView: View {
.tint(RosettaColors.figmaBlue)
.onReceive(NotificationCenter.default.publisher(for: .openChatFromNotification)) { notification in
guard let route = notification.object as? ChatRoute else { return }
// Navigate to the chat from push notification tap
// Navigate to the chat from push notification tap (fast path)
navigationState.path = [route]
// Clear pending route consumed by onReceive (fast path)
AppDelegate.pendingChatRoute = nil
AppDelegate.pendingChatRouteTimestamp = nil
}
.onAppear {
// Fallback: consume pending route if .onReceive missed it.
// Handles terminated app (ChatListView didn't exist when notification was posted)
// and background app (Combine subscription may not fire during app resume).
if let route = AppDelegate.pendingChatRoute {
AppDelegate.pendingChatRoute = nil
// Small delay to let NavigationStack settle after view creation
Task { @MainActor in
try? await Task.sleep(nanoseconds: 100_000_000) // 100ms
navigationState.path = [route]
}
}
// Cold start fallback: ChatListView didn't exist when notification was posted.
// Expiry guard (3s) prevents stale routes from firing on tab switches
// critical for iOS < 26 pager (ZStack opacity 01 re-fires .onAppear).
consumePendingRouteIfFresh()
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
// Backgroundforeground fallback: covers edge cases where .onReceive
// subscription hasn't re-activated after backgroundforeground transition.
// Harmless if .onReceive already consumed the route (statics are nil).
consumePendingRouteIfFresh()
}
}
@@ -132,6 +132,16 @@ struct ChatListView: View {
searchText = ""
viewModel.setSearchQuery("")
}
/// Consume pending notification route only if it was set within the last 3 seconds.
/// Prevents stale routes (from failed .onReceive) from being consumed on tab switches.
private func consumePendingRouteIfFresh() {
guard let route = AppDelegate.consumeFreshPendingRoute() else { return }
Task { @MainActor in
try? await Task.sleep(nanoseconds: 100_000_000) // 100ms for NavigationStack settle
navigationState.path = [route]
}
}
}
// MARK: - Custom Search Bar