Files
Nextcloud-Cookbook-iOS/Nextcloud Cookbook iOS Client/Views/Recipes/RecentRecipesSection.swift
Hendrik Hogertz c8ddb098d1 Redesign search tab, add category cards, recent recipes, and complete German translations
Overhaul SearchTabView with search history, empty/no-results states, and dynamic
navigation title. Extract CategoryCardView and RecentRecipesSection into standalone
views. Update RecipeTabView, RecipeListView, RecipeCardView, and MainView for the
modernized UI. Add all 12 missing German translations in Localizable.xcstrings.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-15 01:47:16 +01:00

111 lines
3.6 KiB
Swift

//
// RecentRecipesSection.swift
// Nextcloud Cookbook iOS Client
//
import SwiftUI
struct RecentRecipesSection: View {
@EnvironmentObject var appState: AppState
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
Text("Recently Viewed")
.font(.title2)
.bold()
Spacer()
Button {
appState.clearRecentRecipes()
} label: {
Text("Clear")
.font(.caption)
.foregroundStyle(.secondary)
}
}
.padding(.horizontal)
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 12) {
ForEach(appState.recentRecipes) { recipe in
NavigationLink(value: recipe) {
RecentRecipeCard(recipe: recipe)
.environmentObject(appState)
}
.buttonStyle(.plain)
}
}
.padding(.horizontal)
}
}
}
}
private struct RecentRecipeCard: View {
@EnvironmentObject var appState: AppState
let recipe: Recipe
@State private var thumbnail: UIImage?
private var keywordsText: String? {
guard let keywords = recipe.keywords, !keywords.isEmpty else { return nil }
let items = keywords.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespaces) }.filter { !$0.isEmpty }
guard !items.isEmpty else { return nil }
return items.prefix(3).joined(separator: " \u{00B7} ")
}
var body: some View {
VStack(alignment: .leading, spacing: 0) {
// Thumbnail
if let thumbnail {
Image(uiImage: thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 160, height: 120)
.clipped()
} else {
LinearGradient(
gradient: Gradient(colors: [.ncGradientDark, .ncGradientLight]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.frame(width: 160, height: 120)
.overlay {
Image(systemName: "fork.knife")
.font(.title2)
.foregroundStyle(.white.opacity(0.6))
}
}
// Text content
VStack(alignment: .leading, spacing: 2) {
Text(recipe.name)
.font(.subheadline)
.fontWeight(.medium)
.lineLimit(2)
.multilineTextAlignment(.leading)
.foregroundStyle(.primary)
if let keywordsText {
Text(keywordsText)
.font(.caption2)
.foregroundStyle(.secondary)
.lineLimit(1)
}
}
.padding(.horizontal, 8)
.padding(.vertical, 6)
}
.frame(width: 160)
.background(Color.backgroundHighlight)
.clipShape(RoundedRectangle(cornerRadius: 14))
.shadow(color: .black.opacity(0.08), radius: 4, y: 2)
.task {
thumbnail = await appState.getImage(
id: recipe.recipe_id,
size: .THUMB,
fetchMode: UserSettings.shared.storeThumb ? .preferLocal : .onlyServer
)
}
}
}