Skip to content

Commit

Permalink
feat: Imporve UI
Browse files Browse the repository at this point in the history
  • Loading branch information
mateopresacastro committed Nov 7, 2024
1 parent 6a9fef3 commit 3cf728d
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 89 deletions.
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ AWS_PRIVATE_BUCKET_ARN=
AWS_PRIVATE_BUCKET_NAME=
AWS_PUBLIC_BUCKET_ARN=
AWS_PUBLIC_BUCKET_NAME=
AWS_PUBLIC_BUCKET_URL=
AWS_PUBLIC_BUCKET_URL=

# Front end
NEXT_PUBLIC_AWS_PRIVATE_BUCKET_NAME=$AWS_PRIVATE_BUCKET_NAME
NEXT_PUBLIC_AWS_PUBLIC_BUCKET_NAME=$AWS_PUBLIC_BUCKET_NAME
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"profesionals",
"samplemakers",
"svix",
"tailwindcss",
"tanstack",
"trpc"
"trpc",
"wavesurfer"
]
}
87 changes: 29 additions & 58 deletions app/[userName]/[samplePackName]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import Image from "next/image";
import { getSamplePack } from "@/lib/db/mod";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import Link from "next/link";
import { getSamplePack } from "@/lib/db/mod";
import { notFound } from "next/navigation";
import { Button } from "@/components/ui/button";
import WaveForm from "@/components/waveform";

