refactor(itinerary): replace anchor-based positioning with day/sortOrder

Replace complex anchor system (anchorType, anchorId, anchorDay) with
simple (day: Int, sortOrder: Double) positioning for custom items.

Changes:
- CustomItineraryItem: Remove anchor fields, add day and sortOrder
- CKModels: Add migration fallback from old CloudKit fields
- ItineraryTableViewController: Add calculateSortOrder() for midpoint insertion
- TripDetailView: Simplify callbacks, itinerarySections, and routeWaypoints
- AddItemSheet: Take simple day parameter instead of anchor
- SavedTrip: Update LocalCustomItem SwiftData model

Benefits:
- Items freely movable via drag-and-drop
- Route waypoints follow exact visual order
- Simpler mental model: position = (day, sortOrder)
- Midpoint insertion allows unlimited reordering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-17 09:47:11 -06:00
parent 59ba2c6965
commit 2a8bfeeff8
10 changed files with 238 additions and 385 deletions

View File

@@ -632,16 +632,19 @@ struct CKCustomItineraryItem {
static let tripIdKey = "tripId"
static let categoryKey = "category"
static let titleKey = "title"
static let anchorTypeKey = "anchorType"
static let anchorIdKey = "anchorId"
static let anchorDayKey = "anchorDay"
static let sortOrderKey = "sortOrder"
static let dayKey = "day" // NEW: replaces anchorDay
static let sortOrderDoubleKey = "sortOrderDouble" // NEW: Double instead of Int
static let createdAtKey = "createdAt"
static let modifiedAtKey = "modifiedAt"
// Location fields for mappable items
static let latitudeKey = "latitude"
static let longitudeKey = "longitude"
static let addressKey = "address"
// DEPRECATED - kept for migration reads only
static let anchorTypeKey = "anchorType"
static let anchorIdKey = "anchorId"
static let anchorDayKey = "anchorDay"
static let sortOrderKey = "sortOrder"
let record: CKRecord
@@ -658,10 +661,8 @@ struct CKCustomItineraryItem {
record[CKCustomItineraryItem.tripIdKey] = item.tripId.uuidString
record[CKCustomItineraryItem.categoryKey] = item.category.rawValue
record[CKCustomItineraryItem.titleKey] = item.title
record[CKCustomItineraryItem.anchorTypeKey] = item.anchorType.rawValue
record[CKCustomItineraryItem.anchorIdKey] = item.anchorId
record[CKCustomItineraryItem.anchorDayKey] = item.anchorDay
record[CKCustomItineraryItem.sortOrderKey] = item.sortOrder
record[CKCustomItineraryItem.dayKey] = item.day
record[CKCustomItineraryItem.sortOrderDoubleKey] = item.sortOrder
record[CKCustomItineraryItem.createdAtKey] = item.createdAt
record[CKCustomItineraryItem.modifiedAtKey] = item.modifiedAt
// Location fields (nil values are not stored in CloudKit)
@@ -679,15 +680,30 @@ struct CKCustomItineraryItem {
let categoryString = record[CKCustomItineraryItem.categoryKey] as? String,
let category = CustomItineraryItem.ItemCategory(rawValue: categoryString),
let title = record[CKCustomItineraryItem.titleKey] as? String,
let anchorTypeString = record[CKCustomItineraryItem.anchorTypeKey] as? String,
let anchorType = CustomItineraryItem.AnchorType(rawValue: anchorTypeString),
let anchorDay = record[CKCustomItineraryItem.anchorDayKey] as? Int,
let createdAt = record[CKCustomItineraryItem.createdAtKey] as? Date,
let modifiedAt = record[CKCustomItineraryItem.modifiedAtKey] as? Date
else { return nil }
let anchorId = record[CKCustomItineraryItem.anchorIdKey] as? String
let sortOrder = record[CKCustomItineraryItem.sortOrderKey] as? Int ?? 0
// Read new fields, with migration fallback from old fields
let day: Int
if let newDay = record[CKCustomItineraryItem.dayKey] as? Int {
day = newDay
} else if let oldDay = record[CKCustomItineraryItem.anchorDayKey] as? Int {
// Migration: use old anchorDay
day = oldDay
} else {
return nil
}
let sortOrder: Double
if let newSortOrder = record[CKCustomItineraryItem.sortOrderDoubleKey] as? Double {
sortOrder = newSortOrder
} else if let oldSortOrder = record[CKCustomItineraryItem.sortOrderKey] as? Int {
// Migration: convert old Int sortOrder to Double
sortOrder = Double(oldSortOrder)
} else {
sortOrder = 0.0
}
// Location fields (optional - nil if not stored)
let latitude = record[CKCustomItineraryItem.latitudeKey] as? Double
@@ -699,9 +715,7 @@ struct CKCustomItineraryItem {
tripId: tripId,
category: category,
title: title,
anchorType: anchorType,
anchorId: anchorId,
anchorDay: anchorDay,
day: day,
sortOrder: sortOrder,
createdAt: createdAt,
modifiedAt: modifiedAt,