feat(itinerary): add custom itinerary items with drag-to-reorder

- Add CustomItineraryItem domain model with sortOrder for ordering
- Add CKCustomItineraryItem CloudKit wrapper for persistence
- Create CustomItemService for CRUD operations
- Create CustomItemSubscriptionService for real-time sync
- Add AppDelegate for push notification handling
- Add AddItemSheet for creating/editing items
- Add CustomItemRow with drag handle
- Update TripDetailView with continuous vertical timeline
- Enable drag-to-reorder using .draggable/.dropDestination
- Add inline "Add" buttons after games and travel segments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-16 00:31:44 -06:00
parent b534ca771b
commit 495ef88303
13 changed files with 1302 additions and 33 deletions

View File

@@ -0,0 +1,48 @@
//
// AppDelegate.swift
// SportsTime
//
// Handles push notification registration and CloudKit subscription notifications
//
import UIKit
import CloudKit
import UserNotifications
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// Register for remote notifications (required for CloudKit subscriptions)
application.registerForRemoteNotifications()
return true
}
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
print("📡 [Push] Registered for remote notifications")
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("📡 [Push] Failed to register: \(error.localizedDescription)")
}
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
// Handle CloudKit subscription notification
Task {
await CustomItemSubscriptionService.shared.handleRemoteNotification(userInfo: userInfo)
completionHandler(.newData)
}
}
}