feat(StatusPage): integrate DirectorySlideshow component for displaying images from active directory
This commit is contained in:
parent
010cd68e3f
commit
290cdb8ae9
59
src/app/components/DirectorySlideshow.tsx
Normal file
59
src/app/components/DirectorySlideshow.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { listDirectory, getWebDAVBaseUrl, type EntryInfo } from '../webdav';
|
||||||
|
import { IMAGE_EXTS } from './MediaEntry';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DirectorySlideshow({ path }: Props) {
|
||||||
|
const [images, setImages] = useState<EntryInfo[]>([]);
|
||||||
|
const [idx, setIdx] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!path) { setImages([]); return; }
|
||||||
|
listDirectory(path)
|
||||||
|
.then(entries => {
|
||||||
|
const imgs = entries.filter(e => {
|
||||||
|
if (e.type !== 'file') return false;
|
||||||
|
const ext = e.name.split('.').pop()?.toLowerCase() ?? '';
|
||||||
|
return IMAGE_EXTS.has(ext);
|
||||||
|
});
|
||||||
|
setImages(imgs);
|
||||||
|
setIdx(0);
|
||||||
|
})
|
||||||
|
.catch(() => setImages([]));
|
||||||
|
}, [path]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (images.length <= 1) return;
|
||||||
|
const t = setInterval(() => setIdx(i => (i + 1) % images.length), 4000);
|
||||||
|
return () => clearInterval(t);
|
||||||
|
}, [images.length]);
|
||||||
|
|
||||||
|
if (images.length === 0) return null;
|
||||||
|
|
||||||
|
const current = images[idx];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-3 rounded-lg overflow-hidden">
|
||||||
|
<img
|
||||||
|
key={current.path}
|
||||||
|
src={getWebDAVBaseUrl() + current.path}
|
||||||
|
alt={current.name}
|
||||||
|
className="w-full h-48 object-contain"
|
||||||
|
/>
|
||||||
|
{images.length > 1 && (
|
||||||
|
<div className="flex justify-center gap-1.5 py-1.5">
|
||||||
|
{images.map((_, i) => (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
onClick={() => setIdx(i)}
|
||||||
|
className={`w-1.5 h-1.5 rounded-full transition-colors ${i === idx ? 'bg-white' : 'bg-white/40'}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import { HardDrive, Activity, Wifi, Radio, Clock, RefreshCw, Loader2, Printer, P
|
||||||
import { listDirectory, deletePath, getFileContents, getWebDAVBaseUrl, humanFileSize, splitPath, type EntryInfo } from '../webdav';
|
import { listDirectory, deletePath, getFileContents, getWebDAVBaseUrl, humanFileSize, splitPath, type EntryInfo } from '../webdav';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { MediaEntry } from './MediaEntry';
|
import { MediaEntry } from './MediaEntry';
|
||||||
|
import DirectorySlideshow from './DirectorySlideshow';
|
||||||
import { useWs } from '../ws';
|
import { useWs } from '../ws';
|
||||||
import DeviceDetailOverlay from './DeviceDetailOverlay';
|
import DeviceDetailOverlay from './DeviceDetailOverlay';
|
||||||
import MediaSet from './MediaSet';
|
import MediaSet from './MediaSet';
|
||||||
|
|
@ -25,7 +26,6 @@ export default function StatusPage({ config, setConfig, onOpenFileManager }: Sta
|
||||||
psram: { total: 8192, free: 4096 },
|
psram: { total: 8192, free: 4096 },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Overlay state for active device
|
|
||||||
const [showDeviceOverlay, setShowDeviceOverlay] = useState(false);
|
const [showDeviceOverlay, setShowDeviceOverlay] = useState(false);
|
||||||
// Find the first enabled device as the active device
|
// Find the first enabled device as the active device
|
||||||
const findActiveDevice = () => {
|
const findActiveDevice = () => {
|
||||||
|
|
@ -41,6 +41,9 @@ export default function StatusPage({ config, setConfig, onOpenFileManager }: Sta
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeDevice = findActiveDevice();
|
const activeDevice = findActiveDevice();
|
||||||
|
const activeDir = activeDevice
|
||||||
|
? (activeDevice.base_url || splitPath(activeDevice.url || '/').parent)
|
||||||
|
: null;
|
||||||
|
|
||||||
const mediaSetFiles: string[] | null = (() => {
|
const mediaSetFiles: string[] | null = (() => {
|
||||||
if (!activeDevice?.url) return null;
|
if (!activeDevice?.url) return null;
|
||||||
|
|
@ -172,6 +175,8 @@ export default function StatusPage({ config, setConfig, onOpenFileManager }: Sta
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{activeDir && <DirectorySlideshow path={activeDir} />}
|
||||||
|
|
||||||
{mediaSetFiles && (
|
{mediaSetFiles && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<MediaSet files={mediaSetFiles} activeUrl={activeDevice.url ?? ''} onSwitch={switchActiveMedia} />
|
<MediaSet files={mediaSetFiles} activeUrl={activeDevice.url ?? ''} onSwitch={switchActiveMedia} />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user