Artikelbilder lokal speichern und auf der Artikelseite anzeigen
Open Food Facts liefert nur eine Bildadresse. Direkt dorthin zu verlinken hiesse:
Das Bild verschwindet, wenn OFF es austauscht, die Installation braucht Internet,
und jeder Seitenaufruf verraet OFF, welche Artikel jemand ansieht.
Das Bild wird daher einmal geholt und in einer eigenen Tabelle product_images
abgelegt - eigene Tabelle, damit Artikellisten die Blobs nicht mitziehen.
Geholt wird beim Anlegen, bei geaenderter Bildadresse und beim ersten Abruf
(so bekommen auch bestehende Artikel ihre Kopie, ohne Wanderung ueber alle
Datensaetze).
GET /products/{id}/image verlangt eine Anmeldung - aus den Bildern liesse sich
sonst ohne Konto ablesen, was im Vorrat liegt. Da ein <img src> keinen
Authorization-Header schickt, laedt die Oberflaeche das Bild ueber fetch und
zeigt es als Objekt-URL.
Angezeigt wird es rechts neben Barcode, Name und Marke. Fehlt ein Bild, rendert
die Komponente nichts und die Felder nehmen die volle Breite ein.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
42
web/src/components/ProduktBild.jsx
Normal file
42
web/src/components/ProduktBild.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { authorizedObjectUrl } from "../api";
|
||||
|
||||
/**
|
||||
* Artikelbild aus der eigenen Datenbank.
|
||||
*
|
||||
* Rendert nichts, solange kein Bild vorliegt – ein Platzhalter für einen
|
||||
* Artikel, der nie ein Bild bekommen wird, wäre nur Lärm.
|
||||
*
|
||||
* Die Objekt-URL wird beim Verlassen wieder freigegeben, sonst hält der Browser
|
||||
* jedes angesehene Bild bis zum Neuladen der Seite im Speicher.
|
||||
*/
|
||||
export default function ProduktBild({ productId, alt, className = "" }) {
|
||||
const [url, setUrl] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!productId) return undefined;
|
||||
let abgebrochen = false;
|
||||
let erzeugt = null;
|
||||
|
||||
authorizedObjectUrl(`/products/${productId}/image`)
|
||||
.then((neu) => {
|
||||
if (!neu) return;
|
||||
if (abgebrochen) {
|
||||
URL.revokeObjectURL(neu);
|
||||
return;
|
||||
}
|
||||
erzeugt = neu;
|
||||
setUrl(neu);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
return () => {
|
||||
abgebrochen = true;
|
||||
if (erzeugt) URL.revokeObjectURL(erzeugt);
|
||||
setUrl(null);
|
||||
};
|
||||
}, [productId]);
|
||||
|
||||
if (!url) return null;
|
||||
return <img className={`produkt-bild ${className}`.trim()} src={url} alt={alt || ""} />;
|
||||
}
|
||||
Reference in New Issue
Block a user