fix: duplicate React keys, update meta tags, improve mobile card layout
This commit is contained in:
parent
35876ee046
commit
50248cf165
3 changed files with 35 additions and 25 deletions
|
|
@ -24,6 +24,9 @@ export const metadata: Metadata = {
|
||||||
statusBarStyle: "black-translucent",
|
statusBarStyle: "black-translucent",
|
||||||
title: "Audiophile Web Player",
|
title: "Audiophile Web Player",
|
||||||
},
|
},
|
||||||
|
other: {
|
||||||
|
"mobile-web-app-capable": "yes",
|
||||||
|
},
|
||||||
icons: {
|
icons: {
|
||||||
icon: "/icons/icon-192x192.png",
|
icon: "/icons/icon-192x192.png",
|
||||||
apple: "/icons/icon-512x512.png",
|
apple: "/icons/icon-512x512.png",
|
||||||
|
|
|
||||||
|
|
@ -180,25 +180,25 @@ export default function Home() {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6">
|
<div className="grid grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2 md:gap-6">
|
||||||
{sortPlaylists(playlists).slice(0, 5).map((playlist: any) => (
|
{sortPlaylists(playlists).slice(0, 5).map((playlist: any) => (
|
||||||
<Link href={`/playlist?id=${playlist.id}`} key={playlist.id}>
|
<Link href={`/playlist?id=${playlist.id}`} key={playlist.id}>
|
||||||
<div className="bg-[#181818] p-4 rounded-md hover:bg-[#282828] transition duration-300 group cursor-pointer relative h-full flex flex-col">
|
<div className="bg-[#181818] p-2 md:p-4 rounded-md hover:bg-[#282828] transition duration-300 group cursor-pointer relative h-full flex flex-col">
|
||||||
<div className="relative mb-4">
|
<div className="relative mb-2 md:mb-4">
|
||||||
<CoverImage
|
<CoverImage
|
||||||
src={playlist.cover_url}
|
src={playlist.cover_url}
|
||||||
alt={playlist.title}
|
alt={playlist.title}
|
||||||
className="w-full aspect-square object-cover rounded-md shadow-lg"
|
className="w-full aspect-square object-cover rounded-md shadow-lg"
|
||||||
fallbackText={playlist.title.substring(0, 2).toUpperCase()}
|
fallbackText={playlist.title.substring(0, 2).toUpperCase()}
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-2 right-2 translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition duration-300 shadow-xl">
|
<div className="absolute bottom-1 right-1 md:bottom-2 md:right-2 translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition duration-300 shadow-xl">
|
||||||
<div className="w-12 h-12 bg-[#1DB954] rounded-full flex items-center justify-center hover:scale-105">
|
<div className="w-8 h-8 md:w-12 md:h-12 bg-[#1DB954] rounded-full flex items-center justify-center hover:scale-105">
|
||||||
<Play className="fill-black text-black ml-1" />
|
<Play className="fill-black text-black ml-0.5 w-4 h-4 md:w-6 md:h-6" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-bold mb-1 truncate">{playlist.title}</h3>
|
<h3 className="font-bold mb-0.5 md:mb-1 truncate text-xs md:text-base">{playlist.title}</h3>
|
||||||
<p className="text-sm text-[#a7a7a7] line-clamp-2">{playlist.description}</p>
|
<p className="text-[10px] md:text-sm text-[#a7a7a7] line-clamp-2">{playlist.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|
@ -314,24 +314,24 @@ function MadeForYouSection() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-6">
|
<div className="grid grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2 md:gap-6">
|
||||||
{recommendations.slice(0, 5).map((track, i) => (
|
{recommendations.slice(0, 5).map((track, i) => (
|
||||||
<div key={i} onClick={() => playTrack(track, recommendations)} className="bg-[#181818] p-4 rounded-md hover:bg-[#282828] transition duration-300 group cursor-pointer relative h-full flex flex-col">
|
<div key={i} onClick={() => playTrack(track, recommendations)} className="bg-[#181818] p-2 md:p-4 rounded-md hover:bg-[#282828] transition duration-300 group cursor-pointer relative h-full flex flex-col">
|
||||||
<div className="relative mb-4">
|
<div className="relative mb-2 md:mb-4">
|
||||||
<CoverImage
|
<CoverImage
|
||||||
src={track.cover_url}
|
src={track.cover_url}
|
||||||
alt={track.title}
|
alt={track.title}
|
||||||
className="w-full aspect-square object-cover rounded-md shadow-lg"
|
className="w-full aspect-square object-cover rounded-md shadow-lg"
|
||||||
fallbackText={track.title?.substring(0, 2).toUpperCase()}
|
fallbackText={track.title?.substring(0, 2).toUpperCase()}
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-2 right-2 translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition duration-300 shadow-xl">
|
<div className="absolute bottom-1 right-1 md:bottom-2 md:right-2 translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition duration-300 shadow-xl">
|
||||||
<div className="w-12 h-12 bg-[#1DB954] rounded-full flex items-center justify-center hover:scale-105">
|
<div className="w-8 h-8 md:w-12 md:h-12 bg-[#1DB954] rounded-full flex items-center justify-center hover:scale-105">
|
||||||
<Play className="fill-black text-black ml-1" />
|
<Play className="fill-black text-black ml-0.5 w-4 h-4 md:w-6 md:h-6" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-bold mb-1 truncate">{track.title}</h3>
|
<h3 className="font-bold mb-0.5 md:mb-1 truncate text-xs md:text-base">{track.title}</h3>
|
||||||
<p className="text-sm text-[#a7a7a7] line-clamp-2">{track.artist}</p>
|
<p className="text-[10px] md:text-sm text-[#a7a7a7] line-clamp-2">{track.artist}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -388,24 +388,24 @@ function RecommendedAlbumsSection() {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4 md:gap-6">
|
<div className="grid grid-cols-3 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2 md:gap-6">
|
||||||
{albums.slice(0, 5).map((album, i) => (
|
{albums.slice(0, 5).map((album, i) => (
|
||||||
<Link href={`/playlist?id=${album.id}`} key={i}>
|
<Link href={`/playlist?id=${album.id}`} key={i}>
|
||||||
<div className="bg-[#181818] p-4 rounded-md hover:bg-[#282828] transition duration-300 group cursor-pointer relative h-full flex flex-col">
|
<div className="bg-[#181818] p-2 md:p-4 rounded-md hover:bg-[#282828] transition duration-300 group cursor-pointer relative h-full flex flex-col">
|
||||||
<div className="relative mb-4">
|
<div className="relative mb-2 md:mb-4">
|
||||||
<CoverImage
|
<CoverImage
|
||||||
src={album.cover_url}
|
src={album.cover_url}
|
||||||
alt={album.title}
|
alt={album.title}
|
||||||
className="w-full aspect-square object-cover rounded-md shadow-lg"
|
className="w-full aspect-square object-cover rounded-md shadow-lg"
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-2 right-2 translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition duration-300 shadow-xl">
|
<div className="absolute bottom-1 right-1 md:bottom-2 md:right-2 translate-y-4 opacity-0 group-hover:translate-y-0 group-hover:opacity-100 transition duration-300 shadow-xl">
|
||||||
<div className="w-12 h-12 bg-[#1DB954] rounded-full flex items-center justify-center hover:scale-105">
|
<div className="w-8 h-8 md:w-12 md:h-12 bg-[#1DB954] rounded-full flex items-center justify-center hover:scale-105">
|
||||||
<Play className="fill-black text-black ml-1" />
|
<Play className="fill-black text-black ml-0.5 w-4 h-4 md:w-6 md:h-6" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-bold mb-1 truncate">{album.title}</h3>
|
<h3 className="font-bold mb-0.5 md:mb-1 truncate text-xs md:text-base">{album.title}</h3>
|
||||||
<p className="text-sm text-[#a7a7a7] line-clamp-2">{album.description}</p>
|
<p className="text-[10px] md:text-sm text-[#a7a7a7] line-clamp-2">{album.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,14 @@ export function LibraryProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
|
||||||
// 2. Local/Backend Content
|
// 2. Local/Backend Content
|
||||||
const browse = await libraryService.getBrowseContent();
|
const browse = await libraryService.getBrowseContent();
|
||||||
const browsePlaylists = Object.values(browse).flat();
|
// Deduplicate by ID to avoid React duplicate key warnings
|
||||||
|
const browsePlaylistsRaw = Object.values(browse).flat();
|
||||||
|
const seenIds = new Map();
|
||||||
|
const browsePlaylists = browsePlaylistsRaw.filter((p: any) => {
|
||||||
|
if (seenIds.has(p.id)) return false;
|
||||||
|
seenIds.set(p.id, true);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
const artistsMap = new Map();
|
const artistsMap = new Map();
|
||||||
const albumsMap = new Map();
|
const albumsMap = new Map();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue