♻️(frontend) refactor prejoin screen for room context flexibility
Decouple prejoin components from conference context to enable different behaviors when inside vs outside room environments. Components can now evolve independently with lighter coupling. Refactor layout structure to prepare for upcoming speaker selector introduction. This decoupling allows for more flexible component evolution and cleaner architecture.
This commit is contained in:
committed by
aleb_the_flash
parent
bd139a1ef9
commit
e4d5ca64b9
@@ -5,7 +5,6 @@ import { Screen } from '@/layout/Screen'
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { LocalVideoTrack, Track } from 'livekit-client'
|
import { LocalVideoTrack, Track } from 'livekit-client'
|
||||||
import { H } from '@/primitives/H'
|
import { H } from '@/primitives/H'
|
||||||
import { SelectToggleDevice } from '../livekit/components/controls/SelectToggleDevice'
|
|
||||||
import { Field } from '@/primitives/Field'
|
import { Field } from '@/primitives/Field'
|
||||||
import { Button, Dialog, Text, Form } from '@/primitives'
|
import { Button, Dialog, Text, Form } from '@/primitives'
|
||||||
import { VStack } from '@/styled-system/jsx'
|
import { VStack } from '@/styled-system/jsx'
|
||||||
@@ -29,6 +28,8 @@ import { ApiAccessLevel } from '../api/ApiRoom'
|
|||||||
import { useLoginHint } from '@/hooks/useLoginHint'
|
import { useLoginHint } from '@/hooks/useLoginHint'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { openPermissionsDialog, permissionsStore } from '@/stores/permissions'
|
import { openPermissionsDialog, permissionsStore } from '@/stores/permissions'
|
||||||
|
import { ToggleDevice } from './join/ToggleDevice'
|
||||||
|
import { SelectDevice } from './join/SelectDevice'
|
||||||
|
|
||||||
const onError = (e: Error) => console.error('ERROR', e)
|
const onError = (e: Error) => console.error('ERROR', e)
|
||||||
|
|
||||||
@@ -502,6 +503,33 @@ export const Join = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className={css({
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '1rem',
|
||||||
|
zIndex: '1',
|
||||||
|
display: 'flex',
|
||||||
|
gap: '1rem',
|
||||||
|
justifyContent: 'center',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<ToggleDevice
|
||||||
|
source={Track.Source.Microphone}
|
||||||
|
initialState={audioEnabled}
|
||||||
|
track={audioTrack}
|
||||||
|
onChange={(enabled) => saveAudioInputEnabled(enabled)}
|
||||||
|
onDeviceError={(error) => console.error(error)}
|
||||||
|
/>
|
||||||
|
<ToggleDevice
|
||||||
|
source={Track.Source.Camera}
|
||||||
|
initialState={videoEnabled}
|
||||||
|
track={videoTrack}
|
||||||
|
onChange={(enabled) => saveVideoInputEnabled(enabled)}
|
||||||
|
onDeviceError={(error) => console.error(error)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className={css({
|
className={css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@@ -528,32 +556,26 @@ export const Join = ({
|
|||||||
marginX: 'auto',
|
marginX: 'auto',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div>
|
<div
|
||||||
<SelectToggleDevice
|
className={css({
|
||||||
source={Track.Source.Microphone}
|
width: '50%',
|
||||||
initialState={audioEnabled}
|
})}
|
||||||
track={audioTrack}
|
>
|
||||||
initialDeviceId={audioDeviceId}
|
<SelectDevice
|
||||||
onChange={(enabled) => saveAudioInputEnabled(enabled)}
|
kind="audioinput"
|
||||||
onDeviceError={(error) => console.error(error)}
|
id={audioDeviceId}
|
||||||
onActiveDeviceChange={(deviceId) =>
|
onSubmit={saveAudioInputDeviceId}
|
||||||
saveAudioInputDeviceId(deviceId ?? '')
|
|
||||||
}
|
|
||||||
variant="tertiary"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div
|
||||||
<SelectToggleDevice
|
className={css({
|
||||||
source={Track.Source.Camera}
|
width: '50%',
|
||||||
initialState={videoEnabled}
|
})}
|
||||||
track={videoTrack}
|
>
|
||||||
initialDeviceId={videoDeviceId}
|
<SelectDevice
|
||||||
onChange={(enabled) => saveVideoInputEnabled(enabled)}
|
kind="videoinput"
|
||||||
onDeviceError={(error) => console.error(error)}
|
id={videoDeviceId}
|
||||||
onActiveDeviceChange={(deviceId) =>
|
onSubmit={saveVideoInputDeviceId}
|
||||||
saveVideoInputDeviceId(deviceId ?? '')
|
|
||||||
}
|
|
||||||
variant="tertiary"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
RemixiconComponentType,
|
||||||
|
RiMicLine,
|
||||||
|
RiVideoOnLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useMediaDeviceSelect } from '@livekit/components-react'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { Select } from '@/primitives/Select'
|
||||||
|
|
||||||
|
type DeviceItems = Array<{ value: string; label: string }>
|
||||||
|
|
||||||
|
type DeviceConfig = {
|
||||||
|
icon: RemixiconComponentType
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelectDeviceProps = {
|
||||||
|
id?: string
|
||||||
|
onSubmit?: (id: string) => void
|
||||||
|
kind: MediaDeviceKind
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SelectDevice = ({ id, onSubmit, kind }: SelectDeviceProps) => {
|
||||||
|
const { t } = useTranslation('rooms', { keyPrefix: 'join' })
|
||||||
|
|
||||||
|
const config = useMemo<DeviceConfig | undefined>(() => {
|
||||||
|
switch (kind) {
|
||||||
|
case 'audioinput':
|
||||||
|
return {
|
||||||
|
icon: RiMicLine,
|
||||||
|
}
|
||||||
|
case 'videoinput':
|
||||||
|
return {
|
||||||
|
icon: RiVideoOnLine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [kind])
|
||||||
|
|
||||||
|
const getDefaultSelectedKey = (items: DeviceItems) => {
|
||||||
|
if (!items || items.length === 0) return
|
||||||
|
const defaultItem =
|
||||||
|
items.find((item) => item.value === 'default') || items[0]
|
||||||
|
return defaultItem.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
devices: devices,
|
||||||
|
activeDeviceId: activeDeviceId,
|
||||||
|
setActiveMediaDevice: setActiveMediaDevice,
|
||||||
|
} = useMediaDeviceSelect({ kind, requestPermissions: false })
|
||||||
|
|
||||||
|
const items: DeviceItems = devices
|
||||||
|
.filter((d) => !!d.deviceId)
|
||||||
|
.map((d) => ({
|
||||||
|
value: d.deviceId,
|
||||||
|
label: d.label,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
aria-label={t(`${kind}.choose`)}
|
||||||
|
label=""
|
||||||
|
isDisabled={items.length === 0}
|
||||||
|
items={items}
|
||||||
|
iconComponent={config?.icon}
|
||||||
|
placeholder={t('selectDevice.loading')}
|
||||||
|
defaultSelectedKey={id || activeDeviceId || getDefaultSelectedKey(items)}
|
||||||
|
onSelectionChange={(key) => {
|
||||||
|
onSubmit?.(key as string)
|
||||||
|
setActiveMediaDevice(key as string)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { useTrackToggle, UseTrackToggleProps } from '@livekit/components-react'
|
||||||
|
import { ToggleDevice as BaseToggleDevice } from '../../livekit/components/controls/ToggleDevice'
|
||||||
|
import {
|
||||||
|
TOGGLE_DEVICE_CONFIG,
|
||||||
|
ToggleSource,
|
||||||
|
} from '../../livekit/config/ToggleDeviceConfig'
|
||||||
|
import { LocalAudioTrack, LocalVideoTrack } from 'livekit-client'
|
||||||
|
import { ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
||||||
|
|
||||||
|
type ToggleDeviceProps<T extends ToggleSource> = UseTrackToggleProps<T> & {
|
||||||
|
track?: LocalAudioTrack | LocalVideoTrack | undefined
|
||||||
|
source: ToggleSource
|
||||||
|
variant?: NonNullable<ButtonRecipeProps>['variant']
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ToggleDevice = <T extends ToggleSource>(
|
||||||
|
props: ToggleDeviceProps<T>
|
||||||
|
) => {
|
||||||
|
const config = TOGGLE_DEVICE_CONFIG[props.source]
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
throw new Error('Invalid source')
|
||||||
|
}
|
||||||
|
|
||||||
|
const trackProps = useTrackToggle(props)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseToggleDevice
|
||||||
|
{...trackProps}
|
||||||
|
config={config}
|
||||||
|
variant="whiteCircle"
|
||||||
|
errorVariant="errorCircle"
|
||||||
|
toggleButtonProps={{
|
||||||
|
groupPosition: undefined,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,14 +5,7 @@ import {
|
|||||||
UseTrackToggleProps,
|
UseTrackToggleProps,
|
||||||
} from '@livekit/components-react'
|
} from '@livekit/components-react'
|
||||||
import { Button, Menu, MenuList } from '@/primitives'
|
import { Button, Menu, MenuList } from '@/primitives'
|
||||||
import {
|
import { RiArrowDownSLine } from '@remixicon/react'
|
||||||
RemixiconComponentType,
|
|
||||||
RiArrowDownSLine,
|
|
||||||
RiMicLine,
|
|
||||||
RiMicOffLine,
|
|
||||||
RiVideoOffLine,
|
|
||||||
RiVideoOnLine,
|
|
||||||
} from '@remixicon/react'
|
|
||||||
import {
|
import {
|
||||||
LocalAudioTrack,
|
LocalAudioTrack,
|
||||||
LocalVideoTrack,
|
LocalVideoTrack,
|
||||||
@@ -20,8 +13,6 @@ import {
|
|||||||
VideoCaptureOptions,
|
VideoCaptureOptions,
|
||||||
} from 'livekit-client'
|
} from 'livekit-client'
|
||||||
|
|
||||||
import { Shortcut } from '@/features/shortcuts/types'
|
|
||||||
|
|
||||||
import { ToggleDevice } from '@/features/rooms/livekit/components/controls/ToggleDevice.tsx'
|
import { ToggleDevice } from '@/features/rooms/livekit/components/controls/ToggleDevice.tsx'
|
||||||
import { css } from '@/styled-system/css'
|
import { css } from '@/styled-system/css'
|
||||||
import { ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
import { ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
||||||
@@ -30,56 +21,17 @@ import { usePersistentUserChoices } from '../../hooks/usePersistentUserChoices'
|
|||||||
import { BackgroundProcessorFactory } from '../blur'
|
import { BackgroundProcessorFactory } from '../blur'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { permissionsStore } from '@/stores/permissions'
|
import { permissionsStore } from '@/stores/permissions'
|
||||||
|
import {
|
||||||
export type ToggleSource = Exclude<
|
TOGGLE_DEVICE_CONFIG,
|
||||||
Track.Source,
|
ToggleSource,
|
||||||
Track.Source.ScreenShareAudio | Track.Source.Unknown
|
} from '../../config/ToggleDeviceConfig'
|
||||||
>
|
|
||||||
|
|
||||||
type SelectToggleSource = Exclude<ToggleSource, Track.Source.ScreenShare>
|
|
||||||
|
|
||||||
export type SelectToggleDeviceConfig = {
|
|
||||||
kind: MediaDeviceKind
|
|
||||||
iconOn: RemixiconComponentType
|
|
||||||
iconOff: RemixiconComponentType
|
|
||||||
shortcut?: Shortcut
|
|
||||||
longPress?: Shortcut
|
|
||||||
}
|
|
||||||
|
|
||||||
type SelectToggleDeviceConfigMap = {
|
|
||||||
[key in SelectToggleSource]: SelectToggleDeviceConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectToggleDeviceConfig: SelectToggleDeviceConfigMap = {
|
|
||||||
[Track.Source.Microphone]: {
|
|
||||||
kind: 'audioinput',
|
|
||||||
iconOn: RiMicLine,
|
|
||||||
iconOff: RiMicOffLine,
|
|
||||||
shortcut: {
|
|
||||||
key: 'd',
|
|
||||||
ctrlKey: true,
|
|
||||||
},
|
|
||||||
longPress: {
|
|
||||||
key: 'Space',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[Track.Source.Camera]: {
|
|
||||||
kind: 'videoinput',
|
|
||||||
iconOn: RiVideoOnLine,
|
|
||||||
iconOff: RiVideoOffLine,
|
|
||||||
shortcut: {
|
|
||||||
key: 'e',
|
|
||||||
ctrlKey: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type SelectToggleDeviceProps<T extends ToggleSource> =
|
type SelectToggleDeviceProps<T extends ToggleSource> =
|
||||||
UseTrackToggleProps<T> & {
|
UseTrackToggleProps<T> & {
|
||||||
track?: LocalAudioTrack | LocalVideoTrack | undefined
|
track?: LocalAudioTrack | LocalVideoTrack | undefined
|
||||||
initialDeviceId?: string
|
initialDeviceId?: string
|
||||||
onActiveDeviceChange: (deviceId: string) => void
|
onActiveDeviceChange: (deviceId: string) => void
|
||||||
source: SelectToggleSource
|
source: ToggleSource
|
||||||
variant?: NonNullable<ButtonRecipeProps>['variant']
|
variant?: NonNullable<ButtonRecipeProps>['variant']
|
||||||
menuVariant?: 'dark' | 'light'
|
menuVariant?: 'dark' | 'light'
|
||||||
hideMenu?: boolean
|
hideMenu?: boolean
|
||||||
@@ -94,7 +46,7 @@ export const SelectToggleDevice = <T extends ToggleSource>({
|
|||||||
menuVariant = 'light',
|
menuVariant = 'light',
|
||||||
...props
|
...props
|
||||||
}: SelectToggleDeviceProps<T>) => {
|
}: SelectToggleDeviceProps<T>) => {
|
||||||
const config = selectToggleDeviceConfig[props.source]
|
const config = TOGGLE_DEVICE_CONFIG[props.source]
|
||||||
if (!config) {
|
if (!config) {
|
||||||
throw new Error('Invalid source')
|
throw new Error('Invalid source')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKey
|
|||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
import { appendShortcutLabel } from '@/features/shortcuts/utils'
|
import { appendShortcutLabel } from '@/features/shortcuts/utils'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { SelectToggleDeviceConfig } from './SelectToggleDevice'
|
|
||||||
import { PermissionNeededButton } from './PermissionNeededButton'
|
import { PermissionNeededButton } from './PermissionNeededButton'
|
||||||
import useLongPress from '@/features/shortcuts/useLongPress'
|
import useLongPress from '@/features/shortcuts/useLongPress'
|
||||||
import { ActiveSpeaker } from '@/features/rooms/components/ActiveSpeaker'
|
import { ActiveSpeaker } from '@/features/rooms/components/ActiveSpeaker'
|
||||||
@@ -15,13 +14,15 @@ import {
|
|||||||
import { ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
import { ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
||||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||||
import { openPermissionsDialog } from '@/stores/permissions'
|
import { openPermissionsDialog } from '@/stores/permissions'
|
||||||
|
import { ToggleDeviceConfig } from '../../config/ToggleDeviceConfig'
|
||||||
|
|
||||||
export type ToggleDeviceProps = {
|
export type ToggleDeviceProps = {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
isPermissionDeniedOrPrompted?: boolean
|
isPermissionDeniedOrPrompted?: boolean
|
||||||
toggle: () => void
|
toggle: () => void
|
||||||
config: SelectToggleDeviceConfig
|
config: ToggleDeviceConfig
|
||||||
variant?: NonNullable<ButtonRecipeProps>['variant']
|
variant?: NonNullable<ButtonRecipeProps>['variant']
|
||||||
|
errorVariant?: NonNullable<ButtonRecipeProps>['variant']
|
||||||
toggleButtonProps?: Partial<ToggleButtonProps>
|
toggleButtonProps?: Partial<ToggleButtonProps>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ export const ToggleDevice = ({
|
|||||||
enabled,
|
enabled,
|
||||||
toggle,
|
toggle,
|
||||||
variant = 'primaryDark',
|
variant = 'primaryDark',
|
||||||
|
errorVariant = 'error2',
|
||||||
toggleButtonProps,
|
toggleButtonProps,
|
||||||
isPermissionDeniedOrPrompted,
|
isPermissionDeniedOrPrompted,
|
||||||
}: ToggleDeviceProps) => {
|
}: ToggleDeviceProps) => {
|
||||||
@@ -72,7 +74,9 @@ export const ToggleDevice = ({
|
|||||||
{isPermissionDeniedOrPrompted && <PermissionNeededButton />}
|
{isPermissionDeniedOrPrompted && <PermissionNeededButton />}
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
isSelected={!enabled}
|
isSelected={!enabled}
|
||||||
variant={enabled && !isPermissionDeniedOrPrompted ? variant : 'error2'}
|
variant={
|
||||||
|
enabled && !isPermissionDeniedOrPrompted ? variant : errorVariant
|
||||||
|
}
|
||||||
shySelected
|
shySelected
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
isPermissionDeniedOrPrompted ? openPermissionsDialog() : toggle()
|
isPermissionDeniedOrPrompted ? openPermissionsDialog() : toggle()
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { Track } from 'livekit-client'
|
||||||
|
import {
|
||||||
|
RemixiconComponentType,
|
||||||
|
RiMicLine,
|
||||||
|
RiMicOffLine,
|
||||||
|
RiVideoOffLine,
|
||||||
|
RiVideoOnLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import { Shortcut } from '@/features/shortcuts/types'
|
||||||
|
|
||||||
|
export type ToggleSource = Exclude<
|
||||||
|
Track.Source,
|
||||||
|
| Track.Source.ScreenShareAudio
|
||||||
|
| Track.Source.Unknown
|
||||||
|
| Track.Source.ScreenShare
|
||||||
|
>
|
||||||
|
|
||||||
|
export type ToggleDeviceConfig = {
|
||||||
|
kind: MediaDeviceKind
|
||||||
|
iconOn: RemixiconComponentType
|
||||||
|
iconOff: RemixiconComponentType
|
||||||
|
shortcut?: Shortcut
|
||||||
|
longPress?: Shortcut
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToggleDeviceConfigMap = {
|
||||||
|
[key in ToggleSource]: ToggleDeviceConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TOGGLE_DEVICE_CONFIG = {
|
||||||
|
[Track.Source.Microphone]: {
|
||||||
|
kind: 'audioinput',
|
||||||
|
iconOn: RiMicLine,
|
||||||
|
iconOff: RiMicOffLine,
|
||||||
|
shortcut: {
|
||||||
|
key: 'd',
|
||||||
|
ctrlKey: true,
|
||||||
|
},
|
||||||
|
longPress: {
|
||||||
|
key: 'Space',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[Track.Source.Camera]: {
|
||||||
|
kind: 'videoinput',
|
||||||
|
iconOn: RiVideoOnLine,
|
||||||
|
iconOff: RiVideoOffLine,
|
||||||
|
shortcut: {
|
||||||
|
key: 'e',
|
||||||
|
ctrlKey: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const satisfies ToggleDeviceConfigMap
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
"back": "Dem Meeting erneut beitreten"
|
"back": "Dem Meeting erneut beitreten"
|
||||||
},
|
},
|
||||||
"join": {
|
"join": {
|
||||||
|
"selectDevice": {
|
||||||
|
"loading": "Laden…"
|
||||||
|
},
|
||||||
"videoinput": {
|
"videoinput": {
|
||||||
"choose": "Kamera auswählen",
|
"choose": "Kamera auswählen",
|
||||||
"disable": "Kamera deaktivieren",
|
"disable": "Kamera deaktivieren",
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
"back": "Rejoin the meeting"
|
"back": "Rejoin the meeting"
|
||||||
},
|
},
|
||||||
"join": {
|
"join": {
|
||||||
|
"selectDevice": {
|
||||||
|
"loading": "Loading…"
|
||||||
|
},
|
||||||
"videoinput": {
|
"videoinput": {
|
||||||
"choose": "Select camera",
|
"choose": "Select camera",
|
||||||
"disable": "Disable camera",
|
"disable": "Disable camera",
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
"back": "Réintégrer la réunion"
|
"back": "Réintégrer la réunion"
|
||||||
},
|
},
|
||||||
"join": {
|
"join": {
|
||||||
|
"selectDevice": {
|
||||||
|
"loading": "Chargement…"
|
||||||
|
},
|
||||||
"videoinput": {
|
"videoinput": {
|
||||||
"choose": "Choisir la webcam",
|
"choose": "Choisir la webcam",
|
||||||
"disable": "Désactiver la webcam",
|
"disable": "Désactiver la webcam",
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
"back": "Sluit weer bij de vergadering aan"
|
"back": "Sluit weer bij de vergadering aan"
|
||||||
},
|
},
|
||||||
"join": {
|
"join": {
|
||||||
|
"selectDevice": {
|
||||||
|
"loading": "Bezig met laden…"
|
||||||
|
},
|
||||||
"videoinput": {
|
"videoinput": {
|
||||||
"choose": "Selecteer camera",
|
"choose": "Selecteer camera",
|
||||||
"disable": "Camera uitschakelen",
|
"disable": "Camera uitschakelen",
|
||||||
|
|||||||
@@ -251,6 +251,20 @@ export const buttonRecipe = cva({
|
|||||||
color: 'error.300',
|
color: 'error.300',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
errorCircle: {
|
||||||
|
backgroundColor: 'error.500',
|
||||||
|
width: '56px',
|
||||||
|
height: '56px',
|
||||||
|
borderRadius: '100%',
|
||||||
|
color: 'white',
|
||||||
|
'&[data-hovered]': {
|
||||||
|
backgroundColor: 'error.600',
|
||||||
|
},
|
||||||
|
'&[data-pressed]': {
|
||||||
|
backgroundColor: 'error.700',
|
||||||
|
color: 'error.200',
|
||||||
|
},
|
||||||
|
},
|
||||||
// @TODO: better handling of colors… this is a mess
|
// @TODO: better handling of colors… this is a mess
|
||||||
success: {
|
success: {
|
||||||
colorPalette: 'success',
|
colorPalette: 'success',
|
||||||
|
|||||||
Reference in New Issue
Block a user