mirror of
https://github.com/NinjaCheetah/RIT-Dining.git
synced 2025-12-02 01:21:35 -05:00
Improvements to FD MealPlanner integration
- Detailed nutrition facts are now shown on the menu item view - Fixed The College Grind not having a "View Menu" button (IDs were mapped incorrectly) - Menu items are now sorted alphabetically - "Allergens" section now hidden on the menu item view if the item contains no allergens - Duplicate items will no longer be added to the menu item list, so no more issues with items randomly appearing and disappearing (thanks FD MealPlanner for having duplicates in the first place)
This commit is contained in:
parent
c7639de06b
commit
85aa9e636d
@ -265,7 +265,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "RIT Dining/RIT Dining.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "RIT Dining/RIT Dining.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -300,7 +300,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "RIT Dining/RIT Dining.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "RIT Dining/RIT Dining.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
DEVELOPMENT_TEAM = 5GF7GKNTK4;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
|||||||
@ -16,6 +16,11 @@ func parseFDMealPlannerMenu(menu: FDMealsParser) -> [FDMenuItem] {
|
|||||||
// will only be a single index to operate on.
|
// will only be a single index to operate on.
|
||||||
if let allMenuRecipes = menu.result[0].allMenuRecipes {
|
if let allMenuRecipes = menu.result[0].allMenuRecipes {
|
||||||
for recipe in allMenuRecipes {
|
for recipe in allMenuRecipes {
|
||||||
|
// Prevent duplicate items from being added, because for some reason the exact same item with the exact same information
|
||||||
|
// might be included in FD MealPlanner more than once.
|
||||||
|
if menuItems.contains(where: { $0.id == recipe.componentId }) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// englishAlternateName holds the proper name of the item, but it's blank for some items for some reason. If that's the
|
// englishAlternateName holds the proper name of the item, but it's blank for some items for some reason. If that's the
|
||||||
// case, then we should fall back on componentName, which is less user-friendly but works as a backup.
|
// case, then we should fall back on componentName, which is less user-friendly but works as a backup.
|
||||||
let realName = if recipe.englishAlternateName != "" {
|
let realName = if recipe.englishAlternateName != "" {
|
||||||
@ -23,7 +28,7 @@ func parseFDMealPlannerMenu(menu: FDMealsParser) -> [FDMenuItem] {
|
|||||||
} else {
|
} else {
|
||||||
recipe.componentName
|
recipe.componentName
|
||||||
}
|
}
|
||||||
let allergens = recipe.allergenName.components(separatedBy: ",")
|
let allergens = recipe.allergenName != "" ? recipe.allergenName.components(separatedBy: ",") : []
|
||||||
// Get the list of dietary markers (Vegan, Vegetarian, Pork, Beef), and drop "Vegetarian" if "Vegan" is also included since
|
// Get the list of dietary markers (Vegan, Vegetarian, Pork, Beef), and drop "Vegetarian" if "Vegan" is also included since
|
||||||
// that's kinda redundant.
|
// that's kinda redundant.
|
||||||
var dietaryMarkers = recipe.recipeProductDietaryName != "" ? recipe.recipeProductDietaryName.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) } : []
|
var dietaryMarkers = recipe.recipeProductDietaryName != "" ? recipe.recipeProductDietaryName.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) } : []
|
||||||
@ -31,6 +36,23 @@ func parseFDMealPlannerMenu(menu: FDMealsParser) -> [FDMenuItem] {
|
|||||||
dietaryMarkers.remove(at: dietaryMarkers.firstIndex(of: "Vegetarian")!)
|
dietaryMarkers.remove(at: dietaryMarkers.firstIndex(of: "Vegetarian")!)
|
||||||
}
|
}
|
||||||
let calories = Int(Double(recipe.calories)!.rounded())
|
let calories = Int(Double(recipe.calories)!.rounded())
|
||||||
|
// Collect and organize all the nutritional entries. I ordered them based off how they were ordered in the nutritional
|
||||||
|
// facts panel on the side of the bag of goldfish that lives on my desk, so presumably they're ordered correctly.
|
||||||
|
let nutritionalEntries = [
|
||||||
|
FDNutritionalEntry(type: "Total Fat", amount: Double(recipe.fat) ?? 0.0, unit: recipe.fatUOM),
|
||||||
|
FDNutritionalEntry(type: "Saturated Fat", amount: Double(recipe.saturatedFat) ?? 0.0, unit: recipe.saturatedFatUOM),
|
||||||
|
FDNutritionalEntry(type: "Trans Fat", amount: Double(recipe.transFattyAcid) ?? 0.0, unit: recipe.transFattyAcidUOM),
|
||||||
|
FDNutritionalEntry(type: "Cholesterol", amount: Double(recipe.cholesterol) ?? 0.0, unit: recipe.cholesterolUOM),
|
||||||
|
FDNutritionalEntry(type: "Sodium", amount: Double(recipe.sodium) ?? 0.0, unit: recipe.sodiumUOM),
|
||||||
|
FDNutritionalEntry(type: "Total Carbohydrates", amount: Double(recipe.carbohydrates) ?? 0.0, unit: recipe.carbohydratesUOM),
|
||||||
|
FDNutritionalEntry(type: "Dietary Fiber", amount: Double(recipe.dietaryFiber) ?? 0.0, unit: recipe.dietaryFiberUOM),
|
||||||
|
FDNutritionalEntry(type: "Total Sugars", amount: Double(recipe.totalSugars) ?? 0.0, unit: recipe.totalSugarsUOM),
|
||||||
|
FDNutritionalEntry(type: "Protein", amount: Double(recipe.protein) ?? 0.0, unit: recipe.proteinUOM),
|
||||||
|
FDNutritionalEntry(type: "Calcium", amount: Double(recipe.calcium) ?? 0.0, unit: recipe.calciumUOM),
|
||||||
|
FDNutritionalEntry(type: "Iron", amount: Double(recipe.iron) ?? 0.0, unit: recipe.ironUOM),
|
||||||
|
FDNutritionalEntry(type: "Vitamin A", amount: Double(recipe.vitaminA) ?? 0.0, unit: recipe.vitaminAUOM),
|
||||||
|
FDNutritionalEntry(type: "Vitamin C", amount: Double(recipe.vitaminC) ?? 0.0, unit: recipe.vitaminCUOM),
|
||||||
|
]
|
||||||
|
|
||||||
let newItem = FDMenuItem(
|
let newItem = FDMenuItem(
|
||||||
id: recipe.componentId,
|
id: recipe.componentId,
|
||||||
@ -39,6 +61,7 @@ func parseFDMealPlannerMenu(menu: FDMealsParser) -> [FDMenuItem] {
|
|||||||
category: recipe.category,
|
category: recipe.category,
|
||||||
allergens: allergens,
|
allergens: allergens,
|
||||||
calories: calories,
|
calories: calories,
|
||||||
|
nutritionalEntries: nutritionalEntries,
|
||||||
dietaryMarkers: dietaryMarkers,
|
dietaryMarkers: dietaryMarkers,
|
||||||
ingredients: recipe.ingredientStatement,
|
ingredients: recipe.ingredientStatement,
|
||||||
price: recipe.sellingPrice,
|
price: recipe.sellingPrice,
|
||||||
|
|||||||
@ -20,6 +20,6 @@ let tCtoFDMPMap: [Int: (Int, Int)] = [
|
|||||||
441: (11, 11), // Loaded Latke
|
441: (11, 11), // Loaded Latke
|
||||||
38: (12, 12), // Midnight Oil
|
38: (12, 12), // Midnight Oil
|
||||||
26: (14, 4), // RITZ
|
26: (14, 4), // RITZ
|
||||||
9041: (18, 17), // The College Grind
|
35: (18, 17), // The College Grind
|
||||||
24: (15, 14), // The Commons
|
24: (15, 14), // The Commons
|
||||||
]
|
]
|
||||||
|
|||||||
@ -54,6 +54,32 @@ struct FDMealsParser: Decodable, Hashable {
|
|||||||
let category: String
|
let category: String
|
||||||
let allergenName: String
|
let allergenName: String
|
||||||
let calories: String
|
let calories: String
|
||||||
|
let carbohydrates: String
|
||||||
|
let carbohydratesUOM: String
|
||||||
|
let dietaryFiber: String
|
||||||
|
let dietaryFiberUOM: String
|
||||||
|
let fat: String
|
||||||
|
let fatUOM: String
|
||||||
|
let protein: String
|
||||||
|
let proteinUOM: String
|
||||||
|
let saturatedFat: String
|
||||||
|
let saturatedFatUOM: String
|
||||||
|
let transFattyAcid: String
|
||||||
|
let transFattyAcidUOM: String
|
||||||
|
let calcium: String
|
||||||
|
let calciumUOM: String
|
||||||
|
let cholesterol: String
|
||||||
|
let cholesterolUOM: String
|
||||||
|
let iron: String
|
||||||
|
let ironUOM: String
|
||||||
|
let sodium: String
|
||||||
|
let sodiumUOM: String
|
||||||
|
let vitaminA: String
|
||||||
|
let vitaminAUOM: String
|
||||||
|
let vitaminC: String
|
||||||
|
let vitaminCUOM: String
|
||||||
|
let totalSugars: String
|
||||||
|
let totalSugarsUOM: String
|
||||||
let recipeProductDietaryName: String
|
let recipeProductDietaryName: String
|
||||||
let ingredientStatement: String
|
let ingredientStatement: String
|
||||||
let sellingPrice: Double
|
let sellingPrice: Double
|
||||||
@ -74,6 +100,13 @@ struct FDMealsParser: Decodable, Hashable {
|
|||||||
let result: [Result]
|
let result: [Result]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single nutritional entry, including the amount and the unit. Used over a tuple for hashable purposes.
|
||||||
|
struct FDNutritionalEntry: Hashable {
|
||||||
|
let type: String
|
||||||
|
let amount: Double
|
||||||
|
let unit: String
|
||||||
|
}
|
||||||
|
|
||||||
/// A single menu item, stripped down and reorganized to a format that actually makes sense for me to use in the rest of the app.
|
/// A single menu item, stripped down and reorganized to a format that actually makes sense for me to use in the rest of the app.
|
||||||
struct FDMenuItem: Hashable, Identifiable {
|
struct FDMenuItem: Hashable, Identifiable {
|
||||||
let id: Int
|
let id: Int
|
||||||
@ -82,6 +115,7 @@ struct FDMenuItem: Hashable, Identifiable {
|
|||||||
let category: String
|
let category: String
|
||||||
let allergens: [String]
|
let allergens: [String]
|
||||||
let calories: Int
|
let calories: Int
|
||||||
|
let nutritionalEntries: [FDNutritionalEntry]
|
||||||
let dietaryMarkers: [String]
|
let dietaryMarkers: [String]
|
||||||
let ingredients: String
|
let ingredients: String
|
||||||
let price: Double
|
let price: Double
|
||||||
|
|||||||
@ -59,15 +59,37 @@ struct MenuItemView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding(.bottom, 12)
|
||||||
|
if !menuItem.allergens.isEmpty {
|
||||||
Text("Allergens")
|
Text("Allergens")
|
||||||
.font(.headline)
|
.font(.title3)
|
||||||
.padding(.top, 8)
|
.fontWeight(.semibold)
|
||||||
Text(menuItem.allergens.joined(separator: ", "))
|
Text(menuItem.allergens.joined(separator: ", "))
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
.padding(.bottom, 8)
|
.padding(.bottom, 12)
|
||||||
|
.onAppear {
|
||||||
|
print(menuItem.allergens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Nutrition Facts")
|
||||||
|
.font(.title3)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
ForEach(menuItem.nutritionalEntries, id: \.self) { entry in
|
||||||
|
HStack(alignment: .top) {
|
||||||
|
Text(entry.type)
|
||||||
|
Spacer()
|
||||||
|
Text("\(String(format: "%.1f", entry.amount))\(entry.unit)")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 12)
|
||||||
Text("Ingredients")
|
Text("Ingredients")
|
||||||
.font(.headline)
|
.font(.title3)
|
||||||
|
.fontWeight(.semibold)
|
||||||
Text(menuItem.ingredients)
|
Text(menuItem.ingredients)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
@ -88,6 +110,7 @@ struct MenuItemView: View {
|
|||||||
category: "Baked Goods",
|
category: "Baked Goods",
|
||||||
allergens: ["Wheat", "Gluten", "Egg", "Milk", "Soy"],
|
allergens: ["Wheat", "Gluten", "Egg", "Milk", "Soy"],
|
||||||
calories: 470,
|
calories: 470,
|
||||||
|
nutritionalEntries: [FDNutritionalEntry(type: "Example", amount: 0.0, unit: "g")],
|
||||||
dietaryMarkers: ["Vegetarian"],
|
dietaryMarkers: ["Vegetarian"],
|
||||||
ingredients: "Some ingredients that you'd expect to find inside of a chocolate chip muffin",
|
ingredients: "Some ingredients that you'd expect to find inside of a chocolate chip muffin",
|
||||||
price: 2.79,
|
price: 2.79,
|
||||||
|
|||||||
@ -67,6 +67,9 @@ struct MenuView: View {
|
|||||||
let searchedLocations = searchText.isEmpty || item.name.localizedCaseInsensitiveContains(searchText)
|
let searchedLocations = searchText.isEmpty || item.name.localizedCaseInsensitiveContains(searchText)
|
||||||
return searchedLocations
|
return searchedLocations
|
||||||
}
|
}
|
||||||
|
newItems.sort { firstItem, secondItem in
|
||||||
|
return firstItem.name.localizedCaseInsensitiveCompare(secondItem.name) == .orderedAscending
|
||||||
|
}
|
||||||
return newItems
|
return newItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user