fix(router): adjust type check for extra parameter in navigation

fix(import): ensure correct typing for passing markdown and imageUrl
fix(recipes): delete local image file on recipe deletion to avoid orphan files
This commit is contained in:
Nils-Johan Gynther
2026-04-22 21:51:16 +02:00
parent 29beb3a85f
commit c8510b431f
3 changed files with 17 additions and 3 deletions
+11
View File
@@ -1,5 +1,7 @@
import { Injectable, NotFoundException } from '@nestjs/common'; import { Injectable, NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { PrismaService } from '../prisma/prisma.service'; import { PrismaService } from '../prisma/prisma.service';
import { CreateRecipeDto } from './dto/create-recipe.dto'; import { CreateRecipeDto } from './dto/create-recipe.dto';
import { ParseMarkdownDto } from './dto/parse-markdown.dto'; import { ParseMarkdownDto } from './dto/parse-markdown.dto';
@@ -272,6 +274,15 @@ export class RecipesService {
await this.prisma.recipeIngredient.deleteMany({ where: { recipeId: id } }); await this.prisma.recipeIngredient.deleteMany({ where: { recipeId: id } });
await this.prisma.recipe.delete({ where: { id } }); await this.prisma.recipe.delete({ where: { id } });
// Radera lokal bildfil om den finns (undviker orphan-filer på disk).
if (existingRecipe.imageUrl?.startsWith('/images/')) {
const filename = path.basename(existingRecipe.imageUrl);
const filePath = path.join(IMAGE_DEST_DIR, filename);
await fs.unlink(filePath).catch(() => {
// Filen kanske redan är borttagen — ignorera felet.
});
}
} }
async updateImage(id: number, sourceUrl: string) { async updateImage(id: number, sourceUrl: string) {
+3 -1
View File
@@ -71,7 +71,9 @@ final appRouterProvider = Provider<GoRouter>((ref) {
final extra = state.extra; final extra = state.extra;
String? initialMarkdown; String? initialMarkdown;
String? initialImageUrl; String? initialImageUrl;
if (extra is Map<String, dynamic>) { // Use 'is Map' without type params — Dart reifies generics, so
// Map<String,String?> does NOT match Map<String,dynamic> at runtime.
if (extra is Map) {
initialMarkdown = extra['markdown'] as String?; initialMarkdown = extra['markdown'] as String?;
initialImageUrl = extra['imageUrl'] as String?; initialImageUrl = extra['imageUrl'] as String?;
} else if (extra is String) { } else if (extra is String) {
@@ -78,8 +78,9 @@ class _RecipeImportTabState extends ConsumerState<RecipeImportTab> {
); );
if (!mounted) return; if (!mounted) return;
// Pass both markdown and imageUrl so CreateRecipeScreen can pre-fill both. // Explicitly typed as <String, dynamic> so the router's
context.push('/recipes/create', extra: { // 'is Map<String, dynamic>' runtime check succeeds.
context.push('/recipes/create', extra: <String, dynamic>{
'markdown': result.markdown, 'markdown': result.markdown,
'imageUrl': result.imageUrl, 'imageUrl': result.imageUrl,
}); });