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:
@@ -18,10 +18,10 @@ struct ItineraryTableViewWrapper<HeaderContent: View>: UIViewControllerRepresent
|
||||
|
||||
// Callbacks
|
||||
var onTravelMoved: ((String, Int) -> Void)?
|
||||
var onCustomItemMoved: ((UUID, Int, CustomItineraryItem.AnchorType, String?) -> Void)?
|
||||
var onCustomItemMoved: ((UUID, Int, Double) -> Void)? // itemId, newDay, newSortOrder
|
||||
var onCustomItemTapped: ((CustomItineraryItem) -> Void)?
|
||||
var onCustomItemDeleted: ((CustomItineraryItem) -> Void)?
|
||||
var onAddButtonTapped: ((Int, CustomItineraryItem.AnchorType, String?) -> Void)?
|
||||
var onAddButtonTapped: ((Int) -> Void)? // Just day number
|
||||
|
||||
init(
|
||||
trip: Trip,
|
||||
@@ -30,10 +30,10 @@ struct ItineraryTableViewWrapper<HeaderContent: View>: UIViewControllerRepresent
|
||||
travelDayOverrides: [String: Int],
|
||||
@ViewBuilder headerContent: () -> HeaderContent,
|
||||
onTravelMoved: ((String, Int) -> Void)? = nil,
|
||||
onCustomItemMoved: ((UUID, Int, CustomItineraryItem.AnchorType, String?) -> Void)? = nil,
|
||||
onCustomItemMoved: ((UUID, Int, Double) -> Void)? = nil,
|
||||
onCustomItemTapped: ((CustomItineraryItem) -> Void)? = nil,
|
||||
onCustomItemDeleted: ((CustomItineraryItem) -> Void)? = nil,
|
||||
onAddButtonTapped: ((Int, CustomItineraryItem.AnchorType, String?) -> Void)? = nil
|
||||
onAddButtonTapped: ((Int) -> Void)? = nil
|
||||
) {
|
||||
self.trip = trip
|
||||
self.games = games
|
||||
@@ -159,47 +159,16 @@ struct ItineraryTableViewWrapper<HeaderContent: View>: UIViewControllerRepresent
|
||||
// Travel before this day (travel is stored on the destination day)
|
||||
let travelBefore: TravelSegment? = travelByDay[dayNum]
|
||||
|
||||
// Custom items after travel (if travel arrives on this day)
|
||||
if let travelSegment = travelBefore {
|
||||
let travelId = stableTravelAnchorId(travelSegment)
|
||||
let itemsAfterTravel = customItems.filter {
|
||||
$0.anchorType == .afterTravel && $0.anchorId == travelId
|
||||
}.sorted { $0.sortOrder < $1.sortOrder }
|
||||
// Custom items for this day - simply filter by day and sort by sortOrder
|
||||
let dayItems = customItems.filter { $0.day == dayNum }
|
||||
.sorted { $0.sortOrder < $1.sortOrder }
|
||||
|
||||
for item in itemsAfterTravel {
|
||||
items.append(ItineraryRowItem.customItem(item))
|
||||
}
|
||||
}
|
||||
|
||||
// Custom items at start of day
|
||||
let itemsAtStart = customItems.filter {
|
||||
$0.anchorDay == dayNum && $0.anchorType == .startOfDay
|
||||
}.sorted { $0.sortOrder < $1.sortOrder }
|
||||
|
||||
for item in itemsAtStart {
|
||||
for item in dayItems {
|
||||
items.append(ItineraryRowItem.customItem(item))
|
||||
}
|
||||
|
||||
// Custom items after game
|
||||
if let lastGame = gamesOnDay.last {
|
||||
let itemsAfterGame = customItems.filter {
|
||||
$0.anchorDay == dayNum && $0.anchorType == .afterGame && $0.anchorId == lastGame.game.id
|
||||
}.sorted { $0.sortOrder < $1.sortOrder }
|
||||
|
||||
for item in itemsAfterGame {
|
||||
items.append(ItineraryRowItem.customItem(item))
|
||||
}
|
||||
}
|
||||
|
||||
// ONE Add button per day - after the last thing (game > travel > rest day)
|
||||
if let lastGame = gamesOnDay.last {
|
||||
items.append(ItineraryRowItem.addButton(day: dayNum, anchorType: .afterGame, anchorId: lastGame.game.id))
|
||||
} else if let travelSegment = travelBefore {
|
||||
let travelId = stableTravelAnchorId(travelSegment)
|
||||
items.append(ItineraryRowItem.addButton(day: dayNum, anchorType: .afterTravel, anchorId: travelId))
|
||||
} else {
|
||||
items.append(ItineraryRowItem.addButton(day: dayNum, anchorType: .startOfDay, anchorId: nil))
|
||||
}
|
||||
// ONE Add button per day
|
||||
items.append(ItineraryRowItem.addButton(day: dayNum))
|
||||
|
||||
let dayData = ItineraryDayData(
|
||||
id: dayNum,
|
||||
|
||||
Reference in New Issue
Block a user