diff options
Diffstat (limited to 'Solverv')
| -rw-r--r-- | Solverv/ContentView.swift | 53 | ||||
| -rw-r--r-- | Solverv/Views/InfoScreenView.swift | 200 |
2 files changed, 212 insertions, 41 deletions
diff --git a/Solverv/ContentView.swift b/Solverv/ContentView.swift index b82e2c7..38c6153 100644 --- a/Solverv/ContentView.swift +++ b/Solverv/ContentView.swift @@ -6,56 +6,27 @@ // import SwiftUI -import SwiftData struct ContentView: View { - @Environment(\.modelContext) private var modelContext - @Query private var items: [Item] - var body: some View { - NavigationSplitView { - List { - ForEach(items) { item in - NavigationLink { - Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") - } label: { - Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) - } - } - .onDelete(perform: deleteItems) - } - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - EditButton() - } - ToolbarItem { - Button(action: addItem) { - Label("Add Item", systemImage: "plus") - } - } - } - } detail: { - Text("Select an item") - } - } - - private func addItem() { - withAnimation { - let newItem = Item(timestamp: Date()) - modelContext.insert(newItem) - } - } + NavigationStack { + VStack { + Text("Solstice Countdown") + .font(.title) - private func deleteItems(offsets: IndexSet) { - withAnimation { - for index in offsets { - modelContext.delete(items[index]) + NavigationLink(destination: InfoScreenView()) { + Label("View Details", systemImage: "info.circle") + .padding() + .background(Color.blue) + .foregroundColor(.white) + .cornerRadius(8) + } } + .navigationTitle("Home") } } } #Preview { ContentView() - .modelContainer(for: Item.self, inMemory: true) } diff --git a/Solverv/Views/InfoScreenView.swift b/Solverv/Views/InfoScreenView.swift new file mode 100644 index 0000000..d876a45 --- /dev/null +++ b/Solverv/Views/InfoScreenView.swift @@ -0,0 +1,200 @@ +// +// InfoScreenView.swift +// Solverv +// +// Created by Ivar Løvlie on 23/03/2026. +// + +import SwiftUI +import Combine + +struct InfoScreenView: View { + @State private var upcomingEvents: [SolsticeEvent] = [] + @State private var nextEvent: SolsticeEvent? + @State private var progressData: (elapsed: Int, total: Int)? = nil + @State private var sunriseTime: Date? + @State private var sunsetTime: Date? + + var body: some View { + ScrollView { + VStack(spacing: 24) { + // Top section: Image + countdown + VStack(spacing: 16) { + if let next = nextEvent { + Image(next.season.assetName) + .resizable() + .scaledToFit() + .frame(height: 200) + .clipped() + + VStack(spacing: 8) { + Text(next.name) + .font(.title3) + + Text("\(next.daysUntil())") + .font(.system(.title, design: .default).weight(.bold)) + .foregroundColor(next.season.colorLight) + + Text("days remaining") + .font(.caption) + .foregroundColor(.secondary) + } + + if let progress = progressData { + ProgressView(value: Double(progress.elapsed) / Double(progress.total)) + .tint(next.season.colorLight) + } + } + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(12) + + // Middle section: Sun times + season info + VStack(spacing: 16) { + VStack(alignment: .leading, spacing: 12) { + Text("Today's Sun Times") + .font(.headline) + + HStack { + Label("Sunrise", systemImage: "sunrise.fill") + Spacer() + if let sunrise = sunriseTime { + Text(sunrise, style: .time) + } else { + Text("—").foregroundColor(.secondary) + } + } + + HStack { + Label("Sunset", systemImage: "sunset.fill") + Spacer() + if let sunset = sunsetTime { + Text(sunset, style: .time) + } else { + Text("—").foregroundColor(.secondary) + } + } + } + + Divider() + + if let next = nextEvent { + VStack(alignment: .leading, spacing: 8) { + Text("Current Season") + .font(.headline) + + HStack(spacing: 8) { + Circle() + .fill(next.season.colorLight) + .frame(width: 12, height: 12) + + VStack(alignment: .leading, spacing: 4) { + Text(next.season.displayName) + .font(.subheadline) + + Text(next.season.description) + .font(.caption) + .foregroundColor(.secondary) + } + } + } + } + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(12) + + // Bottom section: Upcoming events list + eventsListSection + } + .padding() + } + .navigationTitle("Solstices & Equinoxes") + .onAppear(perform: loadData) + .onReceive(Timer.publish(every: 60, on: .main, in: .common).autoconnect()) { _ in + loadData() + } + } + + private var eventsListSection: some View { + VStack(alignment: .leading, spacing: 12) { + Text("Upcoming Events") + .font(.headline) + + if upcomingEvents.isEmpty { + Text("No upcoming events") + .foregroundColor(.secondary) + .font(.caption) + } else { + VStack(spacing: 0) { + ForEach(Array(upcomingEvents.enumerated()), id: \.element.id) { index, event in + VStack(spacing: 0) { + HStack { + VStack(alignment: .leading, spacing: 2) { + Text(event.name) + .font(.subheadline) + .lineLimit(1) + + Text(formatEventDate(event.localDateTime())) + .font(.caption) + .foregroundColor(.secondary) + } + + Spacer() + + VStack(alignment: .trailing, spacing: 2) { + Text("\(event.daysUntil())d") + .font(.subheadline) + + Circle() + .fill(event.season.colorLight) + .frame(width: 12, height: 12) + } + } + .padding(.vertical, 8) + + if index < upcomingEvents.count - 1 { + Divider() + } + } + } + } + } + } + .padding() + .background(Color(.systemGray6)) + .cornerRadius(12) + } + + private func loadData() { + nextEvent = SolsticeData.shared.nextEvent() + upcomingEvents = SolsticeData.shared.upcomingEvents(count: 12) + progressData = SolsticeData.shared.progressToNextEvent() + + // Load sunrise/sunset from location if available + if let location = AppGroupManager.shared.getLocation() { + let sunTimes = SunTimes( + latitude: location.latitude, + longitude: location.longitude, + date: Date() + ) + sunriseTime = sunTimes.sunrise() + sunsetTime = sunTimes.sunset() + } + } + + private func formatEventDate(_ date: Date) -> String { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .short + formatter.timeZone = .current + return formatter.string(from: date) + } +} + +#Preview { + NavigationStack { + InfoScreenView() + } +} |
