feature/ios-time-entries-rework #2

Merged
simonfranken merged 5 commits from feature/ios-time-entries-rework into main 2026-02-23 16:58:45 +00:00
Showing only changes of commit 544b86c948 - Show all commits

View File

@@ -2,10 +2,12 @@ import SwiftUI
struct TimeEntriesView: View { struct TimeEntriesView: View {
@StateObject private var viewModel = TimeEntriesViewModel() @StateObject private var viewModel = TimeEntriesViewModel()
@State private var selectedDate: Date = Calendar.current.startOfDay(for: Date())
// For infinite paging, we use an offset from 'today' // dayOffset is the source of truth: 0 = today, -1 = yesterday, etc.
@State private var dayOffset: Int = 0 @State private var dayOffset: Int = 0
// tabSelection is always snapped back to 1 (middle) after each swipe.
// Pages are: 0 = dayOffset-1, 1 = dayOffset, 2 = dayOffset+1
@State private var tabSelection: Int = 1
@State private var showFilterSheet = false @State private var showFilterSheet = false
@State private var showAddEntry = false @State private var showAddEntry = false
@@ -101,20 +103,37 @@ struct TimeEntriesView: View {
// MARK: - Main content // MARK: - Main content
private var mainContent: some View { private var mainContent: some View {
// We use a wide range of offsets to simulate infinite paging. // Only 3 pages exist at any time: previous, current, next.
// -1000 days is roughly 2.7 years in the past. // After each swipe settles, we reset tabSelection to 1 and shift
// 1000 days is roughly 2.7 years in the future. // dayOffset, so the carousel appears infinite while staying cheap.
TabView(selection: $dayOffset) { TabView(selection: $tabSelection) {
ForEach(-1000...1000, id: \.self) { offset in ForEach(0..<3, id: \.self) { page in
let dayForPage = Calendar.current.date(byAdding: .day, value: offset, to: Calendar.current.startOfDay(for: Date())) ?? Date() let offset = dayOffset + (page - 1)
let day = Calendar.current.date(
byAdding: .day,
value: offset,
to: Calendar.current.startOfDay(for: Date())
) ?? Date()
ScrollView { ScrollView {
dayEntriesSection(for: dayForPage) dayEntriesSection(for: day)
} }
.tag(offset) // Important: tag must match the selection type (Int) .tag(page)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
.onChange(of: tabSelection) { _, newPage in
guard newPage != 1 else { return }
// Shift the logical day offset by how many pages we moved.
dayOffset += newPage - 1
// Snap back to the middle page without animation so the
// surrounding pages are refreshed invisibly.
var tx = Transaction()
tx.disablesAnimations = true
withTransaction(tx) {
tabSelection = 1
} }
} }
.tabViewStyle(.page(indexDisplayMode: .never)) // Native swipe gesture
} }
// MARK: - Day entries section // MARK: - Day entries section