RIT-Dining/TigerDine/Views/LocationList.swift
NinjaCheetah d3d060b5e2
Started work on refactors
- The favorites model now lives inside of the base dining model, since it was only ever used in places where the main dining model was also available and is only relevant when the dining model is available.
- Removed unnecessary instances of models that were going unused.
- Moved the favorite/map/menu buttons in the top right of the DetailView into the right side toolbar.
  - This frees up a good bit of space at the top of the DetailView and looks cleaner, especially with iOS 26's new toolbar style.
- Actually added a copyright string to the about screen.
More refactors, both internally and for the UI, will be coming soon.
2026-01-07 19:29:30 -05:00

117 lines
4.9 KiB
Swift

//
// LocationList.swift
// TigerDine
//
// Created by Campbell on 10/1/25.
//
import SwiftUI
// This view handles the actual location list, because having it inside ContentView was too complex (both visually and for the
// type checker too, apparently).
struct LocationList: View {
@Binding var diningLocations: [DiningLocation]
@Binding var openLocationsFirst: Bool
@Binding var openLocationsOnly: Bool
@Binding var searchText: String
@Environment(DiningModel.self) var model
// The dining locations need to be sorted before being displayed. Favorites should always be shown first, followed by non-favorites.
// Afterwards, filters the sorted list based on any current search text and the "open locations only" filtering option.
private var filteredLocations: [DiningLocation] {
var newLocations = diningLocations
// Because "The Commons" should be C for "Commons" and not T for "The".
func removeThe(_ name: String) -> String {
let lowercased = name.lowercased()
if lowercased.hasPrefix("the ") {
return String(name.dropFirst(4))
}
return name
}
newLocations.sort { firstLoc, secondLoc in
let firstLocIsFavorite = model.favorites.contains(firstLoc)
let secondLocIsFavorite = model.favorites.contains(secondLoc)
// Favorites get priority!
if firstLocIsFavorite != secondLocIsFavorite {
return firstLocIsFavorite && !secondLocIsFavorite
}
// Additional sorting rule that sorts open locations ahead of closed locations, if enabled.
if openLocationsFirst {
let firstIsOpen = (firstLoc.open == .open || firstLoc.open == .closingSoon)
let secondIsOpen = (secondLoc.open == .open || secondLoc.open == .closingSoon)
if firstIsOpen != secondIsOpen {
return firstIsOpen && !secondIsOpen
}
}
return removeThe(firstLoc.name)
.localizedCaseInsensitiveCompare(removeThe(secondLoc.name)) == .orderedAscending
}
// Search/open only filtering step.
newLocations = newLocations.filter { location in
let searchedLocations = searchText.isEmpty || location.name.localizedCaseInsensitiveContains(searchText)
let openLocations = !openLocationsOnly || location.open == .open || location.open == .closingSoon
return searchedLocations && openLocations
}
return newLocations
}
var body: some View {
ForEach(filteredLocations, id: \.self) { location in
NavigationLink(destination: DetailView(locationId: location.id)) {
VStack(alignment: .leading) {
HStack {
Text(location.name)
if model.favorites.contains(location) {
Image(systemName: "star.fill")
.foregroundStyle(.yellow)
}
}
switch location.open {
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)
}
if let times = location.diningTimes, !times.isEmpty {
ForEach(times, id: \.self) { time in
Text("\(dateDisplay.string(from: time.openTime)) - \(dateDisplay.string(from: time.closeTime))")
.foregroundStyle(.secondary)
}
} else {
Text("Not Open Today")
.foregroundStyle(.secondary)
}
}
}
.swipeActions {
Button(action: {
withAnimation {
if model.favorites.contains(location) {
model.favorites.remove(location)
} else {
model.favorites.add(location)
}
}
}) {
if model.favorites.contains(location) {
Label("Unfavorite", systemImage: "star")
} else {
Label("Favorite", systemImage: "star")
}
}
.tint(model.favorites.contains(location) ? .yellow : nil)
}
}
}
}