import SkyRenderer import SwiftUI import UserNotifications /// First launch: what the app does, optional alerts, and location. Can be shown again from Settings. struct OnboardingScreen: View { @AppStorage(SettingsKey.onboarded) private var onboarded = false @AppStorage(SettingsKey.locationMode) private var locationMode = "current" @AppStorage(SettingsKey.cityName) private var cityName = "Chicago" @AppStorage(SettingsKey.cityLatitude) private var cityLatitude = 41.878 @AppStorage(SettingsKey.cityLongitude) private var cityLongitude = -87.630 @AppStorage(SettingsKey.dailySummary) private var dailySummary = false @State private var alerts = AuroraAlerts.shared @State private var page = 0 @State private var choosingCity = false @State private var notificationsDenied = false private let ink = Color(red: 0.95, green: 0.95, blue: 0.93) private let accent = Color(red: 0.88, green: 0.24, blue: 0.19) private let pages = 5 var body: some View { VStack(spacing: 0) { HStack { Spacer() if page < pages - 1 { Button("Skip") { withAnimation { page = pages - 1 } } .font(.subheadline.weight(.medium)) .foregroundStyle(ink.opacity(0.7)) .frame(minHeight: 44) } } .padding(.horizontal, 24) .frame(height: 44) TabView(selection: $page) { welcome.tag(0) sky.tag(1) beyond.tag(2) loop.tag(3) location.tag(4) } .tabViewStyle(.page(indexDisplayMode: .never)) HStack(spacing: 6) { ForEach(0.. some View) -> some View { ScrollView { VStack(alignment: .leading, spacing: 0) { content() } .padding(.horizontal, 24) .padding(.top, 24) .frame(maxWidth: 560, alignment: .leading) .frame(maxWidth: .infinity) } .scrollBounceBehavior(.basedOnSize) } private func heading(_ title: String, _ text: String) -> some View { VStack(alignment: .leading, spacing: 10) { Text(title).font(.title.weight(.medium)) Rectangle().fill(accent).frame(width: 48, height: 5) Text(text).font(.body).foregroundStyle(ink.opacity(0.75)) } .padding(.bottom, 24) } private func point(_ symbol: String, _ title: String, _ text: String) -> some View { HStack(alignment: .top, spacing: 14) { Image(systemName: symbol).font(.title3).frame(width: 30).foregroundStyle(accent).accessibilityHidden(true) VStack(alignment: .leading, spacing: 3) { Text(title).font(.headline) Text(text).font(.subheadline).foregroundStyle(ink.opacity(0.75)) } } .padding(.bottom, 18) .accessibilityElement(children: .combine) } private func toggle(_ title: String, _ text: String, isOn: Binding) -> some View { Toggle(isOn: isOn) { VStack(alignment: .leading, spacing: 3) { Text(title).font(.headline) Text(text).font(.subheadline).foregroundStyle(ink.opacity(0.75)) } } .padding(.bottom, 18) } private func permission() async -> Bool { let granted = (try? await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound])) ?? false notificationsDenied = !granted return granted } private func button(_ title: String, filled: Bool, action: @escaping () -> Void) -> some View { Button(action: action) { Text(title) .font(.headline) .frame(maxWidth: .infinity) .padding(.vertical, 14) .foregroundStyle(filled ? Color.black : ink) .background(filled ? ink : .clear, in: .rect(cornerRadius: 12)) .overlay(RoundedRectangle(cornerRadius: 12).stroke(ink.opacity(filled ? 0 : 0.5), lineWidth: 0.5)) } .buttonStyle(.plain) .padding(.top, 10) } }