mirror of
https://github.com/FranP-code/spooky-spotify-showcase.git
synced 2025-10-13 00:02:36 +00:00
Album showcase
This commit is contained in:
30
src/app/_components/album-showcase.tsx
Normal file
30
src/app/_components/album-showcase.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { TrackByAlbum } from "./spotify-data";
|
||||||
|
|
||||||
|
export default function AlbumShowcase({
|
||||||
|
album,
|
||||||
|
position,
|
||||||
|
tracks,
|
||||||
|
}: TrackByAlbum) {
|
||||||
|
const imageSource = album.images[0]
|
||||||
|
? album.images[0].url
|
||||||
|
: "https://via.placeholder.com/150";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={album.id}
|
||||||
|
className="flex w-full flex-wrap gap-3 rounded-xl bg-white bg-opacity-10 p-5 backdrop-blur-lg backdrop-filter"
|
||||||
|
>
|
||||||
|
<img className="h-36 w-36 rounded" src={imageSource} alt={album.name} />
|
||||||
|
<section className="flex h-36 flex-col">
|
||||||
|
<h4 className="mb-2 text-lg font-semibold leading-none">
|
||||||
|
{album.name}
|
||||||
|
</h4>
|
||||||
|
<ul className="overflow-scroll">
|
||||||
|
{tracks.map((track) => (
|
||||||
|
<li key={track.id}>{track.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,21 @@
|
|||||||
import SpotifyWebApi from "spotify-web-api-node";
|
import SpotifyWebApi from "spotify-web-api-node";
|
||||||
|
import AlbumShowcase from "./album-showcase";
|
||||||
|
|
||||||
const TRACKS_LIMIT = 20;
|
const TRACKS_LIMIT = 20;
|
||||||
|
|
||||||
|
export type TrackByAlbum = {
|
||||||
|
album: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
images: { url: string }[];
|
||||||
|
};
|
||||||
|
position: number;
|
||||||
|
tracks: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
export default async function SpotifyData({
|
export default async function SpotifyData({
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
@@ -33,25 +47,66 @@ export default async function SpotifyData({
|
|||||||
...track,
|
...track,
|
||||||
position: i + 1,
|
position: i + 1,
|
||||||
}));
|
}));
|
||||||
const tracksByAlbum = tracksData.body.items.reduce((acc, track) => {
|
|
||||||
if (!acc[track.album.id]) {
|
|
||||||
const tracksWithAlbum = tracks.filter(
|
|
||||||
(t) => t.album.id === track.album.id,
|
|
||||||
);
|
|
||||||
acc[track.album.id] = {
|
|
||||||
album: track.album,
|
|
||||||
position:
|
|
||||||
tracksWithAlbum.reduce(
|
|
||||||
(acc, _track) => TRACKS_LIMIT / _track.position + acc,
|
|
||||||
0,
|
|
||||||
) / tracksWithAlbum.length,
|
|
||||||
tracks: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
acc[track.album.id].tracks.push(track);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
|
const tracksByAlbum = tracksData.body.items.reduce(
|
||||||
|
(acc: Record<string, TrackByAlbum>, track) => {
|
||||||
|
if (!acc[track.album.id]) {
|
||||||
|
const tracksWithAlbum = tracks.filter(
|
||||||
|
(t) => t.album.id === track.album.id,
|
||||||
|
);
|
||||||
|
acc[track.album.id] = {
|
||||||
|
album: track.album,
|
||||||
|
position:
|
||||||
|
tracksWithAlbum.reduce(
|
||||||
|
(acc, _track) => TRACKS_LIMIT / _track.position + acc,
|
||||||
|
0,
|
||||||
|
) / tracksWithAlbum.length,
|
||||||
|
tracks: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(acc[track.album.id] || ({ tracks: [] } as any)).tracks.push(track);
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
// type Track = {
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
// album: {
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
// images: { url: string }[];
|
||||||
|
// };
|
||||||
|
// position: number;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// type Album = {
|
||||||
|
// album: {
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
// images: { url: string }[];
|
||||||
|
// };
|
||||||
|
// position: number;
|
||||||
|
// tracks: Track[];
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // type Artist = {
|
||||||
|
// // id: string;
|
||||||
|
// // name: string;
|
||||||
|
// // images: { url: string }[];
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // type SpotifyDataProps = {
|
||||||
|
// // accessToken: string;
|
||||||
|
// // refreshToken: string;
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // type SpotifyDataResponse = {
|
||||||
|
// // albums: Album[];
|
||||||
|
// // artists: Artist[];
|
||||||
|
// // tracks: Track[];
|
||||||
|
// // tracksByAlbum: { [key: string]: Album };
|
||||||
|
// // };
|
||||||
console.log("Albums:", albums);
|
console.log("Albums:", albums);
|
||||||
console.log("Artists:", artists);
|
console.log("Artists:", artists);
|
||||||
console.log("Tracks:", tracks);
|
console.log("Tracks:", tracks);
|
||||||
@@ -90,29 +145,10 @@ export default async function SpotifyData({
|
|||||||
{Object.values(tracksByAlbum)
|
{Object.values(tracksByAlbum)
|
||||||
.sort((a, b) => b.position - a.position)
|
.sort((a, b) => b.position - a.position)
|
||||||
.map((album) => (
|
.map((album) => (
|
||||||
<div key={album.album.id}>
|
<AlbumShowcase {...album} />
|
||||||
<h4>{album.album.name}</h4>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexWrap: "wrap",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
style={{ width: "150px", height: "150px" }}
|
|
||||||
src={album.album.images[0].url}
|
|
||||||
alt={album.album.name}
|
|
||||||
/>
|
|
||||||
<ul>
|
|
||||||
{album.tracks.map((track) => (
|
|
||||||
<li key={track.id}>{track.name}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<h3>Albums images</h3>
|
{/* <h3>Albums images</h3>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -137,7 +173,7 @@ export default async function SpotifyData({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<h3>Artists images</h3>
|
<h3>Artists images</h3>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import { LatestPost } from "@/app/_components/post";
|
|
||||||
import { api, HydrateClient } from "@/trpc/server";
|
import { api, HydrateClient } from "@/trpc/server";
|
||||||
import SpotifyLogin from "./_components/spotify-login";
|
import SpotifyLogin from "./_components/spotify-login";
|
||||||
import SpotifyData from "./_components/spotify-data";
|
import SpotifyData from "./_components/spotify-data";
|
||||||
@@ -45,40 +43,8 @@ export default async function Home({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<HydrateClient>
|
<HydrateClient>
|
||||||
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white">
|
<main className="justify-centerbg-gradient-to-r flex min-h-screen flex-col items-center bg-gradient-to-r from-slate-900 to-slate-700 text-slate-200">
|
||||||
<div className="container flex flex-col items-center justify-center gap-12 px-4 py-16">
|
<div className="container flex flex-col items-center justify-center gap-8 px-4 py-16">
|
||||||
<h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]">
|
|
||||||
Create <span className="text-[hsl(280,100%,70%)]">T3</span> App
|
|
||||||
</h1>
|
|
||||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-8">
|
|
||||||
<Link
|
|
||||||
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
|
|
||||||
href="https://create.t3.gg/en/usage/first-steps"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<h3 className="text-2xl font-bold">First Steps →</h3>
|
|
||||||
<div className="text-lg">
|
|
||||||
Just the basics - Everything you need to know to set up your
|
|
||||||
database and authentication.
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
|
|
||||||
href="https://create.t3.gg/en/introduction"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<h3 className="text-2xl font-bold">Documentation →</h3>
|
|
||||||
<div className="text-lg">
|
|
||||||
Learn more about Create T3 App, the libraries it uses, and how
|
|
||||||
to deploy it.
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center gap-2">
|
|
||||||
<p className="text-2xl text-white">
|
|
||||||
{hello ? hello.greeting : "Loading tRPC query..."}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{userIsLogged ? (
|
{userIsLogged ? (
|
||||||
<SpotifyData
|
<SpotifyData
|
||||||
accessToken={access_token as string}
|
accessToken={access_token as string}
|
||||||
@@ -87,7 +53,6 @@ export default async function Home({
|
|||||||
) : (
|
) : (
|
||||||
<SpotifyLogin />
|
<SpotifyLogin />
|
||||||
)}
|
)}
|
||||||
<LatestPost />
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</HydrateClient>
|
</HydrateClient>
|
||||||
|
|||||||
Reference in New Issue
Block a user