Fix category handling, recipe management, and dark mode toggle tint

- Map uncategorized category between * (internal) and empty string
  (API) so selecting Sonstige/Other correctly persists to the server
- Default new recipes to Other (*) category and remove None option
- Add "New Category" option to category picker in recipe edit view
- Include newly created/imported recipes in recently viewed list and
  pre-fetch thumbnails so images display immediately
- Remove deleted recipes from recently viewed list
- Remove broad .tint(.primary) from RecipeTabView that caused white
  toggles in Settings during dark mode
- Rename German "Other" translation from Andere to Sonstige
- Add missing translations for Servings stepper and new category strings

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 07:13:01 +01:00
parent 02118e3d7a
commit fb6b16c1fc
7 changed files with 131 additions and 14 deletions

View File

@@ -33,9 +33,8 @@ struct RecipeMetadataSection: View {
.textFieldStyle(.roundedBorder)
Picker("Choose", selection: $viewModel.observableRecipeDetail.recipeCategory) {
Text("").tag("")
ForEach(categories, id: \.self) { item in
Text(item)
Text(item == "*" ? String(localized: "Other") : item).tag(item)
}
}
.pickerStyle(.menu)
@@ -93,19 +92,50 @@ struct RecipeEditMetadataSection: View {
@EnvironmentObject var appState: AppState
@ObservedObject var viewModel: RecipeView.ViewModel
@State private var showNewCategoryAlert = false
@State private var newCategoryName = ""
private let newCategoryTag = "\0_new_category_"
var categories: [String] {
appState.categories.map { $0.name }
var list = appState.categories.map { $0.name }
let current = viewModel.observableRecipeDetail.recipeCategory
if !current.isEmpty && current != newCategoryTag && !list.contains(current) {
list.append(current)
}
return list
}
var body: some View {
Section("Details") {
Picker("Category", selection: $viewModel.observableRecipeDetail.recipeCategory) {
Text("None").tag("")
ForEach(categories, id: \.self) { item in
Text(item).tag(item)
Text(item == "*" ? String(localized: "Other") : item).tag(item)
}
Divider()
Text("New Category…").tag(newCategoryTag)
}
.pickerStyle(.menu)
.onChange(of: viewModel.observableRecipeDetail.recipeCategory) { _, newValue in
if newValue == newCategoryTag {
newCategoryName = ""
showNewCategoryAlert = true
}
}
.alert("New Category", isPresented: $showNewCategoryAlert) {
TextField("Category name", text: $newCategoryName)
Button("Cancel", role: .cancel) {
viewModel.observableRecipeDetail.recipeCategory = "*"
}
Button("Add") {
let trimmed = newCategoryName.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmed.isEmpty {
viewModel.observableRecipeDetail.recipeCategory = trimmed
} else {
viewModel.observableRecipeDetail.recipeCategory = "*"
}
}
}
Stepper("Servings: \(viewModel.observableRecipeDetail.recipeYield)", value: $viewModel.observableRecipeDetail.recipeYield, in: 1...99)