From 3e94ed0a8bb3e6ff720af14cd72c76e7faf73e23 Mon Sep 17 00:00:00 2001
From: lebaudantoine
Date: Mon, 28 Jul 2025 12:17:21 +0200
Subject: [PATCH] wip refactor effects prejoin
- avoid props drillig, rely on localstorage
- using an explicit flag to trigger the entry instead of depending of userconfig state (better encapsulation)
- init processor from video initiation, avoid weird blink, less hook and callback
overall, simplify and improve the user experience
---
.../features/rooms/components/Conference.tsx | 11 +++-
.../src/features/rooms/components/Join.tsx | 62 +++++--------------
.../src/features/rooms/routes/Room.tsx | 13 +---
3 files changed, 25 insertions(+), 61 deletions(-)
diff --git a/src/frontend/src/features/rooms/components/Conference.tsx b/src/frontend/src/features/rooms/components/Conference.tsx
index 891ce2e8..52e209f7 100644
--- a/src/frontend/src/features/rooms/components/Conference.tsx
+++ b/src/frontend/src/features/rooms/components/Conference.tsx
@@ -1,7 +1,10 @@
import { useEffect, useMemo, useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
-import { LiveKitRoom } from '@livekit/components-react'
+import {
+ LiveKitRoom,
+ usePersistentUserChoices,
+} from '@livekit/components-react'
import {
DisconnectReason,
MediaDeviceFailure,
@@ -29,18 +32,20 @@ import { isFireFox } from '@/utils/livekit'
export const Conference = ({
roomId,
- userConfig,
initialRoomData,
mode = 'join',
}: {
roomId: string
- userConfig: LocalUserChoices
mode?: 'join' | 'create'
initialRoomData?: ApiRoom
}) => {
const posthog = usePostHog()
const { data: apiConfig } = useConfig()
+ const { userChoices: userConfig } = usePersistentUserChoices() as {
+ userChoices: LocalUserChoices
+ }
+
useEffect(() => {
posthog.capture('visit-room', { slug: roomId })
}, [roomId, posthog])
diff --git a/src/frontend/src/features/rooms/components/Join.tsx b/src/frontend/src/features/rooms/components/Join.tsx
index 81097654..2a7ff4bb 100644
--- a/src/frontend/src/features/rooms/components/Join.tsx
+++ b/src/frontend/src/features/rooms/components/Join.tsx
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'
import { usePreviewTracks } from '@livekit/components-react'
import { css } from '@/styled-system/css'
import { Screen } from '@/layout/Screen'
-import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { useEffect, useMemo, useRef, useState } from 'react'
import { LocalVideoTrack, Track } from 'livekit-client'
import { H } from '@/primitives/H'
import { SelectToggleDevice } from '../livekit/components/controls/SelectToggleDevice'
@@ -27,7 +27,6 @@ import { ApiLobbyStatus, ApiRequestEntry } from '../api/requestEntry'
import { Spinner } from '@/primitives/Spinner'
import { ApiAccessLevel } from '../api/ApiRoom'
import { useLoginHint } from '@/hooks/useLoginHint'
-import { LocalUserChoices } from '@/stores/userChoices'
const onError = (e: Error) => console.error('ERROR', e)
@@ -107,10 +106,10 @@ const Effects = ({
}
export const Join = ({
- onSubmit,
+ enterRoom,
roomId,
}: {
- onSubmit: (choices: LocalUserChoices) => void
+ enterRoom: () => void
roomId: string
}) => {
const { t } = useTranslation('rooms', { keyPrefix: 'join' })
@@ -132,23 +131,14 @@ export const Join = ({
saveProcessorSerialized,
} = usePersistentUserChoices()
- const [processor, setProcessor] = useState(
- BackgroundProcessorFactory.deserializeProcessor(processorSerialized)
- )
-
- useEffect(() => {
- saveProcessorSerialized(processor?.serialize())
- }, [
- processor,
- saveProcessorSerialized,
- // eslint-disable-next-line react-hooks/exhaustive-deps
- JSON.stringify(processor?.serialize()),
- ])
-
const tracks = usePreviewTracks(
{
audio: { deviceId: audioDeviceId },
- video: { deviceId: videoDeviceId },
+ video: {
+ deviceId: videoDeviceId,
+ processor:
+ BackgroundProcessorFactory.deserializeProcessor(processorSerialized),
+ },
},
onError
)
@@ -192,25 +182,6 @@ export const Join = ({
}
}, [videoTrack, videoEnabled])
- const enterRoom = useCallback(() => {
- onSubmit({
- audioEnabled,
- videoEnabled,
- audioDeviceId,
- videoDeviceId,
- username,
- processorSerialized: processor?.serialize(),
- })
- }, [
- onSubmit,
- audioEnabled,
- videoEnabled,
- audioDeviceId,
- videoDeviceId,
- username,
- processor,
- ])
-
// Room data strategy:
// 1. Initial fetch is performed to check access and get LiveKit configuration
// 2. Data remains valid for 6 hours to avoid unnecessary refetches
@@ -269,16 +240,6 @@ export const Join = ({
enterRoom()
}
- // This hook is used to setup the persisted user choice processor on initialization.
- // So it's on purpose that processor is not included in the deps.
- // We just want to wait for the videoTrack to be loaded to apply the default processor.
- useEffect(() => {
- if (processor && videoTrack && !videoTrack.getProcessor()) {
- videoTrack.setProcessor(processor)
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [videoTrack])
-
const renderWaitingState = () => {
switch (status) {
case ApiLobbyStatus.TIMEOUT:
@@ -453,7 +414,12 @@ export const Join = ({
)}
-
+
+ saveProcessorSerialized(processor?.serialize())
+ }
+ />
{
const { isLoggedIn } = useUser()
- const { userChoices: existingUserChoices } = usePersistentUserChoices()
- const [userConfig, setUserConfig] = useState(null)
+ const [hasSubmittedEntry, setHasSubmittedEntry] = useState(false)
const { roomId } = useParams()
const [location, setLocation] = useLocation()
@@ -48,10 +45,10 @@ export const Room = () => {
return
}
- if (!userConfig && !skipJoinScreen) {
+ if (!hasSubmittedEntry && !skipJoinScreen) {
return (
-
+ setHasSubmittedEntry(true)} roomId={roomId} />
)
}
@@ -62,10 +59,6 @@ export const Room = () => {
initialRoomData={initialRoomData}
roomId={roomId}
mode={mode}
- userConfig={{
- ...existingUserChoices,
- ...userConfig,
- }}
/>
)