crossmate

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

commit e15e4042b3e7d02b47475a82843345224a076444
parent afd967245f8b5ff626d547ea218180f364f39f16
Author: Michael Camilleri <[email protected]>
Date:   Sun,  7 Jun 2026 00:44:57 +0900

Hoist clue-at-position lookup onto Puzzle

Both ClueBar and ClueList carried an identical private
clue(atRow:col:direction:) helper for resolving the replay playhead's
cursor to a clue. The two copies were character-for-character the same
and would have invited a third the next time a surface needed
replay-aware clue resolution. This commit moves the resolver onto Puzzle
and routes both views through session.puzzle.clue(...).

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

Diffstat:
MCrossmate/Models/Puzzle.swift | 10++++++++++
MCrossmate/Views/ClueList.swift | 10+---------
MCrossmate/Views/PuzzleView.swift | 10+---------
3 files changed, 12 insertions(+), 18 deletions(-)

diff --git a/Crossmate/Models/Puzzle.swift b/Crossmate/Models/Puzzle.swift @@ -357,6 +357,16 @@ struct Puzzle: Sendable { return result.count > 1 ? result : [] } + /// Returns the clue for the word containing `(row, col)` in the given + /// direction, or `nil` if the cell isn't part of a numbered word. + func clue(atRow row: Int, col: Int, direction: Direction) -> Clue? { + guard let number = wordCells(atRow: row, col: col, direction: direction).first?.number else { + return nil + } + let clues = direction == .across ? acrossClues : downClues + return clues.first { $0.number == number } + } + /// Returns the canonical cursor track for a focused cell: the start cell /// of the answer slot in `direction`, paired with that direction. This is /// the low-frequency collaborative presence value persisted to CloudKit; diff --git a/Crossmate/Views/ClueList.swift b/Crossmate/Views/ClueList.swift @@ -197,7 +197,7 @@ struct ClueList: View { let direction = replayFrame?.cursorDirection else { return nil } return ClueDisplay( - clue: clue(atRow: position.row, col: position.col, direction: direction), + clue: session.puzzle.clue(atRow: position.row, col: position.col, direction: direction), direction: direction ) } @@ -211,14 +211,6 @@ struct ClueList: View { } } - private func clue(atRow row: Int, col: Int, direction: Puzzle.Direction) -> Puzzle.Clue? { - guard let number = session.puzzle.wordCells(atRow: row, col: col, direction: direction).first?.number else { - return nil - } - let clues = direction == .across ? session.puzzle.acrossClues : session.puzzle.downClues - return clues.first { $0.number == number } - } - @ViewBuilder private func row( for clue: Puzzle.Clue, diff --git a/Crossmate/Views/PuzzleView.swift b/Crossmate/Views/PuzzleView.swift @@ -1556,7 +1556,7 @@ private struct ClueBar: View { let position = replayClueTarget.position guard let direction = replayClueTarget.direction else { return nil } return ClueDisplay( - clue: clue(atRow: position.row, col: position.col, direction: direction), + clue: session.puzzle.clue(atRow: position.row, col: position.col, direction: direction), direction: direction ) } @@ -1570,14 +1570,6 @@ private struct ClueBar: View { } } - private func clue(atRow row: Int, col: Int, direction: Puzzle.Direction) -> Puzzle.Clue? { - guard let number = session.puzzle.wordCells(atRow: row, col: col, direction: direction).first?.number else { - return nil - } - let clues = direction == .across ? session.puzzle.acrossClues : session.puzzle.downClues - return clues.first { $0.number == number } - } - private func label(for clue: Puzzle.Clue?, direction: Puzzle.Direction) -> String { let direction = direction == .across ? "Across" : "Down" if let clue {