Quellcode durchsuchen

Mute bug fix

Sviluppo_Carrello_Immagini
Lorenzo Pollutri vor 22 Stunden
Ursprung
Commit
ba86f8fbe8
3 geänderte Dateien mit 88 neuen und 19 gelöschten Zeilen
  1. +34
    -9
      app/admin/page.tsx
  2. +52
    -9
      components/PublicGrid.tsx
  3. +2
    -1
      types/index.ts

+ 34
- 9
app/admin/page.tsx Datei anzeigen

@@ -149,6 +149,15 @@ export default function AdminDashboard() {
}));
};
const setVolume = (index: number, volume: number) => {
setIsEditing(prev => ({
...prev,
extraMedia: (prev?.extraMedia || []).map((m, i) =>
i === index ? { ...m, volume } : m
),
}));
};
const handleSaveCard = async () => {
if (!isEditing) return;
const generateSafeId = () => 'card-' + Date.now().toString(36) + Math.random().toString(36).substring(2);
@@ -505,15 +514,31 @@ export default function AdminDashboard() {
{video ? 'Video' : 'Image'}
</div>
{video && (
<label className="flex items-center gap-2 mt-1 cursor-pointer">
<input
type="checkbox"
checked={!!item.autoplay}
onChange={() => toggleAutoplay(i)}
className="w-4 h-4 text-blue-600 rounded"
/>
<span className="text-sm text-gray-700">Autoplay (muted)</span>
</label>
<div className="mt-1 space-y-1.5">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={!!item.autoplay}
onChange={() => toggleAutoplay(i)}
className="w-4 h-4 text-blue-600 rounded"
/>
<span className="text-sm text-gray-700">Autoplay</span>
</label>
<div className="flex items-center gap-2">
<span className="text-sm text-gray-700 shrink-0">Volume:</span>
<input
type="range"
min={0}
max={100}
value={Math.round((item.volume ?? 1) * 100)}
onChange={e => setVolume(i, parseInt(e.target.value, 10) / 100)}
className="flex-1 accent-blue-600"
/>
<span className="text-xs text-gray-600 w-10 text-right tabular-nums">
{Math.round((item.volume ?? 1) * 100)}%
</span>
</div>
</div>
)}
</div>
<button


+ 52
- 9
components/PublicGrid.tsx Datei anzeigen

@@ -19,11 +19,22 @@ function MediaCarousel({
const markPlaying = (i: number) => setPlaying(p => { const n = new Set(p); n.add(i); return n; });
const markPaused = (i: number) => setPlaying(p => { const n = new Set(p); n.delete(i); return n; });

const applyVolume = (v: HTMLVideoElement, item: MediaItem) => {
const desired = item.volume ?? 1;
v.volume = desired;
v.muted = desired === 0;
};

const togglePlay = (i: number) => {
const v = videoRefs.current[i];
if (!v) return;
if (v.paused) v.play().catch(() => {});
else v.pause();
if (v.paused) {
const item = items[i];
if (item) applyVolume(v, item);
v.play().catch(() => {});
} else {
v.pause();
}
};

const prev = useCallback(() => setCurrent(i => (i - 1 + items.length) % items.length), [items.length]);
@@ -48,8 +59,12 @@ function MediaCarousel({
if (idx !== current) { vid.pause(); return; }
const item = items[idx];
if (item && isVideoUrl(item.url) && item.autoplay) {
vid.muted = true;
vid.play().catch(() => {});
applyVolume(vid, item);
vid.play().catch(() => {
// Browser blocked unmuted autoplay — fall back to muted.
vid.muted = true;
vid.play().catch(() => {});
});
}
});
}, [current, items]);
@@ -90,8 +105,8 @@ function MediaCarousel({
src={item.url}
className="w-full h-full object-contain bg-black pointer-events-none"
playsInline
muted={!!item.autoplay}
preload="metadata"
onLoadedMetadata={(e) => applyVolume(e.currentTarget, item)}
onPlay={() => markPlaying(i)}
onPause={() => markPaused(i)}
onEnded={() => markPaused(i)}
@@ -178,11 +193,22 @@ function FullscreenViewer({
const markPlaying = (i: number) => setPlaying(p => { const n = new Set(p); n.add(i); return n; });
const markPaused = (i: number) => setPlaying(p => { const n = new Set(p); n.delete(i); return n; });

const applyVolume = (v: HTMLVideoElement, item: MediaItem) => {
const desired = item.volume ?? 1;
v.volume = desired;
v.muted = desired === 0;
};

const togglePlay = (i: number) => {
const v = videoRefs.current[i];
if (!v) return;
if (v.paused) v.play().catch(() => {});
else v.pause();
if (v.paused) {
const item = items[i];
if (item) applyVolume(v, item);
v.play().catch(() => {});
} else {
v.pause();
}
};

const prev = useCallback(() => setCurrent(i => (i - 1 + items.length) % items.length), [items.length]);
@@ -198,6 +224,23 @@ function FullscreenViewer({
return () => window.removeEventListener('keydown', onKey);
}, [prev, next, onClose]);

// Pause non-current videos in fullscreen
useEffect(() => {
Object.entries(videoRefs.current).forEach(([key, vid]) => {
if (!vid) return;
const idx = parseInt(key, 10);
if (idx !== current) { vid.pause(); return; }
const item = items[idx];
if (item && isVideoUrl(item.url) && item.autoplay) {
applyVolume(vid, item);
vid.play().catch(() => {
vid.muted = true;
vid.play().catch(() => {});
});
}
});
}, [current, items]);

const onTouchStart = (e: React.TouchEvent) => { touchStartX.current = e.touches[0].clientX; };
const onTouchEnd = (e: React.TouchEvent) => {
if (touchStartX.current === null) return;
@@ -231,8 +274,8 @@ function FullscreenViewer({
src={item.url}
className="max-w-full max-h-full pointer-events-none"
playsInline
autoPlay={!!item.autoplay}
muted={!!item.autoplay}
preload="metadata"
onLoadedMetadata={(e) => applyVolume(e.currentTarget, item)}
onPlay={() => markPlaying(i)}
onPause={() => markPaused(i)}
onEnded={() => markPaused(i)}


+ 2
- 1
types/index.ts Datei anzeigen

@@ -2,7 +2,8 @@ export type CardType = 'INFO_PAGE' | 'EXTERNAL_LINK' | 'IMAGE_GALLERY' | 'SERVIC
export type MediaItem = {
url: string;
autoplay?: boolean; // videos only — autoplay muted when shown
autoplay?: boolean; // videos only — start playing as soon as the slide is shown
volume?: number; // videos only — initial volume 0..1 (default 1). Volume === 0 means muted.
};
export interface Card {


Laden…
Abbrechen
Speichern