feat(polls): implement group trip polling MVP

Add complete group trip polling feature allowing users to share trips
with friends for voting using Borda count scoring.

New components:
- TripPoll and PollVote domain models with share codes and rankings
- LocalTripPoll and LocalPollVote SwiftData models for persistence
- CKTripPoll and CKPollVote CloudKit record wrappers
- PollService actor for CloudKit CRUD operations and subscriptions
- PollCreation/Detail/Voting views and view models
- Deep link handling for sportstime://poll/{code} URLs
- Debug Pro status override toggle in Settings

Integration:
- HomeView shows polls section in My Trips
- SportsTimeApp registers SwiftData models and handles deep links

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-13 21:54:42 -06:00
parent 8e78828bde
commit 13385b6562
16 changed files with 2416 additions and 19 deletions

View File

@@ -31,10 +31,31 @@ final class StoreManager {
private(set) var isLoading = false
private(set) var error: StoreError?
// MARK: - Debug Override (DEBUG builds only)
#if DEBUG
private static let debugProOverrideKey = "debugProOverride"
/// When true, isPro returns true regardless of actual subscription status.
/// Defaults to true in debug builds. Only compiled in DEBUG configuration.
var debugProOverride: Bool {
get {
// Default to true by checking if key exists; object(forKey:) returns nil if not set
UserDefaults.standard.object(forKey: Self.debugProOverrideKey) as? Bool ?? true
}
set {
UserDefaults.standard.set(newValue, forKey: Self.debugProOverrideKey)
}
}
#endif
// MARK: - Computed Properties
var isPro: Bool {
!purchasedProductIDs.intersection(Self.proProductIDs).isEmpty
#if DEBUG
if debugProOverride { return true }
#endif
return !purchasedProductIDs.intersection(Self.proProductIDs).isEmpty
}
var monthlyProduct: Product? {