Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Strand/Liquid/LiquidTodayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,14 @@ struct LiquidTodayView: View {
}
)
}
/// Horizontal swipe between days (left = older, right = newer), clamped to [today, earliest].
/// Horizontal swipe between days (right = older, left = newer — `TodayView.daySwipeDelta`, #2378),
/// clamped to [today, earliest].
private var daySwipeGesture: some Gesture {
DragGesture(minimumDistance: 24)
.onEnded { value in
let dx = value.translation.width, dy = value.translation.height
guard abs(dx) > abs(dy) * 1.5, abs(dx) > 50 else { return }
let delta = dx < 0 ? 1 : -1
let delta = TodayView.daySwipeDelta(dx: dx)
let next = Self.clampedDayOffset(current: selectedDayOffset, delta: delta,
maxOffset: earliestDayOffset)
guard next != selectedDayOffset else { return }
Expand Down
18 changes: 16 additions & 2 deletions Strand/Screens/TodayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,20 @@ struct TodayView: View {
return min(upper, max(0, current + delta))
}

/// #2378 - the day step a horizontal swipe of `dx` points asks for: +1 OLDER, -1 NEWER.
///
/// Rightward (dx > 0) is OLDER and leftward is NEWER, which is direct manipulation — dragging the
/// content left brings the page to its right, the later day, into view — and the direction the
/// Kotlin twin already takes (`dayNavSwipeTarget`, pinned by `DayNavTest`). Apple ran the opposite
/// way in both shells, so the same gesture moved the day backwards here and forwards there.
///
/// Pure and shared by both Apple shells so the direction is pinned by a test rather than living
/// twice inside gesture closures, which is how the two platforms drifted apart unnoticed.
/// Mirror EXACTLY in Kotlin.
static func daySwipeDelta(dx: CGFloat) -> Int {
dx > 0 ? 1 : -1
}

/// #16 - whole days-back offset for a date chosen in the day-nav picker, measured from the LOGICAL day
/// (not raw Date()). Pure + unit-testable so the 00:00-04:00 rollover case is locked: in that window the
/// logical day is the PREVIOUS calendar day, so anchoring the offset here (rather than on raw Date())
Expand Down Expand Up @@ -1251,8 +1265,8 @@ struct TodayView: View {
let dy = value.translation.height
// Horizontal-dominant and far enough to count as a deliberate day flip.
guard abs(dx) > abs(dy) * 1.5, abs(dx) > 50 else { return }
// Swipe LEFT (dx < 0) -> OLDER day (+1 offset); swipe RIGHT -> NEWER day (-1 offset).
let delta = dx < 0 ? 1 : -1
// Swipe RIGHT (dx > 0) -> OLDER day (+1 offset); swipe LEFT -> NEWER day (-1 offset).
let delta = Self.daySwipeDelta(dx: dx)
let next = Self.clampedDayOffset(current: selectedDayOffset, delta: delta,
maxOffset: earliestDayOffset)
guard next != selectedDayOffset else { return }
Expand Down
26 changes: 26 additions & 0 deletions StrandTests/TodayDayNavClampTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,30 @@ final class TodayDayNavClampTests: XCTestCase {
let picked = cal.date(from: DateComponents(year: 2026, month: 6, day: 24))!
XCTAssertEqual(TodayView.pickedDayOffset(pickedDate: picked, anchorLogicalDay: logical), 4)
}

// MARK: - daySwipeDelta (#2378)

/// A rightward drag reveals the past, so it steps OLDER (+1). Twin of the Kotlin
/// `DayNavTest.rightwardSwipeGoesOlder`, which pins the same direction for `dayNavSwipeTarget`.
func testRightwardSwipeStepsOlder() {
XCTAssertEqual(TodayView.daySwipeDelta(dx: 120), 1)
XCTAssertEqual(TodayView.daySwipeDelta(dx: 51), 1)
}

/// A leftward drag brings the later day in from the right, so it steps NEWER (-1). Twin of the
/// Kotlin `DayNavTest.leftwardSwipeGoesNewerClampedAtToday` (the clamp itself is covered above).
func testLeftwardSwipeStepsNewer() {
XCTAssertEqual(TodayView.daySwipeDelta(dx: -120), -1)
XCTAssertEqual(TodayView.daySwipeDelta(dx: -51), -1)
}

/// Composed with the clamp: from today a leftward swipe cannot reach a future day, and a rightward
/// one reaches yesterday — the two halves the gesture closures actually chain.
func testSwipeDeltaComposedWithClamp() {
let older = TodayView.daySwipeDelta(dx: 200)
XCTAssertEqual(TodayView.clampedDayOffset(current: 0, delta: older, maxOffset: 30), 1)
let newer = TodayView.daySwipeDelta(dx: -200)
XCTAssertEqual(TodayView.clampedDayOffset(current: 0, delta: newer, maxOffset: 30), 0)
XCTAssertEqual(TodayView.clampedDayOffset(current: 5, delta: newer, maxOffset: 30), 4)
}
}
Loading