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:
134
SportsTime/Core/Models/Local/LocalPoll.swift
Normal file
134
SportsTime/Core/Models/Local/LocalPoll.swift
Normal file
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// LocalPoll.swift
|
||||
// SportsTime
|
||||
//
|
||||
// SwiftData models for local poll persistence
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
// MARK: - Local Trip Poll
|
||||
|
||||
@Model
|
||||
final class LocalTripPoll {
|
||||
@Attribute(.unique) var id: UUID
|
||||
var title: String
|
||||
var ownerId: String
|
||||
var shareCode: String
|
||||
var tripSnapshotsData: Data // Encoded [Trip]
|
||||
var tripVersions: [String]
|
||||
var createdAt: Date
|
||||
var modifiedAt: Date
|
||||
var lastSyncedAt: Date?
|
||||
|
||||
@Relationship(deleteRule: .cascade)
|
||||
var votes: [LocalPollVote]?
|
||||
|
||||
init(
|
||||
id: UUID = UUID(),
|
||||
title: String,
|
||||
ownerId: String,
|
||||
shareCode: String,
|
||||
tripSnapshotsData: Data,
|
||||
tripVersions: [String],
|
||||
createdAt: Date = Date(),
|
||||
modifiedAt: Date = Date(),
|
||||
lastSyncedAt: Date? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.ownerId = ownerId
|
||||
self.shareCode = shareCode
|
||||
self.tripSnapshotsData = tripSnapshotsData
|
||||
self.tripVersions = tripVersions
|
||||
self.createdAt = createdAt
|
||||
self.modifiedAt = modifiedAt
|
||||
self.lastSyncedAt = lastSyncedAt
|
||||
}
|
||||
|
||||
var tripSnapshots: [Trip] {
|
||||
(try? JSONDecoder().decode([Trip].self, from: tripSnapshotsData)) ?? []
|
||||
}
|
||||
|
||||
func toPoll() -> TripPoll {
|
||||
var poll = TripPoll(
|
||||
id: id,
|
||||
title: title,
|
||||
ownerId: ownerId,
|
||||
shareCode: shareCode,
|
||||
tripSnapshots: tripSnapshots,
|
||||
createdAt: createdAt,
|
||||
modifiedAt: modifiedAt
|
||||
)
|
||||
poll.tripVersions = tripVersions
|
||||
return poll
|
||||
}
|
||||
|
||||
static func from(_ poll: TripPoll) -> LocalTripPoll? {
|
||||
guard let tripsData = try? JSONEncoder().encode(poll.tripSnapshots) else { return nil }
|
||||
return LocalTripPoll(
|
||||
id: poll.id,
|
||||
title: poll.title,
|
||||
ownerId: poll.ownerId,
|
||||
shareCode: poll.shareCode,
|
||||
tripSnapshotsData: tripsData,
|
||||
tripVersions: poll.tripVersions,
|
||||
createdAt: poll.createdAt,
|
||||
modifiedAt: poll.modifiedAt
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Local Poll Vote
|
||||
|
||||
@Model
|
||||
final class LocalPollVote {
|
||||
@Attribute(.unique) var id: UUID
|
||||
var pollId: UUID
|
||||
var voterId: String
|
||||
var rankings: [Int]
|
||||
var votedAt: Date
|
||||
var modifiedAt: Date
|
||||
var lastSyncedAt: Date?
|
||||
|
||||
init(
|
||||
id: UUID = UUID(),
|
||||
pollId: UUID,
|
||||
voterId: String,
|
||||
rankings: [Int],
|
||||
votedAt: Date = Date(),
|
||||
modifiedAt: Date = Date(),
|
||||
lastSyncedAt: Date? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.pollId = pollId
|
||||
self.voterId = voterId
|
||||
self.rankings = rankings
|
||||
self.votedAt = votedAt
|
||||
self.modifiedAt = modifiedAt
|
||||
self.lastSyncedAt = lastSyncedAt
|
||||
}
|
||||
|
||||
func toVote() -> PollVote {
|
||||
PollVote(
|
||||
id: id,
|
||||
pollId: pollId,
|
||||
odg: voterId,
|
||||
rankings: rankings,
|
||||
votedAt: votedAt,
|
||||
modifiedAt: modifiedAt
|
||||
)
|
||||
}
|
||||
|
||||
static func from(_ vote: PollVote) -> LocalPollVote {
|
||||
LocalPollVote(
|
||||
id: vote.id,
|
||||
pollId: vote.pollId,
|
||||
voterId: vote.odg,
|
||||
rankings: vote.rankings,
|
||||
votedAt: vote.votedAt,
|
||||
modifiedAt: vote.modifiedAt
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user