fix: prevent infinite effect loop in recipe translation workflow
All checks were successful
CI / update (push) Successful in 1m14s

Convert recipe data functions to $derived reactive variables to prevent
infinite $effect loops. Previously, calling functions inline in component
props created new objects on every reactive check, causing the
TranslationApproval component's syncBaseRecipeReferences $effect to run
continuously, resulting in the translation workflow hanging.
This commit is contained in:
2026-01-13 15:12:12 +01:00
parent 0a49e20c02
commit 0dc950c824
2 changed files with 22 additions and 24 deletions

View File

@@ -85,24 +85,22 @@
return season; return season;
} }
// Prepare German recipe data // Prepare German recipe data - use $derived to prevent infinite effect loops
function getGermanRecipeData() { let germanRecipeData = $derived({
return { ...card_data,
...card_data, ...add_info,
...add_info, images: uploaded_image_filename ? [{ mediapath: uploaded_image_filename, alt: "", caption: "" }] : [],
images: uploaded_image_filename ? [{ mediapath: uploaded_image_filename, alt: "", caption: "" }] : [], season: season_local,
season: season_local, short_name: short_name.trim(),
short_name: short_name.trim(), portions: portions_local,
portions: portions_local, datecreated: new Date(),
datecreated: new Date(), datemodified: new Date(),
datemodified: new Date(), instructions,
instructions, ingredients,
ingredients, preamble,
preamble, addendum,
addendum, isBaseRecipe,
isBaseRecipe, });
};
}
// Show translation workflow before submission // Show translation workflow before submission
function prepareSubmit() { function prepareSubmit() {
@@ -385,7 +383,7 @@ button.action_button {
{#if showTranslationWorkflow} {#if showTranslationWorkflow}
<div id="translation-section"> <div id="translation-section">
<TranslationApproval <TranslationApproval
germanData={getGermanRecipeData()} germanData={germanRecipeData}
onapproved={handleTranslationApproved} onapproved={handleTranslationApproved}
onskipped={handleTranslationSkipped} onskipped={handleTranslationSkipped}
oncancelled={handleTranslationCancelled} oncancelled={handleTranslationCancelled}

View File

@@ -103,8 +103,8 @@
return season; return season;
} }
// Get current German recipe data // Get current German recipe data - use $derived to prevent infinite effect loops
function getCurrentRecipeData() { let currentRecipeData = $derived.by(() => {
// Ensure we always have a valid images array with at least one item // Ensure we always have a valid images array with at least one item
let recipeImages; let recipeImages;
if (uploaded_image_filename) { if (uploaded_image_filename) {
@@ -142,11 +142,11 @@
note, note,
isBaseRecipe, isBaseRecipe,
}; };
} });
// Detect which fields have changed from the original // Detect which fields have changed from the original
function detectChangedFields(): string[] { function detectChangedFields(): string[] {
const current = getCurrentRecipeData(); const current = currentRecipeData;
const changed: string[] = []; const changed: string[] = [];
const fieldsToCheck = [ const fieldsToCheck = [
@@ -486,7 +486,7 @@
{#if showTranslationWorkflow} {#if showTranslationWorkflow}
<div id="translation-section"> <div id="translation-section">
<TranslationApproval <TranslationApproval
germanData={getCurrentRecipeData()} germanData={currentRecipeData}
englishData={translationData} englishData={translationData}
changedFields={changedFields} changedFields={changedFields}
isEditMode={true} isEditMode={true}