♻️(frontend) refactor settings from context to valtio global store
Replace settings context provider with valtio global store for easier access outside room components and better long-term maintainability. Prepares for upcoming prejoin screen settings access by making settings globally available without React context limitations.
This commit is contained in:
committed by
aleb_the_flash
parent
e80b9c2485
commit
c1c2d0260d
@@ -1,8 +1,8 @@
|
||||
import { useSettingsDialog } from '../SettingsDialogContext'
|
||||
import { Button } from '@/primitives'
|
||||
import { RiSettings3Line } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||
import { useSettingsDialog } from '@/features/settings/hook/useSettingsDialog'
|
||||
|
||||
export const SettingsButton = ({
|
||||
settingTab,
|
||||
@@ -12,7 +12,7 @@ export const SettingsButton = ({
|
||||
onPress?: () => void
|
||||
}) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'selectDevice' })
|
||||
const { setDialogOpen, setDefaultSelectedKey } = useSettingsDialog()
|
||||
const { openSettingsDialog } = useSettingsDialog()
|
||||
|
||||
return (
|
||||
<Button
|
||||
@@ -22,8 +22,7 @@ export const SettingsButton = ({
|
||||
aria-label={t(`settings.${settingTab}`)}
|
||||
variant="primaryDark"
|
||||
onPress={() => {
|
||||
setDefaultSelectedKey(settingTab)
|
||||
setDialogOpen(true)
|
||||
openSettingsDialog(settingTab)
|
||||
onPress?.()
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -2,16 +2,16 @@ import { RiSettings3Line } from '@remixicon/react'
|
||||
import { MenuItem } from 'react-aria-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { menuRecipe } from '@/primitives/menuRecipe'
|
||||
import { useSettingsDialog } from '../SettingsDialogContext'
|
||||
import { useSettingsDialog } from '@/features/settings/hook/useSettingsDialog'
|
||||
|
||||
export const SettingsMenuItem = () => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
|
||||
const { setDialogOpen } = useSettingsDialog()
|
||||
const { openSettingsDialog } = useSettingsDialog()
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
className={menuRecipe({ icon: true, variant: 'dark' }).item}
|
||||
onAction={() => setDialogOpen(true)}
|
||||
onAction={() => openSettingsDialog()}
|
||||
>
|
||||
<RiSettings3Line size={20} />
|
||||
{t('settings')}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import React, { createContext, useContext, useState } from 'react'
|
||||
import { SettingsDialogExtended } from '@/features/settings/components/SettingsDialogExtended'
|
||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||
|
||||
const SettingsDialogContext = createContext<
|
||||
| {
|
||||
dialogOpen: boolean
|
||||
defaultSelectedKey?: SettingsDialogExtendedKey
|
||||
setDefaultSelectedKey: React.Dispatch<
|
||||
React.SetStateAction<SettingsDialogExtendedKey | undefined>
|
||||
>
|
||||
setDialogOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
| undefined
|
||||
>(undefined)
|
||||
|
||||
export const SettingsDialogProvider: React.FC<{
|
||||
children: React.ReactNode
|
||||
}> = ({ children }) => {
|
||||
const [defaultSelectedKey, setDefaultSelectedKey] = useState<
|
||||
SettingsDialogExtendedKey | undefined
|
||||
>(undefined)
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
return (
|
||||
<SettingsDialogContext.Provider
|
||||
value={{
|
||||
dialogOpen,
|
||||
setDialogOpen,
|
||||
defaultSelectedKey,
|
||||
setDefaultSelectedKey,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<SettingsDialogExtended
|
||||
isOpen={dialogOpen}
|
||||
defaultSelectedKey={defaultSelectedKey}
|
||||
onOpenChange={(v) => {
|
||||
if (!v) {
|
||||
setDefaultSelectedKey(undefined)
|
||||
}
|
||||
setDialogOpen(v)
|
||||
}}
|
||||
/>
|
||||
</SettingsDialogContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export const useSettingsDialog = () => {
|
||||
const context = useContext(SettingsDialogContext)
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useSettingsDialog must be used within a SettingsDialogProvider'
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import * as React from 'react'
|
||||
|
||||
import { MobileControlBar } from './MobileControlBar'
|
||||
import { DesktopControlBar } from './DesktopControlBar'
|
||||
import { SettingsDialogProvider } from '../../components/controls/SettingsDialogContext'
|
||||
import { useIsMobile } from '@/utils/useIsMobile'
|
||||
|
||||
export interface ControlBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
@@ -16,15 +15,11 @@ export interface ControlBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
*/
|
||||
export function ControlBar({ onDeviceError }: ControlBarProps) {
|
||||
const isMobile = useIsMobile()
|
||||
return (
|
||||
<SettingsDialogProvider>
|
||||
{isMobile ? (
|
||||
<MobileControlBar onDeviceError={onDeviceError} />
|
||||
) : (
|
||||
<DesktopControlBar onDeviceError={onDeviceError} />
|
||||
)}
|
||||
</SettingsDialogProvider>
|
||||
)
|
||||
|
||||
if (isMobile) {
|
||||
return <MobileControlBar onDeviceError={onDeviceError} />
|
||||
}
|
||||
return <DesktopControlBar onDeviceError={onDeviceError} />
|
||||
}
|
||||
|
||||
export type ControlBarAuxProps = Pick<ControlBarProps, 'onDeviceError'>
|
||||
|
||||
@@ -18,13 +18,13 @@ import { ChatToggle } from '../../components/controls/ChatToggle'
|
||||
import { ParticipantsToggle } from '../../components/controls/Participants/ParticipantsToggle'
|
||||
import { useSidePanel } from '../../hooks/useSidePanel'
|
||||
import { LinkButton } from '@/primitives'
|
||||
import { useSettingsDialog } from '../../components/controls/SettingsDialogContext'
|
||||
import { ResponsiveMenu } from './ResponsiveMenu'
|
||||
import { ToolsToggle } from '../../components/controls/ToolsToggle'
|
||||
import { CameraSwitchButton } from '../../components/controls/CameraSwitchButton'
|
||||
import { useConfig } from '@/api/useConfig'
|
||||
import { AudioDevicesControl } from '../../components/controls/Device/AudioDevicesControl'
|
||||
import { VideoDeviceControl } from '../../components/controls/Device/VideoDeviceControl'
|
||||
import { useSettingsDialog } from '@/features/settings/hook/useSettingsDialog'
|
||||
|
||||
export function MobileControlBar({
|
||||
onDeviceError,
|
||||
@@ -33,7 +33,7 @@ export function MobileControlBar({
|
||||
const [isMenuOpened, setIsMenuOpened] = React.useState(false)
|
||||
const browserSupportsScreenSharing = supportsScreenSharing()
|
||||
const { toggleEffects } = useSidePanel()
|
||||
const { setDialogOpen } = useSettingsDialog()
|
||||
const { openSettingsDialog } = useSettingsDialog()
|
||||
|
||||
const { data } = useConfig()
|
||||
|
||||
@@ -152,7 +152,7 @@ export function MobileControlBar({
|
||||
)}
|
||||
<Button
|
||||
onPress={() => {
|
||||
setDialogOpen(true)
|
||||
openSettingsDialog()
|
||||
setIsMenuOpened(false)
|
||||
}}
|
||||
variant="primaryTextDark"
|
||||
|
||||
@@ -33,6 +33,7 @@ import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
|
||||
import { useConnectionObserver } from '../hooks/useConnectionObserver'
|
||||
import { useNoiseReduction } from '../hooks/useNoiseReduction'
|
||||
import { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
|
||||
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
|
||||
|
||||
const LayoutWrapper = styled(
|
||||
'div',
|
||||
@@ -240,6 +241,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
|
||||
<RoomAudioRenderer />
|
||||
<ConnectionStateToast />
|
||||
<RecordingStateToast />
|
||||
<SettingsDialogProvider />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export type SettingsDialogExtended = Pick<
|
||||
DialogProps,
|
||||
'isOpen' | 'onOpenChange'
|
||||
> & {
|
||||
defaultSelectedKey?: SettingsDialogExtendedKey
|
||||
defaultSelectedTab?: SettingsDialogExtendedKey
|
||||
}
|
||||
|
||||
export const SettingsDialogExtended = (props: SettingsDialogExtended) => {
|
||||
@@ -63,7 +63,7 @@ export const SettingsDialogExtended = (props: SettingsDialogExtended) => {
|
||||
<Tabs
|
||||
orientation="vertical"
|
||||
className={tabsStyle}
|
||||
defaultSelectedKey={props.defaultSelectedKey}
|
||||
defaultSelectedKey={props.defaultSelectedTab}
|
||||
>
|
||||
<div
|
||||
className={tabListContainerStyle}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { SettingsDialogExtended } from './SettingsDialogExtended'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { settingsStore } from '@/stores/settings'
|
||||
|
||||
export const SettingsDialogProvider = () => {
|
||||
const { areSettingsOpen, defaultSelectedTab } = useSnapshot(settingsStore)
|
||||
|
||||
return (
|
||||
<SettingsDialogExtended
|
||||
isOpen={areSettingsOpen}
|
||||
defaultSelectedTab={defaultSelectedTab}
|
||||
onOpenChange={(v) => {
|
||||
if (!v && settingsStore.defaultSelectedTab) {
|
||||
settingsStore.defaultSelectedTab = undefined
|
||||
}
|
||||
settingsStore.areSettingsOpen = v
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
20
src/frontend/src/features/settings/hook/useSettingsDialog.ts
Normal file
20
src/frontend/src/features/settings/hook/useSettingsDialog.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useSnapshot } from 'valtio/index'
|
||||
import { settingsStore } from '@/stores/settings'
|
||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||
|
||||
export const useSettingsDialog = () => {
|
||||
const { areSettingsOpen } = useSnapshot(settingsStore)
|
||||
|
||||
const openSettingsDialog = (
|
||||
defaultSelectedTab?: SettingsDialogExtendedKey
|
||||
) => {
|
||||
if (areSettingsOpen) return
|
||||
if (defaultSelectedTab)
|
||||
settingsStore.defaultSelectedTab = defaultSelectedTab
|
||||
settingsStore.areSettingsOpen = true
|
||||
}
|
||||
|
||||
return {
|
||||
openSettingsDialog,
|
||||
}
|
||||
}
|
||||
12
src/frontend/src/stores/settings.ts
Normal file
12
src/frontend/src/stores/settings.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { proxy } from 'valtio'
|
||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||
|
||||
type State = {
|
||||
areSettingsOpen: boolean
|
||||
defaultSelectedTab?: SettingsDialogExtendedKey
|
||||
}
|
||||
|
||||
export const settingsStore = proxy<State>({
|
||||
areSettingsOpen: false,
|
||||
defaultSelectedTab: undefined,
|
||||
})
|
||||
Reference in New Issue
Block a user