commit e214e94eae963a12f441679a52c1ce68daad627a
parent 81bcea74679ed16287721106fd00b544fb94cc22
Author: Michael Camilleri <[email protected]>
Date: Thu, 2 Jul 2026 18:34:55 +0900
Tolerate duplicate roster authors in puzzle renders
This commit stops puzzle render paths from trapping when the roster
contains duplicate author IDs. The previous grid tint, scoreboard, and
Success Panel dictionaries all required unique keys, so a duplicated
roster entry could crash while drawing otherwise recoverable state.
Now those lookups keep the first roster entry for a duplicate author,
matching the existing recent-change border resolver and preserving a
deterministic render.
Co-Authored-By: Codex GPT 5.5 <[email protected]>
Diffstat:
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/Crossmate/Views/Puzzle/GridView.swift b/Crossmate/Views/Puzzle/GridView.swift
@@ -41,7 +41,8 @@ struct GridView: View {
// (the rewind reads as coloured per author, matching the scoreboard).
let authorTintByID: [String: Color] = (showsSharedAnnotations || isReplaying)
? Dictionary(
- uniqueKeysWithValues: roster.entries.map { ($0.authorID, $0.color.tint) }
+ roster.entries.map { ($0.authorID, $0.color.tint) },
+ uniquingKeysWith: { first, _ in first }
)
: [:]
// Colour for the replay playhead: the acting author's selection fill,
diff --git a/Crossmate/Views/Puzzle/PuzzleScoreboard.swift b/Crossmate/Views/Puzzle/PuzzleScoreboard.swift
@@ -121,7 +121,10 @@ struct PuzzleScoreboard: View {
let entries = roster.entries
let usesLocalFallback = entries.isEmpty
- let entryByAuthorID = Dictionary(uniqueKeysWithValues: entries.map { ($0.authorID, $0) })
+ let entryByAuthorID = Dictionary(
+ entries.map { ($0.authorID, $0) },
+ uniquingKeysWith: { first, _ in first }
+ )
let rosterAuthorIDs = Set(entries.map(\.authorID))
let rosterScores: [Score]
diff --git a/Crossmate/Views/Puzzle/SuccessPanel.swift b/Crossmate/Views/Puzzle/SuccessPanel.swift
@@ -175,7 +175,10 @@ struct SuccessPanel: View {
}
let entries = roster.entries
- let entryByAuthorID = Dictionary(uniqueKeysWithValues: entries.map { ($0.authorID, $0) })
+ let entryByAuthorID = Dictionary(
+ entries.map { ($0.authorID, $0) },
+ uniquingKeysWith: { first, _ in first }
+ )
let hasRemotePlayers = entries.contains { !$0.isLocal }
let usesLocalFallback = entries.isEmpty
let rosterContributions: [Contribution]