fix(recipe): Favoriten-Markierung persistiert beim Neuladen
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 50s
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 50s
Bug: Beim Neuanzeigen einer Rezeptseite war der Favoriten-Button immer grau — isFav wurde als local $state(false) initialisiert und die checkFavorite()-Funktion war eine Stub-Implementation, die nichts gemacht hat. State lebte nur innerhalb einer Session. Fix: - Neue Server-Funktion listFavoriteProfiles(db, recipeId): number[] in $lib/server/recipes/actions.ts - +page.server.ts lädt favorite_profile_ids mit in die Page-Daten - +page.svelte macht isFav zum $derived aus favoriteProfileIds + aktivem Profil. toggleFavorite mutiert die lokale Liste (Add/Remove der aktiven Profil-ID) — beim nächsten Load ist die Server-Liste wieder Source of Truth. - Alte Stub-Funktion checkFavorite() entfernt (inkl. Aufruf in onMount).
This commit is contained in:
@@ -73,6 +73,17 @@ export function isFavorite(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listFavoriteProfiles(
|
||||||
|
db: Database.Database,
|
||||||
|
recipeId: number
|
||||||
|
): number[] {
|
||||||
|
return (
|
||||||
|
db
|
||||||
|
.prepare('SELECT profile_id FROM favorite WHERE recipe_id = ?')
|
||||||
|
.all(recipeId) as { profile_id: number }[]
|
||||||
|
).map((r) => r.profile_id);
|
||||||
|
}
|
||||||
|
|
||||||
export function logCooked(
|
export function logCooked(
|
||||||
db: Database.Database,
|
db: Database.Database,
|
||||||
recipeId: number,
|
recipeId: number,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { getRecipeById } from '$lib/server/recipes/repository';
|
|||||||
import {
|
import {
|
||||||
listComments,
|
listComments,
|
||||||
listCookingLog,
|
listCookingLog,
|
||||||
|
listFavoriteProfiles,
|
||||||
listRatings
|
listRatings
|
||||||
} from '$lib/server/recipes/actions';
|
} from '$lib/server/recipes/actions';
|
||||||
|
|
||||||
@@ -17,7 +18,8 @@ export const load: PageServerLoad = async ({ params }) => {
|
|||||||
const ratings = listRatings(db, id);
|
const ratings = listRatings(db, id);
|
||||||
const comments = listComments(db, id);
|
const comments = listComments(db, id);
|
||||||
const cooking_log = listCookingLog(db, id);
|
const cooking_log = listCookingLog(db, id);
|
||||||
|
const favorite_profile_ids = listFavoriteProfiles(db, id);
|
||||||
const avg_stars =
|
const avg_stars =
|
||||||
ratings.length === 0 ? null : ratings.reduce((s, r) => s + r.stars, 0) / ratings.length;
|
ratings.length === 0 ? null : ratings.reduce((s, r) => s + r.stars, 0) / ratings.length;
|
||||||
return { recipe, ratings, comments, cooking_log, avg_stars };
|
return { recipe, ratings, comments, cooking_log, favorite_profile_ids, avg_stars };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
let ratings = $state<typeof data.ratings>([]);
|
let ratings = $state<typeof data.ratings>([]);
|
||||||
let comments = $state<CommentRow[]>([]);
|
let comments = $state<CommentRow[]>([]);
|
||||||
let cookingLog = $state<typeof data.cooking_log>([]);
|
let cookingLog = $state<typeof data.cooking_log>([]);
|
||||||
let isFav = $state(false);
|
let favoriteProfileIds = $state<number[]>([]);
|
||||||
let onWishlist = $state(false);
|
let onWishlist = $state(false);
|
||||||
let newComment = $state('');
|
let newComment = $state('');
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
ratings = [...data.ratings];
|
ratings = [...data.ratings];
|
||||||
comments = [...data.comments];
|
comments = [...data.comments];
|
||||||
cookingLog = [...data.cooking_log];
|
cookingLog = [...data.cooking_log];
|
||||||
|
favoriteProfileIds = [...data.favorite_profile_ids];
|
||||||
});
|
});
|
||||||
|
|
||||||
const myRating = $derived(
|
const myRating = $derived(
|
||||||
@@ -29,14 +30,9 @@
|
|||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
async function checkFavorite() {
|
const isFav = $derived(
|
||||||
if (!profileStore.active) {
|
profileStore.active ? favoriteProfileIds.includes(profileStore.active.id) : false
|
||||||
isFav = false;
|
);
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Fetch favorite status via list endpoint (quick hack: GET not implemented, infer from no-op)
|
|
||||||
// Not critical for MVP — we mutate state on toggle.
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setRating(stars: number) {
|
async function setRating(stars: number) {
|
||||||
if (!profileStore.active) {
|
if (!profileStore.active) {
|
||||||
@@ -64,13 +60,16 @@
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const profileId = profileStore.active.id;
|
||||||
const method = isFav ? 'DELETE' : 'PUT';
|
const method = isFav ? 'DELETE' : 'PUT';
|
||||||
await fetch(`/api/recipes/${data.recipe.id}/favorite`, {
|
await fetch(`/api/recipes/${data.recipe.id}/favorite`, {
|
||||||
method,
|
method,
|
||||||
headers: { 'content-type': 'application/json' },
|
headers: { 'content-type': 'application/json' },
|
||||||
body: JSON.stringify({ profile_id: profileStore.active.id })
|
body: JSON.stringify({ profile_id: profileId })
|
||||||
});
|
});
|
||||||
isFav = !isFav;
|
favoriteProfileIds = isFav
|
||||||
|
? favoriteProfileIds.filter((id) => id !== profileId)
|
||||||
|
: [...favoriteProfileIds, profileId];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logCooked() {
|
async function logCooked() {
|
||||||
@@ -183,7 +182,6 @@
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
void requestWakeLock();
|
void requestWakeLock();
|
||||||
void checkFavorite();
|
|
||||||
void refreshWishlistState();
|
void refreshWishlistState();
|
||||||
const onVisibility = () => {
|
const onVisibility = () => {
|
||||||
if (document.visibilityState === 'visible' && !wakeLock) void requestWakeLock();
|
if (document.visibilityState === 'visible' && !wakeLock) void requestWakeLock();
|
||||||
|
|||||||
Reference in New Issue
Block a user