feat: implement recently played albums and sidebar shortcut preferences

This commit is contained in:
2025-07-10 16:35:11 +00:00
committed by GitHub
parent 31aec81e8e
commit 5653460e06
5 changed files with 127 additions and 4 deletions

View File

@@ -1 +1 @@
NEXT_PUBLIC_COMMIT_SHA=c1541e6
NEXT_PUBLIC_COMMIT_SHA=31aec81

View File

@@ -7,9 +7,11 @@ import { Button } from "../../components/ui/button";
import { ScrollArea } from "../../components/ui/scroll-area";
import Link from "next/link";
import Image from "next/image";
import { Playlist } from "@/lib/navidrome";
import { Playlist, Album } from "@/lib/navidrome";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useNavidrome } from "./NavidromeContext";
import { useRecentlyPlayedAlbums } from "@/hooks/use-recently-played-albums";
import { useSidebarShortcuts } from "@/hooks/use-sidebar-shortcuts";
import {
ContextMenu,
ContextMenuContent,
@@ -29,6 +31,8 @@ interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
export function Sidebar({ className, playlists, collapsed = false, onToggle, visible = true, favoriteAlbums = [], onRemoveFavoriteAlbum }: SidebarProps) {
const pathname = usePathname();
const { api } = useNavidrome();
const { recentAlbums } = useRecentlyPlayedAlbums();
const { showPlaylists, showAlbums } = useSidebarShortcuts();
if (!visible) {
return null;
@@ -337,7 +341,7 @@ export function Sidebar({ className, playlists, collapsed = false, onToggle, vis
</Link>
{/* Favorite Albums Section */}
{favoriteAlbums.length > 0 && (
{showAlbums && favoriteAlbums.length > 0 && (
<>
<div className="border-t my-2"></div>
{favoriteAlbums.slice(0, 5).map((album) => (
@@ -393,9 +397,41 @@ export function Sidebar({ className, playlists, collapsed = false, onToggle, vis
))}
</>
)}
{/* Recently Played Albums Section */}
{showAlbums && recentAlbums.length > 0 && (
<>
<div className="border-t my-2"></div>
{recentAlbums.slice(0, 5).map((album) => {
const albumImageUrl = album.coverArt && api
? api.getCoverArtUrl(album.coverArt, 32)
: '/play.png';
return (
<Link key={album.id} href={`/album/${album.id}`}>
<Button
variant="ghost"
className="w-full justify-center px-2 h-10"
title={`${album.name} - ${album.artist} (Recently Played)`}
>
<div className="w-6 h-6 rounded-sm overflow-hidden">
<Image
src={albumImageUrl}
alt={album.name}
width={24}
height={24}
className="w-full h-full object-cover"
/>
</div>
</Button>
</Link>
);
})}
</>
)}
{/* Playlists Section */}
{playlists.length > 0 && (
{showPlaylists && playlists.length > 0 && (
<>
<div className="border-t my-2"></div>
{playlists.slice(0, 5).map((playlist) => {

View File

@@ -10,6 +10,7 @@ import { useTheme } from '@/app/components/ThemeProvider';
import { useNavidromeConfig } from '@/app/components/NavidromeConfigContext';
import { useToast } from '@/hooks/use-toast';
import { useStandaloneLastFm } from '@/hooks/use-standalone-lastfm';
import { useSidebarShortcuts, SidebarShortcutType } from '@/hooks/use-sidebar-shortcuts';
import { FaServer, FaUser, FaLock, FaCheck, FaTimes, FaLastfm, FaCog } from 'react-icons/fa';
import { Settings, ExternalLink } from 'lucide-react';
@@ -18,6 +19,7 @@ const SettingsPage = () => {
const { config, updateConfig, isConnected, testConnection, clearConfig } = useNavidromeConfig();
const { toast } = useToast();
const { isEnabled: isStandaloneLastFmEnabled, getCredentials, getAuthUrl, getSessionKey } = useStandaloneLastFm();
const { shortcutType, updateShortcutType } = useSidebarShortcuts();
const [formData, setFormData] = useState({
serverUrl: '',
@@ -568,9 +570,29 @@ const SettingsPage = () => {
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="sidebar-shortcuts">Sidebar Shortcuts</Label>
<Select
value={shortcutType}
onValueChange={(value: SidebarShortcutType) => updateShortcutType(value)}
>
<SelectTrigger id="sidebar-shortcuts">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="both">Albums & Playlists</SelectItem>
<SelectItem value="albums">Albums Only</SelectItem>
<SelectItem value="playlists">Playlists Only</SelectItem>
</SelectContent>
</Select>
</div>
<div className="text-sm text-muted-foreground space-y-2">
<p><strong>Visible:</strong> Sidebar is always shown with icon navigation</p>
<p><strong>Hidden:</strong> Sidebar is completely hidden for maximum space</p>
<p><strong>Albums & Playlists:</strong> Show both favorite albums, recently played albums, and playlists as shortcuts</p>
<p><strong>Albums Only:</strong> Show only favorite and recently played albums as shortcuts</p>
<p><strong>Playlists Only:</strong> Show only playlists as shortcuts</p>
<p className="mt-3"><strong>Note:</strong> The sidebar now shows only icons with tooltips on hover for a cleaner interface.</p>
</div>
</CardContent>

View File

@@ -0,0 +1,36 @@
'use client';
import { useState, useEffect } from 'react';
import { Album } from '@/lib/navidrome';
import { useNavidrome } from '@/app/components/NavidromeContext';
export function useRecentlyPlayedAlbums() {
const [recentAlbums, setRecentAlbums] = useState<Album[]>([]);
const [loading, setLoading] = useState(true);
const { api } = useNavidrome();
const fetchRecentAlbums = async () => {
if (!api) return;
try {
setLoading(true);
const albums = await api.getAlbums('recent', 5, 0);
setRecentAlbums(albums);
} catch (error) {
console.error('Failed to fetch recent albums:', error);
setRecentAlbums([]);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchRecentAlbums();
}, [api]);
return {
recentAlbums,
loading,
refetch: fetchRecentAlbums
};
}

View File

@@ -0,0 +1,29 @@
'use client';
import { useState, useEffect } from 'react';
export type SidebarShortcutType = 'playlists' | 'albums' | 'both';
export function useSidebarShortcuts() {
const [shortcutType, setShortcutType] = useState<SidebarShortcutType>('both');
useEffect(() => {
// Load preference from localStorage
const savedType = localStorage.getItem('sidebar-shortcut-type');
if (savedType && ['playlists', 'albums', 'both'].includes(savedType)) {
setShortcutType(savedType as SidebarShortcutType);
}
}, []);
const updateShortcutType = (type: SidebarShortcutType) => {
setShortcutType(type);
localStorage.setItem('sidebar-shortcut-type', type);
};
return {
shortcutType,
updateShortcutType,
showPlaylists: shortcutType === 'playlists' || shortcutType === 'both',
showAlbums: shortcutType === 'albums' || shortcutType === 'both'
};
}