diff --git a/.env.example b/.env.example index f2d7c34b..3e24d19e 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,9 @@ UMAMI_WEBSITE_ID= HOST= -LASTFM_API_KEY= GITHUB_TOKEN= +LASTFM_API_KEY= + +SPOTIFY_CLIENT_ID= +SPOTIFY_CLIENT_SECRET= +SPOTIFY_REFRESH_TOKEN= diff --git a/next.config.js b/next.config.js index b664b576..4f61bac4 100644 --- a/next.config.js +++ b/next.config.js @@ -9,7 +9,8 @@ const nextConfig = { 'i.imgur.com', 'mateusf.com', 'lastfm.freetls.fastly.net', - 'contribution.catsjuice.com' + 'contribution.catsjuice.com', + 'i.scdn.co' ] }, async rewrites() { diff --git a/package.json b/package.json index 963e2e1a..fd5bd740 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "simple-git": "^3.18.0", "tailwind-scrollbar": "^3.1.0", "tailwindcss": "^3.3.3", + "tailwindcss-animate": "^1.0.7", "tsx": "^4.0.0", "typescript": "5.3.3", "velite": "0.1.0-beta.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32b01cbb..e02615e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -199,6 +199,9 @@ devDependencies: tailwindcss: specifier: ^3.3.3 version: 3.4.3 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.3) tsx: specifier: ^4.0.0 version: 4.7.2 @@ -7423,6 +7426,14 @@ packages: tailwindcss: 3.4.3 dev: true + /tailwindcss-animate@1.0.7(tailwindcss@3.4.3): + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + tailwindcss: 3.4.3 + dev: true + /tailwindcss-animated@1.0.1(tailwindcss@3.4.3): resolution: {integrity: sha512-u5wusj89ZwP8I+s8WZlaAd7aZTWBN/XEG6QgMKpkIKmAf3xP1A6WYf7oYIKmGaB10UAQaSqWopi/i1ozzZEs8Q==} peerDependencies: diff --git a/src/app/_components/grid/cards/cat.jpg b/src/app/_components/grid/cards/cat.jpg new file mode 100644 index 00000000..4356c41a Binary files /dev/null and b/src/app/_components/grid/cards/cat.jpg differ diff --git a/src/app/_components/grid/cards/discord-status.tsx b/src/app/_components/grid/cards/discord-status.tsx new file mode 100644 index 00000000..baac75ca --- /dev/null +++ b/src/app/_components/grid/cards/discord-status.tsx @@ -0,0 +1,67 @@ +import { DiscordLogo } from '@phosphor-icons/react/dist/ssr' + +enum status { + online, + idle, + dnd + // offline, +} + +export type LanyardResponse = { + data: { + discord_user: { + id: string + username: string + discriminator: string + avatar: string + } + discord_status: status + active_on_discord_web: boolean + active_on_discord_desktop: boolean + active_on_discord_mobile: boolean + listening_to_spotify: boolean + activities: { + id: string + name: string + type: number + state: string + timestamps: { + end: number + } + emoji: { + name: string + } + created_at: number + }[] + success: boolean + } +} + +export async function DiscordStatus() { + const { data }: LanyardResponse = await fetch( + 'https://api.lanyard.rest/v1/users/274521154230812672', + { + headers: { + 'Content-Type': 'application/json', + 'cache-control': 'public, s-maxage=60, stale-while-revalidate=30' + } + } + ).then(data => data.json()) + + return ( +
+
+ {/* */} + + +

+ {data.discord_status} +

(@mateusfg7)

+

+
+
+ ) +} diff --git a/src/app/_components/grid/cards/github-link.tsx b/src/app/_components/grid/cards/github-link.tsx new file mode 100644 index 00000000..bd0bc7c5 --- /dev/null +++ b/src/app/_components/grid/cards/github-link.tsx @@ -0,0 +1,40 @@ +// https://github.com/arnvgh/www/blob/main/components/misc/(home)/cards/gh-link.tsx + +import Image from 'next/image' +import { GithubLogo } from '@phosphor-icons/react/dist/ssr' + +import catImg from './cat.jpg' + +export const GithubLink = () => { + return ( + + + + + GitHub + My experiments (aka projects) + + + ) +} diff --git a/src/app/_components/grid/cards/github-stats.tsx b/src/app/_components/grid/cards/github-stats.tsx new file mode 100644 index 00000000..ae37097b --- /dev/null +++ b/src/app/_components/grid/cards/github-stats.tsx @@ -0,0 +1,62 @@ +import { getGithubRepositories, getGithubUserData } from '~/lib/github' + +export const GithubStats = async () => { + const { followers, public_repos } = await getGithubUserData() + const repositories = await getGithubRepositories() + const stars = repositories.reduce( + (acc, repo) => acc + repo.stargazers_count, + 0 + ) + return ( +
+ + +
+ + + +
+
+
+ ) +} + +const GitHubStatsData = ({ + label, + value +}: { + label: React.ReactNode + value: number +}) => { + return ( +
+ + {label}: + + {value} +
+ ) +} + +const BackgroundPattern = () => { + let seed = 1 + function seededRandom() { + const x = Math.sin(seed++) * 10000 + return x - Math.floor(x) + } + const colours = ['#39d353', '#0e4429', '#0e4429', '#006d32', '#161b22'] + const days = new Array(62) + .fill(null) + .map(_ => colours[Math.floor(seededRandom() * colours.length)]) + return ( +
+ {days.map((c, i) => ( +
+ ))} +
+ ) +} diff --git a/src/app/_components/grid/cards/icons/bash.tsx b/src/app/_components/grid/cards/icons/bash.tsx new file mode 100644 index 00000000..b86a24d7 --- /dev/null +++ b/src/app/_components/grid/cards/icons/bash.tsx @@ -0,0 +1,17 @@ +import { SVGProps } from 'react' + +export function BashIcon(props: SVGProps) { + return ( + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/cloudflare.tsx b/src/app/_components/grid/cards/icons/cloudflare.tsx new file mode 100644 index 00000000..9cd61a00 --- /dev/null +++ b/src/app/_components/grid/cards/icons/cloudflare.tsx @@ -0,0 +1,20 @@ +import { SVGProps } from 'react' + +export function CloudflareIcon(props: SVGProps) { + return ( + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/css.tsx b/src/app/_components/grid/cards/icons/css.tsx new file mode 100644 index 00000000..d6734fa9 --- /dev/null +++ b/src/app/_components/grid/cards/icons/css.tsx @@ -0,0 +1,32 @@ +import { SVGProps } from 'react' + +export function CSS3Icon(props: SVGProps) { + return ( + + + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/docker.tsx b/src/app/_components/grid/cards/icons/docker.tsx new file mode 100644 index 00000000..5820ef50 --- /dev/null +++ b/src/app/_components/grid/cards/icons/docker.tsx @@ -0,0 +1,12 @@ +import React from 'react' + +export function DockerIcon(props: React.SVGProps) { + return ( + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/expo.tsx b/src/app/_components/grid/cards/icons/expo.tsx new file mode 100644 index 00000000..e540f456 --- /dev/null +++ b/src/app/_components/grid/cards/icons/expo.tsx @@ -0,0 +1,16 @@ +import { SVGProps } from 'react' + +export function ExpoIcon({ className, ...props }: SVGProps) { + return ( + + Expo + + + ) +} diff --git a/src/app/_components/grid/cards/icons/figma.tsx b/src/app/_components/grid/cards/icons/figma.tsx new file mode 100644 index 00000000..f05f278d --- /dev/null +++ b/src/app/_components/grid/cards/icons/figma.tsx @@ -0,0 +1,28 @@ +import { SVGProps } from 'react' + +export function FigmaIcon(props: SVGProps) { + return ( + + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/google-cloud.tsx b/src/app/_components/grid/cards/icons/google-cloud.tsx new file mode 100644 index 00000000..3d11969e --- /dev/null +++ b/src/app/_components/grid/cards/icons/google-cloud.tsx @@ -0,0 +1,24 @@ +import { SVGProps } from 'react' + +export function GoogleCloudIcon(props: SVGProps) { + return ( + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/heroku.tsx b/src/app/_components/grid/cards/icons/heroku.tsx new file mode 100644 index 00000000..54f736fc --- /dev/null +++ b/src/app/_components/grid/cards/icons/heroku.tsx @@ -0,0 +1,12 @@ +import { SVGProps } from 'react' + +export function HerokuIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/html.tsx b/src/app/_components/grid/cards/icons/html.tsx new file mode 100644 index 00000000..23601c1d --- /dev/null +++ b/src/app/_components/grid/cards/icons/html.tsx @@ -0,0 +1,21 @@ +import { SVGProps } from 'react' + +export function HTML5Icon(props: SVGProps) { + return ( + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/linux.tsx b/src/app/_components/grid/cards/icons/linux.tsx new file mode 100644 index 00000000..186dfa80 --- /dev/null +++ b/src/app/_components/grid/cards/icons/linux.tsx @@ -0,0 +1,3092 @@ +import { SVGProps } from 'react' + +export function LinuxIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/nest.tsx b/src/app/_components/grid/cards/icons/nest.tsx new file mode 100644 index 00000000..367f2125 --- /dev/null +++ b/src/app/_components/grid/cards/icons/nest.tsx @@ -0,0 +1,12 @@ +import { SVGProps } from 'react' + +export function NestIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/next.tsx b/src/app/_components/grid/cards/icons/next.tsx new file mode 100644 index 00000000..a082f8aa --- /dev/null +++ b/src/app/_components/grid/cards/icons/next.tsx @@ -0,0 +1,40 @@ +import { SVGProps } from 'react' + +export function NextIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/node.tsx b/src/app/_components/grid/cards/icons/node.tsx new file mode 100644 index 00000000..7e6e8561 --- /dev/null +++ b/src/app/_components/grid/cards/icons/node.tsx @@ -0,0 +1,73 @@ +import { SVGProps } from 'react' + +export function NodeIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/prisma.tsx b/src/app/_components/grid/cards/icons/prisma.tsx new file mode 100644 index 00000000..d4e27b9e --- /dev/null +++ b/src/app/_components/grid/cards/icons/prisma.tsx @@ -0,0 +1,12 @@ +import { SVGProps } from 'react' + +export function PrismaIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/python.tsx b/src/app/_components/grid/cards/icons/python.tsx new file mode 100644 index 00000000..4da91633 --- /dev/null +++ b/src/app/_components/grid/cards/icons/python.tsx @@ -0,0 +1,58 @@ +import { SVGProps } from 'react' + +export function PythonIcon(props: SVGProps) { + return ( + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/react.tsx b/src/app/_components/grid/cards/icons/react.tsx new file mode 100644 index 00000000..f454432f --- /dev/null +++ b/src/app/_components/grid/cards/icons/react.tsx @@ -0,0 +1,12 @@ +import { SVGProps } from 'react' + +export function ReactIcon(props: SVGProps) { + return ( + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/rust.tsx b/src/app/_components/grid/cards/icons/rust.tsx new file mode 100644 index 00000000..ff5efeef --- /dev/null +++ b/src/app/_components/grid/cards/icons/rust.tsx @@ -0,0 +1,15 @@ +import { SVGProps } from 'react' + +export function RustIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/svelte.tsx b/src/app/_components/grid/cards/icons/svelte.tsx new file mode 100644 index 00000000..fdd90447 --- /dev/null +++ b/src/app/_components/grid/cards/icons/svelte.tsx @@ -0,0 +1,26 @@ +import { SVGProps } from 'react' + +export function SvelteIcon(props: SVGProps) { + return ( + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/tailwindcss.tsx b/src/app/_components/grid/cards/icons/tailwindcss.tsx new file mode 100644 index 00000000..058fc15a --- /dev/null +++ b/src/app/_components/grid/cards/icons/tailwindcss.tsx @@ -0,0 +1,12 @@ +import { SVGProps } from 'react' + +export function TailwindcssIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/typescript.tsx b/src/app/_components/grid/cards/icons/typescript.tsx new file mode 100644 index 00000000..c55b4635 --- /dev/null +++ b/src/app/_components/grid/cards/icons/typescript.tsx @@ -0,0 +1,14 @@ +import { SVGProps } from 'react' + +export function TypescriptIcon(props: SVGProps) { + return ( + + + + + ) +} diff --git a/src/app/_components/grid/cards/icons/vercel.tsx b/src/app/_components/grid/cards/icons/vercel.tsx new file mode 100644 index 00000000..3d51170e --- /dev/null +++ b/src/app/_components/grid/cards/icons/vercel.tsx @@ -0,0 +1,12 @@ +import { SVGProps } from 'react' + +export function VercelIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/src/app/_components/grid/cards/latest-post.tsx b/src/app/_components/grid/cards/latest-post.tsx new file mode 100644 index 00000000..6de529d0 --- /dev/null +++ b/src/app/_components/grid/cards/latest-post.tsx @@ -0,0 +1,37 @@ +import { posts } from '#content' +import Link from 'next/link' +import { Date } from '~/components/date' +import { getSortedPosts } from '~/lib/get-sorted-posts' + +export function LatestPost() { + const { title } = { + title: 'My latest post dsdsadsadasdsadsa' + } + + const latestPost = getSortedPosts(posts)[0] + + const Divider = () => ( + + ) + + return ( + + Latest post + + + + + {latestPost.title} + + + + + + + + + ) +} diff --git a/src/app/_components/grid/cards/letterboxed-link.tsx b/src/app/_components/grid/cards/letterboxed-link.tsx new file mode 100644 index 00000000..f060a169 --- /dev/null +++ b/src/app/_components/grid/cards/letterboxed-link.tsx @@ -0,0 +1,45 @@ +import { ArrowUpRight } from '@phosphor-icons/react/dist/ssr' + +export function LetterboxedLink() { + return ( + + + + + +
+ Letter + boxed +
+ +
+ Letter + boxed +
+ +
+ Letter + boxed +
+ +
+ Letter + boxed +
+ +
+ Letter + boxed +
+ +
+ Letter + boxed +
+
+ ) +} diff --git a/src/app/_components/grid/cards/links.tsx b/src/app/_components/grid/cards/links.tsx new file mode 100644 index 00000000..bcb63266 --- /dev/null +++ b/src/app/_components/grid/cards/links.tsx @@ -0,0 +1,47 @@ +import { + LinkedinLogo, + XLogo, + RedditLogo, + CodepenLogo +} from '@phosphor-icons/react/dist/ssr' + +export const LinksCard = () => { + return ( + + ) +} diff --git a/src/app/_components/grid/cards/most-listened-music.tsx b/src/app/_components/grid/cards/most-listened-music.tsx new file mode 100644 index 00000000..b68f8484 --- /dev/null +++ b/src/app/_components/grid/cards/most-listened-music.tsx @@ -0,0 +1,51 @@ +import { LastfmLogo } from '@phosphor-icons/react/dist/ssr' +import Image from 'next/image' +import { getAlbumCover } from '~/lib/get-album-cover' +import { getLastFmTopTracks } from '~/lib/lastFm' + +export async function MostListenedMusic() { + const { + name: title, + artist, + url + } = await getLastFmTopTracks('1month').then(tracks => tracks[0]) + + const cover = await getAlbumCover(`${title} - ${artist.name}`) + + return ( + + +
+ + {artist.name} + +
{title}
+
+ Top listened this month +
+
+ {title!} + {title!} +
+ ) +} diff --git a/src/app/_components/grid/cards/stack-icons.tsx b/src/app/_components/grid/cards/stack-icons.tsx new file mode 100644 index 00000000..4d03b701 --- /dev/null +++ b/src/app/_components/grid/cards/stack-icons.tsx @@ -0,0 +1,63 @@ +import { IconType } from 'react-icons' + +import { DockerIcon } from './icons/docker' +import { TypescriptIcon } from './icons/typescript' +import { HTML5Icon } from './icons/html' +import { CSS3Icon } from './icons/css' +import { ReactIcon } from './icons/react' +import { NextIcon } from './icons/next' +import { SvelteIcon } from './icons/svelte' +import { TailwindcssIcon } from './icons/tailwindcss' +import { ExpoIcon } from './icons/expo' +import { PythonIcon } from './icons/python' +import { RustIcon } from './icons/rust' +import { BashIcon } from './icons/bash' +import { NodeIcon } from './icons/node' +import { PrismaIcon } from './icons/prisma' +import { NestIcon } from './icons/nest' +import { HerokuIcon } from './icons/heroku' +import { LinuxIcon } from './icons/linux' +import { GoogleCloudIcon } from './icons/google-cloud' +import { VercelIcon } from './icons/vercel' +import { CloudflareIcon } from './icons/cloudflare' +import { FigmaIcon } from './icons/figma' + +export type IconItem = { + title: string + icon: IconType + color: string +} + +type StackLine = { + top: IconItem[] + bottom: IconItem[] +} + +export const stackLines: StackLine = { + top: [ + { title: 'Typescript', icon: TypescriptIcon, color: '#3178C6' }, + { title: 'HTML5', icon: HTML5Icon, color: '#E34F26' }, + { title: 'CSS3', icon: CSS3Icon, color: '#1572B6' }, + { title: 'React.js', icon: ReactIcon, color: '#61DAFB' }, + { title: 'Next.js', icon: NextIcon, color: '#FFFFFF' }, + { title: 'Svelte', icon: SvelteIcon, color: '#FF3E00' }, + { title: 'Tailwind CSS', icon: TailwindcssIcon, color: '#06B6D4' }, + { title: 'React Native', icon: ReactIcon, color: '#61DAFB' }, + { title: 'Expo', icon: ExpoIcon, color: '#000020' }, + { title: 'Figma', icon: FigmaIcon, color: '#F24E1E' } + ], + bottom: [ + { title: 'Python', icon: PythonIcon, color: '#3776AB' }, + { title: 'Rust', icon: RustIcon, color: '#F74C00' }, + { title: 'Bash Script', icon: BashIcon, color: '#4EAA25' }, + { title: 'Node.js', icon: NodeIcon, color: '#339933' }, + { title: 'Prisma', icon: PrismaIcon, color: '#2D3748' }, + { title: 'Nest.js', icon: NestIcon, color: '#E0234E' }, + { title: 'Heroku', icon: HerokuIcon, color: '#430098' }, + { title: 'Docker', icon: DockerIcon, color: '#2496ED' }, + { title: 'Linux', icon: LinuxIcon, color: '#FCC624' }, + { title: 'Google Cloud', icon: GoogleCloudIcon, color: '#4285F4' }, + { title: 'Vercel', icon: VercelIcon, color: '#000000' }, + { title: 'Cloudflare', icon: CloudflareIcon, color: '#F38020' } + ] +} diff --git a/src/app/_components/grid/cards/stacks-card.tsx b/src/app/_components/grid/cards/stacks-card.tsx new file mode 100644 index 00000000..ae9b5a86 --- /dev/null +++ b/src/app/_components/grid/cards/stacks-card.tsx @@ -0,0 +1,97 @@ +import React from 'react' +import { stackLines, IconItem } from './stack-icons' + +type MarqueeProps = { + children: React.ReactNode + direction?: 'left' | 'up' + pauseOnHover?: boolean + reverse?: boolean + fade?: boolean +} + +const range = (start: number, end: number): number[] => + Array.from({ length: end - start }, (_, k) => k + start) + +function Marquee(props: MarqueeProps) { + const { + children, + direction = 'left', + pauseOnHover = false, + reverse = false, + fade = false + } = props + + const ifToRightOrToBottom = (direction: string) => { + if (direction === 'left') { + return 'to right' + } else { + return 'to bottom' + } + } + + return ( +
+ {range(0, 2).map(i => ( +
+ {children} +
+ ))} +
+ ) +} + +const IconElement = ({ + data: { color, icon: Icon, title } +}: { + data: IconItem +}) => ( +
+ +
+) + +export const StacksCard = () => { + return ( +
+ + {stackLines.top.map(data => ( + + ))} + + + + {stackLines.bottom.map(data => ( + + ))} + +
+ ) +} diff --git a/src/app/_components/grid/index.tsx b/src/app/_components/grid/index.tsx new file mode 100644 index 00000000..14a44a9e --- /dev/null +++ b/src/app/_components/grid/index.tsx @@ -0,0 +1,63 @@ +import { Book, Wrench } from '@phosphor-icons/react/dist/ssr' +import { DiscordStatus } from './cards/discord-status' +import { GithubLink } from './cards/github-link' +import { GithubStats } from './cards/github-stats' +import { LatestPost } from './cards/latest-post' +import { LetterboxedLink } from './cards/letterboxed-link' +import { LinksCard } from './cards/links' +import { MostListenedMusic } from './cards/most-listened-music' +import { StacksCard } from './cards/stacks-card' + +const BooksCard = () => ( +
+
+ + Books +
+ + + Under construction... +
+) + +export function Grid() { + return ( +
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+
+ +
+
+ +
+ +
+
+ +
+
+ + + +
+ +
+
+
+ ) +} diff --git a/src/app/about/sections/knowledge/accordion.tsx b/src/app/about/sections/knowledge/accordion.tsx index b3c69ecc..24732724 100644 --- a/src/app/about/sections/knowledge/accordion.tsx +++ b/src/app/about/sections/knowledge/accordion.tsx @@ -40,7 +40,7 @@ export const AccordionTrigger = React.forwardRef( {children} diff --git a/src/app/about/statistics/_components/spotify-dashboard/cards/top-track.tsx b/src/app/about/statistics/_components/spotify-dashboard/cards/top-track.tsx index 081b5c27..beff4151 100644 --- a/src/app/about/statistics/_components/spotify-dashboard/cards/top-track.tsx +++ b/src/app/about/statistics/_components/spotify-dashboard/cards/top-track.tsx @@ -3,13 +3,14 @@ import { MusicNotes } from '@phosphor-icons/react/dist/ssr' import { placeholder } from '~/lib/placeholder' import { getLastFmTopTracks } from '~/lib/lastFm' +import { getAlbumCover } from '~/lib/get-album-cover' export async function TopTrack() { const track = await getLastFmTopTracks('1month').then(tracks => tracks[0]) - const imageUrl = track.image.find(image => image.size === 'extralarge')?.[ - '#text' - ] + const imageUrl = await getAlbumCover( + `${track.name} - ${track.artist.name}` + ).then(data => data.url) return (
diff --git a/src/app/page.tsx b/src/app/page.tsx index 6062bf8c..9a736c48 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,13 +1,15 @@ +import { Grid } from './_components/grid' import { MainTitle } from './_components/main-title' import { StartButton } from './_components/start-button' export default function Page() { return ( -
-
+
+
+
) } diff --git a/src/lib/get-album-cover.ts b/src/lib/get-album-cover.ts new file mode 100644 index 00000000..deb861c0 --- /dev/null +++ b/src/lib/get-album-cover.ts @@ -0,0 +1,54 @@ +// this is a workaround to get track cover from spotify if lastfm fails to get the image + +const ENDPOINT = 'https://api.spotify.com/v1/search?q=' + +const CLIENT_ID = process.env.SPOTIFY_CLIENT_ID as string +const CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET as string +const REFRESH_TOKEN = process.env.SPOTIFY_REFRESH_TOKEN as string + +const BASIC = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64') + +const TOKEN_ENDPOINT = 'https://accounts.spotify.com/api/token' + +const getAccessToken = async () => { + const response = await fetch(TOKEN_ENDPOINT, { + method: 'POST', + headers: { + Authorization: `Basic ${BASIC}`, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: new URLSearchParams({ + grant_type: 'refresh_token', + refresh_token: REFRESH_TOKEN + }) + }) + return await response.json() +} + +type Response = { + tracks: { + items: { + album: { + images: { + url: string + height: 64 | 300 | 640 + width: 64 | 300 | 640 + }[] + } + }[] + } +} + +export async function getAlbumCover(track: string) { + const { access_token } = await getAccessToken() + const URL = ENDPOINT + encodeURI(track) + '&type=track&market=IN&limit=1' + const res: Response = await fetch(URL, { + headers: { + Authorization: `Bearer ${access_token}` + } + }).then(async res => { + return res.json() + }) + + return res.tracks.items[0].album.images.filter(img => img.height === 640)[0] +} diff --git a/tailwind.config.ts b/tailwind.config.ts index 66d582e5..dbde3a01 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -39,7 +39,9 @@ const config: Config = { shine: 'shine 4s linear 0s forwards', 'custom-fade-down': 'custom-fade-down 200ms linear', 'slide-left': 'slide-left 70ms linear', - 'slide-right': 'slide-right 70ms linear' + 'slide-right': 'slide-right 70ms linear', + 'marquee-left': 'marquee-left var(--duration, 30s) linear infinite', + 'marquee-up': 'marquee-up var(--duration, 30s) linear infinite' }, keyframes: { typing: { @@ -109,6 +111,14 @@ const config: Config = { transform: 'translate(0, var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))' } + }, + 'marquee-left': { + from: { transform: 'translateX(0)' }, + to: { transform: 'translateX(calc(-100% - var(--gap)))' } + }, + 'marquee-up': { + from: { transform: 'translateY(0)' }, + to: { transform: 'translateY(calc(-100% - var(--gap)))' } } } }, @@ -127,6 +137,7 @@ const config: Config = { }, plugins: [ require('tailwindcss-animated'), + require('tailwindcss-animate'), require('@tailwindcss/forms')({ strategy: 'class' }), require('tailwind-scrollbar')({ nocompatible: true,