crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 5c9571d6bcd460844133c536f07218558685b15d
parent 9e364976cc2580514a47c3b3bce474db46def332
Author: Michael Camilleri <[email protected]>
Date:   Fri, 15 May 2026 18:12:25 +0900

Show seven completed puzzles on launch

This commit changes Game List to only show seven completed puzzles on
cold launch.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

Diffstat:
MCrossmate/Views/GameListView.swift | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 55 insertions(+), 24 deletions(-)

diff --git a/Crossmate/Views/GameListView.swift b/Crossmate/Views/GameListView.swift @@ -24,6 +24,9 @@ struct GameListView: View { @State private var leaveTarget: GameSummary? @State private var leaveError: Error? @State private var summaryCache = GameSummaryCache() + @State private var completedVisibleCount = completedPageSize + + private static let completedPageSize = 7 var body: some View { GeometryReader { geometry in @@ -119,40 +122,68 @@ struct GameListView: View { let completed = summaries .filter { $0.completedAt != nil } .sorted { ($0.completedAt ?? .distantPast) > ($1.completedAt ?? .distantPast) } + let visibleCount = min(completedVisibleCount, completed.count) + let visibleCompleted = Array(completed.prefix(visibleCount)) + let hasMore = visibleCount < completed.count - Group { - List { - if !inProgress.isEmpty { - Section { - ForEach(inProgress) { game in - rowView(for: game, usesRoomierType: usesRoomierType) - } - } header: { - Text("In Progress") + List { + if !inProgress.isEmpty { + Section { + ForEach(inProgress) { game in + rowView(for: game, usesRoomierType: usesRoomierType) } + } header: { + Text("In Progress") } + } - if !completed.isEmpty { - Section { - ForEach(completed) { game in - rowView(for: game, usesRoomierType: usesRoomierType) + if !completed.isEmpty { + Section { + ForEach(visibleCompleted) { game in + rowView(for: game, usesRoomierType: usesRoomierType) + } + } header: { + Text("Completed") + } footer: { + if hasMore { + HStack { + Spacer() + Button { + withAnimation(.easeInOut(duration: 0.25)) { + completedVisibleCount += Self.completedPageSize + } + } label: { + Text("Load More") + .font(.subheadline.weight(.semibold)) + .foregroundColor(.secondary) + .padding(.horizontal, 18) + .padding(.vertical, 8) + .background(Color(.tertiarySystemFill), in: Capsule()) + } + .buttonStyle(.plain) + .textCase(nil) + Spacer() } - } header: { - Text("Completed") + .padding(.top, 8) } } } - .overlay { - if games.isEmpty { - ContentUnavailableView { - Label("No Puzzles", systemImage: "square.grid.3x3") - } description: { - Text("Tap the + button to start a new puzzle, or pull down to refresh.") - } + } + .overlay { + if games.isEmpty { + ContentUnavailableView { + Label("No Puzzles", systemImage: "square.grid.3x3") + } description: { + Text("Tap the + button to start a new puzzle, or pull down to refresh.") } } - .refreshable { - await onRefresh() + } + .refreshable { + await onRefresh() + } + .onChange(of: completed.count) { oldCount, newCount in + if newCount > oldCount { + completedVisibleCount += (newCount - oldCount) } } }