57358797e1
- 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
109 lines
3.4 KiB
Swift
109 lines
3.4 KiB
Swift
//
|
|
// AppSettings.swift
|
|
// IceGlass
|
|
//
|
|
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
|
|
//
|
|
|
|
import ServiceManagement
|
|
|
|
class AppSettings: @unchecked Sendable {
|
|
private let logger = IceGlassLogger(
|
|
subsystem: Bundle.main.bundleIdentifier ?? "dev.rzen.indie.IceGlass",
|
|
category: "AppSettings"
|
|
)
|
|
|
|
static let shared = AppSettings()
|
|
|
|
private enum UserDefaultsKey {
|
|
static let launchAtLogin = "launchAtLogin"
|
|
static let displayOption = "displayOption"
|
|
static let statusBarOption = "statusBarOption"
|
|
}
|
|
|
|
/// Controls which days are shown in the menu
|
|
enum DisplayOption: String, CaseIterable {
|
|
case yesterdayTodayTomorrow = "yesterdayTodayTomorrow"
|
|
case todayTomorrow = "todayTomorrow"
|
|
case todayOnly = "todayOnly"
|
|
|
|
var title: String {
|
|
switch self {
|
|
case .yesterdayTodayTomorrow: return "Yesterday / Today / Tomorrow"
|
|
case .todayTomorrow: return "Today / Tomorrow"
|
|
case .todayOnly: return "Today"
|
|
}
|
|
}
|
|
|
|
func includedDates() -> Set<String> {
|
|
switch self {
|
|
case .yesterdayTodayTomorrow:
|
|
return [Date.yesterdayET, Date.todayET, Date.tomorrowET]
|
|
case .todayTomorrow:
|
|
return [Date.todayET, Date.tomorrowET]
|
|
case .todayOnly:
|
|
return [Date.todayET]
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Launch at login
|
|
var launchAtLogin: Bool {
|
|
get { UserDefaults.standard.bool(forKey: UserDefaultsKey.launchAtLogin) }
|
|
set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.launchAtLogin) }
|
|
}
|
|
|
|
// Display option
|
|
var displayOption: DisplayOption {
|
|
get {
|
|
if let rawValue = UserDefaults.standard.string(forKey: UserDefaultsKey.displayOption),
|
|
let option = DisplayOption(rawValue: rawValue) {
|
|
return option
|
|
}
|
|
return .yesterdayTodayTomorrow
|
|
}
|
|
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) {
|
|
do {
|
|
if enabled {
|
|
try SMAppService.mainApp.register()
|
|
} else {
|
|
try SMAppService.mainApp.unregister()
|
|
}
|
|
} catch {
|
|
logger.error("Failed to \(enabled ? "enable" : "disable") launch at login: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
private init() {}
|
|
}
|