aaffa3771c
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.
83 lines
2.7 KiB
Swift
83 lines
2.7 KiB
Swift
//
|
|
// MainView.swift
|
|
// IceGlass-iOS
|
|
//
|
|
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct MainView: View {
|
|
@Environment(ScoreboardViewModel.self) private var viewModel
|
|
@State private var showingSettings = false
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ScrollView {
|
|
LazyVStack(alignment: .leading, spacing: 16, pinnedViews: []) {
|
|
UpdatedHeader(
|
|
lastUpdated: viewModel.lastUpdated,
|
|
isRefreshing: viewModel.isRefreshing
|
|
)
|
|
.padding(.horizontal)
|
|
|
|
if !viewModel.currentRoundSeriesItems.isEmpty,
|
|
let round = viewModel.currentRoundNumber {
|
|
PlayoffRoundSection(
|
|
round: round,
|
|
items: viewModel.currentRoundSeriesItems
|
|
)
|
|
.padding(.horizontal)
|
|
}
|
|
|
|
let gameDays = viewModel.gamesByDate
|
|
if gameDays.isEmpty && viewModel.currentRoundSeriesItems.isEmpty {
|
|
emptyState
|
|
.padding(.horizontal)
|
|
.padding(.top, 40)
|
|
} else {
|
|
ForEach(gameDays, id: \.date) { gameDay in
|
|
GameDaySection(gameDay: gameDay)
|
|
.padding(.horizontal)
|
|
}
|
|
}
|
|
}
|
|
.padding(.vertical)
|
|
}
|
|
.refreshable {
|
|
await viewModel.refreshNow()
|
|
}
|
|
.navigationTitle("IceGlass")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button {
|
|
showingSettings = true
|
|
} label: {
|
|
Image(systemName: "gearshape")
|
|
}
|
|
.accessibilityLabel("Settings")
|
|
}
|
|
}
|
|
.sheet(isPresented: $showingSettings) {
|
|
SettingsSheet()
|
|
}
|
|
}
|
|
}
|
|
|
|
private var emptyState: some View {
|
|
VStack(spacing: 8) {
|
|
Image(systemName: "hockey.puck")
|
|
.font(.system(size: 40))
|
|
.foregroundStyle(.tertiary)
|
|
Text("No games scheduled")
|
|
.font(.headline)
|
|
.foregroundStyle(.secondary)
|
|
Text("Pull down to refresh")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.tertiary)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|