From 703e9f7bde668c6678c7a01bf585e501774e5936 Mon Sep 17 00:00:00 2001 From: Jonas Schell Date: Thu, 12 Sep 2024 10:45:03 +0200 Subject: [PATCH] update frontend --- frontend/.env.example | 4 + frontend/app/connection-details/route.ts | 63 ++++---------- frontend/app/layout.tsx | 4 +- frontend/app/page.tsx | 106 ++++++++++++----------- 4 files changed, 78 insertions(+), 99 deletions(-) create mode 100644 frontend/.env.example diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..30b59b6 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,4 @@ +# Enviroment variables needed to connect to the LiveKit server. +LIVEKIT_API_KEY= +LIVEKIT_API_SECRET= +LIVEKIT_SERVER_URL=wss://.livekit.cloud diff --git a/frontend/app/connection-details/route.ts b/frontend/app/connection-details/route.ts index 87ae9db..0aeca8a 100644 --- a/frontend/app/connection-details/route.ts +++ b/frontend/app/connection-details/route.ts @@ -3,52 +3,38 @@ import { AccessTokenOptions, VideoGrant, } from "livekit-server-sdk"; -import { NextRequest, NextResponse } from "next/server"; +import { NextResponse } from "next/server"; const API_KEY = process.env.LIVEKIT_API_KEY; const API_SECRET = process.env.LIVEKIT_API_SECRET; -const LIVEKIT_URL = process.env.LIVEKIT_URL; +const LIVEKIT_SERVER_URL = process.env.LIVEKIT_SERVER_URL; -export async function GET(request: NextRequest) { - try { - // Parse query parameters - const roomName = request.nextUrl.searchParams.get("roomName"); - const participantName = request.nextUrl.searchParams.get("participantName"); - const metadata = request.nextUrl.searchParams.get("metadata") ?? ""; - const region = request.nextUrl.searchParams.get("region"); - const livekitServerUrl = region ? getLiveKitURL(region) : LIVEKIT_URL; - if (livekitServerUrl === undefined) { - throw new Error("Invalid region"); - } - - // if (typeof roomName !== "string") { - // return new NextResponse("Missing required query parameter: roomName", { - // status: 400, - // }); - // } - // if (participantName === null) { - // return new NextResponse( - // "Missing required query parameter: participantName", - // { status: 400 } - // ); - // } +export type ConnectionDetails = { + serverUrl: string; + roomName: string; + participantName: string; + participantToken: string; +}; +export async function GET() { + try { // Generate participant token + const participantIdentity = `voice_assistant_user_${Math.round( + Math.random() * 10_000 + )}`; const participantToken = await createParticipantToken( { - identity: `${participantName}__${Math.round(Math.random() * 10000)}`, - name: "participantName", - metadata, + identity: participantIdentity, }, "roomName" ); // Return connection details const data = { - serverUrl: livekitServerUrl, - roomName: roomName, + serverUrl: LIVEKIT_SERVER_URL, + roomName: "voice_assistant_room", participantToken: participantToken, - participantName: participantName, + participantName: participantIdentity, }; return NextResponse.json(data); } catch (error) { @@ -74,18 +60,3 @@ function createParticipantToken( at.addGrant(grant); return at.toJwt(); } - -/** - * Get the LiveKit server URL for the given region. - */ -function getLiveKitURL(region: string | null): string { - let targetKey = "LIVEKIT_URL"; - if (region) { - targetKey = `LIVEKIT_URL_${region}`.toUpperCase(); - } - const url = process.env[targetKey]; - if (!url) { - throw new Error(`${targetKey} is not defined`); - } - return url; -} diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 756fcce..1a3f956 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -12,8 +12,8 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - {children} + + {children} ); } diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index fc22242..bad0a46 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,74 +1,78 @@ "use client"; +import "@livekit/components-styles"; + import { LiveKitRoom, - useToken, useVoiceAssistant, BarVisualizer, RoomAudioRenderer, VoiceAssistantControlBar, } from "@livekit/components-react"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useState } from "react"; import { MediaDeviceFailure } from "livekit-client"; - -function SimpleVoiceAssistant() { - const { state, audioTrack } = useVoiceAssistant(); - return ( - - ); -} +import type { ConnectionDetails } from "./connection-details/route"; export default function Page() { - const [shouldConnect, setShouldConnect] = useState(false); - const [details, setDetails] = useState(undefined); + const [connectionDetails, updateConnectionDetails] = useState< + ConnectionDetails | undefined + >(undefined); - const handlePreJoinSubmit = useCallback(async () => { + const onConnectButtonClicked = useCallback(async () => { const url = new URL( - process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT!, + process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ?? "connection-details", window.location.origin ); - const connectionDetailsResp = await fetch(url.toString()); - const connectionDetailsData = await connectionDetailsResp.json(); - console.log({ connectionDetailsData }); - - setDetails(connectionDetailsData); + const response = await fetch(url.toString()); + const connectionDetailsData = await response.json(); + updateConnectionDetails(connectionDetailsData); }, []); - const onDeviceFailure = (e?: MediaDeviceFailure) => { - console.error(e); - alert( - "Error acquiring camera or microphone permissions. Please make sure you grant the necessary permissions in your browser and reload the tab" - ); - }; - return ( -
- {details ? ( - setShouldConnect(false)} - className="" - > -
- +
+ { + updateConnectionDetails(undefined); + }} + className="grid items-center grid-rows-[1fr_min-content]" + > + {connectionDetails ? ( + + ) : ( +
+
- - -
- ) : ( - - )} + )} + + +
); } + +function SimpleVoiceAssistant() { + const { state, audioTrack } = useVoiceAssistant(); + return ( +
+ +
+ ); +} + +function onDeviceFailure(error?: MediaDeviceFailure) { + console.error(error); + alert( + "Error acquiring camera or microphone permissions. Please make sure you grant the necessary permissions in your browser and reload the tab" + ); +}