diff --git a/umd-dining-app/Utilities/UMDColors.swift b/umd-dining-app/Utilities/UMDColors.swift index 6d9b953..7878332 100644 --- a/umd-dining-app/Utilities/UMDColors.swift +++ b/umd-dining-app/Utilities/UMDColors.swift @@ -34,6 +34,8 @@ extension Color { struct HomeCardStyle: ViewModifier { var tint: Color + // Secondary cards (gray) wear a lighter outline than the red ones + var thin: Bool = false @Environment(\.colorScheme) private var colorScheme func body(content: Content) -> some View { @@ -42,7 +44,9 @@ struct HomeCardStyle: ViewModifier { .clipShape(RoundedRectangle(cornerRadius: 12)) .overlay(RoundedRectangle(cornerRadius: 12).stroke( tint.opacity(colorScheme == .dark ? 0.7 : 0.35), - lineWidth: colorScheme == .dark ? 2 : 1.5 + lineWidth: thin + ? (colorScheme == .dark ? 1.5 : 1) + : (colorScheme == .dark ? 2 : 1.5) )) .shadow(color: .black.opacity(colorScheme == .dark ? 0.35 : 0.10), radius: 5, x: 0, y: 2) } @@ -59,31 +63,9 @@ struct PressableButtonStyle: ButtonStyle { } } -// Same dip for views driven by tap gestures rather than a Button -struct PressEffect: ViewModifier { - @State private var isPressed = false - - func body(content: Content) -> some View { - content - .scaleEffect(isPressed ? 0.97 : 1) - .animation(.easeOut(duration: 0.12), value: isPressed) - .simultaneousGesture( - DragGesture(minimumDistance: 0) - .onChanged { _ in isPressed = true } - .onEnded { _ in isPressed = false } - ) - } -} - -extension View { - func pressEffect() -> some View { - modifier(PressEffect()) - } -} - extension View { - func homeCard(tint: Color = .umdRed) -> some View { - modifier(HomeCardStyle(tint: tint)) + func homeCard(tint: Color = .umdRed, thin: Bool = false) -> some View { + modifier(HomeCardStyle(tint: tint, thin: thin)) } } diff --git a/umd-dining-app/Views/DiningHallPickerView.swift b/umd-dining-app/Views/DiningHallPickerView.swift index d54d183..db08b7e 100644 --- a/umd-dining-app/Views/DiningHallPickerView.swift +++ b/umd-dining-app/Views/DiningHallPickerView.swift @@ -35,9 +35,9 @@ struct DiningHallPickerView: View { .padding(.top, 12) .padding(.bottom, 8) - // Prompt + View Hours link + // Section label + View Hours link HStack { - Text("Hungry? Pick a dining hall.") + Text("Dining Halls") .font(.inter(size: 17, weight: .semibold)) .foregroundStyle(.primary) Spacer() diff --git a/umd-dining-app/Views/FoodItemRow.swift b/umd-dining-app/Views/FoodItemRow.swift index fd2e371..b89bdc3 100644 --- a/umd-dining-app/Views/FoodItemRow.swift +++ b/umd-dining-app/Views/FoodItemRow.swift @@ -15,6 +15,7 @@ struct FoodItemRow: View { @State private var pendingNutrition: [String: String]? @State private var showHeartAnimation = false @State private var resolvedCalories: String? + @State private var isPressed = false private var displayCalories: String? { // Prefer nutrition bundled inline in API response (e.g. from fetchMenu) @@ -59,7 +60,7 @@ struct FoodItemRow: View { .padding(.vertical, 16) .padding(.horizontal, 16) .homeCard() - .pressEffect() + .scaleEffect(isPressed ? 0.97 : 1) .overlay { if showHeartAnimation { Image(systemName: "heart.fill") @@ -83,7 +84,16 @@ struct FoodItemRow: View { withAnimation(.easeOut(duration: 0.25)) { showHeartAnimation = false } } } - .onTapGesture(count: 1) { onTap?() } + .onTapGesture(count: 1) { + // Dip on the tap itself, not on touch-down, so scrolling stays clean. + // Navigation fires immediately; the dip plays out behind the push. + onTap?() + withAnimation(.easeOut(duration: 0.05)) { isPressed = true } + Task { + try? await Task.sleep(for: .milliseconds(45)) + withAnimation(.easeOut(duration: 0.08)) { isPressed = false } + } + } .fullScreenCover(isPresented: $showServingPicker) { if let nutrition = pendingNutrition { ServingPickerSheet( diff --git a/umd-dining-app/Views/HomeView.swift b/umd-dining-app/Views/HomeView.swift index 4fed5ec..6aa578f 100644 --- a/umd-dining-app/Views/HomeView.swift +++ b/umd-dining-app/Views/HomeView.swift @@ -62,21 +62,6 @@ struct HomeView: View { ) .navigationTransition(.zoom(sourceID: "\(data.station)-\(data.diningHallId)", in: namespace)) } - .sheet(isPresented: $showFilter) { - FilterOverlay( - filterVegetarian: $viewModel.filterVegetarian, - filterVegan: $viewModel.filterVegan, - filterHalal: $viewModel.filterHalal, - filterGlutenFree: $viewModel.filterGlutenFree, - filterDairyFree: $viewModel.filterDairyFree, - filterHighProtein: $viewModel.filterHighProtein, - filterAllergens: $viewModel.filterAllergens, - onDismiss: { - showFilter = false - Task { await viewModel.loadMenus() } - } - ) - } .task { viewModel.autoSelectMealPeriod() await viewModel.loadMenus() @@ -99,6 +84,24 @@ struct HomeView: View { .transition(.opacity) .zIndex(2) } + + if showFilter { + FilterOverlay( + filterVegetarian: $viewModel.filterVegetarian, + filterVegan: $viewModel.filterVegan, + filterHalal: $viewModel.filterHalal, + filterGlutenFree: $viewModel.filterGlutenFree, + filterDairyFree: $viewModel.filterDairyFree, + filterHighProtein: $viewModel.filterHighProtein, + filterAllergens: $viewModel.filterAllergens, + onDismiss: { + withAnimation(.easeInOut(duration: 0.3)) { showFilter = false } + Task { await viewModel.loadMenus() } + } + ) + .transition(.opacity) + .zIndex(3) + } } .id(tabResetID) .onChange(of: tabResetID) { @@ -118,7 +121,9 @@ struct HomeView: View { Spacer() - Button { showFilter = true } label: { + Button { + withAnimation(.easeInOut(duration: 0.3)) { showFilter = true } + } label: { Image(systemName: "slider.horizontal.3") .font(.system(size: 18, weight: .medium)) .foregroundStyle(Color.umdRed) diff --git a/umd-dining-app/Views/NutritionDetailView.swift b/umd-dining-app/Views/NutritionDetailView.swift index 87a3336..3523289 100644 --- a/umd-dining-app/Views/NutritionDetailView.swift +++ b/umd-dining-app/Views/NutritionDetailView.swift @@ -236,7 +236,7 @@ struct NutritionDetailView: View { .foregroundStyle(.secondary) .padding(16) .frame(maxWidth: .infinity, alignment: .leading) - .homeCard() + .homeCard(tint: .gray, thin: true) } .padding(.horizontal, 16) } @@ -407,7 +407,7 @@ struct NutritionDetailView: View { } } } - .homeCard() + .homeCard(tint: .gray, thin: true) } .padding(.horizontal, 16) } diff --git a/umd-dining-app/Views/ProfileView.swift b/umd-dining-app/Views/ProfileView.swift index 5f5ce03..193d159 100644 --- a/umd-dining-app/Views/ProfileView.swift +++ b/umd-dining-app/Views/ProfileView.swift @@ -80,7 +80,7 @@ struct ProfileView: View { } } .padding(16) - .homeCard(tint: .gray) + .homeCard(tint: .gray, thin: true) .padding(.horizontal, 16) .padding(.top, 8) .padding(.bottom, 20) @@ -235,7 +235,7 @@ struct ProfileView: View { ) -> some View { Button(action: action) { content() - .homeCard(tint: .gray) + .homeCard(tint: .gray, thin: true) } .buttonStyle(PressableButtonStyle()) } @@ -258,7 +258,7 @@ struct ProfileView: View { } .padding(.horizontal, 16) .padding(.vertical, 16) - .homeCard(tint: .gray) + .homeCard(tint: .gray, thin: true) } // MARK: - Header