mirror of
https://github.com/NinjaCheetah/RIT-Dining.git
synced 2026-01-17 12:05:57 -05:00
Fixed multi opening period locations in widgets
Widgets for locations with multiple opening periods will still only display the first time span, but the bar will now show multiple filled in sections and the opening status label will correctly represent both periods.
This commit is contained in:
parent
f78de2f6ff
commit
71c37749e3
@ -1,5 +1,5 @@
|
||||
//
|
||||
// SharedComponents.swift
|
||||
// SharedUtils.swift
|
||||
// TigerDine
|
||||
//
|
||||
// Created by Campbell on 9/8/25.
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Gets the current open status of a location based on the open time and close time.
|
||||
func parseOpenStatus(openTime: Date, closeTime: Date) -> OpenStatus {
|
||||
// This can probably be done a little cleaner but it's okay for now. If the location is open but the close date is within the next
|
||||
// 30 minutes, label it as closing soon, and do the opposite if it's closed but the open date is within the next 30 minutes.
|
||||
@ -31,6 +32,25 @@ func parseOpenStatus(openTime: Date, closeTime: Date) -> OpenStatus {
|
||||
return openStatus
|
||||
}
|
||||
|
||||
/// Gets the current open status of a location with multiple opening periods based on all of its open and close times.
|
||||
func parseMultiOpenStatus(diningTimes: [DiningTimes]?) -> OpenStatus {
|
||||
var openStatus: OpenStatus = .closed
|
||||
if let diningTimes = diningTimes, !diningTimes.isEmpty {
|
||||
for i in diningTimes.indices {
|
||||
openStatus = parseOpenStatus(openTime: diningTimes[i].openTime, closeTime: diningTimes[i].closeTime)
|
||||
// If the first event pass came back closed, loop again in case a later event has a different status. This is mostly to
|
||||
// accurately catch Gracie's/Brick City Cafe's multiple open periods each day.
|
||||
if openStatus != .closed {
|
||||
break
|
||||
}
|
||||
}
|
||||
return openStatus
|
||||
} else {
|
||||
return .closed
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the JSON responses from the TigerCenter API into the format used throughout TigerDine.
|
||||
func parseLocationInfo(location: DiningLocationParser, forDate: Date?) -> DiningLocation {
|
||||
print("beginning parse for \(location.name)")
|
||||
|
||||
@ -277,20 +297,8 @@ extension DiningLocation {
|
||||
// Updates the open status of a location and of its visiting chefs, so that the labels in the UI update automatically as
|
||||
// time progresses and locations open/close/etc.
|
||||
mutating func updateOpenStatus() {
|
||||
var openStatus: OpenStatus = .closed
|
||||
if let diningTimes = diningTimes, !diningTimes.isEmpty {
|
||||
for i in diningTimes.indices {
|
||||
openStatus = parseOpenStatus(openTime: diningTimes[i].openTime, closeTime: diningTimes[i].closeTime)
|
||||
// If the first event pass came back closed, loop again in case a later event has a different status. This is mostly to
|
||||
// accurately catch Gracie's multiple open periods each day.
|
||||
if openStatus != .closed {
|
||||
break
|
||||
}
|
||||
}
|
||||
self.open = openStatus
|
||||
} else {
|
||||
self.open = .closed
|
||||
}
|
||||
// Gets the open status with the multi opening period compatible function.
|
||||
self.open = parseMultiOpenStatus(diningTimes: diningTimes)
|
||||
if let visitingChefs = visitingChefs, !visitingChefs.isEmpty {
|
||||
let now = Date()
|
||||
for i in visitingChefs.indices {
|
||||
@ -55,7 +55,10 @@
|
||||
374CDA742F10B24F00D8C50A /* Exceptions for "Shared" folder in "TigerDineWidgets" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
SharedComponents.swift,
|
||||
Components/SharedUtils.swift,
|
||||
Components/TigerCenterParsers.swift,
|
||||
Data/Static/FDMPMealPeriods.swift,
|
||||
Data/Static/TCtoFDMPMap.swift,
|
||||
Types/TigerCenterTypes.swift,
|
||||
);
|
||||
target = 374CDA572F10A19500D8C50A /* TigerDineWidgets */;
|
||||
@ -289,7 +292,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = TigerDineWidgets/TigerDineWidgets.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = TigerDineWidgets/Info.plist;
|
||||
@ -322,7 +325,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = TigerDineWidgets/TigerDineWidgets.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = TigerDineWidgets/Info.plist;
|
||||
@ -478,7 +481,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = TigerDine/TigerDine.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@ -515,7 +518,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = TigerDine/TigerDine.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 27;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
|
||||
@ -8,28 +8,21 @@
|
||||
import SwiftUI
|
||||
|
||||
struct OpeningHoursGauge: View {
|
||||
let openTime: Date?
|
||||
let closeTime: Date?
|
||||
let diningTimes: [DiningTimes]?
|
||||
let now: Date
|
||||
|
||||
private let dayDuration: TimeInterval = 86_400
|
||||
|
||||
private var barFillColor: Color {
|
||||
let calendar = Calendar.current
|
||||
|
||||
if let openTime = openTime, let closeTime = closeTime {
|
||||
if now >= openTime && now <= closeTime {
|
||||
if closeTime == calendar.date(byAdding: .day, value: 1, to: openTime)! {
|
||||
return Color.green
|
||||
} else if closeTime < calendar.date(byAdding: .minute, value: 30, to: now)! {
|
||||
return Color.orange
|
||||
} else {
|
||||
return Color.green
|
||||
}
|
||||
} else if openTime <= calendar.date(byAdding: .minute, value: 30, to: now)! && closeTime > now {
|
||||
return Color.orange
|
||||
} else {
|
||||
if let diningTimes = diningTimes {
|
||||
let openStatus = parseMultiOpenStatus(diningTimes: diningTimes)
|
||||
switch openStatus {
|
||||
case .open:
|
||||
return Color.green
|
||||
case .closed:
|
||||
return Color.red
|
||||
case .openingSoon, .closingSoon:
|
||||
return Color.orange
|
||||
}
|
||||
} else {
|
||||
return Color.red
|
||||
@ -53,24 +46,27 @@ struct OpeningHoursGauge: View {
|
||||
|
||||
// We can skip drawing this entire capsule if the location is never open, since there would be no opening period
|
||||
// to draw.
|
||||
if let openTime = openTime, let closeTime = closeTime {
|
||||
let openX = position(for: openTime, start: startOfToday, width: width)
|
||||
let closeX = position(
|
||||
for: closeTime,
|
||||
start: closeTime < openTime ? startOfTomorrow : startOfToday,
|
||||
width: width
|
||||
)
|
||||
|
||||
Capsule()
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [barFillColor.opacity(0.7), barFillColor],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
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
|
||||
)
|
||||
.frame(width: max(0, closeX - openX), height: barHeight)
|
||||
.offset(x: openX)
|
||||
|
||||
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()
|
||||
|
||||
@ -18,8 +18,9 @@ struct Provider: AppIntentTimelineProvider {
|
||||
return OpenEntry(
|
||||
date: Date(),
|
||||
name: "Select a Location",
|
||||
openTime: startOfToday,
|
||||
closeTime: startOfTomorrow
|
||||
diningTimes: [
|
||||
DiningTimes(openTime: startOfToday, closeTime: startOfTomorrow)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
@ -44,16 +45,15 @@ struct Provider: AppIntentTimelineProvider {
|
||||
|
||||
let updateDates = buildUpdateSchedule(
|
||||
now: Date(),
|
||||
open: baseEntry.openTime,
|
||||
close: baseEntry.closeTime
|
||||
open: baseEntry.diningTimes?.first!.openTime,
|
||||
close: baseEntry.diningTimes?.first!.closeTime
|
||||
)
|
||||
|
||||
let entries = updateDates.map {
|
||||
OpenEntry(
|
||||
date: $0,
|
||||
name: baseEntry.name,
|
||||
openTime: baseEntry.openTime,
|
||||
closeTime: baseEntry.closeTime
|
||||
diningTimes: baseEntry.diningTimes
|
||||
)
|
||||
}
|
||||
|
||||
@ -79,8 +79,7 @@ struct Provider: AppIntentTimelineProvider {
|
||||
return OpenEntry(
|
||||
date: Date(),
|
||||
name: location.name,
|
||||
openTime: location.diningTimes?.first?.openTime,
|
||||
closeTime: location.diningTimes?.first?.closeTime
|
||||
diningTimes: location.diningTimes
|
||||
)
|
||||
}
|
||||
|
||||
@ -117,8 +116,7 @@ struct Provider: AppIntentTimelineProvider {
|
||||
struct OpenEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let name: String
|
||||
let openTime: Date?
|
||||
let closeTime: Date?
|
||||
let diningTimes: [DiningTimes]?
|
||||
}
|
||||
|
||||
struct OpenWidgetEntryView : View {
|
||||
@ -133,32 +131,28 @@ struct OpenWidgetEntryView : View {
|
||||
.fontWeight(.bold)
|
||||
|
||||
// Should maybe try to unify this with the almost-identical UI code in DetailView.
|
||||
if let openTime = entry.openTime, let closeTime = entry.closeTime {
|
||||
if entry.date >= openTime && entry.date <= closeTime {
|
||||
if closeTime == calendar.date(byAdding: .day, value: 1, to: openTime)! {
|
||||
Text("Open")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.green)
|
||||
} else if closeTime < calendar.date(byAdding: .minute, value: 30, to: entry.date)! {
|
||||
Text("Closing Soon")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.orange)
|
||||
} else {
|
||||
Text("Open")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.green)
|
||||
}
|
||||
} else if openTime <= calendar.date(byAdding: .minute, value: 30, to: entry.date)! && closeTime > entry.date {
|
||||
Text("Opening Soon")
|
||||
if let diningTimes = entry.diningTimes {
|
||||
let openStatus = parseMultiOpenStatus(diningTimes: diningTimes)
|
||||
switch openStatus {
|
||||
case .open:
|
||||
Text("Open")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.orange)
|
||||
} else {
|
||||
.foregroundStyle(.green)
|
||||
case .closed:
|
||||
Text("Closed")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.red)
|
||||
case .openingSoon:
|
||||
Text("Opening Soon")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.orange)
|
||||
case .closingSoon:
|
||||
Text("Closing Soon")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.orange)
|
||||
}
|
||||
|
||||
Text("\(dateDisplay.string(from: openTime)) - \(dateDisplay.string(from: closeTime))")
|
||||
Text("\(dateDisplay.string(from: diningTimes[0].openTime)) - \(dateDisplay.string(from: diningTimes[0].closeTime))")
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Text("Closed")
|
||||
@ -172,8 +166,7 @@ struct OpenWidgetEntryView : View {
|
||||
Spacer()
|
||||
|
||||
OpeningHoursGauge(
|
||||
openTime: entry.openTime,
|
||||
closeTime: entry.closeTime,
|
||||
diningTimes: entry.diningTimes,
|
||||
now: entry.date
|
||||
)
|
||||
}
|
||||
@ -201,6 +194,24 @@ struct HoursWidget: Widget {
|
||||
#Preview(as: .systemSmall) {
|
||||
HoursWidget()
|
||||
} timeline: {
|
||||
OpenEntry(date: .now, name: "Beanz", openTime: Date(timeIntervalSince1970: 1767963600), closeTime: Date(timeIntervalSince1970: 1767988800))
|
||||
OpenEntry(date: Date(timeIntervalSince1970: 1767978000), name: "Beanz", openTime: Date(timeIntervalSince1970: 1767963600), closeTime: Date(timeIntervalSince1970: 1767988800))
|
||||
OpenEntry(
|
||||
date: .now,
|
||||
name: "Beanz",
|
||||
diningTimes: [
|
||||
DiningTimes(
|
||||
openTime: Date(timeIntervalSince1970: 1767963600),
|
||||
closeTime: Date(timeIntervalSince1970: 1767988800)
|
||||
)
|
||||
]
|
||||
)
|
||||
OpenEntry(
|
||||
date: Date(timeIntervalSince1970: 1767978000),
|
||||
name: "Beanz",
|
||||
diningTimes: [
|
||||
DiningTimes(
|
||||
openTime: Date(timeIntervalSince1970: 1767963600),
|
||||
closeTime: Date(timeIntervalSince1970: 1767988800)
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user