From b830170642b45de698e66143ec65ff1c00203bbd Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Fri, 24 Jan 2025 23:32:20 +0100 Subject: [PATCH] feat: add stream info editing functionality --- next.config.mjs | 8 +- package.json | 5 +- .../migration.sql | 15 +++ .../20250118223753_add_is_live/migration.sql | 8 ++ .../migration.sql | 11 ++ prisma/schema.prisma | 15 +++ src/app/(protected)/[username]/page.tsx | 10 +- src/app/layout.tsx | 7 +- .../app/EditLivestream/EditLivestream.tsx | 63 +++++++++ src/components/app/Livestream/Livestream.tsx | 14 +- src/components/app/NavBar/NavBar.tsx | 15 ++- .../app/StreamPlayer/StreamPlayer.tsx | 2 + .../app/UniversalForm/UniversalForm.tsx | 7 +- .../app/UserInfoCard/UserInfoCard.tsx | 13 +- src/components/ui/dialog.tsx | 122 ++++++++++++++++++ src/lib/auth/index.ts | 2 +- src/lib/form/actions.ts | 30 +++++ src/lib/form/zod.ts | 8 +- yarn.lock | 110 +++++++++------- 19 files changed, 387 insertions(+), 78 deletions(-) create mode 100644 prisma/migrations/20250118220020_stream_infov1/migration.sql create mode 100644 prisma/migrations/20250118223753_add_is_live/migration.sql create mode 100644 prisma/migrations/20250124215305_owned_by_stream_info/migration.sql create mode 100644 src/components/app/EditLivestream/EditLivestream.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/lib/form/actions.ts diff --git a/next.config.mjs b/next.config.mjs index b5bcefb..8c4eaec 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,9 +1,9 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - webpack: (config) => { - config.externals.push("@node-rs/argon2"); - return config; - } + webpack: (config) => { + config.externals.push('@node-rs/argon2'); + return config; + }, }; export default nextConfig; diff --git a/package.json b/package.json index 85021f7..85f6c07 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "docker compose --file dev/docker-compose.yml up -d && next dev --turbo --experimental-https", + "dev": "docker compose --file dev/docker-compose.yml up -d && next dev --experimental-https --turbo", "setup": "docker compose --file dev/docker-compose.yml up -d && prisma migrate deploy", "build": "prisma generate && next build", "start": "next start", @@ -32,7 +32,7 @@ "livekit-server-sdk": "^2.9.7", "lucia": "^3.2.2", "lucide-react": "^0.473.0", - "next": "^15.1.2", + "next": "^15.1.6", "next-themes": "^0.4.4", "react": "19", "react-dom": "19", @@ -40,6 +40,7 @@ "sonner": "^1.4.41", "tailwind-merge": "^2.2.2", "tailwindcss-animate": "^1.0.7", + "valtio": "^2.1.2", "zod": "^3.24.1" }, "devDependencies": { diff --git a/prisma/migrations/20250118220020_stream_infov1/migration.sql b/prisma/migrations/20250118220020_stream_infov1/migration.sql new file mode 100644 index 0000000..f3f255f --- /dev/null +++ b/prisma/migrations/20250118220020_stream_infov1/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "StreamInfo" ( + "id" TEXT NOT NULL, + "username" TEXT NOT NULL, + "title" TEXT NOT NULL, + "thumbnail" TEXT NOT NULL, + "viewers" INTEGER NOT NULL, + "category" TEXT NOT NULL, + "startedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "StreamInfo_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "StreamInfo_username_key" ON "StreamInfo"("username"); diff --git a/prisma/migrations/20250118223753_add_is_live/migration.sql b/prisma/migrations/20250118223753_add_is_live/migration.sql new file mode 100644 index 0000000..95aa300 --- /dev/null +++ b/prisma/migrations/20250118223753_add_is_live/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `isLive` to the `StreamInfo` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "StreamInfo" ADD COLUMN "isLive" BOOLEAN NOT NULL; diff --git a/prisma/migrations/20250124215305_owned_by_stream_info/migration.sql b/prisma/migrations/20250124215305_owned_by_stream_info/migration.sql new file mode 100644 index 0000000..85a7e62 --- /dev/null +++ b/prisma/migrations/20250124215305_owned_by_stream_info/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - Added the required column `userId` to the `StreamInfo` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "StreamInfo" ADD COLUMN "userId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "StreamInfo" ADD CONSTRAINT "StreamInfo_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2d73c2d..a58d79c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,6 +19,7 @@ model User { pfpUrl String username String @unique sessions Session[] + streams StreamInfo[] } model Session { @@ -26,4 +27,18 @@ model Session { userId String expiresAt DateTime user User @relation(references: [id], fields: [userId], onDelete: Cascade) +} + +model StreamInfo { + id String @id @default(cuid()) + username String @unique + title String + thumbnail String + viewers Int + category String + startedAt DateTime + isLive Boolean + + ownedBy User @relation(fields: [userId], references: [id]) + userId String } \ No newline at end of file diff --git a/src/app/(protected)/[username]/page.tsx b/src/app/(protected)/[username]/page.tsx index de54a65..57015b4 100644 --- a/src/app/(protected)/[username]/page.tsx +++ b/src/app/(protected)/[username]/page.tsx @@ -1,8 +1,16 @@ import LiveStream from "@/components/app/Livestream/Livestream"; +import prisma from "@/lib/db"; export default async function Page({ params }: { params: Promise<{ username: string }> }) { const { username } = await params; + const streamInfo = await prisma.streamInfo.findUnique({ + where: { username }, + include: { ownedBy: true }, + }); + if (!streamInfo) { + return
Stream not found
; + } return ( - + ); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 797a15f..2699fc1 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,6 +8,8 @@ import { Toaster } from '@/components/ui/sonner'; import { ThemeProvider } from '@/lib/providers/ThemeProvider'; import { SidebarProvider } from '@/components/ui/sidebar'; import Sidebar from '@/components/app/Sidebar/Sidebar'; +import { cn } from '@/lib/utils'; +import EditLivestream from '@/components/app/EditLivestream/EditLivestream'; const inter = Inter({ subsets: ['latin'] }); @@ -24,7 +26,7 @@ export default async function RootLayout({ const sessionData = await validateRequest(); return ( - + - + {/* this promise is ugly but i'm lazy to fix the type errors */} + )}/>
{/* pt-16 for navbar height */}
diff --git a/src/components/app/EditLivestream/EditLivestream.tsx b/src/components/app/EditLivestream/EditLivestream.tsx new file mode 100644 index 0000000..21826e2 --- /dev/null +++ b/src/components/app/EditLivestream/EditLivestream.tsx @@ -0,0 +1,63 @@ +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { validateRequest } from '@/lib/auth'; +import prisma from '@/lib/db'; +import { roomService } from '@/lib/services/livekit'; +import { UniversalForm } from '../UniversalForm/UniversalForm'; +import { editStreamInfo } from '@/lib/form/actions'; + +export default async function EditLivestream() { + const { user } = await validateRequest(); + if ((await prisma.streamInfo.count({ where: { username: user!.username } })) === 0) { + const isLive = + (await roomService.listRooms()).filter((r) => r.name === user!.username)[0].numPublishers >= 1; + await prisma.streamInfo.create({ + data: { + username: user!.username, + title: 'Untitled', + category: 'Uncategorized', + startedAt: new Date(), + thumbnail: 'https://placehold.co/150', + viewers: 0, + isLive, + ownedBy: { connect: { username: user!.username } }, + }, + }); + console.log('created'); + } + const streamInfo = await prisma.streamInfo.findUnique({ + where: { username: user!.username! }, + }); + return ( + + + + + + + Edit livestream + Regenerate a key or edit your stream metadata + + + + + ); +} diff --git a/src/components/app/Livestream/Livestream.tsx b/src/components/app/Livestream/Livestream.tsx index a4b2a39..7332916 100644 --- a/src/components/app/Livestream/Livestream.tsx +++ b/src/components/app/Livestream/Livestream.tsx @@ -5,15 +5,16 @@ import { useEffect, useState } from 'react'; import StreamPlayer from '../StreamPlayer/StreamPlayer'; import UserInfoCard from '../UserInfoCard/UserInfoCard'; import ChatPanel from '../ChatPanel/ChatPanel'; +import type { StreamInfo, User } from '@prisma/client'; -export default function LiveStream({ username }: { username: string }) { +export default function LiveStream(props: Props) { const [token, setToken] = useState(''); useEffect(() => { - fetch(`/api/livekit/viewerToken?room=${username}`) + fetch(`/api/livekit/viewerToken?room=${props.username}`) .then((res) => res.json()) .then((data) => setToken(data.token)); - }, [username]); + }, [props.username]); if (!token) return
Loading...
; @@ -22,10 +23,15 @@ export default function LiveStream({ username }: { username: string }) {
- +
); } + +interface Props { + username: string; + streamInfo: StreamInfo & { ownedBy: User }; +} \ No newline at end of file diff --git a/src/components/app/NavBar/NavBar.tsx b/src/components/app/NavBar/NavBar.tsx index 3867111..03edb6a 100644 --- a/src/components/app/NavBar/NavBar.tsx +++ b/src/components/app/NavBar/NavBar.tsx @@ -1,4 +1,4 @@ -'use client'; +'use client' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Button } from '@/components/ui/button'; @@ -19,9 +19,7 @@ import { ThemeSwitcher } from '../ThemeSwitcher/ThemeSwitcher'; import { Slack } from 'lucide-react'; import { SidebarTrigger } from '@/components/ui/sidebar'; -export const links = [ - { href: '/srizan', name: 'test stream' }, -]; +export const links = [{ href: '/srizan', name: 'test stream' }]; function NavbarLinks() { return ( @@ -35,12 +33,12 @@ function NavbarLinks() { ); } -export default function Navbar() { +export default function Navbar(props: Props) { const { user } = useSession(); return ( <>