Add game-ended notification
Fires the first time a game transitions from non-over to any over-state
(OVER/FINAL/OFF), so the notification lands when the clock runs out
rather than hours later when NHL statisticians stamp OFF. Shows the
winning team's logo with a "{TEAM} wins" title and final score body.
Deduped by game ID via gameEndsSent; cleared on resetForNewDay.
Dev menu: "Test Game Ended Notification" for manual trigger.
This commit is contained in:
@@ -25,6 +25,9 @@ class NotificationManager: @unchecked Sendable {
|
||||
/// Track which score changes we've already notified about (gameId-awayScore-homeScore)
|
||||
private var scoreChangesSent = Set<String>()
|
||||
|
||||
/// Track which game endings we've already notified about
|
||||
private var gameEndsSent = Set<Int>()
|
||||
|
||||
private init() {
|
||||
logger.info("Initializing")
|
||||
requestNotificationPermissions()
|
||||
@@ -143,6 +146,63 @@ class NotificationManager: @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Game Ended
|
||||
|
||||
func notifyGameEnded(_ game: Scoreboard.Game, bypassDedup: Bool = false) {
|
||||
if !bypassDedup {
|
||||
guard !gameEndsSent.contains(game.id) else { return }
|
||||
gameEndsSent.insert(game.id)
|
||||
}
|
||||
|
||||
let awayScore = game.awayTeam.score ?? 0
|
||||
let homeScore = game.homeTeam.score ?? 0
|
||||
let winner: Scoreboard.Game.Team
|
||||
let loser: Scoreboard.Game.Team
|
||||
if awayScore > homeScore {
|
||||
winner = game.awayTeam
|
||||
loser = game.homeTeam
|
||||
} else if homeScore > awayScore {
|
||||
winner = game.homeTeam
|
||||
loser = game.awayTeam
|
||||
} else {
|
||||
// Tie — shouldn't happen in NHL, but handle gracefully
|
||||
winner = game.homeTeam
|
||||
loser = game.awayTeam
|
||||
}
|
||||
let winnerScore = max(awayScore, homeScore)
|
||||
let loserScore = min(awayScore, homeScore)
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = "\(winner.abbrev) wins"
|
||||
content.body = "\(winner.abbrev) \(winnerScore) — \(loser.abbrev) \(loserScore)"
|
||||
content.sound = .default
|
||||
content.interruptionLevel = .active
|
||||
content.userInfo = ["url": game.gameCenterUrl]
|
||||
|
||||
// Attach winning team's logo
|
||||
if let attachment = teamLogoAttachment(for: winner.abbrev) {
|
||||
content.attachments = [attachment]
|
||||
}
|
||||
|
||||
let identifier = bypassDedup
|
||||
? "game-end-test-\(UUID().uuidString)"
|
||||
: "game-end-\(game.id)"
|
||||
|
||||
let request = UNNotificationRequest(
|
||||
identifier: identifier,
|
||||
content: content,
|
||||
trigger: UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
|
||||
)
|
||||
|
||||
UNUserNotificationCenter.current().add(request) { [weak self] error in
|
||||
if let error = error {
|
||||
self?.logger.error("Error sending game end notification: \(error.localizedDescription)")
|
||||
} else {
|
||||
self?.logger.info("Game end notification sent: \(winner.abbrev) \(winnerScore) — \(loser.abbrev) \(loserScore)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Team Logo Attachment
|
||||
|
||||
/// Creates a UNNotificationAttachment from the bundled team logo PNG.
|
||||
@@ -185,6 +245,7 @@ class NotificationManager: @unchecked Sendable {
|
||||
logger.debug("Resetting notification tracking for new day")
|
||||
gameStartsSent.removeAll()
|
||||
scoreChangesSent.removeAll()
|
||||
gameEndsSent.removeAll()
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
Reference in New Issue
Block a user