From 3f812495b02334112777eaf7a21e6fed84e04af9 Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:21:38 -0400 Subject: [PATCH] Added support for Gracie's multiple open times --- RIT Dining/ContentView.swift | 71 ++++++++++++++--------- RIT Dining/FetchData.swift | 108 +++++++++++++++++++++-------------- 2 files changed, 108 insertions(+), 71 deletions(-) diff --git a/RIT Dining/ContentView.swift b/RIT Dining/ContentView.swift index 9340b70..4548ef7 100644 --- a/RIT Dining/ContentView.swift +++ b/RIT Dining/ContentView.swift @@ -9,7 +9,7 @@ import SwiftUI struct Location: Hashable { let name: String - let todaysHours: String + let todaysHours: [String] let isOpen: openStatus } @@ -17,6 +17,7 @@ struct ContentView: View { @State private var isLoading = true @State private var rotationDegrees: Double = 0 @State private var diningLocations: [Location] = [] + @State private var lastRefreshed: Date? private var animation: Animation { .linear @@ -43,14 +44,15 @@ struct ContentView: View { // Parse the open status and times to create the hours string. If either time is missing, assume it has no openings // and use "Not Open Today". If there are times, then set those to be displayed. - let todaysHours: String - if diningInfo.openTime == .none || diningInfo.closeTime == .none { - todaysHours = "Not Open Today" + var todaysHours: [String] = [] + if diningInfo.diningTimes == .none { + todaysHours = ["Not Open Today"] } else { - print("Open:", display.string(from: diningInfo.openTime!), - "Close:", display.string(from: diningInfo.closeTime!)) - - todaysHours = "\(display.string(from: diningInfo.openTime!)) - \(display.string(from: diningInfo.closeTime!))" + for time in diningInfo.diningTimes! { + print("Open:", display.string(from: time.openTime), + "Close:", display.string(from: time.closeTime)) + todaysHours.append("\(display.string(from: time.openTime)) - \(display.string(from: time.closeTime))") + } } DispatchQueue.global().sync { newDiningLocations.append( @@ -60,6 +62,7 @@ struct ContentView: View { isOpen: diningInfo.open ) ) + lastRefreshed = Date() } } DispatchQueue.global().sync { @@ -91,26 +94,40 @@ struct ContentView: View { } .padding() } else { - Form { - ForEach(diningLocations, id: \.self) { location in - VStack(alignment: .leading) { - Text(location.name) - Text(location.todaysHours) - switch location.isOpen { - case .open: - Text("Open") - .foregroundStyle(.green) - case .closed: - Text("Closed") - .foregroundStyle(.red) - case .openingSoon: - Text("Opening Soon") - .foregroundStyle(.orange) - case .closingSoon: - Text("Closing Soon") - .foregroundStyle(.orange) + VStack() { + List { + Section(content: { + ForEach(diningLocations, id: \.self) { location in + VStack(alignment: .leading) { + Text(location.name) + ForEach(location.todaysHours, id: \.self) { hours in + Text(hours) + } + switch location.isOpen { + case .open: + Text("Open") + .foregroundStyle(.green) + case .closed: + Text("Closed") + .foregroundStyle(.red) + case .openingSoon: + Text("Opening Soon") + .foregroundStyle(.orange) + case .closingSoon: + Text("Closing Soon") + .foregroundStyle(.orange) + } + } } - } + }, footer: { + if let lastRefreshed { + VStack(alignment: .center) { + Text("Last refreshed: \(lastRefreshed.formatted())") + .foregroundStyle(.secondary) + .frame(maxWidth: .infinity) + } + } + }) } } .navigationTitle("RIT Dining") diff --git a/RIT Dining/FetchData.swift b/RIT Dining/FetchData.swift index d1fc674..bc9a5df 100644 --- a/RIT Dining/FetchData.swift +++ b/RIT Dining/FetchData.swift @@ -79,11 +79,15 @@ enum openStatus { case closingSoon } +struct DiningTimes: Equatable { + let openTime: Date + let closeTime: Date +} + struct DiningInfo { let id: Int let name: String - let openTime: Date? - let closeTime: Date? + let diningTimes: [DiningTimes]? let open: openStatus } @@ -95,13 +99,12 @@ func getLocationInfo(location: DiningLocation) -> DiningInfo { return DiningInfo( id: location.id, name: location.name, - openTime: .none, - closeTime: .none, + diningTimes: .none, open: .closed) } - let openString: String - let closeString: String + var openStrings: [String] = [] + var closeStrings: [String] = [] // Dining locations have a regular schedule, but then they also have exceptions listed for days like weekends or holidays. If there // are exceptions, use those times for the day, otherwise we can just use the default times. @@ -111,67 +114,84 @@ func getLocationInfo(location: DiningLocation) -> DiningInfo { return DiningInfo( id: location.id, name: location.name, - openTime: .none, - closeTime: .none, + diningTimes: .none, open: .closed) } - openString = location.events[0].exceptions![0].startTime - closeString = location.events[0].exceptions![0].endTime + openStrings.append(location.events[0].exceptions![0].startTime) + closeStrings.append(location.events[0].exceptions![0].endTime) } else { - openString = location.events[0].startTime - closeString = location.events[0].endTime + for event in location.events { + openStrings.append(event.startTime) + closeStrings.append(event.endTime) + } } // I hate all of this date component nonsense. - let openParts = openString.split(separator: ":").map { Int($0) ?? 0 } - let openTimeComponents = DateComponents(hour: openParts[0], minute: openParts[1], second: openParts[2]) - - let closeParts = closeString.split(separator: ":").map { Int($0) ?? 0 } - let closeTimeComponents = DateComponents(hour: closeParts[0], minute: closeParts[1], second: closeParts[2]) + var openDates: [Date] = [] + var closeDates: [Date] = [] let calendar = Calendar.current let now = Date() - let openDate = calendar.date( - bySettingHour: openTimeComponents.hour!, - minute: openTimeComponents.minute!, - second: openTimeComponents.second!, - of: now)! - - var closeDate = calendar.date( - bySettingHour: closeTimeComponents.hour!, - minute: closeTimeComponents.minute!, - second: closeTimeComponents.second!, - of: now)! + for i in 0..= openDate && now <= closeDate) - let openStatus: openStatus - if isOpen { - if closeDate < calendar.date(byAdding: .minute, value: 30, to: now)! { - openStatus = .closingSoon + var openStatus: openStatus = .closed + for i in 0..= openDates[i] && now <= closeDates[i]) + if isOpen { + if closeDates[i] < calendar.date(byAdding: .minute, value: 30, to: now)! { + openStatus = .closingSoon + break + } else { + openStatus = .open + break + } } else { - openStatus = .open - } - } else { - if openDate < calendar.date(byAdding: .minute, value: 30, to: now)! { - openStatus = .openingSoon - } else { - openStatus = .closed + if openDates[i] < calendar.date(byAdding: .minute, value: 30, to: now)! { + openStatus = .openingSoon + break + } else { + openStatus = .closed + } } } + var diningTimes: [DiningTimes] = [] + for i in 0..