Web: "Entfernen" nur zeigen, wenn ein Artikelbild vorliegt
Der Entfernen-Knopf stand auch ohne Bild da. ProduktBild meldet jetzt via onLoaded, ob ein Bild geladen wurde; das Produktformular blendet "Entfernen" entsprechend ein oder aus. "Foto machen" und "Galerie" bleiben immer sichtbar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { authorizedObjectUrl } from "../api";
|
import { authorizedObjectUrl } from "../api";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
@@ -7,9 +7,16 @@ import Icon from "./Icon";
|
|||||||
*
|
*
|
||||||
* Die Objekt-URL wird beim Verlassen wieder freigegeben, sonst hält der Browser
|
* Die Objekt-URL wird beim Verlassen wieder freigegeben, sonst hält der Browser
|
||||||
* jedes angesehene Bild bis zum Neuladen der Seite im Speicher.
|
* jedes angesehene Bild bis zum Neuladen der Seite im Speicher.
|
||||||
|
*
|
||||||
|
* ``onLoaded`` meldet, ob ein Bild vorliegt – so kann die Artikelseite z.B. den
|
||||||
|
* „Entfernen"-Knopf ausblenden, wenn es nichts zu entfernen gibt.
|
||||||
*/
|
*/
|
||||||
function useBildUrl(productId, version = 0) {
|
function useBildUrl(productId, version = 0, onLoaded) {
|
||||||
const [url, setUrl] = useState(null);
|
const [url, setUrl] = useState(null);
|
||||||
|
// Über einen Ref, damit ein bei jedem Render neu erzeugtes onLoaded den Effekt
|
||||||
|
// nicht erneut auslöst.
|
||||||
|
const onLoadedRef = useRef(onLoaded);
|
||||||
|
onLoadedRef.current = onLoaded;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!productId) return undefined;
|
if (!productId) return undefined;
|
||||||
@@ -20,15 +27,21 @@ function useBildUrl(productId, version = 0) {
|
|||||||
// Speichern nicht seine zwischengespeicherte Fassung ausliefert.
|
// Speichern nicht seine zwischengespeicherte Fassung ausliefert.
|
||||||
authorizedObjectUrl(`/products/${productId}/image${version ? `?v=${version}` : ""}`)
|
authorizedObjectUrl(`/products/${productId}/image${version ? `?v=${version}` : ""}`)
|
||||||
.then((neu) => {
|
.then((neu) => {
|
||||||
if (!neu) return;
|
|
||||||
if (abgebrochen) {
|
if (abgebrochen) {
|
||||||
URL.revokeObjectURL(neu);
|
if (neu) URL.revokeObjectURL(neu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onLoadedRef.current?.(Boolean(neu));
|
||||||
|
if (!neu) {
|
||||||
|
setUrl(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
erzeugt = neu;
|
erzeugt = neu;
|
||||||
setUrl(neu);
|
setUrl(neu);
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {
|
||||||
|
if (!abgebrochen) onLoadedRef.current?.(false);
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
abgebrochen = true;
|
abgebrochen = true;
|
||||||
@@ -46,8 +59,8 @@ function useBildUrl(productId, version = 0) {
|
|||||||
* Rendert nichts, solange kein Bild vorliegt – ein leerer Rahmen neben einem
|
* Rendert nichts, solange kein Bild vorliegt – ein leerer Rahmen neben einem
|
||||||
* Artikel, der nie ein Bild bekommen wird, wäre nur Lärm.
|
* Artikel, der nie ein Bild bekommen wird, wäre nur Lärm.
|
||||||
*/
|
*/
|
||||||
export default function ProduktBild({ productId, alt, className = "", version = 0 }) {
|
export default function ProduktBild({ productId, alt, className = "", version = 0, onLoaded }) {
|
||||||
const url = useBildUrl(productId, version);
|
const url = useBildUrl(productId, version, onLoaded);
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
return <img className={`produkt-bild ${className}`.trim()} src={url} alt={alt || ""} />;
|
return <img className={`produkt-bild ${className}`.trim()} src={url} alt={alt || ""} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ export default function ProductForm() {
|
|||||||
const [offDaten, setOffDaten] = useState(null);
|
const [offDaten, setOffDaten] = useState(null);
|
||||||
const [offBusy, setOffBusy] = useState(false);
|
const [offBusy, setOffBusy] = useState(false);
|
||||||
const [bildVersion, setBildVersion] = useState(0);
|
const [bildVersion, setBildVersion] = useState(0);
|
||||||
|
// Ob gerade ein Bild vorliegt – steuert, ob „Entfernen" angeboten wird.
|
||||||
|
const [hatBild, setHatBild] = useState(false);
|
||||||
|
|
||||||
const selectedUnit = units.find((u) => String(u.id) === String(form.unit_id)) || null;
|
const selectedUnit = units.find((u) => String(u.id) === String(form.unit_id)) || null;
|
||||||
const unitFactor = selectedUnit ? selectedUnit.factor : 1;
|
const unitFactor = selectedUnit ? selectedUnit.factor : 1;
|
||||||
@@ -561,7 +563,7 @@ export default function ProductForm() {
|
|||||||
</div>
|
</div>
|
||||||
{!isNew && (
|
{!isNew && (
|
||||||
<div className="produkt-foto">
|
<div className="produkt-foto">
|
||||||
<ProduktBild productId={id} alt={form.name} version={bildVersion} />
|
<ProduktBild productId={id} alt={form.name} version={bildVersion} onLoaded={setHatBild} />
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<div className="foto-knoepfe">
|
<div className="foto-knoepfe">
|
||||||
<label className="btn sm" title="Mit der Kamera aufnehmen">
|
<label className="btn sm" title="Mit der Kamera aufnehmen">
|
||||||
@@ -572,7 +574,9 @@ export default function ProductForm() {
|
|||||||
Galerie
|
Galerie
|
||||||
<input hidden type="file" accept="image/*" onChange={onPickPhoto} />
|
<input hidden type="file" accept="image/*" onChange={onPickPhoto} />
|
||||||
</label>
|
</label>
|
||||||
<button type="button" className="btn sm ghost" onClick={removePhoto}>Entfernen</button>
|
{hatBild && (
|
||||||
|
<button type="button" className="btn sm ghost" onClick={removePhoto}>Entfernen</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user