feat(recent): Tab 'Neu' (letzte 10 Uploads) + -Badge für letzte 5; API markiert isRecent via mtime
This commit is contained in:
parent
beeffb7605
commit
129578cb3a
3 changed files with 44 additions and 4 deletions
|
|
@ -248,16 +248,40 @@ app.get('/api/sounds', (req: Request, res: Response) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const allItems = [...rootFiles, ...folderItems].sort((a, b) => a.name.localeCompare(b.name));
|
const allItems = [...rootFiles, ...folderItems].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
// Zeitstempel für Neu-Logik
|
||||||
|
type ItemWithTime = { fileName: string; name: string; folder: string; relativePath: string; mtimeMs: number };
|
||||||
|
const allWithTime: ItemWithTime[] = [...allItems].map((it) => {
|
||||||
|
const stat = fs.statSync(path.join(SOUNDS_DIR, it.relativePath));
|
||||||
|
return { ...it, mtimeMs: stat.mtimeMs };
|
||||||
|
});
|
||||||
|
const sortedByNewest = [...allWithTime].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
||||||
|
const recentTop10 = sortedByNewest.slice(0, 10);
|
||||||
|
const recentTop5Set = new Set(recentTop10.slice(0, 5).map((x) => x.relativePath));
|
||||||
let itemsByFolder = allItems;
|
let itemsByFolder = allItems;
|
||||||
if (folderFilter !== '__all__') {
|
if (folderFilter !== '__all__') {
|
||||||
|
if (folderFilter === '__recent__') {
|
||||||
|
itemsByFolder = recentTop10.map(({ fileName, name, folder, relativePath }) => ({ fileName, name, folder, relativePath }));
|
||||||
|
} else {
|
||||||
itemsByFolder = allItems.filter((it) => (folderFilter === '' ? it.folder === '' : it.folder === folderFilter));
|
itemsByFolder = allItems.filter((it) => (folderFilter === '' ? it.folder === '' : it.folder === folderFilter));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const filteredItems = itemsByFolder.filter((s) => (q ? s.name.toLowerCase().includes(q) : true));
|
const filteredItems = itemsByFolder.filter((s) => (q ? s.name.toLowerCase().includes(q) : true));
|
||||||
|
|
||||||
const total = allItems.length;
|
const total = allItems.length;
|
||||||
const foldersOut = [{ key: '__all__', name: 'Alle', count: total }, ...folders];
|
const recentCount = Math.min(10, total);
|
||||||
|
const foldersOut = [
|
||||||
|
{ key: '__all__', name: 'Alle', count: total },
|
||||||
|
{ key: '__recent__', name: 'Neu', count: recentCount },
|
||||||
|
...folders
|
||||||
|
];
|
||||||
|
// isRecent-Flag für UI (Top 5 der neuesten)
|
||||||
|
const withRecentFlag = filteredItems.map((it) => ({
|
||||||
|
...it,
|
||||||
|
isRecent: recentTop5Set.has(it.relativePath ?? it.fileName)
|
||||||
|
}));
|
||||||
|
|
||||||
res.json({ items: filteredItems, total, folders: foldersOut });
|
res.json({ items: withRecentFlag, total, folders: foldersOut });
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/channels', (_req: Request, res: Response) => {
|
app.get('/api/channels', (_req: Request, res: Response) => {
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,21 @@ export default function App() {
|
||||||
>
|
>
|
||||||
Favoriten ({favCount})
|
Favoriten ({favCount})
|
||||||
</button>
|
</button>
|
||||||
|
{/* Neueste 10 */}
|
||||||
|
<button
|
||||||
|
key="__recent__"
|
||||||
|
className={`tab ${activeFolder === '__recent__' ? 'active' : ''}`}
|
||||||
|
type="button"
|
||||||
|
onClick={async () => {
|
||||||
|
setActiveFolder('__recent__');
|
||||||
|
const resp = await fetchSounds(undefined, '__recent__');
|
||||||
|
setSounds(resp.items);
|
||||||
|
setTotal(resp.total);
|
||||||
|
setFolders(resp.folders);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Neu
|
||||||
|
</button>
|
||||||
{folders.map((f) => (
|
{folders.map((f) => (
|
||||||
<button
|
<button
|
||||||
key={f.key}
|
key={f.key}
|
||||||
|
|
@ -192,7 +207,7 @@ export default function App() {
|
||||||
return (
|
return (
|
||||||
<div key={`${s.fileName}-${s.name}`} className="sound-wrap">
|
<div key={`${s.fileName}-${s.name}`} className="sound-wrap">
|
||||||
<button className="sound" type="button" onClick={() => handlePlay(s.name, s.relativePath)} disabled={loading}>
|
<button className="sound" type="button" onClick={() => handlePlay(s.name, s.relativePath)} disabled={loading}>
|
||||||
{s.name}
|
{s.isRecent ? '🆕 ' : ''}{s.name}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`fav ${isFav ? 'active' : ''}`}
|
className={`fav ${isFav ? 'active' : ''}`}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ export type Sound = {
|
||||||
name: string;
|
name: string;
|
||||||
folder?: string;
|
folder?: string;
|
||||||
relativePath?: string;
|
relativePath?: string;
|
||||||
|
isRecent?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SoundsResponse = {
|
export type SoundsResponse = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue