Add Swiper component and AlbumImage for enhanced album display

This commit is contained in:
2024-10-12 18:58:07 -03:00
parent 6fdb87c32a
commit fc590d959c
6 changed files with 185 additions and 84 deletions

View File

@@ -31,6 +31,7 @@
"server-only": "^0.0.1",
"spotify-web-api-node": "^5.0.2",
"superjson": "^2.2.1",
"swiper": "^11.1.14",
"zod": "^3.23.3"
},
"devDependencies": {

27
pnpm-lock.yaml generated
View File

@@ -56,6 +56,9 @@ importers:
superjson:
specifier: ^2.2.1
version: 2.2.1
swiper:
specifier: ^11.1.14
version: 11.1.14
zod:
specifier: ^3.23.3
version: 3.23.8
@@ -1731,6 +1734,10 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
swiper@11.1.14:
resolution: {integrity: sha512-VbQLQXC04io6AoAjIUWuZwW4MSYozkcP9KjLdrsG/00Q/yiwvhz9RQyt0nHXV10hi9NVnDNy1/wv7Dzq1lkOCQ==}
engines: {node: '>= 4.7.0'}
tailwindcss@3.4.13:
resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==}
engines: {node: '>=14.0.0'}
@@ -2565,8 +2572,8 @@ snapshots:
'@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.1)
eslint-plugin-react: 7.37.1(eslint@8.57.1)
eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1)
@@ -2585,37 +2592,37 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
debug: 4.3.7
enhanced-resolve: 5.17.1
eslint: 8.57.1
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
fast-glob: 3.3.2
get-tsconfig: 4.8.1
is-bun-module: 1.2.1
is-glob: 4.0.3
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies:
- '@typescript-eslint/parser'
- eslint-import-resolver-node
- eslint-import-resolver-webpack
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.8.1(eslint@8.57.1)(typescript@5.6.3)
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1)
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -2626,7 +2633,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.1
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.8.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@@ -3635,6 +3642,8 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
swiper@11.1.14: {}
tailwindcss@3.4.13:
dependencies:
'@alloc/quick-lru': 5.2.0

View File

@@ -0,0 +1,98 @@
import React from "react";
import Tilt from "react-parallax-tilt";
interface AlbumImageProps {
showSpookyImage: boolean;
imageSource: string;
spookyImageSource: string | null;
album: { name: string };
generateSpookyImageData: string | null;
lastSpookyImageLoaded: number;
place: number;
spookyImageLoaded: boolean;
onImageLoad: () => void;
}
export function AlbumImage({
showSpookyImage,
imageSource,
spookyImageSource,
album,
generateSpookyImageData,
lastSpookyImageLoaded,
place,
spookyImageLoaded,
onImageLoad,
}: AlbumImageProps) {
return (
<Tilt tiltMaxAngleX={10} tiltMaxAngleY={10} transitionSpeed={200}>
<img
className="h-36 w-36 cursor-pointer rounded"
style={{
display: showSpookyImage ? "none" : "block",
}}
src={imageSource}
alt={album.name}
/>
{spookyImageSource &&
(generateSpookyImageData
? lastSpookyImageLoaded >= place ||
lastSpookyImageLoaded + 1 === place
: true) && (
<img
className="h-36 w-36 cursor-pointer rounded"
style={{
display: showSpookyImage && spookyImageLoaded ? "block" : "none",
}}
src={spookyImageSource}
alt={album.name}
onLoad={onImageLoad}
onError={() => {
onImageLoad();
}}
/>
)}
{showSpookyImage && !spookyImageLoaded && (
<div className="flex h-36 w-36 items-center justify-center rounded bg-slate-300 bg-opacity-10">
<div>
{(() => {
if (generateSpookyImageData) {
if (lastSpookyImageLoaded < place - 1) {
return (
<>
<l-pulsar
size="100"
speed="1.75"
color="white"
></l-pulsar>{" "}
<p className="text-center">On queue...</p>
</>
);
}
return (
<>
<l-quantum
size="100"
speed="1.75"
color="white"
></l-quantum>
<p className="text-center">Generating...</p>
</>
);
} else {
return (
<>
<l-ring-2 size="100" speed="1.75" color="white"></l-ring-2>
<p className="text-center">Getting image...</p>
</>
);
}
})()}
</div>
</div>
)}
</Tilt>
);
}
export default AlbumImage;

