commit 984269e2e365158c7856f2e8a25ab3795103666d
parent 8a88a8d61c94b3539dfd10b2b52dc11dba2e580b
Author: Michael Camilleri <[email protected]>
Date: Thu, 18 Jun 2026 10:56:50 +0900
Derive the Game List strip order from one participant list
This commit collapses GameSummary's two participant arrays into a single
source of truth. The full list and the strip-specific ordering held the
same players in different orders, so a pure score change altered neither
stored value and the row would not re-diff. GameSummary now keeps only
allParticipants, and stripParticipants becomes a computed ordering of
that list, so a score shift flows through the summary's equality.
Each player's score now travels on the participant itself. The summary
builders take a scoreByAuthorID and stamp it onto every
GameParticipantSummary, and the strip sorts by that stored score rather
than a side table threaded through a second pass. The friend roster,
which has no scores, keeps the defaulted empty map and reads zero.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Diffstat:
2 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/Crossmate/Models/ParticipantSummaries.swift b/Crossmate/Models/ParticipantSummaries.swift
@@ -6,6 +6,9 @@ struct GameParticipantSummary: Identifiable, Equatable {
let name: String
let color: PlayerColor
let isLocal: Bool
+ /// This player's puzzle score, used to order the Game List strip. Zero
+ /// for contexts that don't supply scores (e.g. the friend roster).
+ let score: Int
var id: String { authorID }
}
@@ -17,6 +20,7 @@ enum ParticipantSummaries {
localAuthorID: String?,
localName: String,
localColor: PlayerColor,
+ scoreByAuthorID: [String: Int] = [:],
additionalAuthorIDs: [String] = []
) -> [GameParticipantSummary] {
let remotes = remoteParticipants(
@@ -25,6 +29,7 @@ enum ParticipantSummaries {
nicknamesByAuthor: nicknamesByAuthor,
localAuthorID: localAuthorID,
localColor: localColor,
+ scoreByAuthorID: scoreByAuthorID,
additionalAuthorIDs: additionalAuthorIDs
)
guard let localAuthorID, !localAuthorID.isEmpty else { return remotes }
@@ -33,7 +38,8 @@ enum ParticipantSummaries {
authorID: localAuthorID,
name: resolvedLocalName(localName),
color: localColor,
- isLocal: true
+ isLocal: true,
+ score: scoreByAuthorID[localAuthorID] ?? 0
),
] + remotes
}
@@ -44,6 +50,7 @@ enum ParticipantSummaries {
nicknamesByAuthor: [String: String],
localAuthorID: String?,
localColor: PlayerColor,
+ scoreByAuthorID: [String: Int] = [:],
additionalAuthorIDs: [String] = []
) -> [GameParticipantSummary] {
var authorIDs = Set(namesByAuthor.keys)
@@ -67,7 +74,8 @@ enum ParticipantSummaries {
authorID: authorID,
name: name,
color: color,
- isLocal: false
+ isLocal: false,
+ score: scoreByAuthorID[authorID] ?? 0
))
}
return summaries.sorted {
diff --git a/Crossmate/Persistence/GameStore.swift b/Crossmate/Persistence/GameStore.swift
@@ -33,7 +33,18 @@ struct GameSummary: Identifiable, Equatable {
let isAccessRevoked: Bool
let hasUnreadOtherMoves: Bool
let allParticipants: [GameParticipantSummary]
- let stripParticipants: [GameParticipantSummary]
+
+ /// The participants ordered for the Game List strip: highest scorer at the
+ /// leading edge. Derived from `allParticipants`, whose summaries already
+ /// carry each player's score, so the ordering lives in one place.
+ var stripParticipants: [GameParticipantSummary] {
+ ParticipantSummaries.sortedByScore(
+ allParticipants,
+ score: \.score,
+ name: \.name,
+ id: \.authorID
+ )
+ }
init?(
entity: GameEntity,
@@ -133,15 +144,11 @@ struct GameSummary: Identifiable, Equatable {
self.isOwned = entity.databaseScope == 0
self.isShared = entity.ckShareRecordName != nil || entity.databaseScope == 1
self.isAccessRevoked = entity.isAccessRevoked
- let allParticipants = Self.computeParticipants(
+ self.allParticipants = Self.computeParticipants(
entity: entity,
localAuthorID: localAuthorID,
localName: localName,
- localColor: localColor
- )
- self.allParticipants = allParticipants
- self.stripParticipants = Self.stripParticipants(
- allParticipants,
+ localColor: localColor,
scoreByAuthorID: scoreByAuthorID
)
self.hasUnreadOtherMoves = Self.computeHasUnread(
@@ -171,7 +178,8 @@ struct GameSummary: Identifiable, Equatable {
entity: GameEntity,
localAuthorID: String?,
localName: String,
- localColor: PlayerColor
+ localColor: PlayerColor,
+ scoreByAuthorID: [String: Int]
) -> [GameParticipantSummary] {
guard entity.ckShareRecordName != nil || entity.databaseScope == 1 else {
return []
@@ -201,19 +209,8 @@ struct GameSummary: Identifiable, Equatable {
nicknamesByAuthor: nicknames,
localAuthorID: localAuthorID,
localName: localName,
- localColor: localColor
- )
- }
-
- private static func stripParticipants(
- _ participants: [GameParticipantSummary],
- scoreByAuthorID: [String: Int]
- ) -> [GameParticipantSummary] {
- ParticipantSummaries.sortedByScore(
- participants,
- score: { scoreByAuthorID[$0.authorID] ?? 0 },
- name: \.name,
- id: \.authorID
+ localColor: localColor,
+ scoreByAuthorID: scoreByAuthorID
)
}