NinjaCheetah b51335768f
Mostly fixed multi opening period widgets!
- The opening status label on widgets should update properly on time now.
- Improved some of the logic related to determining opening statuses. Guards exist!
- Reduced the text sizes so that more of the location names fits in the widgets. Also ensures that the full opening times will be displayed (this always worked for 24-hour time but wasn't guaranteed to fit for 12-hour time).
2026-01-14 21:43:33 -05:00

89 lines
3.2 KiB
Swift

//
// HoursGague.swift
// TigerDineWidgets
//
// Created by Campbell on 1/8/26.
//
import SwiftUI
struct OpeningHoursGauge: View {
let diningTimes: [DiningTimes]?
let referenceTime: Date
private let dayDuration: TimeInterval = 86_400
private var barFillColor: Color {
if let diningTimes = diningTimes {
let openStatus = parseMultiOpenStatus(diningTimes: diningTimes, referenceTime: referenceTime)
switch openStatus {
case .open:
return Color.green
case .closed:
return Color.red
case .openingSoon, .closingSoon:
return Color.orange
}
} else {
return Color.red
}
}
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width
let barHeight: CGFloat = 16
let startOfToday = Calendar.current.startOfDay(for: referenceTime)
let startOfTomorrow = Calendar.current.date(byAdding: .day, value: 1, to: startOfToday)!
let nowX = position(for: referenceTime, start: startOfToday, width: width)
ZStack(alignment: .leading) {
Capsule()
.fill(Color.gray.opacity(0.25))
.frame(height: barHeight)
// We can skip drawing this entire capsule if the location is never open, since there would be no opening period
// to draw.
if let diningTimes = diningTimes {
// Need to iterate here to account for locations that have multiple opening periods (Gracie's/Brick City Cafe).
ForEach(diningTimes, id: \.self) { diningTime in
let openX = position(for: diningTime.openTime, start: startOfToday, width: width)
let closeX = position(
for: diningTime.closeTime,
start: diningTime.closeTime < diningTime.openTime ? startOfTomorrow : startOfToday,
width: width
)
Capsule()
.fill(
LinearGradient(
colors: [barFillColor.opacity(0.7), barFillColor],
startPoint: .leading,
endPoint: .trailing
)
)
.frame(width: max(0, closeX - openX), height: barHeight)
.offset(x: openX)
}
}
Circle()
.fill(Color.white)
.frame(width: 18, height: 18)
.shadow(radius: 1)
.offset(x: nowX - 5)
}
.frame(height: 20)
}
.frame(height: 20)
}
private func position(for date: Date, start: Date, width: CGFloat) -> CGFloat {
let seconds = date.timeIntervalSince(start)
let normalized = seconds / dayDuration
return normalized * width
}
}