mirror of
https://github.com/NinjaCheetah/RIT-Dining.git
synced 2026-03-05 05:25:29 -05:00
Replace all instances of "RIT Dining" with "TigerDine"
The project and some files were still named that way, so that's been fixed now. The bundle ID is stuck that way forever but oh well. Nobody will see that.
This commit is contained in:
143
TigerDine/Views/Visiting Chefs/VisitingChefs.swift
Normal file
143
TigerDine/Views/Visiting Chefs/VisitingChefs.swift
Normal file
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// VisitingChefs.swift
|
||||
// TigerDine
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
148
TigerDine/Views/Visiting Chefs/VisitingChefsPush.swift
Normal file
148
TigerDine/Views/Visiting Chefs/VisitingChefsPush.swift
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// VisitingChefsPush.swift
|
||||
// TigerDine
|
||||
//
|
||||
// Created by Campbell on 10/1/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct VisitingChefPush: View {
|
||||
@AppStorage("visitingChefPushEnabled") var pushEnabled: Bool = false
|
||||
@AppStorage("notificationOffset") var notificationOffset: Int = 2
|
||||
@Environment(DiningModel.self) var model
|
||||
@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 TigerDine to use this feature." : "")) {
|
||||
Toggle(isOn: $pushEnabled) {
|
||||
Text("Notifications Enabled")
|
||||
}
|
||||
.disabled(!pushAllowed)
|
||||
.onChange(of: pushEnabled) {
|
||||
if pushEnabled {
|
||||
Task {
|
||||
await model.scheduleAllPushes()
|
||||
}
|
||||
} else {
|
||||
Task {
|
||||
await model.cancelAllPushes()
|
||||
}
|
||||
}
|
||||
}
|
||||
Picker("Send Notifications", selection: $notificationOffset) {
|
||||
Text("1 Hour Before").tag(1)
|
||||
Text("2 Hours Before").tag(2)
|
||||
Text("3 Hours Before").tag(3)
|
||||
}
|
||||
.disabled(!pushAllowed || !pushEnabled)
|
||||
.onChange(of: notificationOffset) {
|
||||
Task {
|
||||
// If we changed the offset, we need to reschedule everything.
|
||||
await model.cancelAllPushes()
|
||||
await model.scheduleAllPushes()
|
||||
}
|
||||
}
|
||||
}
|
||||
Section(footer: Text("Get notified when and where a specific visiting chef will be on campus.")) {
|
||||
ForEach(visitingChefs, id: \.self) { chef in
|
||||
Toggle(isOn: Binding(
|
||||
get: {
|
||||
model.notifyingChefs.contains(chef)
|
||||
},
|
||||
set: { isOn in
|
||||
if isOn {
|
||||
model.notifyingChefs.add(chef)
|
||||
Task {
|
||||
await model.schedulePushesForChef(chef)
|
||||
}
|
||||
} else {
|
||||
model.notifyingChefs.remove(chef)
|
||||
model.visitingChefPushes.cancelPushesForChef(name: chef)
|
||||
}
|
||||
}
|
||||
)) {
|
||||
Text(chef)
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(!pushAllowed || !pushEnabled)
|
||||
#if DEBUG
|
||||
Section(header: Text("DEBUG - Scheduled Pushes")) {
|
||||
Button(action: {
|
||||
Task {
|
||||
await model.scheduleAllPushes()
|
||||
}
|
||||
}) {
|
||||
Text("Schedule All")
|
||||
}
|
||||
Button(action: {
|
||||
let uuids = model.visitingChefPushes.pushes.map(\.uuid)
|
||||
Task {
|
||||
await cancelVisitingChefNotifs(uuids: uuids)
|
||||
model.visitingChefPushes.pushes.removeAll()
|
||||
}
|
||||
}) {
|
||||
Text("Cancel All")
|
||||
}
|
||||
.tint(.red)
|
||||
ForEach(model.visitingChefPushes.pushes, id: \.uuid) { push in
|
||||
VStack(alignment: .leading) {
|
||||
Text("\(push.name) at \(push.location)")
|
||||
Text(push.uuid)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("\(push.startTime) - \(push.endTime)")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.swipeActions {
|
||||
Button(action: {
|
||||
Task {
|
||||
await cancelVisitingChefNotifs(uuids: [push.uuid])
|
||||
model.visitingChefPushes.pushes.remove(at: model.visitingChefPushes.pushes.firstIndex(of: push)!)
|
||||
}
|
||||
}) {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
do {
|
||||
try await center.requestAuthorization(options: [.alert, .sound])
|
||||
} 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