mirror of
https://github.com/NinjaCheetah/RIT-Dining.git
synced 2026-03-05 13:35:29 -05:00
Added filtering options to dining menu view
There is now a filter button by the search bar in the menu view for locations! This opens a menu that allows you to filter by diets and to filter out any allergens that you need to avoid. These options are all written to UserDefaults, allowing you to set your options once and have them persist across menus and sessions. Also started on some refactors, these will be furthered in a later commit.
This commit is contained in:
143
RIT Dining/Views/Visiting Chefs/VisitingChefs.swift
Normal file
143
RIT Dining/Views/Visiting Chefs/VisitingChefs.swift
Normal file
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// VisitingChefs.swift
|
||||
// RIT Dining
|
||||
//
|
||||
// Created by Campbell on 9/8/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct IdentifiableURL: Identifiable {
|
||||
let id = UUID()
|
||||
let url: URL
|
||||
}
|
||||
|
||||
struct VisitingChefs: View {
|
||||
@Environment(DiningModel.self) var model
|
||||
@State private var locationsWithChefs: [DiningLocation] = []
|
||||
@State private var safariUrl: IdentifiableURL?
|
||||
@State private var chefDays: [String] = []
|
||||
@State private var focusedIndex: Int = 0
|
||||
|
||||
// Builds a list of days that each contain a list of dining locations that have visiting chefs to make displaying them
|
||||
// as easy as possible.
|
||||
private var locationsWithChefsByDay: [[DiningLocation]] {
|
||||
var locationsWithChefsByDay = [[DiningLocation]]()
|
||||
for day in model.locationsByDay {
|
||||
var locationsWithChefs = [DiningLocation]()
|
||||
for location in day {
|
||||
if let visitingChefs = location.visitingChefs, !visitingChefs.isEmpty {
|
||||
locationsWithChefs.append(location)
|
||||
}
|
||||
}
|
||||
locationsWithChefsByDay.append(locationsWithChefs)
|
||||
}
|
||||
return locationsWithChefsByDay
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
HStack(alignment: .center) {
|
||||
Button(action: {
|
||||
focusedIndex -= 1
|
||||
}) {
|
||||
Image(systemName: "chevron.left.circle")
|
||||
.font(.title)
|
||||
}
|
||||
.disabled(focusedIndex == 0)
|
||||
Spacer()
|
||||
Text("Visiting Chefs for \(visitingChefDateDisplay.string(from: model.daysRepresented[focusedIndex]))")
|
||||
.font(.title)
|
||||
.fontWeight(.semibold)
|
||||
.multilineTextAlignment(.center)
|
||||
Spacer()
|
||||
Button(action: {
|
||||
focusedIndex += 1
|
||||
}) {
|
||||
Image(systemName: "chevron.right.circle")
|
||||
.font(.title)
|
||||
}
|
||||
.disabled(focusedIndex == 6)
|
||||
}
|
||||
if locationsWithChefsByDay[focusedIndex].isEmpty {
|
||||
VStack {
|
||||
Divider()
|
||||
Text("No visiting chefs today")
|
||||
.font(.title2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
ForEach(locationsWithChefsByDay[focusedIndex], id: \.self) { location in
|
||||
if let visitingChefs = location.visitingChefs, !visitingChefs.isEmpty {
|
||||
VStack(alignment: .leading) {
|
||||
Divider()
|
||||
HStack(alignment: .center) {
|
||||
Text(location.name)
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
Spacer()
|
||||
Button(action: {
|
||||
safariUrl = IdentifiableURL(url: URL(string: location.mapsUrl)!)
|
||||
}) {
|
||||
Image(systemName: "map")
|
||||
.foregroundStyle(.accent)
|
||||
}
|
||||
}
|
||||
ForEach(visitingChefs, id: \.self) { chef in
|
||||
Spacer()
|
||||
Text(chef.name)
|
||||
.fontWeight(.semibold)
|
||||
HStack(spacing: 3) {
|
||||
if focusedIndex == 0 {
|
||||
switch chef.status {
|
||||
case .hereNow:
|
||||
Text("Here Now")
|
||||
.foregroundStyle(.green)
|
||||
case .gone:
|
||||
Text("Left For Today")
|
||||
.foregroundStyle(.red)
|
||||
case .arrivingLater:
|
||||
Text("Arriving Later")
|
||||
.foregroundStyle(.red)
|
||||
case .arrivingSoon:
|
||||
Text("Arriving Soon")
|
||||
.foregroundStyle(.orange)
|
||||
case .leavingSoon:
|
||||
Text("Leaving Soon")
|
||||
.foregroundStyle(.orange)
|
||||
}
|
||||
} else {
|
||||
Text("Arriving on \(weekdayFromDate.string(from: model.daysRepresented[focusedIndex]))")
|
||||
.foregroundStyle(.red)
|
||||
}
|
||||
Text("•")
|
||||
.foregroundStyle(.secondary)
|
||||
Text("\(dateDisplay.string(from: chef.openTime)) - \(dateDisplay.string(from: chef.closeTime))")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Text(chef.description)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
}
|
||||
.sheet(item: $safariUrl) { url in
|
||||
SafariView(url: url.url)
|
||||
}
|
||||
.refreshable {
|
||||
do {
|
||||
try await model.getHoursByDay()
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
VisitingChefs()
|
||||
}
|
||||
78
RIT Dining/Views/Visiting Chefs/VisitingChefsPush.swift
Normal file
78
RIT Dining/Views/Visiting Chefs/VisitingChefsPush.swift
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// VisitingChefsPush.swift
|
||||
// RIT Dining
|
||||
//
|
||||
// Created by Campbell on 10/1/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct VisitingChefPush: View {
|
||||
@AppStorage("visitingChefPushEnabled") var pushEnabled: Bool = false
|
||||
@Environment(NotifyingChefs.self) var notifyingChefs
|
||||
@State private var pushAllowed: Bool = false
|
||||
private let visitingChefs = [
|
||||
"California Rollin' Sushi",
|
||||
"D'Mangu",
|
||||
"Esan's Kitchen",
|
||||
"Halal n Out",
|
||||
"just chik'n",
|
||||
"KO-BQ",
|
||||
"Macarollin'",
|
||||
"P.H. Express",
|
||||
"Tandoor of India"
|
||||
]
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Form {
|
||||
Section(header: Text("Visiting Chef Notifications"),
|
||||
footer: Text(!pushAllowed ? "You must allow notifications from RIT Dining to use this feature." : "")) {
|
||||
Toggle(isOn: $pushEnabled) {
|
||||
Text("Notifications Enabled")
|
||||
}
|
||||
.disabled(!pushAllowed)
|
||||
}
|
||||
Section(footer: Text("Get notified when a specific visiting chef is on campus and where they'll be.")) {
|
||||
ForEach(visitingChefs, id: \.self) { chef in
|
||||
Toggle(isOn: Binding(
|
||||
get: {
|
||||
notifyingChefs.contains(chef)
|
||||
},
|
||||
set: { isOn in
|
||||
if isOn {
|
||||
notifyingChefs.add(chef)
|
||||
} else {
|
||||
notifyingChefs.remove(chef)
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Text(chef)
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(!pushAllowed || !pushEnabled)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
do {
|
||||
try await center.requestAuthorization(options: [.alert])
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
let settings = await center.notificationSettings()
|
||||
guard (settings.authorizationStatus == .authorized) else { pushEnabled = false; return }
|
||||
pushAllowed = true
|
||||
}
|
||||
}
|
||||
.navigationTitle("Notifications")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
VisitingChefPush()
|
||||
}
|
||||
Reference in New Issue
Block a user