Add playoff round view, game numbers, goal scorer notifications, standings

- Fetch NHL standings and surface league/season game counts in the menu bar
- Prefix regular-season rows with the league-wide game number (from gameId)
- New ROUND section shows each active playoff series (matchup, series score,
  next game number + time) derived from /v1/playoff-bracket; rows always open
  the NHL series page so completed series remain clickable
- Goal notifications include scorer sweater, abbreviated name, and strength
  (PPG/SHG/EN), resolved via /v1/gamecenter/{id}/play-by-play
- Drop the per-team filter submenu and NHLTeam enum
- Regenerate AppIcon with the full 10-size macOS set (alpha preserved) so
  notifications render the app icon correctly; rename the iOS marketing PNG
  to icon-ios-1024.png
- gitignore .claude/ local tooling settings
This commit is contained in:
2026-04-18 21:51:27 -04:00
parent 8f8f8b2755
commit 57358797e1
44 changed files with 596 additions and 286 deletions
+30 -20
View File
@@ -17,11 +17,11 @@ class AppSettings: @unchecked Sendable {
private enum UserDefaultsKey {
static let launchAtLogin = "launchAtLogin"
static let selectedTeam = "selectedTeam"
static let displayOption = "displayOption"
static let statusBarOption = "statusBarOption"
}
/// Controls which days are shown in the menu and counted in the status bar
/// Controls which days are shown in the menu
enum DisplayOption: String, CaseIterable {
case yesterdayTodayTomorrow = "yesterdayTodayTomorrow"
case todayTomorrow = "todayTomorrow"
@@ -35,7 +35,6 @@ class AppSettings: @unchecked Sendable {
}
}
/// Which date strings to include
func includedDates() -> Set<String> {
switch self {
case .yesterdayTodayTomorrow:
@@ -48,24 +47,25 @@ class AppSettings: @unchecked Sendable {
}
}
// Launch at login
var launchAtLogin: Bool {
get {
UserDefaults.standard.bool(forKey: UserDefaultsKey.launchAtLogin)
}
set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.launchAtLogin)
/// Controls what number shows next to the menu bar icon
enum StatusBarOption: String, CaseIterable {
case gameCount = "gameCount"
case gamesPlayed = "gamesPlayed"
case gamesPlayedTotal = "gamesPlayedTotal"
var title: String {
switch self {
case .gameCount: return "Game Count"
case .gamesPlayed: return "Games Played"
case .gamesPlayedTotal: return "Games Played / Total"
}
}
}
// Selected team filter (nil = all teams)
var selectedTeam: String? {
get {
UserDefaults.standard.string(forKey: UserDefaultsKey.selectedTeam)
}
set {
UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.selectedTeam)
}
// Launch at login
var launchAtLogin: Bool {
get { UserDefaults.standard.bool(forKey: UserDefaultsKey.launchAtLogin) }
set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.launchAtLogin) }
}
// Display option
@@ -77,9 +77,19 @@ class AppSettings: @unchecked Sendable {
}
return .yesterdayTodayTomorrow
}
set {
UserDefaults.standard.set(newValue.rawValue, forKey: UserDefaultsKey.displayOption)
set { UserDefaults.standard.set(newValue.rawValue, forKey: UserDefaultsKey.displayOption) }
}
// Status bar option
var statusBarOption: StatusBarOption {
get {
if let rawValue = UserDefaults.standard.string(forKey: UserDefaultsKey.statusBarOption),
let option = StatusBarOption(rawValue: rawValue) {
return option
}
return .gameCount
}
set { UserDefaults.standard.set(newValue.rawValue, forKey: UserDefaultsKey.statusBarOption) }
}
func updateLoginItem(enabled: Bool) {