export default async function Page({
params,
Expand All @@ -16,61 +12,36 @@ export default async function Page({
}) {
const { userName, samplePackName } = await params;
const samplePack = await getSamplePack({ userName, samplePackName });

if (!samplePack) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<h1 className="text-2xl font-bold text-red-500">
Sample pack not found
</h1>
</div>
);
notFound();
}

return (
<div className="container mx-auto px-4 py-8 pt-40">
<div className="flex items-center gap-6 mb-8">
<div className="relative h-32 w-32">
<Image
src={samplePack.imgUrl}
alt={samplePack.title}
fill
className="object-cover rounded-lg"
/>
</div>
<div>
<h1 className="text-3xl font-bold">{samplePack.title}</h1>
<Link href={`/${userName}`} prefetch={true}>
<p className="text-neutral-500">
by @{samplePack.creator.userName}
</p>
</Link>
{samplePack.description && (
<p className="mt-2 text-neutral-600">{samplePack.description}</p>
)}
{samplePack.price && (
<p className="mt-2 text-xl font-semibold">
${samplePack.price.toFixed(2)}
</p>
)}
</div>
<div className="flex flex-col items-center justify-start min-h-screen py-24 sm:py-32">
<div className="rounded-2xl size-80 aspect-square mb-2 object-cover">
<Image
src={samplePack.imgUrl}
alt={samplePack.title}
width={160}
height={160}
className="rounded-2xl w-full h-full object-cover"
/>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{samplePack.samples.map((sample) => (
<Card key={sample.id}>
<CardHeader>
<CardTitle className="text-lg">{sample.title}</CardTitle>
<CardDescription>
Created {new Date(sample.createdAt).toLocaleDateString()}
</CardDescription>
</CardHeader>
<CardContent>
<audio controls className="w-full" src={sample.url}>
Your browser does not support the audio element.
</audio>
</CardContent>
</Card>
<span className="block pt-5 text-xl">{samplePack.title}</span>
<span className="block text-sm text-neutral-400">
{samplePack.description}
</span>
<Link href={`/${userName}`} prefetch={true}>
<span className="text-neutral-600 block pt-0.5 font-medium text-xs">
by @{userName}
</span>
</Link>
<Button className="font-medium my-10" size="lg">
${samplePack.price}
</Button>
<div className="w-full max-w-96 flex flex-col gap-16 pt-16">
{samplePack.samples.map(({ url }) => (
<WaveForm url={url} key={url} />
))}
</div>
</div>
Expand Down
44 changes: 33 additions & 11 deletions app/[userName]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Link from "next/link";
import UploadButton from "@/components/upload-button";
import { getData } from "@/lib/db/mod";
import { notFound } from "next/navigation";
import { DotIcon } from "lucide-react";

export default async function Page({
params,
Expand All @@ -16,19 +17,28 @@ export default async function Page({
}

return (
<div className="flex flex-col items-center justify-start min-h-screen pt-40">
<div className="rounded-full size-32 overflow-hidden">
<div className="flex flex-col items-center justify-start min-h-screen pt-24 sm:pt-32">
<div className="rounded-full size-24 overflow-hidden">
<Image
priority
src={data.imgUrl}
height={160}
width={160}
height={96}
width={96}
quality={80}
alt={`${userName}`}
className="rounded-full object-cover h-32 w-32"
className="rounded-full object-cover h-24 w-24"
/>
</div>
<span className="block pt-3">{data.name}</span>
<span className="text-neutral-400 block pt-0.5 text-sm">@{userName}</span>
<span className="block pt-6 text-xl">{data.name}</span>
<div className="flex items-center justify-center">
<span className="text-neutral-400 block pt-0.5 text-sm font-medium">
@{userName}
</span>
<DotIcon className="text-neutral-400 size-4 mt-1" />
<span className="text-neutral-400 block pt-0.5 text-sm font-medium">
{data.samplePacks.length} Packs
</span>
</div>
<UploadButton userName={userName} />
<SamplePacks samplePacks={data.samplePacks} userName={userName} />
</div>
Expand All @@ -47,7 +57,11 @@ function SamplePacks({
userName: string;
}) {
return (
<div className="pt-32 flex flex-grow flex-wrap gap-4">
<div
className="pt-10 sm:pt-16 grid grid-cols-2 sm:grid-cols-3
lg:grid-cols-4 xl:grid-cols-5 gap-6 sm:gap-14 w-full mb-32"
>
{samplePacks.map((samplePack) => (
<SamplePack
key={samplePack.title}
Expand All @@ -68,15 +82,23 @@ function SamplePack({
}) {
return (
<Link href={`/${userName}/${samplePack.name}`} prefetch={true}>
<div className="flex flex-col items-start justify-center object-cover rounded-xl text-left p-4 bg-neutral-900 hover:bg-neutral-800">
<div className="flex flex-col items-start justify-center rounded-2xl w-full aspect-square hover:opacity-80 transition-opacity duration-200 mb-2">
<Image
src={samplePack.imgUrl}
alt={samplePack.title}
width={160}
height={160}
className="rounded-xl size-40 object-cover"
className="rounded-2xl w-full h-full object-cover"
priority
/>
<h4 className="font-medium">{samplePack.title}</h4>
<div>
<span className="text-sm block pt-1 truncate text-ellipsis">
{samplePack.title}
</span>
<span className="text-xs block text-neutral-400">
{samplePack.samples.length} samples
</span>
</div>
</div>
</Link>
);
Expand Down
Binary file removed app/fonts/GeistMonoVF.woff
Binary file not shown.
Binary file removed app/fonts/GeistVF.woff
Binary file not shown.
20 changes: 5 additions & 15 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { ClerkProvider } from "@clerk/nextjs";
import { dark } from "@clerk/themes";
import Header from "@/components/header";
Expand All @@ -11,23 +11,13 @@ import colors from "tailwindcss/colors";

export const tailwind = resolveConfig(tailwindConfig);

const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});

const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});

export const metadata: Metadata = {
title: "noos",
description: "The marketplace for samplemakers",
};

const inter = Inter({ subsets: ["latin"] });

export default function RootLayout({
children,
}: Readonly<{
Expand Down Expand Up @@ -74,7 +64,7 @@ export default function RootLayout({
>
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased max-w-6xl px-6 mx-auto bg-neutral-950 text-neutral-50 dark`}
className={`${inter.className} antialiased max-w-6xl px-6 mx-auto bg-neutral-950 text-neutral-50 dark`}
>
<Providers>
<Header />
Expand Down
2 changes: 1 addition & 1 deletion components/upload-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function UploadButton({ userName }: { userName: string }) {
return isOwner ? (
<Dialog>
<DialogTrigger asChild>
<Button className="mt-4">Upload</Button>
<Button className="mt-6">Upload</Button>
</DialogTrigger>
<DialogContent className="max-h-[95dvh] max-w-2xl overflow-y-auto ">
<DialogHeader>
Expand Down
10 changes: 9 additions & 1 deletion components/upload-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,16 @@ export default function UploadForm() {

function createPublicUrl(key: string, visibility: "private" | "public") {
if (isDev) {
if (
!process.env.NEXT_PUBLIC_AWS_PRIVATE_BUCKET_NAME ||
!process.env.NEXT_PUBLIC_AWS_PUBLIC_BUCKET_NAME
) {
throw new Error("AWS bucket names not set");
}
const bucketName =
visibility === "private" ? "noos-private-assets" : "noos-public-assets";
visibility === "private"
? process.env.NEXT_PUBLIC_AWS_PRIVATE_BUCKET_NAME
: process.env.NEXT_PUBLIC_AWS_PUBLIC_BUCKET_NAME;
return `https://localhost.localstack.cloud:4566/${bucketName}/${key}`;
}

Expand Down
44 changes: 44 additions & 0 deletions components/waveform.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import WaveSurfer from "wavesurfer.js";
import { useRef, useEffect } from "react";
import { Button } from "@/components/ui/button";

export default function WaveForm({ url }: { url: string }) {
const waveformRef = useRef<HTMLDivElement>(null);
const waveSurfer = useRef<WaveSurfer>();

useEffect(() => {
if (!waveformRef.current) return;

const OPTIONS = {
container: waveformRef.current,
barHeight: 5,
barWidth: 1,
height: 30,
normalize: true,
waveColor: "#a1a1aa",
progressColor: "#3f3f46",
cursorColor: "#f4f4f5",
hideScrollbar: true,
url,
};

const wavesurfer = WaveSurfer.create(OPTIONS);
waveSurfer.current = wavesurfer;

return () => wavesurfer.destroy();
}, [url]);

return (
<div
className="flex h-20 w-full flex-col items-start justify-center"
key={`waveform-${url}`}
>
<div ref={waveformRef} className="w-full" />
<Button onClick={() => waveSurfer.current?.playPause()} variant="ghost">
Play
</Button>
</div>
);
}
8 changes: 7 additions & 1 deletion lib/db/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@ export async function getData(userName: string) {
try {
const data = await prisma.user.findUnique({
where: { userName },
include: { samplePacks: true },
include: {
samplePacks: {
include: {
samples: true,
},
},
},
});

if (!data) throw new Error("User not found");
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^3.1.0",
"wavesurfer.js": "^7.8.8",
"zod": "^3.23.8"
},
"devDependencies": {
Expand Down

0 comments on commit 3cf728d

Please sign in to comment.