crossmate

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

commit 0b64302783d46977b677cf3f2bb51204ce6ac81b
parent 646cb9ffcd01f9d2a9a7fc7e69b1044de5408b26
Author: Michael Camilleri <[email protected]>
Date:   Thu, 30 Apr 2026 13:30:43 +0900

Tweak game list styling

Diffstat:
MCrossmate/Views/GameListView.swift | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 75 insertions(+), 14 deletions(-)

diff --git a/Crossmate/Views/GameListView.swift b/Crossmate/Views/GameListView.swift @@ -213,21 +213,12 @@ private struct GameRowView: View { .foregroundStyle(.secondary) } } - if let puzzleDate = game.puzzleDate { - Text(puzzleDate, format: .dateTime.day().month(.abbreviated).year()) - .font(.footnote) - } - if let publisher = game.publisher { - Text(publisher) - .font(.caption) - .foregroundStyle(.secondary) - .lineLimit(1) - .truncationMode(.tail) - } + GameMetadataView( + puzzleDate: game.puzzleDate, + publisher: game.publisher + ) if let date = game.updatedAt { - Text(date, format: Calendar.current.isDateInToday(date) ? .dateTime.hour().minute() : .dateTime.year().month().day()) - .font(.caption) - .foregroundStyle(.secondary) + LastUpdatedView(date: date) } } Spacer() @@ -264,3 +255,73 @@ private struct GameRowView: View { } } } + +private struct LastUpdatedView: View { + let date: Date + + var body: some View { + Text(text) + .font(.caption) + .foregroundStyle(.secondary) + } + + private var text: String { + let elapsed = max(0, Date().timeIntervalSince(date)) + if elapsed < 60 { + let seconds = max(1, Int(elapsed.rounded(.down))) + return "Last updated \(seconds) \(seconds == 1 ? "second" : "seconds") ago" + } + if elapsed < 60 * 60 { + let minutes = Int((elapsed / 60).rounded(.down)) + return "Last updated \(minutes) \(minutes == 1 ? "minute" : "minutes") ago" + } + if elapsed <= 48 * 60 * 60 { + let hours = Int((elapsed / (60 * 60)).rounded(.down)) + return "Last updated \(hours) \(hours == 1 ? "hour" : "hours") ago" + } + return "Last updated on \(date.formatted(.dateTime.day().month(.abbreviated).year()))" + } +} + +private struct GameMetadataView: View { + let puzzleDate: Date? + let publisher: String? + + var body: some View { + if let puzzleDate, let publisher { + ViewThatFits(in: .horizontal) { + HStack(spacing: 0) { + Text(puzzleDate, format: .dateTime.day().month(.abbreviated).year()) + Text(" • ") + Text(publisher) + } + .font(.footnote) + .lineLimit(1) + + VStack(alignment: .leading, spacing: 2) { + puzzleDateView(puzzleDate) + publisherView(publisher) + } + } + } else { + if let puzzleDate { + puzzleDateView(puzzleDate) + } + if let publisher { + publisherView(publisher) + } + } + } + + private func puzzleDateView(_ puzzleDate: Date) -> some View { + Text(puzzleDate, format: .dateTime.day().month(.abbreviated).year()) + .font(.footnote) + } + + private func publisherView(_ publisher: String) -> some View { + Text(publisher) + .font(.footnote) + .lineLimit(1) + .truncationMode(.tail) + } +}