ios widget showing what is available at chucks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: refactor the display

+88 -33
wasup-chucks.xcodeproj/project.xcworkspace/xcuserdata/kierank.xcuserdatad/UserInterfaceState.xcuserstate

This is a binary file and will not be displayed.

+88 -33
wasup-chucks/ContentView.swift
··· 200 200 @Environment(\.dismiss) private var dismiss 201 201 202 202 var venues: [VenueMenu] { 203 - let matching = menu.filter { $0.slot == meal.phase.apiSlot || $0.slot == "anytime" } 204 - // Merge venues that appear with both meal-specific and anytime slots 205 - var merged: [String: VenueMenu] = [:] 206 - for venue in matching { 207 - if let existing = merged[venue.venue] { 208 - // Combine items, preferring meal-specific slot 209 - let combinedItems = existing.items + venue.items.filter { item in 210 - !existing.items.contains { $0.name == item.name } 211 - } 212 - let preferredSlot = existing.slot != "anytime" ? existing.slot : venue.slot 213 - merged[venue.venue] = VenueMenu(venue: venue.venue, meal: venue.meal ?? existing.meal, slot: preferredSlot, items: combinedItems) 214 - } else { 215 - merged[venue.venue] = venue 216 - } 217 - } 218 - return Array(merged.values).sorted { $0.venue < $1.venue } 203 + // Only show meal-specific stations (not anytime) 204 + menu.filter { $0.slot == meal.phase.apiSlot } 205 + .sorted { $0.venue < $1.venue } 219 206 } 220 207 221 208 var body: some View { 222 209 NavigationStack { 223 210 ScrollView { 224 211 VStack(alignment: .leading, spacing: 16) { 225 - ForEach(venues) { venue in 226 - StationCard(venue: venue, highlightAsSpecial: venue.venue == "Home Cooking") 212 + if venues.isEmpty { 213 + Text("No specific menu for \(meal.phase.rawValue)") 214 + .foregroundStyle(.secondary) 215 + .frame(maxWidth: .infinity, alignment: .center) 216 + .padding(.top, 40) 217 + } else { 218 + ForEach(venues) { venue in 219 + StationCard(venue: venue, highlightAsSpecial: venue.venue == "Home Cooking") 220 + } 227 221 } 228 222 } 229 223 .padding() ··· 248 242 let slot: String 249 243 let isOpen: Bool 250 244 251 - var specialsVenue: VenueMenu? { 252 - menu.first { $0.venue == "Home Cooking" && $0.slot == slot } 245 + var mealSpecificVenues: [VenueMenu] { 246 + menu.filter { $0.slot == slot } 247 + .sorted { $0.venue < $1.venue } 248 + } 249 + 250 + var alwaysAvailableVenues: [VenueMenu] { 251 + menu.filter { $0.slot == "anytime" } 252 + .sorted { $0.venue < $1.venue } 253 + } 254 + 255 + var mealLabel: String { 256 + switch slot { 257 + case "breakfast": return "Breakfast" 258 + case "lunch": return "Lunch" 259 + case "dinner": return "Dinner" 260 + default: return "This Meal" 261 + } 253 262 } 254 263 255 264 var body: some View { 256 - VStack(alignment: .leading, spacing: 16) { 257 - if let specials = specialsVenue, !specials.items.isEmpty { 258 - VStack(alignment: .leading, spacing: 10) { 259 - Text("Home Cooking") 265 + VStack(alignment: .leading, spacing: 24) { 266 + // Meal-specific stations 267 + if !mealSpecificVenues.isEmpty { 268 + VStack(alignment: .leading, spacing: 12) { 269 + Label("\(mealLabel) Only", systemImage: "clock.fill") 260 270 .font(.headline) 271 + .foregroundStyle(.primary) 261 272 262 - ForEach(specials.items) { item in 263 - HStack(spacing: 8) { 264 - Text("•") 265 - .foregroundStyle(.secondary) 266 - Text(item.name) 267 - .font(.body) 268 - Spacer() 269 - AllergenRow(allergens: item.allergens) 270 - } 273 + ForEach(mealSpecificVenues, id: \.venue) { venue in 274 + VenueSection(venue: venue) 271 275 } 272 276 } 273 277 .padding() 274 278 .background(.regularMaterial) 275 279 .clipShape(RoundedRectangle(cornerRadius: 12)) 276 280 } 281 + 282 + // Divider between sections 283 + if !mealSpecificVenues.isEmpty && !alwaysAvailableVenues.isEmpty { 284 + HStack { 285 + VStack { Divider() } 286 + Text("Always Available") 287 + .font(.caption) 288 + .foregroundStyle(.secondary) 289 + VStack { Divider() } 290 + } 291 + .padding(.vertical, 4) 292 + } 293 + 294 + // Always available stations 295 + if !alwaysAvailableVenues.isEmpty { 296 + VStack(alignment: .leading, spacing: 12) { 297 + ForEach(alwaysAvailableVenues, id: \.venue) { venue in 298 + VenueSection(venue: venue) 299 + } 300 + } 301 + .padding() 302 + .background(.regularMaterial) 303 + .clipShape(RoundedRectangle(cornerRadius: 12)) 304 + } 305 + } 306 + } 307 + } 308 + 309 + struct VenueSection: View { 310 + let venue: VenueMenu 311 + @State private var isExpanded = true 312 + 313 + var body: some View { 314 + DisclosureGroup(isExpanded: $isExpanded) { 315 + VStack(alignment: .leading, spacing: 6) { 316 + ForEach(venue.items) { item in 317 + HStack(spacing: 8) { 318 + Text("•") 319 + .foregroundStyle(.secondary) 320 + Text(item.name) 321 + .font(.subheadline) 322 + Spacer() 323 + AllergenRow(allergens: item.allergens) 324 + } 325 + } 326 + } 327 + .padding(.top, 4) 328 + } label: { 329 + Text(venue.venue) 330 + .font(.subheadline) 331 + .fontWeight(.medium) 277 332 } 278 333 } 279 334 }