Files
iceglass/IceGlass/Models/ScoreboardModel.swift
T
rzen 8f8f8b2755 Initial commit: IceGlass NHL game tracker
macOS menu bar app providing NHL game situational awareness with
league-wide scoreboard, dynamic polling, notifications with team
logos, and configurable display options.
2026-04-13 21:44:08 -04:00

103 lines
2.7 KiB
Swift

//
// ScoreboardModel.swift
// IceGlass
//
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
//
import Foundation
struct Scoreboard: Codable {
let focusedDate: String
let focusedDateCount: Int
let gamesByDate: [GameDay]
struct GameDay: Codable {
let date: String // "YYYY-MM-DD"
let games: [Game]
}
struct Game: Codable, Equatable {
static func == (lhs: Game, rhs: Game) -> Bool {
lhs.id == rhs.id
}
let id: Int
let season: Int
let gameType: Int
let gameDate: String
let gameCenterLink: String
let startTimeUTC: String
let gameState: String
let gameScheduleState: String
let awayTeam: Team
let homeTeam: Team
let period: Int?
let periodDescriptor: PeriodDescriptor?
struct LocalizedString: Codable {
let `default`: String
}
struct Team: Codable {
let id: Int
let name: LocalizedString
let commonName: LocalizedString
let abbrev: String
let score: Int?
let record: String?
let logo: String
}
struct PeriodDescriptor: Codable {
let number: Int
let periodType: String
let maxRegulationPeriods: Int
}
// MARK: - Computed Properties
var parsedGameState: GameState {
GameState(rawValue: gameState) ?? .future
}
var date: Date {
ISO8601DateFormatter().date(from: startTimeUTC) ?? .now
}
var gameCenterUrl: String {
"https://www.nhl.com\(gameCenterLink)"
}
var videocastUrl: String {
"https://videocast.nhl.com/game/\(id)/usnded?autoplay=true"
}
/// Time string in ET for display (e.g., "7:00 PM")
var startTimeET: String {
date.formatDateET(format: "h:mm a")
}
/// Formatted menu title: "NYR @ WAS 0:2 (FINAL)" or "DAL @ TOR Today @ 7:30 PM"
var menuTitle: String {
let state = parsedGameState
let matchup = "\(awayTeam.abbrev) @ \(homeTeam.abbrev)"
if state.isFuture {
let isToday = gameDate == Date.todayET
let prefix = isToday ? "Today @ " : ""
return "\(matchup) \(prefix)\(startTimeET)"
}
// Has scores
let score = "\(awayTeam.score ?? 0):\(homeTeam.score ?? 0)"
return "\(matchup) \(score) (\(gameState))"
}
/// Whether this game involves a specific team
func involves(team abbrev: String) -> Bool {
awayTeam.abbrev == abbrev || homeTeam.abbrev == abbrev
}
}
}