feat(polls): add DeepLinkHandler and test mock helpers

- Extract deep link handling into dedicated DeepLinkHandler service
- Add MockData+Polls.swift with reusable test mocks for Trip, TripStop,
  TripPoll, PollVote, and PollResults
- Update SportsTimeApp to use DeepLinkHandler.shared
- Add error alert for deep link failures

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-13 22:03:22 -06:00
parent 136c356384
commit 2f9546f792
3 changed files with 284 additions and 15 deletions

View File

@@ -81,7 +81,7 @@ struct BootstrappedContentView: View {
@State private var bootstrapError: Error?
@State private var hasCompletedInitialSync = false
@State private var showOnboardingPaywall = false
@State private var pollShareCode: String?
@State private var deepLinkHandler = DeepLinkHandler.shared
private var shouldShowOnboardingPaywall: Bool {
!UserDefaults.standard.bool(forKey: "hasSeenOnboardingPaywall") && !StoreManager.shared.isPro
@@ -103,11 +103,16 @@ struct BootstrappedContentView: View {
OnboardingPaywallView(isPresented: $showOnboardingPaywall)
.interactiveDismissDisabled()
}
.sheet(item: $pollShareCode) { code in
.sheet(item: $deepLinkHandler.pendingPollShareCode) { code in
NavigationStack {
PollDetailView(shareCode: code)
}
}
.alert("Error", isPresented: .constant(deepLinkHandler.error != nil)) {
Button("OK") { deepLinkHandler.clearPending() }
} message: {
Text(deepLinkHandler.error?.localizedDescription ?? "")
}
.onAppear {
if shouldShowOnboardingPaywall {
showOnboardingPaywall = true
@@ -119,7 +124,7 @@ struct BootstrappedContentView: View {
await performBootstrap()
}
.onOpenURL { url in
handleDeepLink(url)
deepLinkHandler.handleURL(url)
}
.onChange(of: scenePhase) { _, newPhase in
switch newPhase {
@@ -204,18 +209,6 @@ struct BootstrappedContentView: View {
}
}
// MARK: - Deep Link Handling
private func handleDeepLink(_ url: URL) {
// Handle sportstime://poll/{code} deep links
guard url.scheme == "sportstime",
url.host == "poll",
let code = url.pathComponents.dropFirst().first,
code.count == 6
else { return }
pollShareCode = code.uppercased()
}
}
// MARK: - String Identifiable for Sheet