not working, claude is ass

This commit is contained in:
Trey t
2026-01-17 23:23:22 -06:00
parent 64e54b3b11
commit cd1666e7d1

View File

@@ -328,9 +328,16 @@ final class ItineraryTableViewController: UITableViewController {
/// The item currently being dragged (nil when no drag active)
private var draggingItem: ItineraryRowItem?
/// Row indices that are invalid drop targets for the current drag
/// The day number the user is targeting during custom item drag (for stable positioning)
private var dragTargetDay: Int?
/// Row indices that are invalid drop targets for the current drag (for visual dimming)
private var invalidRowIndices: Set<Int> = []
/// Row indices that ARE valid drop targets - pre-calculated at drag start for stability
/// Using a sorted array enables O(log n) nearest-neighbor lookup
private var validDropRows: [Int] = []
/// IDs of games that act as barriers for the current travel drag (for gold highlighting)
private var barrierGameIds: Set<String> = []
@@ -605,7 +612,9 @@ final class ItineraryTableViewController: UITableViewController {
/// 4. Removes visual dimming and highlighting
private func endDrag() {
draggingItem = nil
dragTargetDay = nil
invalidRowIndices = []
validDropRows = []
barrierGameIds = []
isInValidZone = true
@@ -629,41 +638,40 @@ final class ItineraryTableViewController: UITableViewController {
// Get valid day range from pre-calculated ranges
guard let validRange = travelValidRanges[travelId] else {
invalidRowIndices = []
validDropRows = []
barrierGameIds = []
return
}
// Calculate invalid row indices (rows outside valid day range)
// Calculate invalid and valid row indices based on day range
// Pre-calculate ALL valid positions for stable drag behavior
var invalidRows = Set<Int>()
var validRows: [Int] = []
for (index, rowItem) in flatItems.enumerated() {
let dayNum: Int
switch rowItem {
case .dayHeader(let dayNum, _):
// Day header rows for days outside valid range are invalid drop targets
// (travel would appear BEFORE this header, making it belong to this day)
if !validRange.contains(dayNum) {
invalidRows.insert(index)
}
case .games(_, let dayNum):
// Games on days outside valid range are invalid
if !validRange.contains(dayNum) {
invalidRows.insert(index)
}
case .travel(_, let dayNum):
// Other travel rows on days outside valid range
if !validRange.contains(dayNum) {
invalidRows.insert(index)
}
case .dayHeader(let d, _):
dayNum = d
case .games(_, let d):
dayNum = d
case .travel(_, let d):
dayNum = d
case .customItem(let item):
// Custom items on days outside valid range
if !validRange.contains(item.day) {
invalidRows.insert(index)
}
dayNum = item.day
}
if validRange.contains(dayNum) {
validRows.append(index)
} else {
invalidRows.insert(index)
}
}
invalidRowIndices = invalidRows
validDropRows = validRows // Already sorted since we iterate in order
// Find barrier games using ItineraryConstraints
// First, find or create the ItineraryItem for this travel
if let travelItem = findItineraryItem(for: segment),
let constraints = constraints {
let barriers = constraints.barrierGames(for: travelItem)
@@ -678,15 +686,22 @@ final class ItineraryTableViewController: UITableViewController {
/// Custom items can go on any day, but we mark certain positions as
/// less ideal (e.g., directly on day headers or before travel).
private func calculateCustomItemDragZones(item: ItineraryItem) {
// Custom items are flexible - no day restrictions
// But we can mark day headers as invalid since items shouldn't drop ON them
// Custom items are flexible - can go anywhere except ON day headers
// Pre-calculate ALL valid row indices for stable drag behavior
var invalidRows = Set<Int>()
var validRows: [Int] = []
for (index, rowItem) in flatItems.enumerated() {
if case .dayHeader = rowItem {
invalidRows.insert(index)
} else {
// All non-header rows are valid drop targets
validRows.append(index)
}
}
invalidRowIndices = invalidRows
validDropRows = validRows // Already sorted since we iterate in order
barrierGameIds = [] // No barrier highlighting for custom items
}
@@ -859,6 +874,16 @@ final class ItineraryTableViewController: UITableViewController {
// Calculate the new day and sortOrder for the dropped position
let destinationDay = dayNumber(forRow: destinationIndexPath.row)
let sortOrder = calculateSortOrder(at: destinationIndexPath.row)
// DEBUG: Log the final state after insertion
print("🎯 [Drop] source=\(sourceIndexPath.row) → dest=\(destinationIndexPath.row)")
print("🎯 [Drop] flatItems around dest:")
for i in max(0, destinationIndexPath.row - 2)...min(flatItems.count - 1, destinationIndexPath.row + 2) {
let marker = i == destinationIndexPath.row ? "" : " "
print("🎯 [Drop] \(marker) [\(i)] \(flatItems[i])")
}
print("🎯 [Drop] Calculated day=\(destinationDay), sortOrder=\(sortOrder)")
onCustomItemMoved?(customItem.id, destinationDay, sortOrder)
default:
@@ -924,7 +949,7 @@ final class ItineraryTableViewController: UITableViewController {
// DRAG START DETECTION
// The first call to this method indicates drag has started.
// Initialize drag state, calculate invalid zones, and trigger pickup haptic.
// Initialize drag state, calculate valid/invalid zones, and trigger pickup haptic.
if draggingItem == nil {
beginDrag(at: sourceIndexPath)
}
@@ -941,75 +966,10 @@ final class ItineraryTableViewController: UITableViewController {
checkZoneTransition(at: proposedRow)
switch item {
case .travel(let segment, _):
// TRAVEL CONSTRAINT LOGIC
// Travel can only be on certain days (must finish games in departure city,
// must arrive by game time in destination city)
let travelId = "travel:\(segment.fromLocation.name.lowercased())->\(segment.toLocation.name.lowercased())"
guard let validRange = travelValidRanges[travelId] else {
print("No valid range for travel: \(travelId)")
return proposedDestinationIndexPath
}
// Figure out which day the user is trying to drop onto
var proposedDay = dayForTravelAtProposed(row: proposedRow, excluding: sourceIndexPath.row)
// Clamp to valid range - this is what creates the "snap" effect
if proposedDay < validRange.lowerBound {
proposedDay = validRange.lowerBound
} else if proposedDay > validRange.upperBound {
proposedDay = validRange.upperBound
}
// Convert day number back to row position (right before that day's header)
if let headerRow = dayHeaderRow(forDay: proposedDay) {
var targetRow = headerRow
// Account for the fact that the source row will be removed first
// This is a UITableView quirk - the destination index is calculated
// as if the source has already been removed
if sourceIndexPath.row < headerRow {
targetRow -= 1
}
return IndexPath(row: max(0, targetRow), section: 0)
}
return proposedDestinationIndexPath
case .customItem(let customItem):
// CUSTOM ITEM CONSTRAINT LOGIC
// Custom items are flexible - they can go anywhere within the itinerary,
// but we prevent dropping in places that would be confusing
// Use ItineraryConstraints to validate position
let proposedDay = dayNumber(forRow: proposedRow)
let proposedSortOrder = calculateSortOrder(at: proposedRow)
if let constraints = constraints {
if !constraints.isValidPosition(for: customItem, day: proposedDay, sortOrder: proposedSortOrder) {
// If position is invalid, try to find a valid nearby position
// For now, just prevent dropping on day headers
}
}
// Don't drop ON a day header - go after it instead
if proposedRow < flatItems.count, case .dayHeader = flatItems[proposedRow] {
return IndexPath(row: proposedRow + 1, section: 0)
}
// Don't drop before travel at the start of a day
// (would visually appear before the travel card, which is confusing)
if proposedRow < flatItems.count, case .travel = flatItems[proposedRow] {
// Find the day header after this travel and drop after the header
for i in proposedRow..<flatItems.count {
if case .dayHeader = flatItems[i] {
return IndexPath(row: i + 1, section: 0)
}
}
}
return IndexPath(row: proposedRow, section: 0)
case .travel, .customItem:
// UNIFIED CONSTRAINT LOGIC using pre-calculated validDropRows
// This eliminates bouncing by using a simple lookup instead of recalculating
return snapToValidRow(proposedRow)
default:
// Fixed items (shouldn't reach here since canMoveRowAt returns false)
@@ -1017,6 +977,55 @@ final class ItineraryTableViewController: UITableViewController {
}
}
/// Snaps a proposed row to the nearest valid drop position.
///
/// Uses pre-calculated `validDropRows` for O(log n) lookup.
/// If the proposed row is already valid, returns it immediately (prevents bouncing).
/// Otherwise, finds the nearest valid row using binary search.
private func snapToValidRow(_ proposedRow: Int) -> IndexPath {
// Fast path: if proposed row is valid, return it immediately
// This is the key to preventing bouncing - no recalculation needed
if validDropRows.contains(proposedRow) {
return IndexPath(row: proposedRow, section: 0)
}
// Proposed row is invalid - find the nearest valid row
guard !validDropRows.isEmpty else {
return IndexPath(row: proposedRow, section: 0)
}
// Binary search for insertion point
var low = 0
var high = validDropRows.count
while low < high {
let mid = (low + high) / 2
if validDropRows[mid] < proposedRow {
low = mid + 1
} else {
high = mid
}
}
// low is now the insertion point - check neighbors to find nearest
let before = low > 0 ? validDropRows[low - 1] : nil
let after = low < validDropRows.count ? validDropRows[low] : nil
let nearest: Int
if let b = before, let a = after {
// Both neighbors exist - pick the closer one
nearest = (proposedRow - b) <= (a - proposedRow) ? b : a
} else if let b = before {
nearest = b
} else if let a = after {
nearest = a
} else {
nearest = proposedRow // Fallback (shouldn't happen)
}
return IndexPath(row: nearest, section: 0)
}
/// Calculates which day a travel segment would belong to if dropped at a proposed position.
///
/// Similar to `dayForTravelAt`, but used during the drag (before the move completes).