Files
iceglass/IceGlass/Managers/AppSettings.swift
T
rzen 57358797e1 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
2026-04-18 21:51:27 -04:00

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() {}
}