- SwiftUI + async/await architecture - Barcode scanning with AVFoundation - Product display with score ring, NOVA badge, nutrition - Alternatives with sort/filter - Auth (login/register) - Favorites & history - Account management - Dark theme - Connected to food.payfrit.com API (Open Food Facts proxy) Co-Authored-By: Claude <noreply@anthropic.com>
114 lines
4 KiB
Swift
114 lines
4 KiB
Swift
import SwiftUI
|
|
|
|
struct FavoritesScreen: View {
|
|
@EnvironmentObject var appState: AppState
|
|
@State private var favorites: [Product] = []
|
|
@State private var isLoading = true
|
|
@State private var selectedProduct: Product?
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Group {
|
|
if !appState.isAuthenticated {
|
|
// Not logged in
|
|
VStack(spacing: 20) {
|
|
Spacer()
|
|
Image(systemName: "heart.fill")
|
|
.font(.system(size: 60))
|
|
.foregroundColor(.gray)
|
|
|
|
Text("Save Your Favorites")
|
|
.font(.title2.bold())
|
|
|
|
Text("Log in to save products you love and access them anytime.")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal, 40)
|
|
|
|
Button {
|
|
appState.showLoginSheet = true
|
|
} label: {
|
|
Text("Log In")
|
|
.font(.headline)
|
|
.foregroundColor(.black)
|
|
.frame(maxWidth: .infinity)
|
|
.padding()
|
|
.background(Color.green)
|
|
.cornerRadius(12)
|
|
}
|
|
.padding(.horizontal, 40)
|
|
|
|
Spacer()
|
|
}
|
|
} else if isLoading {
|
|
ProgressView("Loading favorites...")
|
|
} else if favorites.isEmpty {
|
|
VStack(spacing: 20) {
|
|
Spacer()
|
|
Image(systemName: "heart")
|
|
.font(.system(size: 60))
|
|
.foregroundColor(.gray)
|
|
|
|
Text("No Favorites Yet")
|
|
.font(.title2.bold())
|
|
|
|
Text("Scan products and tap the heart icon to save them here.")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal, 40)
|
|
|
|
Spacer()
|
|
}
|
|
} else {
|
|
ScrollView {
|
|
LazyVStack(spacing: 12) {
|
|
ForEach(favorites) { product in
|
|
ProductCard(product: product)
|
|
.onTapGesture {
|
|
selectedProduct = product
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Favorites")
|
|
.navigationDestination(isPresented: Binding(
|
|
get: { selectedProduct != nil },
|
|
set: { if !$0 { selectedProduct = nil } }
|
|
)) {
|
|
if let product = selectedProduct {
|
|
ProductScreen(product: product)
|
|
.environmentObject(appState)
|
|
}
|
|
}
|
|
.task {
|
|
if appState.isAuthenticated {
|
|
await loadFavorites()
|
|
}
|
|
}
|
|
.refreshable {
|
|
await loadFavorites()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func loadFavorites() async {
|
|
isLoading = true
|
|
do {
|
|
let result = try await APIService.shared.getFavorites()
|
|
await MainActor.run {
|
|
favorites = result
|
|
isLoading = false
|
|
}
|
|
} catch {
|
|
await MainActor.run {
|
|
isLoading = false
|
|
appState.showError(error.localizedDescription)
|
|
}
|
|
}
|
|
}
|
|
}
|