Add iPhone target with shared data layer and persistent cache
Two-target restructure: shared sources (models, services, settings, extensions, team logos) move into Shared/, consumed by both the existing macOS menu bar app and a new iOS app. MainService no longer imports AppKit — platform code attaches via a MainServiceObserver protocol (MacObserverAdapter wires back to MenuManager / StatusItemManager / NotificationManager). iPhone app is a single SwiftUI page mirroring the macOS menu (playoff round + yesterday/today/tomorrow), with a gear-icon settings sheet (display option + IndieAbout for license/changelog). Persistent JSON snapshot in Application Support paints last-known data on cold launch; "Updated …" header escalates secondary → orange (>5min) → red (>30min) so staleness is visually unmistakable. Foreground polling, scenePhase refresh, and pull-to-refresh; no notifications on iOS in v1.
This commit is contained in:
@@ -366,7 +366,7 @@ class MenuManager: @unchecked Sendable {
|
||||
trailing = ""
|
||||
} else if let n = series.nextGameNumber {
|
||||
statusTag = "(Game \(n))"
|
||||
trailing = roundItem.nextGame.map { Self.nextGameLabel(for: $0) } ?? ""
|
||||
trailing = roundItem.nextGame?.nextGameLabel ?? ""
|
||||
} else {
|
||||
statusTag = ""
|
||||
trailing = ""
|
||||
@@ -381,28 +381,4 @@ class MenuManager: @unchecked Sendable {
|
||||
return item
|
||||
}
|
||||
|
||||
private static func nextGameLabel(for game: Scoreboard.Game) -> String {
|
||||
let state = game.parsedGameState
|
||||
if state.isLive {
|
||||
return state.shortTag
|
||||
}
|
||||
let dayLabel: String
|
||||
switch game.gameDate {
|
||||
case Date.todayET: dayLabel = "Today"
|
||||
case Date.tomorrowET: dayLabel = "Tomorrow"
|
||||
case Date.yesterdayET: dayLabel = "Yesterday"
|
||||
default:
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd"
|
||||
formatter.timeZone = TimeZone(identifier: "America/New_York")
|
||||
if let date = formatter.date(from: game.gameDate) {
|
||||
dayLabel = date.formatDateET(format: "EEE")
|
||||
} else {
|
||||
dayLabel = ""
|
||||
}
|
||||
}
|
||||
let time = game.startTimeET.trimmingCharacters(in: .whitespaces)
|
||||
let base = "\(dayLabel) \(time)"
|
||||
return state == .pre ? "\(base) (PRE)" : base
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user