Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,20 @@ struct BMMealCategory: Codable, Hashable {
let menuItems: [BMMenuItem]
}

struct BMMenuItemDetail: Codable, Hashable {
let servingSize: String?
let nutrition: [String: String]?
let ingredients: String?
let allergens: [String]?
}

struct BMMenuItem: Codable, Hashable {
let name: String
let icons: [BMMealIcon]
let menuId: String?
let itemId: String?
let dataLocation: String?
let recipeDetails: BMMenuItemDetail?
}

struct BMMealIcon: Codable, Hashable {
Expand Down
169 changes: 154 additions & 15 deletions berkeley-mobile/Home/Dining/DiningDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,12 @@ struct DiningDetailView: View {
.padding(.top, 25)
Spacer()
} else {
List {
ScrollView {
DiningItemsListView(selectedTabIndex: $selectedTabIndex,
categoriesAndMenuItems: categoriesAndMenuItems,
diningHall: diningHall,
filteredTabNames: filteredTabNames)
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
.contentMargins(.top, 0)
}
}
}
Expand Down Expand Up @@ -133,26 +130,23 @@ struct DiningItemsListView: View {
.padding(.top, 10)
}
ForEach(categoriesAndMenuItems, id: \.categoryName) { mealCategory in
Section {
ForEach(mealCategory.menuItems, id: \.itemId) { menuItem in
HStack {
Text(mealCategory.categoryName)
.font(Font(BMFont.bold(20)))
Spacer()
}
ForEach(mealCategory.menuItems, id: \.self) { menuItem in
NavigationLink(value: menuItem) {
DiningDetailRowView {
Text(menuItem.name)
.font(Font(BMFont.regular(15)))
DiningMenuItemIconsView(menuItem: menuItem)
}
}
} header: {
HStack {
Text(mealCategory.categoryName)
.font(Font(BMFont.bold(20)))
.font(.headline)
Spacer()
}
.buttonStyle(.plain)
}
}
}
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
}
}

Expand Down Expand Up @@ -203,3 +197,148 @@ struct DiningMenuItemIconsView: View {
}
}
}

// MARK: - DiningMenuItemDetailView

struct DiningMenuItemDetailView: View {

var menuItem: BMMenuItem

var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 20) {
if let menuDetail = menuItem.recipeDetails {
if let nutrition = menuDetail.nutrition, !nutrition.isEmpty {
let calories = Double(nutrition["Calories (kcal)"] ?? "0") ?? 0
let remainingNutrition = nutrition.filter { $0.key != "Calories (kcal)" }
let protein = Int(nutrition["Protein (g)"]?.filter { $0.isNumber } ?? "0") ?? 0
let carb = Int(nutrition["Carbohydrate (g)"]?.filter { $0.isNumber } ?? "0") ?? 0
let fat = (Int(nutrition["Total Lipid/Fat (g)"]?.filter { $0.isNumber } ?? "0") ?? 0) + (Int(nutrition["Trans Fat (g)"]?.filter { $0.isNumber } ?? "0") ?? 0)

if let servingSize = menuDetail.servingSize {
VStack(alignment: .leading, spacing: 8) {
Text("Serving Size")
.font(Font(BMFont.bold(20)))
DiningDetailRowView {
Text(servingSize)
.font(Font(BMFont.regular(15)))
}
}
}

if !remainingNutrition.isEmpty {
let macros: [(label: String, color: Color)] = [
("Protein", .green),
("Carb", .blue),
("Fat", .orange)
]
VStack(alignment: .leading, spacing: 8) {
Text("MacroNutrients")
.font(Font(BMFont.bold(20)))
VStack(alignment: .leading, spacing: 8) {
Text("Calories: \(String(format: "%.1f", calories))")
.font(Font(BMFont.regular(15)))
ProgressCapsule(protein: protein, fat: fat, carb: carb)
HStack {
Spacer()
ForEach(macros, id: \.label) { macro in
HStack(spacing: 6) {
Circle()
.fill(macro.color)
.frame(width: 12, height: 12)
Text(macro.label)
}
Spacer()
}
}
}
.padding()
.frame(maxWidth: .infinity)
.shadowfy()
}
}


if !remainingNutrition.isEmpty {
VStack(alignment: .leading, spacing: 8) {
Text("Nutrition Facts")
.font(Font(BMFont.bold(20)))
VStack(spacing: 8) {
ForEach(remainingNutrition.sorted(by: { $0.key < $1.key }), id: \.key) { key, value in
DiningDetailRowView {
HStack {
Text(key)
.font(Font(BMFont.regular(15)))
Spacer()
Text(value)
.font(Font(BMFont.medium(15)))
}
}
}
}
}
}
}

if let ingredients = menuDetail.ingredients {
VStack(alignment: .leading, spacing: 8) {
Text("Ingredients")
.font(Font(BMFont.bold(20)))
Text(ingredients)
.font(Font(BMFont.regular(15)))
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.shadowfy()
}

if let allergens = menuDetail.allergens, !allergens.isEmpty {
VStack(alignment: .leading, spacing: 8) {
Text("Allergens")
.font(Font(BMFont.bold(20)))
DiningDetailRowView {
Text(allergens.joined(separator: ", "))
.font(Font(BMFont.regular(15)))
}
}
}
} else {
Text("No Recipe Details")
.font(Font(BMFont.regular(15)))
.foregroundStyle(.secondary)
}
}
.padding()
}
.navigationTitle(menuItem.name)
.navigationBarTitleDisplayMode(.inline)
}
}

struct ProgressCapsule: View {
var protein: Int
var fat: Int
var carb: Int

var body: some View {
let total = protein + fat + carb
if total > 0 {
let segments: [(value: Int, color: Color)] = [
(protein, .green),
(carb, .blue),
(fat, .orange)
]
GeometryReader { geo in
HStack(spacing: 0) {
ForEach(segments, id: \.color) { segment in
Rectangle()
.fill(segment.color)
.frame(width: geo.size.width * Double(segment.value) / Double(total))
}
}
}
.clipShape(Capsule())
.frame(maxHeight: 70)
}
}
}
5 changes: 5 additions & 0 deletions berkeley-mobile/Home/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ struct HomeView: View {
LibraryDetailView(library: library)
.containerBackground(.clear, for: .navigation)
}
.navigationDestination(for: BMMenuItem.self) { menuItem in
DiningMenuItemDetailView(menuItem: menuItem)
.containerBackground(.clear, for: .navigation)
.environment(\.menuIconCache, menuIconCacheManager)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a recent merged commit, I added Factory, a dependency injection framework, which removes the need for injecting instances of objects into a view's environment. So if you pull and rebase against master, you can see the changes and you won't need .environment(\.menuIconCache, menuIconCacheManager) anymore.

}
}
}
}
Expand Down