crossmate

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

commit f35b10e4e63640b521c73e774b0a08fd552225c5
parent 43da5e7fcf59df9f7e33a7bb38b040cee0d9bda9
Author: Michael Camilleri <[email protected]>
Date:   Thu, 16 Jul 2026 14:48:39 +0900

Reword the share card to credit the puzzle author clearly

The completion card read 'Completed <title> / by <author> in <time>',
which a viewer could parse as the author being the person who solved the
puzzle — the constructor's credit scanned as the solver's name.  This
commit restructures the card's text into the conventional crossword
byline: the title as the headline, 'by <author>' as a secondary subtitle
beneath it, and a final line describing the solve — 'Completed Solo' or
'Completed with N Crossmate(s)' — with the time appended. The 'Include
Time' toggle still drops the time from that line, so the user can share
the card without disclosing how long the solve took.

SuccessShareCard now takes the pre-built completion line rather than a
bare time string, and reads the author directly from the puzzle — the
credit always appears when the puzzle names one.

Co-Authored-By: Claude Fable 5 <[email protected]>

Diffstat:
MCrossmate/Views/Puzzle/SuccessPanel.swift | 66+++++++++++++++++++++++++++++++-----------------------------------
1 file changed, 31 insertions(+), 35 deletions(-)

diff --git a/Crossmate/Views/Puzzle/SuccessPanel.swift b/Crossmate/Views/Puzzle/SuccessPanel.swift @@ -138,12 +138,24 @@ struct SuccessPanel: View { } } + /// The card's bottom line: "Completed Solo" or "Completed with N + /// Crossmate(s)", with "in <time>" appended when a clock exists and the + /// user hasn't opted it out. + private func completionLine(includeClock: Bool) -> String { + var text = friendCount > 0 + ? "Completed with \(friendCount) Crossmate\(friendCount == 1 ? "" : "s")" + : "Completed Solo" + if includeClock, hasClock { + text += " in \(TimeLog.clockString(roster.solveTime()))" + } + return text + } + @MainActor private func makeShareImage(includeClock: Bool) -> Image? { - let timeText = (includeClock && hasClock) ? TimeLog.clockString(roster.solveTime()) : nil let card = SuccessShareCard( puzzle: session.puzzle, cellTints: shareCellTints, - timeText: timeText + completionText: completionLine(includeClock: includeClock) ) let renderer = ImageRenderer(content: card) renderer.scale = displayScale @@ -584,7 +596,9 @@ private struct SuccessShareCard: View { let puzzle: Puzzle /// Per-cell author tints, parallel to `puzzle.cells`; `nil` draws neutral. let cellTints: [[Color?]] - let timeText: String? + /// "Completed Solo/with N Crossmate(s)", plus the solve time unless the + /// user opted it out. + let completionText: String /// Fixed render width; the grid sizes itself to the puzzle's aspect ratio. private static let width: CGFloat = 480 @@ -612,51 +626,33 @@ private struct SuccessShareCard: View { .frame(maxWidth: 320) .padding(.vertical, 12) - VStack(spacing: 8) { - Text("Completed ‘\(puzzle.title)’") + VStack(spacing: 6) { + Text("‘\(puzzle.title)’") .font(.system(size: 24, weight: .semibold, design: .rounded)) .foregroundStyle(.black) .multilineTextAlignment(.center) .lineLimit(2) - if puzzle.author != nil || timeText != nil { - ViewThatFits(in: .horizontal) { - shareCreditLine - VStack(spacing: 4) { - if let author = puzzle.author { - Text("by \(author)") - .multilineTextAlignment(.center) - } - if let timeText { - Text("in \(timeText)") - .monospacedDigit() - .lineLimit(1) - } - } - } + if let author = puzzle.author { + Text("by \(author)") + .font(.system(size: 17, weight: .medium, design: .rounded)) + .foregroundStyle(.secondary) + .multilineTextAlignment(.center) + .lineLimit(1) + } + + Text(completionText) .font(.system(size: 20, weight: .medium, design: .rounded)) + .monospacedDigit() .foregroundStyle(.black) - } + .lineLimit(1) + .padding(.top, 6) } } .padding(24) .frame(width: Self.width) .background(Color.white) } - - private var shareCreditLine: some View { - HStack(spacing: 8) { - if let author = puzzle.author { - Text("by \(author)") - .lineLimit(1) - } - if let timeText { - Text("in \(timeText)") - .monospacedDigit() - .lineLimit(1) - } - } - } } /// A two-tone rendering of the grid shape — open squares light, blocks dark —