Skip to content

Commit 2a528dc

Browse files
chore: migrate to useSession hook
1 parent 11fc465 commit 2a528dc

File tree

14 files changed

+181
-253
lines changed

14 files changed

+181
-253
lines changed

app/ui/layout.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
import * as React from 'react';
21
import { headers } from 'next/headers';
3-
import { SessionProvider } from '@/components/app/session-provider';
2+
import { AppSessionProvider } from '@/hooks/useAppSession';
43
import { getAppConfig } from '@/lib/utils';
54

6-
export default async function ComponentsLayout({ children }: { children: React.ReactNode }) {
5+
interface LayoutProps {
6+
children: React.ReactNode;
7+
}
8+
9+
export default async function Layout({ children }: LayoutProps) {
710
const hdrs = await headers();
811
const appConfig = await getAppConfig(hdrs);
912

1013
return (
11-
<SessionProvider appConfig={appConfig}>
14+
<AppSessionProvider appConfig={appConfig}>
1215
<div className="bg-muted/20 min-h-svh p-8">
1316
<div className="mx-auto max-w-3xl space-y-8">
1417
<header className="space-y-2">
1518
<h1 className="text-5xl font-bold tracking-tight">LiveKit UI</h1>
1619
<p className="text-muted-foreground max-w-80 leading-tight text-pretty">
17-
A set of UI components for building LiveKit-powered voice experiences.
20+
A set of UI Layouts for building LiveKit-powered voice experiences.
1821
</p>
1922
<p className="text-muted-foreground max-w-prose text-balance">
2023
Built with{' '}
@@ -37,6 +40,6 @@ export default async function ComponentsLayout({ children }: { children: React.R
3740
<main className="space-y-20">{children}</main>
3841
</div>
3942
</div>
40-
</SessionProvider>
43+
</AppSessionProvider>
4144
);
4245
}

components/app/app.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
import { RoomAudioRenderer, StartAudio } from '@livekit/components-react';
44
import type { AppConfig } from '@/app-config';
5-
import { SessionProvider } from '@/components/app/session-provider';
65
import { ViewController } from '@/components/app/view-controller';
76
import { Toaster } from '@/components/livekit/toaster';
7+
import { AppSessionProvider } from '@/hooks/useAppSession';
88

99
interface AppProps {
1010
appConfig: AppConfig;
1111
}
1212

1313
export function App({ appConfig }: AppProps) {
1414
return (
15-
<SessionProvider appConfig={appConfig}>
15+
<AppSessionProvider appConfig={appConfig}>
1616
<main className="grid h-svh grid-cols-1 place-content-center">
17-
<ViewController />
17+
<ViewController appConfig={appConfig} />
1818
</main>
1919
<StartAudio label="Start Audio" />
2020
<RoomAudioRenderer />
2121
<Toaster />
22-
</SessionProvider>
22+
</AppSessionProvider>
2323
);
2424
}

components/app/chat-transcript.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import { AnimatePresence, type HTMLMotionProps, motion } from 'motion/react';
4-
import { type ReceivedChatMessage } from '@livekit/components-react';
4+
import { type ReceivedMessage } from '@livekit/components-react';
55
import { ChatEntry } from '@/components/livekit/chat-entry';
66

77
const MotionContainer = motion.create('div');
@@ -50,7 +50,7 @@ const MESSAGE_MOTION_PROPS = {
5050

5151
interface ChatTranscriptProps {
5252
hidden?: boolean;
53-
messages?: ReceivedChatMessage[];
53+
messages?: ReceivedMessage[];
5454
}
5555

5656
export function ChatTranscript({
@@ -62,10 +62,9 @@ export function ChatTranscript({
6262
<AnimatePresence>
6363
{!hidden && (
6464
<MotionContainer {...CONTAINER_MOTION_PROPS} {...props}>
65-
{messages.map(({ id, timestamp, from, message, editTimestamp }: ReceivedChatMessage) => {
65+
{messages.map(({ id, timestamp, from, message }: ReceivedMessage) => {
6666
const locale = navigator?.language ?? 'en-US';
6767
const messageOrigin = from?.isLocal ? 'local' : 'remote';
68-
const hasBeenEdited = !!editTimestamp;
6968

7069
return (
7170
<MotionChatEntry
@@ -74,7 +73,6 @@ export function ChatTranscript({
7473
timestamp={timestamp}
7574
message={message}
7675
messageOrigin={messageOrigin}
77-
hasBeenEdited={hasBeenEdited}
7876
{...MESSAGE_MOTION_PROPS}
7977
/>
8078
);

components/app/preconnect-message.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import { AnimatePresence, motion } from 'motion/react';
4-
import { type ReceivedChatMessage } from '@livekit/components-react';
4+
import { type ReceivedMessage } from '@livekit/components-react';
55
import { ShimmerText } from '@/components/livekit/shimmer-text';
66
import { cn } from '@/lib/utils';
77

@@ -32,7 +32,7 @@ const VIEW_MOTION_PROPS = {
3232
};
3333

3434
interface PreConnectMessageProps {
35-
messages?: ReceivedChatMessage[];
35+
messages?: ReceivedMessage[];
3636
className?: string;
3737
}
3838

components/app/session-provider.tsx

Lines changed: 0 additions & 41 deletions
This file was deleted.

components/app/session-view.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import React, { useEffect, useRef, useState } from 'react';
44
import { motion } from 'motion/react';
5+
import { useSessionMessages } from '@livekit/components-react';
56
import type { AppConfig } from '@/app-config';
67
import { ChatTranscript } from '@/components/app/chat-transcript';
78
import { PreConnectMessage } from '@/components/app/preconnect-message';
@@ -10,7 +11,7 @@ import {
1011
AgentControlBar,
1112
type ControlBarControls,
1213
} from '@/components/livekit/agent-control-bar/agent-control-bar';
13-
import { useChatMessages } from '@/hooks/useChatMessages';
14+
import { useAppSession } from '@/hooks/useAppSession';
1415
import { useConnectionTimeout } from '@/hooks/useConnectionTimout';
1516
import { useDebugMode } from '@/hooks/useDebug';
1617
import { cn } from '@/lib/utils';
@@ -58,6 +59,7 @@ export function Fade({ top = false, bottom = false, className }: FadeProps) {
5859
/>
5960
);
6061
}
62+
6163
interface SessionViewProps {
6264
appConfig: AppConfig;
6365
}
@@ -66,10 +68,11 @@ export const SessionView = ({
6668
appConfig,
6769
...props
6870
}: React.ComponentProps<'section'> & SessionViewProps) => {
69-
useConnectionTimeout(200_000);
71+
useConnectionTimeout(20_000);
7072
useDebugMode({ enabled: IN_DEVELOPMENT });
7173

72-
const messages = useChatMessages();
74+
const { session, isSessionActive, endSession } = useAppSession();
75+
const { messages } = useSessionMessages(session);
7376
const [chatOpen, setChatOpen] = useState(false);
7477
const scrollAreaRef = useRef<HTMLDivElement>(null);
7578

@@ -90,6 +93,11 @@ export const SessionView = ({
9093
}
9194
}, [messages]);
9295

96+
const handleDisconnect = () => {
97+
// pass false so we can manually end the session when the exit animation completes
98+
endSession(false);
99+
};
100+
93101
return (
94102
<section className="bg-background relative z-10 h-full w-full overflow-hidden" {...props}>
95103
{/* Chat Transcript */}
@@ -100,7 +108,7 @@ export const SessionView = ({
100108
)}
101109
>
102110
<Fade top className="absolute inset-x-4 top-0 h-40" />
103-
<ScrollArea ref={scrollAreaRef} className="px-4 pt-40 pb-[150px] md:px-6 md:pb-[180px]">
111+
<ScrollArea ref={scrollAreaRef} className="px-4 pt-40 pb-[150px] md:px-6 md:pb-[200px]">
104112
<ChatTranscript
105113
hidden={!chatOpen}
106114
messages={messages}
@@ -122,7 +130,12 @@ export const SessionView = ({
122130
)}
123131
<div className="bg-background relative mx-auto max-w-2xl pb-3 md:pb-12">
124132
<Fade bottom className="absolute inset-x-0 top-0 h-4 -translate-y-full" />
125-
<AgentControlBar controls={controls} onChatOpenChange={setChatOpen} />
133+
<AgentControlBar
134+
controls={controls}
135+
isSessionActive={isSessionActive}
136+
onDisconnect={handleDisconnect}
137+
onChatOpenChange={setChatOpen}
138+
/>
126139
</div>
127140
</MotionBottom>
128141
</section>

components/app/view-controller.tsx

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
'use client';
22

3-
import { useRef } from 'react';
4-
import { AnimatePresence, motion } from 'motion/react';
5-
import { useRoomContext } from '@livekit/components-react';
6-
import { useSession } from '@/components/app/session-provider';
3+
import { useCallback } from 'react';
4+
import { AnimatePresence, type AnimationDefinition, motion } from 'motion/react';
5+
import { useSessionContext } from '@livekit/components-react';
6+
import { AppConfig } from '@/app-config';
77
import { SessionView } from '@/components/app/session-view';
88
import { WelcomeView } from '@/components/app/welcome-view';
9+
import { useAppSession } from '@/hooks/useAppSession';
910

1011
const MotionWelcomeView = motion.create(WelcomeView);
1112
const MotionSessionView = motion.create(SessionView);
@@ -28,20 +29,23 @@ const VIEW_MOTION_PROPS = {
2829
},
2930
};
3031

31-
export function ViewController() {
32-
const room = useRoomContext();
33-
const isSessionActiveRef = useRef(false);
34-
const { appConfig, isSessionActive, startSession } = useSession();
32+
interface ViewControllerProps {
33+
appConfig: AppConfig;
34+
}
3535

36-
// animation handler holds a reference to stale isSessionActive value
37-
isSessionActiveRef.current = isSessionActive;
36+
export function ViewController({ appConfig }: ViewControllerProps) {
37+
const session = useSessionContext();
38+
const { isSessionActive, startSession } = useAppSession();
3839

39-
// disconnect room after animation completes
40-
const handleAnimationComplete = () => {
41-
if (!isSessionActiveRef.current && room.state !== 'disconnected') {
42-
room.disconnect();
43-
}
44-
};
40+
const handleAnimationComplete = useCallback(
41+
(definition: AnimationDefinition) => {
42+
// manually end the session when the exit animation completes
43+
if (definition === 'hidden') {
44+
session.end();
45+
}
46+
},
47+
[session]
48+
);
4549

4650
return (
4751
<AnimatePresence mode="wait">
@@ -50,7 +54,7 @@ export function ViewController() {
5054
<MotionWelcomeView
5155
key="welcome"
5256
{...VIEW_MOTION_PROPS}
53-
startButtonText={appConfig.startButtonText}
57+
startButtonText={appConfig?.startButtonText ?? ''}
5458
onStartCall={startSession}
5559
/>
5660
)}

components/livekit/agent-control-bar/agent-control-bar.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { type HTMLAttributes, useCallback, useState } from 'react';
44
import { Track } from 'livekit-client';
55
import { useChat, useRemoteParticipants } from '@livekit/components-react';
66
import { ChatTextIcon, PhoneDisconnectIcon } from '@phosphor-icons/react/dist/ssr';
7-
import { useSession } from '@/components/app/session-provider';
87
import { TrackToggle } from '@/components/livekit/agent-control-bar/track-toggle';
98
import { Button } from '@/components/livekit/button';
109
import { Toggle } from '@/components/livekit/toggle';
@@ -23,8 +22,8 @@ export interface ControlBarControls {
2322
}
2423

2524
export interface AgentControlBarProps extends UseInputControlsProps {
25+
isSessionActive?: boolean;
2626
controls?: ControlBarControls;
27-
onDisconnect?: () => void;
2827
onChatOpenChange?: (open: boolean) => void;
2928
onDeviceError?: (error: { source: Track.Source; error: Error }) => void;
3029
}
@@ -36,6 +35,7 @@ export function AgentControlBar({
3635
controls,
3736
saveUserChoices = true,
3837
className,
38+
isSessionActive = false,
3939
onDisconnect,
4040
onDeviceError,
4141
onChatOpenChange,
@@ -45,8 +45,6 @@ export function AgentControlBar({
4545
const participants = useRemoteParticipants();
4646
const [chatOpen, setChatOpen] = useState(false);
4747
const publishPermissions = usePublishPermissions();
48-
const { isSessionActive, endSession } = useSession();
49-
5048
const {
5149
micTrackRef,
5250
cameraToggle,
@@ -70,11 +68,6 @@ export function AgentControlBar({
7068
[onChatOpenChange, setChatOpen]
7169
);
7270

73-
const handleDisconnect = useCallback(async () => {
74-
endSession();
75-
onDisconnect?.();
76-
}, [endSession, onDisconnect]);
77-
7871
const visibleControls = {
7972
leave: controls?.leave ?? true,
8073
microphone: controls?.microphone ?? publishPermissions.microphone,
@@ -164,7 +157,7 @@ export function AgentControlBar({
164157
{visibleControls.leave && (
165158
<Button
166159
variant="destructive"
167-
onClick={handleDisconnect}
160+
onClick={onDisconnect}
168161
disabled={!isSessionActive}
169162
className="font-mono"
170163
>

0 commit comments

Comments
 (0)