chore: initial ui wiring, swiftlint

Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
2026-08-09 11:10:09 -04:00
parent e1da561ff6
commit 461c516016
19 changed files with 140 additions and 42 deletions
@@ -44,4 +44,21 @@ public final class MainViewModel {
loadError = error
}
}
public var menuBarSystemImage: String {
forecast?.weatherCode.systemImageName ?? "questionmark.circle"
}
public var menuBarTitle: String? {
guard let temperature = forecast?.temperature else { return nil }
let unit: UnitTemperature = Locale.current.measurementSystem == .metric ? .celsius : .fahrenheit
let measurement = Measurement(value: temperature, unit: unit)
let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
formatter.numberFormatter.maximumFractionDigits = 0
return formatter.string(from: measurement)
}
}
@@ -1,3 +1,4 @@
import AppKit
import SwiftUI
public struct MainView: View {
@@ -10,10 +11,31 @@ public struct MainView: View {
}
public var body: some View {
NavigationStack {}
.frame(width: 320, height: 400)
.task {
await viewModel.load()
VStack(spacing: 0) {
HStack {
Button {
Task {
await viewModel.load(forceRefresh: true)
}
} label: {
Image(systemName: "arrow.clockwise")
}
.help("Refresh")
.buttonStyle(.borderless)
Spacer()
Button("Quit") {
NSApplication.shared.terminate(nil)
}
.buttonStyle(.borderless)
}
.padding(8)
Divider()
NavigationStack {}
}
.frame(width: 320, height: 400)
}
}
@@ -0,0 +1,25 @@
import SwiftUI
public struct MenuBarView: View {
private let viewModel: MainViewModel
public init(
viewModel: MainViewModel
) {
self.viewModel = viewModel
}
public var body: some View {
Group {
if let title = viewModel.menuBarTitle {
Label(title, systemImage: viewModel.menuBarSystemImage)
.labelStyle(.titleAndIcon)
} else {
Image(systemName: viewModel.menuBarSystemImage)
}
}
.task {
await viewModel.load()
}
}
}
@@ -0,0 +1,22 @@
import TempestasDomain
extension WeatherCode {
var systemImageName: String {
switch self {
case .clearSky: "sun.max.fill"
case .mainlyClear: "sun.min.fill"
case .partlyCloudy: "cloud.sun.fill"
case .overcast: "cloud.fill"
case .fog: "cloud.fog.fill"
case .drizzle: "cloud.drizzle.fill"
case .rain: "cloud.rain.fill"
case .freezingRain: "cloud.sleet.fill"
case .rainShowers: "cloud.heavyrain.fill"
case .snow: "cloud.snow.fill"
case .snowShowers: "cloud.snow.fill"
case .thunderstorm: "cloud.bolt.fill"
case .thunderstormWithHail: "cloud.bolt.rain.fill"
case .unknown: "questionmark.circle"
}
}
}
@@ -1,5 +1,5 @@
import Testing
@testable import TempestasPresentation
import Testing
@Test func example() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.