feat(MediaSet): refactor MediaSet to use MediaSetEntry type for improved type safety
This commit is contained in:
parent
b8d3041035
commit
d6b50164e4
|
|
@ -4,7 +4,7 @@ import { motion, AnimatePresence } from 'motion/react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { fileExists, getFileContents, joinPath } from '../webdav';
|
import { fileExists, getFileContents, joinPath } from '../webdav';
|
||||||
import MediaBrowser from './MediaBrowser';
|
import MediaBrowser from './MediaBrowser';
|
||||||
import MediaSet from './MediaSet';
|
import MediaSet, { type MediaSetEntry } from './MediaSet';
|
||||||
|
|
||||||
interface Device {
|
interface Device {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -122,11 +122,11 @@ export default function DeviceDetailOverlay({
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prefer an explicit .lst-derived mediaSet stored in config; fall back to pattern detection.
|
// Prefer an explicit .lst-derived mediaSet stored in config; fall back to pattern detection.
|
||||||
const [mediaSetFiles, setMediaSetFiles] = useState<string[] | null>(null);
|
const [mediaSetFiles, setMediaSetFiles] = useState<MediaSetEntry[] | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Array.isArray(deviceData.media_set) && deviceData.media_set.length > 0) {
|
if (Array.isArray(deviceData.media_set) && deviceData.media_set.length > 0) {
|
||||||
setMediaSetFiles(deviceData.media_set as string[]);
|
setMediaSetFiles(deviceData.media_set as MediaSetEntry[]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!deviceData.url) { setMediaSetFiles(null); return; }
|
if (!deviceData.url) { setMediaSetFiles(null); return; }
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,20 @@
|
||||||
|
export type MediaSetEntry = string | { url: string; name?: string };
|
||||||
|
|
||||||
|
export function mediaSetEntryUrl(e: MediaSetEntry): string {
|
||||||
|
return typeof e === 'string' ? e : e.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entryLabel(e: MediaSetEntry, index: number): string {
|
||||||
|
if (typeof e === 'object' && e.name) return `${index + 1}: ${e.name}`;
|
||||||
|
const file = mediaSetEntryUrl(e);
|
||||||
|
const fileName = file.split('/').pop() || file;
|
||||||
|
return `${index + 1}: ${fileName.replace(/\.[^.]+$/, '')}`;
|
||||||
|
}
|
||||||
|
|
||||||
interface MediaSetProps {
|
interface MediaSetProps {
|
||||||
files: string[];
|
files: MediaSetEntry[];
|
||||||
activeUrl: string;
|
activeUrl: string;
|
||||||
onSwitch: (file: string) => void;
|
onSwitch: (url: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MediaSet({ files, activeUrl, onSwitch }: MediaSetProps) {
|
export default function MediaSet({ files, activeUrl, onSwitch }: MediaSetProps) {
|
||||||
|
|
@ -9,21 +22,21 @@ export default function MediaSet({ files, activeUrl, onSwitch }: MediaSetProps)
|
||||||
<div>
|
<div>
|
||||||
<div className="text-sm text-neutral-500 mb-2">Media Set</div>
|
<div className="text-sm text-neutral-500 mb-2">Media Set</div>
|
||||||
<div className="flex gap-2 flex-wrap">
|
<div className="flex gap-2 flex-wrap">
|
||||||
{files.map((file, index) => {
|
{files.map((entry, index) => {
|
||||||
const fileName = file.split('/').pop() || file;
|
const url = mediaSetEntryUrl(entry);
|
||||||
const title = fileName.replace(/\.[^.]+$/, '');
|
const label = entryLabel(entry, index);
|
||||||
const active = activeUrl === file;
|
const active = activeUrl === url;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={file}
|
key={url}
|
||||||
onClick={() => onSwitch(file)}
|
onClick={() => onSwitch(url)}
|
||||||
className={`px-3 py-1.5 rounded-lg text-sm ${
|
className={`px-3 py-1.5 rounded-lg text-sm ${
|
||||||
active
|
active
|
||||||
? 'bg-blue-600 text-white'
|
? 'bg-blue-600 text-white'
|
||||||
: 'bg-neutral-100 text-neutral-700 hover:bg-neutral-200'
|
: 'bg-neutral-100 text-neutral-700 hover:bg-neutral-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{`${index + 1}: ${title}`}
|
{label}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { MediaEntry } from './MediaEntry';
|
||||||
import DirectorySlideshow from './DirectorySlideshow';
|
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, { type MediaSetEntry } from './MediaSet';
|
||||||
import { ImageWithFallback } from './figma/ImageWithFallback';
|
import { ImageWithFallback } from './figma/ImageWithFallback';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog';
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog';
|
||||||
import { ConfirmDialog, type ConfirmOptions } from './ui/confirm-dialog';
|
import { ConfirmDialog, type ConfirmOptions } from './ui/confirm-dialog';
|
||||||
|
|
@ -45,10 +45,10 @@ export default function StatusPage({ config, setConfig, onOpenFileManager }: Sta
|
||||||
? (activeDevice.base_url || splitPath(activeDevice.url || '/').parent)
|
? (activeDevice.base_url || splitPath(activeDevice.url || '/').parent)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const mediaSetFiles: string[] | null = (() => {
|
const mediaSetFiles: MediaSetEntry[] | null = (() => {
|
||||||
if (!activeDevice?.url) return null;
|
if (!activeDevice?.url) return null;
|
||||||
if (Array.isArray(activeDevice.media_set) && activeDevice.media_set.length > 0)
|
if (Array.isArray(activeDevice.media_set) && activeDevice.media_set.length > 0)
|
||||||
return activeDevice.media_set as string[];
|
return activeDevice.media_set as MediaSetEntry[];
|
||||||
const match = (activeDevice.url as string).match(/^(.+?)(\d+)(\.[^.]+)$/);
|
const match = (activeDevice.url as string).match(/^(.+?)(\d+)(\.[^.]+)$/);
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
const [, prefix, , ext] = match;
|
const [, prefix, , ext] = match;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user