♻️(frontend) refactor audio/video tabs to share common layout components
Extract shared layout components from audio and video tabs to eliminate code duplication and improve maintainability. Creates reusable layout components that both tabs can utilize, reducing redundancy and ensuring consistent styling and behavior across settings tabs.
This commit is contained in:
committed by
aleb_the_flash
parent
c330ec6ff4
commit
2215b621f4
@@ -1,4 +1,4 @@
|
|||||||
import { DialogProps, Field, H, Switch, Text } from '@/primitives'
|
import { DialogProps, Field, Switch, Text } from '@/primitives'
|
||||||
|
|
||||||
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
||||||
import {
|
import {
|
||||||
@@ -9,78 +9,11 @@ import {
|
|||||||
import { isSafari } from '@/utils/livekit'
|
import { isSafari } from '@/utils/livekit'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { SoundTester } from '@/components/SoundTester'
|
import { SoundTester } from '@/components/SoundTester'
|
||||||
import { HStack } from '@/styled-system/jsx'
|
|
||||||
import { ActiveSpeaker } from '@/features/rooms/components/ActiveSpeaker'
|
import { ActiveSpeaker } from '@/features/rooms/components/ActiveSpeaker'
|
||||||
import { usePersistentUserChoices } from '@/features/rooms/livekit/hooks/usePersistentUserChoices'
|
import { usePersistentUserChoices } from '@/features/rooms/livekit/hooks/usePersistentUserChoices'
|
||||||
import { ReactNode } from 'react'
|
|
||||||
import { css } from '@/styled-system/css'
|
|
||||||
import posthog from 'posthog-js'
|
|
||||||
import { useNoiseReductionAvailable } from '@/features/rooms/livekit/hooks/useNoiseReductionAvailable'
|
import { useNoiseReductionAvailable } from '@/features/rooms/livekit/hooks/useNoiseReductionAvailable'
|
||||||
|
import posthog from 'posthog-js'
|
||||||
type RowWrapperProps = {
|
import { RowWrapper } from './layout/RowWrapper'
|
||||||
heading: string
|
|
||||||
children: ReactNode[]
|
|
||||||
beta?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const BetaBadge = () => (
|
|
||||||
<span
|
|
||||||
className={css({
|
|
||||||
content: '"Beta"',
|
|
||||||
display: 'block',
|
|
||||||
letterSpacing: '-0.02rem',
|
|
||||||
padding: '0 0.25rem',
|
|
||||||
backgroundColor: '#E8EDFF',
|
|
||||||
color: '#0063CB',
|
|
||||||
fontSize: '12px',
|
|
||||||
fontWeight: 500,
|
|
||||||
margin: '0 0 0.9375rem 0.3125rem',
|
|
||||||
lineHeight: '1rem',
|
|
||||||
borderRadius: '4px',
|
|
||||||
width: 'fit-content',
|
|
||||||
height: 'fit-content',
|
|
||||||
marginTop: { base: '10px', sm: '5px' },
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
Beta
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
|
|
||||||
const RowWrapper = ({ heading, children, beta }: RowWrapperProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<HStack>
|
|
||||||
<H lvl={2}>{heading}</H>
|
|
||||||
{beta && <BetaBadge />}
|
|
||||||
</HStack>
|
|
||||||
<HStack
|
|
||||||
gap={0}
|
|
||||||
style={{
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
flex: '1 1 215px',
|
|
||||||
minWidth: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children[0]}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: '10rem',
|
|
||||||
justifyContent: 'center',
|
|
||||||
display: 'flex',
|
|
||||||
paddingLeft: '1.5rem',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children[1]}
|
|
||||||
</div>
|
|
||||||
</HStack>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AudioTabProps = Pick<DialogProps, 'onOpenChange'> &
|
export type AudioTabProps = Pick<DialogProps, 'onOpenChange'> &
|
||||||
Pick<TabPanelProps, 'id'>
|
Pick<TabPanelProps, 'id'>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { DialogProps, Field, H } from '@/primitives'
|
import { DialogProps, Field } from '@/primitives'
|
||||||
|
|
||||||
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
||||||
import { useMediaDeviceSelect, useRoomContext } from '@livekit/components-react'
|
import { useMediaDeviceSelect, useRoomContext } from '@livekit/components-react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { HStack } from '@/styled-system/jsx'
|
|
||||||
import { usePersistentUserChoices } from '@/features/rooms/livekit/hooks/usePersistentUserChoices'
|
import { usePersistentUserChoices } from '@/features/rooms/livekit/hooks/usePersistentUserChoices'
|
||||||
import { ReactNode, useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import { css } from '@/styled-system/css'
|
import { css } from '@/styled-system/css'
|
||||||
import {
|
import {
|
||||||
createLocalVideoTrack,
|
createLocalVideoTrack,
|
||||||
@@ -16,44 +15,7 @@ import {
|
|||||||
} from 'livekit-client'
|
} from 'livekit-client'
|
||||||
import { BackgroundProcessorFactory } from '@/features/rooms/livekit/components/blur'
|
import { BackgroundProcessorFactory } from '@/features/rooms/livekit/components/blur'
|
||||||
import { VideoResolution } from '@/stores/userChoices'
|
import { VideoResolution } from '@/stores/userChoices'
|
||||||
|
import { RowWrapper } from './layout/RowWrapper'
|
||||||
type RowWrapperProps = {
|
|
||||||
heading: string
|
|
||||||
children: ReactNode[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const RowWrapper = ({ heading, children }: RowWrapperProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<H lvl={2}>{heading}</H>
|
|
||||||
<HStack
|
|
||||||
gap={0}
|
|
||||||
style={{
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
flex: '1 1 215px',
|
|
||||||
minWidth: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children[0]}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: '10rem',
|
|
||||||
justifyContent: 'center',
|
|
||||||
display: 'flex',
|
|
||||||
paddingLeft: '1.5rem',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children[1]}
|
|
||||||
</div>
|
|
||||||
</HStack>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export type VideoTabProps = Pick<DialogProps, 'onOpenChange'> &
|
export type VideoTabProps = Pick<DialogProps, 'onOpenChange'> &
|
||||||
Pick<TabPanelProps, 'id'>
|
Pick<TabPanelProps, 'id'>
|
||||||
@@ -212,104 +174,65 @@ export const VideoTab = ({ id }: VideoTabProps) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</RowWrapper>
|
</RowWrapper>
|
||||||
<H lvl={2}>{t('resolution.heading')}</H>
|
<RowWrapper heading={t('resolution.heading')}>
|
||||||
<HStack
|
<Field
|
||||||
gap={0}
|
type="select"
|
||||||
style={{
|
label={t('resolution.publish.label')}
|
||||||
flexWrap: 'wrap',
|
items={[
|
||||||
}}
|
{
|
||||||
>
|
value: 'h720',
|
||||||
<div
|
label: `${t('resolution.publish.items.high')} (720p)`,
|
||||||
style={{
|
},
|
||||||
flex: '1 1 215px',
|
{
|
||||||
minWidth: 0,
|
value: 'h360',
|
||||||
|
label: `${t('resolution.publish.items.medium')} (360p)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'h180',
|
||||||
|
label: `${t('resolution.publish.items.low')} (180p)`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
selectedKey={videoPublishResolution}
|
||||||
|
onSelectionChange={async (key) => {
|
||||||
|
await handleVideoResolutionChange(key as VideoResolution)
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="select"
|
|
||||||
label={t('resolution.publish.label')}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
value: 'h720',
|
|
||||||
label: `${t('resolution.publish.items.high')} (720p)`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'h360',
|
|
||||||
label: `${t('resolution.publish.items.medium')} (360p)`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'h180',
|
|
||||||
label: `${t('resolution.publish.items.low')} (180p)`,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
selectedKey={videoPublishResolution}
|
|
||||||
onSelectionChange={async (key) => {
|
|
||||||
await handleVideoResolutionChange(key as VideoResolution)
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
style={{
|
||||||
width: '10rem',
|
width: '100%',
|
||||||
justifyContent: 'center',
|
|
||||||
display: 'flex',
|
|
||||||
paddingLeft: '1.5rem',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
<></>
|
||||||
<HStack
|
</RowWrapper>
|
||||||
gap={0}
|
<RowWrapper>
|
||||||
style={{
|
<Field
|
||||||
flexWrap: 'wrap',
|
type="select"
|
||||||
}}
|
label={t('resolution.subscribe.label')}
|
||||||
>
|
items={[
|
||||||
<div
|
{
|
||||||
style={{
|
value: VideoQuality.HIGH.toString(),
|
||||||
flex: '1 1 215px',
|
label: t('resolution.subscribe.items.high'),
|
||||||
minWidth: 0,
|
},
|
||||||
|
{
|
||||||
|
value: VideoQuality.MEDIUM.toString(),
|
||||||
|
label: t('resolution.subscribe.items.medium'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: VideoQuality.LOW.toString(),
|
||||||
|
label: t('resolution.subscribe.items.low'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
selectedKey={videoSubscribeQuality?.toString()}
|
||||||
|
onSelectionChange={(key) => {
|
||||||
|
if (key == undefined) return
|
||||||
|
const selectedQuality = Number(String(key))
|
||||||
|
saveVideoSubscribeQuality(selectedQuality)
|
||||||
|
updateExistingRemoteVideoQuality(selectedQuality)
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<Field
|
|
||||||
type="select"
|
|
||||||
label={t('resolution.subscribe.label')}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
value: VideoQuality.HIGH.toString(),
|
|
||||||
label: t('resolution.subscribe.items.high'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: VideoQuality.MEDIUM.toString(),
|
|
||||||
label: t('resolution.subscribe.items.medium'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: VideoQuality.LOW.toString(),
|
|
||||||
label: t('resolution.subscribe.items.low'),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
selectedKey={videoSubscribeQuality?.toString()}
|
|
||||||
onSelectionChange={(key) => {
|
|
||||||
if (key == undefined) return
|
|
||||||
const selectedQuality = Number(String(key))
|
|
||||||
saveVideoSubscribeQuality(selectedQuality)
|
|
||||||
updateExistingRemoteVideoQuality(selectedQuality)
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
style={{
|
||||||
width: '10rem',
|
width: '100%',
|
||||||
justifyContent: 'center',
|
|
||||||
display: 'flex',
|
|
||||||
paddingLeft: '1.5rem',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
<></>
|
||||||
|
</RowWrapper>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { ReactNode } from 'react'
|
||||||
|
import { H } from '@/primitives'
|
||||||
|
import { HStack } from '@/styled-system/jsx'
|
||||||
|
import { css } from '@/styled-system/css'
|
||||||
|
|
||||||
|
export type RowWrapperProps = {
|
||||||
|
heading?: string
|
||||||
|
children: ReactNode[]
|
||||||
|
beta?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const BetaBadge = () => (
|
||||||
|
<span
|
||||||
|
className={css({
|
||||||
|
content: '"Beta"',
|
||||||
|
display: 'block',
|
||||||
|
letterSpacing: '-0.02rem',
|
||||||
|
padding: '0 0.25rem',
|
||||||
|
backgroundColor: '#E8EDFF',
|
||||||
|
color: '#0063CB',
|
||||||
|
fontSize: '12px',
|
||||||
|
fontWeight: 500,
|
||||||
|
margin: '0 0 0.9375rem 0.3125rem',
|
||||||
|
lineHeight: '1rem',
|
||||||
|
borderRadius: '4px',
|
||||||
|
width: 'fit-content',
|
||||||
|
height: 'fit-content',
|
||||||
|
marginTop: { base: '10px', sm: '5px' },
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
Beta
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const RowWrapper = ({ heading, children, beta }: RowWrapperProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{heading && (
|
||||||
|
<HStack>
|
||||||
|
<H lvl={2}>{heading}</H>
|
||||||
|
{beta && <BetaBadge />}
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
|
<HStack
|
||||||
|
gap={0}
|
||||||
|
style={{
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: '1 1 215px',
|
||||||
|
minWidth: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children[0]}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '10rem',
|
||||||
|
justifyContent: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
paddingLeft: '1.5rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children[1]}
|
||||||
|
</div>
|
||||||
|
</HStack>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user