View File

@@ -1,12 +1,14 @@
"use client";
import { use, useEffect, useState } from "react";
import Tilt from "react-parallax-tilt";
import { useEffect, useState } from "react";
import { TrackByAlbum } from "./spotify-data";
import { api } from "@/trpc/react";
import { quantum } from "ldrs";
import { ring2 } from "ldrs";
import { pulsar } from "ldrs";
import Swiper from "./swiper";
import { SwiperSlide } from "swiper/react";
import AlbumImage from "./album-image";
pulsar.register();
ring2.register();
@@ -104,80 +106,36 @@ export default function AlbumShowcase({
>
<div
onClick={() => setShowSpookyImage(!showSpookyImage)}
className="cursor-pointer"
className="cursor-pointer *:select-none"
>
<Tilt tiltMaxAngleX={10} tiltMaxAngleY={10} transitionSpeed={200}>
<img
className="h-36 w-36 cursor-pointer rounded"
style={{
display: showSpookyImage ? "none" : "block",
}}
src={imageSource}
alt={album.name}
/>
{spookyImageSource &&
(generateSpookyImage.data
? lastSpookyImageLoaded >= place ||
lastSpookyImageLoaded + 1 === place
: true) && (
<img
className="h-36 w-36 cursor-pointer rounded"
style={{
display:
showSpookyImage && spookyImageLoaded ? "block" : "none",
}}
src={spookyImageSource}
alt={album.name}
onLoad={onImageLoad}
onError={() => {
onImageLoad();
}}
/>
)}
{showSpookyImage && !spookyImageLoaded && (
<div className="flex h-36 w-36 items-center justify-center rounded bg-slate-300 bg-opacity-10">
<div>
{(() => {
if (generateSpookyImage.data) {
if (lastSpookyImageLoaded < place - 1) {
return (
<>
<l-pulsar
size="100"
speed="1.75"
color="white"
></l-pulsar>{" "}
<p className="text-center">On queue...</p>
</>
);
}
return (
<>
<l-quantum
size="100"
speed="1.75"
color="white"
></l-quantum>
<p className="text-center">Generating...</p>
</>
);
} else {
return (
<>
<l-ring-2
size="100"
speed="1.75"
color="white"
></l-ring-2>
<p className="text-center">Getting image...</p>
</>
);
}
})()}
</div>
</div>
)}
</Tilt>
<Swiper>
<SwiperSlide>
<AlbumImage
showSpookyImage={showSpookyImage}
imageSource={imageSource}
spookyImageSource={spookyImageSource}
album={album}
generateSpookyImageData={generateSpookyImage.data as string}
lastSpookyImageLoaded={lastSpookyImageLoaded}
place={place}
spookyImageLoaded={spookyImageLoaded}
onImageLoad={onImageLoad}
/>
</SwiperSlide>
<SwiperSlide>
<AlbumImage
showSpookyImage={showSpookyImage}
imageSource={imageSource}
spookyImageSource={spookyImageSource}
album={album}
generateSpookyImageData={generateSpookyImage.data as string}
lastSpookyImageLoaded={lastSpookyImageLoaded}
place={place}
spookyImageLoaded={spookyImageLoaded}
onImageLoad={onImageLoad}
/>
</SwiperSlide>
</Swiper>
</div>
<section className="flex h-36 flex-grow flex-col">
<h4 className="mb-2 text-lg font-semibold leading-none">

View File

@@ -0,0 +1,9 @@
.swiper-slide {
display: flex;
align-items: center;
justify-content: center;
border-radius: 18px;
font-size: 22px;
font-weight: bold;
color: #fff;
}

View File

@@ -0,0 +1,26 @@
"use client";
import React, { useRef, useState } from "react";
import { Swiper as SwiperReact, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/effect-cards";
import "./swiper.css";
import { EffectCards } from "swiper/modules";
export function Swiper({ children }: { children: React.ReactNode }) {
return (
<SwiperReact
style={{
width: "144px",
height: "144px",
}}
effect={"cards"}
grabCursor={true}
modules={[EffectCards]}
className="mySwiper"
>
{children}
</SwiperReact>
);
}
export default Swiper;