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>
83 lines
2.8 KiB
Swift
83 lines
2.8 KiB
Swift
//
|
|
// RecipeCardView.swift
|
|
// Nextcloud Cookbook iOS Client
|
|
//
|
|
// Created by Vincent Meilinger on 15.09.23.
|
|
//
|
|
|
|
import Foundation
|
|
import SwiftUI
|
|
|
|
struct RecipeCardView: View {
|
|
@EnvironmentObject var appState: AppState
|
|
@State var recipe: Recipe
|
|
@State var recipeThumb: 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 recipeThumb = recipeThumb {
|
|
Image(uiImage: recipeThumb)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fill)
|
|
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 120, maxHeight: 120)
|
|
.clipped()
|
|
} else {
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [.ncGradientDark, .ncGradientLight]),
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 120, maxHeight: 120)
|
|
.overlay {
|
|
Image(systemName: "fork.knife")
|
|
.font(.title2)
|
|
.foregroundStyle(.white.opacity(0.7))
|
|
}
|
|
}
|
|
|
|
// Text content
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
Text(recipe.name)
|
|
.font(.subheadline)
|
|
.fontWeight(.medium)
|
|
.lineLimit(2)
|
|
.multilineTextAlignment(.leading)
|
|
|
|
if let keywordsText {
|
|
Text(keywordsText)
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 6)
|
|
}
|
|
.background(Color.backgroundHighlight)
|
|
.clipShape(RoundedRectangle(cornerRadius: 14))
|
|
.shadow(color: .black.opacity(0.08), radius: 4, y: 2)
|
|
.task {
|
|
recipeThumb = await appState.getImage(
|
|
id: recipe.recipe_id,
|
|
size: .THUMB,
|
|
fetchMode: UserSettings.shared.storeThumb ? .preferLocal : .onlyServer
|
|
)
|
|
}
|
|
.refreshable {
|
|
recipeThumb = await appState.getImage(
|
|
id: recipe.recipe_id,
|
|
size: .THUMB,
|
|
fetchMode: UserSettings.shared.storeThumb ? .preferServer : .onlyServer
|
|
)
|
|
}
|
|
}
|
|
}
|