-
Notifications
You must be signed in to change notification settings - Fork 135
Update examples with useSession/useAgent hooks #1242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f514318
b5b3cde
de906a0
55854f7
6a3a037
0745049
8629563
d9dd885
bfc77d5
6de8642
f8375f0
dc0e3a6
f35d5a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| '@livekit/component-example-next': patch | ||
| '@livekit/components-react': patch | ||
| --- | ||
|
|
||
| Update nextjs examples with useSession/useAgent hooks |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,32 +1,75 @@ | ||
| 'use client'; | ||
|
|
||
| import { AudioConference, LiveKitRoom, useToken } from '@livekit/components-react'; | ||
| import { | ||
| AudioConference, | ||
| SessionProvider, | ||
| useSession, | ||
| SessionEvent, | ||
| } from '@livekit/components-react'; | ||
| import type { NextPage } from 'next'; | ||
| import { generateRandomUserId } from '../lib/helper'; | ||
| import { useState } from 'react'; | ||
| import { useMemo, useState, useEffect } from 'react'; | ||
| import { TokenSource, MediaDeviceFailure } from 'livekit-client'; | ||
|
|
||
| const AudioExample: NextPage = () => { | ||
| const params = typeof window !== 'undefined' ? new URLSearchParams(location.search) : null; | ||
| const params = useMemo( | ||
| () => (typeof window !== 'undefined' ? new URLSearchParams(location.search) : null), | ||
| [], | ||
| ); | ||
| const roomName = params?.get('room') ?? 'test-room'; | ||
| const [userIdentity] = useState(params?.get('user') ?? generateRandomUserId()); | ||
| const [userIdentity] = useState(() => params?.get('user') ?? generateRandomUserId()); | ||
|
|
||
| const tokenSource = useMemo(() => { | ||
| return TokenSource.endpoint(process.env.NEXT_PUBLIC_LK_TOKEN_ENDPOINT!); | ||
| }, []); | ||
|
Comment on lines
+22
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: I think if at all possible, the examples (where it can be done) should define the Making sure that the |
||
|
|
||
| const token = useToken(process.env.NEXT_PUBLIC_LK_TOKEN_ENDPOINT, roomName, { | ||
| userInfo: { | ||
| identity: userIdentity, | ||
| name: userIdentity, | ||
| }, | ||
| const session = useSession(tokenSource, { | ||
| roomName, | ||
| participantIdentity: userIdentity, | ||
| participantName: userIdentity, | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| session | ||
| .start({ | ||
| tracks: { | ||
| microphone: { enabled: true }, | ||
| }, | ||
| roomConnectOptions: { | ||
| autoSubscribe: true, | ||
| }, | ||
| }) | ||
| .catch((err) => { | ||
| console.error('Failed to start session:', err); | ||
| }); | ||
| return () => { | ||
| session.end().catch((err) => { | ||
| console.error('Failed to end session:', err); | ||
| }); | ||
| }; | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [session.start, session.end]); | ||
|
|
||
| useEffect(() => { | ||
| const handleMediaDevicesError = (error: Error) => { | ||
| const failure = MediaDeviceFailure.getFailure(error); | ||
| console.error(failure); | ||
| alert( | ||
| 'Error acquiring camera or microphone permissions. Please make sure you grant the necessary permissions in your browser and reload the tab', | ||
| ); | ||
| }; | ||
|
|
||
| session.internal.emitter.on(SessionEvent.MediaDevicesError, handleMediaDevicesError); | ||
| return () => { | ||
| session.internal.emitter.off(SessionEvent.MediaDevicesError, handleMediaDevicesError); | ||
| }; | ||
| }, [session]); | ||
|
|
||
| return ( | ||
| <div data-lk-theme="default"> | ||
| <LiveKitRoom | ||
| video={false} | ||
| audio={true} | ||
| token={token} | ||
| serverUrl={process.env.NEXT_PUBLIC_LK_SERVER_URL} | ||
| > | ||
| <SessionProvider session={session}> | ||
| <AudioConference /> | ||
| </LiveKitRoom> | ||
| </SessionProvider> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,52 +2,87 @@ | |
|
|
||
| import { | ||
| ControlBar, | ||
| LiveKitRoom, | ||
| SessionProvider, | ||
| useSession, | ||
| RoomAudioRenderer, | ||
| RoomName, | ||
| TrackLoop, | ||
| TrackMutedIndicator, | ||
| useIsMuted, | ||
| useIsSpeaking, | ||
| useToken, | ||
| useTrackRefContext, | ||
| useTracks, | ||
| SessionEvent, | ||
| } from '@livekit/components-react'; | ||
| import styles from '../styles/Clubhouse.module.scss'; | ||
| import { Track } from 'livekit-client'; | ||
| import { useMemo, useState } from 'react'; | ||
| import { Track, TokenSource, MediaDeviceFailure } from 'livekit-client'; | ||
| import { useMemo, useState, useEffect } from 'react'; | ||
| import { generateRandomUserId } from '../lib/helper'; | ||
| import type { NextPage } from 'next'; | ||
|
|
||
| const Clubhouse = () => { | ||
| const params = typeof window !== 'undefined' ? new URLSearchParams(location.search) : null; | ||
| const Clubhouse: NextPage = () => { | ||
| const params = useMemo( | ||
| () => (typeof window !== 'undefined' ? new URLSearchParams(location.search) : null), | ||
| [], | ||
| ); | ||
| const roomName = params?.get('room') ?? 'test-room'; | ||
| const userIdentity = params?.get('user') ?? generateRandomUserId(); | ||
| const [userIdentity] = useState(() => params?.get('user') ?? generateRandomUserId()); | ||
|
||
|
|
||
| const tokenSource = useMemo(() => { | ||
| return TokenSource.endpoint(process.env.NEXT_PUBLIC_LK_TOKEN_ENDPOINT!); | ||
| }, []); | ||
|
|
||
| const token = useToken(process.env.NEXT_PUBLIC_LK_TOKEN_ENDPOINT, roomName, { | ||
| userInfo: { | ||
| identity: userIdentity, | ||
| name: userIdentity, | ||
| }, | ||
| const session = useSession(tokenSource, { | ||
| roomName, | ||
| participantIdentity: userIdentity, | ||
| participantName: userIdentity, | ||
| }); | ||
|
|
||
| const [tryToConnect, setTryToConnect] = useState(false); | ||
| const [connected, setConnected] = useState(false); | ||
|
|
||
| useEffect(() => { | ||
| if (tryToConnect) { | ||
| session | ||
| .start({ | ||
| tracks: { | ||
| microphone: { enabled: true }, | ||
| }, | ||
| }) | ||
| .catch((err) => { | ||
| console.error('Failed to start session:', err); | ||
| }); | ||
| } else { | ||
| session.end().catch((err) => { | ||
| console.error('Failed to end session:', err); | ||
| }); | ||
| } | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought(non-blocking): 😢
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also a |
||
| }, [tryToConnect, session.start, session.end]); | ||
|
|
||
| useEffect(() => { | ||
| if (session.connectionState === 'disconnected') { | ||
| setTryToConnect(false); | ||
| } | ||
| }, [session.connectionState]); | ||
|
|
||
| useEffect(() => { | ||
| const handleMediaDevicesError = (error: Error) => { | ||
| const failure = MediaDeviceFailure.getFailure(error); | ||
| console.error(failure); | ||
| alert( | ||
| 'Error acquiring camera or microphone permissions. Please make sure you grant the necessary permissions in your browser and reload the tab', | ||
| ); | ||
| }; | ||
|
|
||
| session.internal.emitter.on(SessionEvent.MediaDevicesError, handleMediaDevicesError); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thought: @1egoman maybe we should think about a cleaner high level helper for this. using the internal emitters in the examples doesn't feel great.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a better way to do this proposed, So the thinking is the above could be something like the below instead: useEvents(session, SessionEvent.MediaDevicesError, handleMediaDevicesError); |
||
| return () => { | ||
| session.internal.emitter.off(SessionEvent.MediaDevicesError, handleMediaDevicesError); | ||
| }; | ||
| }, [session]); | ||
|
|
||
| return ( | ||
| <div data-lk-theme="default" className={styles.container}> | ||
| <LiveKitRoom | ||
| token={token} | ||
| serverUrl={process.env.NEXT_PUBLIC_LK_SERVER_URL} | ||
| connect={tryToConnect} | ||
| video={false} | ||
| audio={true} | ||
| // simulateParticipants={15} | ||
| onConnected={() => setConnected(true)} | ||
| onDisconnected={() => { | ||
| setTryToConnect(false); | ||
| setConnected(false); | ||
| }} | ||
| > | ||
| <SessionProvider session={session}> | ||
| <div style={{ display: 'grid', placeContent: 'center', height: '100%' }}> | ||
| <button | ||
| className="lk-button" | ||
|
|
@@ -59,7 +94,7 @@ | |
| </button> | ||
| </div> | ||
|
|
||
| <div className={styles.slider} style={{ bottom: connected ? '0px' : '-100%' }}> | ||
| <div className={styles.slider} style={{ bottom: session.isConnected ? '0px' : '-100%' }}> | ||
| <h1> | ||
| <RoomName /> | ||
| </h1> | ||
|
|
@@ -70,15 +105,15 @@ | |
| /> | ||
| <RoomAudioRenderer /> | ||
| </div> | ||
| </LiveKitRoom> | ||
| </SessionProvider> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const Stage = () => { | ||
| const tracksReferences = useTracks([Track.Source.Microphone]); | ||
| return ( | ||
| <div className=""> | ||
| <div> | ||
| <div className={styles.stageGrid}> | ||
| <TrackLoop tracks={tracksReferences}> | ||
| <CustomParticipantTile></CustomParticipantTile> | ||
|
|
@@ -98,14 +133,10 @@ | |
| return ( | ||
| <section className={styles['participant-tile']} title={trackRef.participant.name}> | ||
| <div | ||
| // className={`rounded-full border-2 p-0.5 transition-colors duration-1000 ${ | ||
| className={styles['avatar-container']} | ||
| style={{ borderColor: isSpeaking ? 'greenyellow' : 'transparent' }} | ||
| > | ||
| <div | ||
| className={styles.avatar} | ||
| // className="z-10 grid aspect-square items-center overflow-hidden rounded-full bg-beige transition-all will-change-transform" | ||
| > | ||
| <div className={styles.avatar}> | ||
| <img | ||
| src={`https://avatars.dicebear.com/api/avataaars/${id}.svg?mouth=default,smile,tongue&eyes=default,happy,hearts&eyebrows=default,defaultNatural,flatNatural`} | ||
| className="fade-in" | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.