'use client'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslations } from 'next-intl'; import type { WidgetProps } from '@/components/dashboard/widget-registry'; import { dashboardImageSrc } from '@/lib/dashboard-images-api'; import { type PictureFrameEntry, entryKey, pickNextIndex, resolvePictureFrameConfig, } from './picture-frame-config'; import { PictureFrameLightbox } from './picture-frame-lightbox'; /** * PictureFrameWidget — Diashow aus hochgeladenen Bildern und https-Adressen * (quick-260921-pi9). * * Woher die Bytes kommen: hochgeladene Bilder ueber den Next-Rewrite * `/api-proxy/dashboard/images/:id` (Cookie laeuft mit, eigener Origin); * Webadressen laedt AUSSCHLIESSLICH der Browser des Benutzers direkt per * `` — der Server ruft nie eine Adresse ab (T-PI9-05: keine * SSRF-Flaeche, kein Proxy-Endpunkt, der eine URL entgegennimmt; interne * Hosts sieht damit nur, wer sie ohnehin erreicht). `referrerPolicy= * "no-referrer"` an jedem Bild, damit der Fremdhost die Tessera-Adresse * nicht erfaehrt (T-PI9-09). * * Was gerendert wird, entscheidet `resolvePictureFrameConfig` (T-PI9-07): * nur https-Adressen, nur Upload-Eintraege mit Kennung. Ein Bild, das nicht * laedt (`onError`), verlaesst den Umlauf; sind alle kaputt, steht * „Bild nicht verfuegbar“ (Entscheidung 7: leerer Zustand als zentrierter * grauer Text wie die anderen Widgets). * * Wechsel per Timer nur bei > 1 sichtbarem Bild, Intervall > 0 und * geschlossener Grossansicht; Zufall waehlt nie das aktuelle erneut * (`pickNextIndex`); der Timer wird beim Aushaengen und bei jeder Aenderung * der Eingaben geraeumt (Entscheidung 6). * * Klick auf das Bild NUR ausserhalb des Bearbeitungsmodus (Entscheidung 5): * dort liegt Bild + Streifen in einem ` )} {lightboxOpen && !isEditMode && ( )} ); } function entrySrc(entry: PictureFrameEntry): string { return entry.kind === 'upload' ? dashboardImageSrc(entry.imageId) : entry.url; }