crossmate

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

commit 77ddc8c530c8137a3fbce5ee83ae20b89362ff38
parent 6a360059c435bee2c8e8d21589aec43072fbacfb
Author: Michael Camilleri <[email protected]>
Date:   Thu, 23 Jul 2026 22:08:35 +0900

Skip foreground grid refreshes for Chronicles

Foregrounding an open Chronicle still treated its local projection as an
active live-game target, issuing an empty CloudKit Game, Moves and
Player fetch.

This commit gives GameStore an explicit archived-game classification and
excludes Chronicles from foreground puzzle-grid refreshes while
retaining their archived replay routing.

Co-Authored-By: Codex GPT 5.6 Sol <[email protected]>

Diffstat:
MCrossmate/Persistence/GameStore.swift | 13+++++++++----
MCrossmate/Services/AppServices.swift | 3++-
MTests/Unit/ArchiveTests.swift | 1+
3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/Crossmate/Persistence/GameStore.swift b/Crossmate/Persistence/GameStore.swift @@ -2226,10 +2226,8 @@ final class GameStore { return entity.ckShareRecordName != nil || entity.databaseScope == 1 } - /// A materialised Chronicle carries its replay as cached Journal rows, - /// regardless of whether the original game was shared. It must use the - /// merged replay loader rather than the live game's local-journal shortcut. - func usesArchivedReplay(gameID: UUID) -> Bool { + /// Whether `gameID` is the local read-only projection of a Chronicle. + func isGameArchived(gameID: UUID) -> Bool { let request = NSFetchRequest<GameEntity>(entityName: "GameEntity") request.predicate = NSPredicate(format: "id == %@", gameID as CVarArg) request.fetchLimit = 1 @@ -2237,6 +2235,13 @@ final class GameStore { return isMaterializedArchive(entity) } + /// A materialised Chronicle carries its replay as cached Journal rows, + /// regardless of whether the original game was shared. It must use the + /// merged replay loader rather than the live game's local-journal shortcut. + func usesArchivedReplay(gameID: UUID) -> Bool { + isGameArchived(gameID: gameID) + } + /// Reads, mutates, and re-persists the local author's `Player.timeLog`, /// creating a stub `PlayerEntity` if none exists (works for solo games — no /// `isShared` gate). `updatedAt` is left untouched on an existing row: the diff --git a/Crossmate/Services/AppServices.swift b/Crossmate/Services/AppServices.swift @@ -2113,7 +2113,8 @@ final class AppServices { private func activePuzzleGridTarget() -> (UUID, CKDatabase.Scope)? { guard let entity = store.currentEntity, - let gameID = entity.id + let gameID = entity.id, + !store.isGameArchived(gameID: gameID) else { return nil } switch entity.databaseScope { case 0: diff --git a/Tests/Unit/ArchiveTests.swift b/Tests/Unit/ArchiveTests.swift @@ -620,6 +620,7 @@ struct ArchiveTests { #expect(Set(players.compactMap(\.authorID)) == ["alice", "bob"]) #expect(players.allSatisfy { ($0.name ?? "").isEmpty }) #expect(GameSummary(entity: game)?.isShared == true) + #expect(store.isGameArchived(gameID: game.id!)) #expect(store.usesArchivedReplay(gameID: game.id!)) #expect(await store.cachedRemoteJournals(forGameID: game.id!)?.count == 2) }