🚸(frontend) disable device controls when user lacks room permissions
Update user experience by clearly marking device toggle and control components as disabled when users have insufficient room permissions. Prevents confusion by providing visual feedback that device controls are unavailable, improving clarity about what actions users can perform in their current role.
This commit is contained in:
committed by
aleb_the_flash
parent
7d1f15ef91
commit
3d3242e148
@@ -124,6 +124,7 @@ const config: Config = {
|
||||
...pandaPreset.theme.tokens.colors,
|
||||
primaryDark: {
|
||||
50: { value: '#161622' },
|
||||
75: { value: '#222234' },
|
||||
100: { value: '#2D2D46' },
|
||||
200: { value: '#43436A' },
|
||||
300: { value: '#5A5A8F' },
|
||||
|
||||
@@ -7,12 +7,14 @@ import { Track } from 'livekit-client'
|
||||
import { ToggleDevice } from './ToggleDevice'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { usePersistentUserChoices } from '../../../hooks/usePersistentUserChoices'
|
||||
import { useCanPublishTrack } from '../../../hooks/useCanPublishTrack'
|
||||
import { useCannotUseDevice } from '../../../hooks/useCannotUseDevice'
|
||||
import Source = Track.Source
|
||||
import * as React from 'react'
|
||||
import { SelectDevice } from './SelectDevice'
|
||||
import { SettingsButton } from './SettingsButton'
|
||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||
import { TrackSource } from '@livekit/protocol'
|
||||
import Source = Track.Source
|
||||
|
||||
type AudioDevicesControlProps = Omit<
|
||||
UseTrackToggleProps<Source.Microphone>,
|
||||
@@ -50,6 +52,8 @@ export const AudioDevicesControl = ({
|
||||
const cannotUseDevice = useCannotUseDevice(kind)
|
||||
const selectLabel = t(`settings.${SettingsDialogExtendedKey.AUDIO}`)
|
||||
|
||||
const canPublishTrack = useCanPublishTrack(TrackSource.MICROPHONE)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
@@ -59,6 +63,7 @@ export const AudioDevicesControl = ({
|
||||
>
|
||||
<ToggleDevice
|
||||
{...trackProps}
|
||||
isDisabled={!canPublishTrack}
|
||||
kind={kind}
|
||||
toggle={trackProps.toggle as () => Promise<void>}
|
||||
overrideToggleButtonProps={{
|
||||
@@ -77,7 +82,9 @@ export const AudioDevicesControl = ({
|
||||
groupPosition="right"
|
||||
square
|
||||
variant={
|
||||
trackProps.enabled && !cannotUseDevice ? 'primaryDark' : 'error2'
|
||||
!canPublishTrack || !trackProps.enabled || cannotUseDevice
|
||||
? 'error2'
|
||||
: 'primaryDark'
|
||||
}
|
||||
>
|
||||
<RiArrowUpSLine />
|
||||
|
||||
@@ -27,6 +27,7 @@ type ToggleDeviceStyleProps = {
|
||||
|
||||
export type ToggleDeviceProps<T extends ToggleSource> = {
|
||||
enabled: boolean
|
||||
isDisabled?: boolean
|
||||
toggle: (
|
||||
forceState?: boolean,
|
||||
captureOptions?: CaptureOptionsBySource<T>
|
||||
@@ -39,6 +40,7 @@ export type ToggleDeviceProps<T extends ToggleSource> = {
|
||||
export const ToggleDevice = <T extends ToggleSource>({
|
||||
kind,
|
||||
enabled,
|
||||
isDisabled,
|
||||
toggle,
|
||||
context = 'room',
|
||||
overrideToggleButtonProps,
|
||||
@@ -105,7 +107,9 @@ export const ToggleDevice = <T extends ToggleSource>({
|
||||
}, [enabled, kind, deviceShortcut, t])
|
||||
|
||||
const Icon =
|
||||
enabled && !cannotUseDevice ? deviceIcons.toggleOn : deviceIcons.toggleOff
|
||||
isDisabled || cannotUseDevice || !enabled
|
||||
? deviceIcons.toggleOff
|
||||
: deviceIcons.toggleOn
|
||||
|
||||
const roomContext = useMaybeRoomContext()
|
||||
if (kind === 'audioinput' && pushToTalk && roomContext) {
|
||||
@@ -117,7 +121,10 @@ export const ToggleDevice = <T extends ToggleSource>({
|
||||
{cannotUseDevice && <PermissionNeededButton />}
|
||||
<ToggleButton
|
||||
isSelected={!enabled}
|
||||
variant={enabled && !cannotUseDevice ? variant : errorVariant}
|
||||
isDisabled={isDisabled}
|
||||
variant={
|
||||
isDisabled || cannotUseDevice || !enabled ? errorVariant : variant
|
||||
}
|
||||
shySelected
|
||||
onPress={() => (cannotUseDevice ? openPermissionsDialog() : toggle())}
|
||||
aria-label={toggleLabel}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Track, VideoCaptureOptions } from 'livekit-client'
|
||||
import { ToggleDevice } from './ToggleDevice'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { usePersistentUserChoices } from '../../../hooks/usePersistentUserChoices'
|
||||
import { useCanPublishTrack } from '../../../hooks/useCanPublishTrack'
|
||||
import { useCannotUseDevice } from '../../../hooks/useCannotUseDevice'
|
||||
import { useSidePanel } from '../../../hooks/useSidePanel'
|
||||
import { BackgroundProcessorFactory } from '../../blur'
|
||||
@@ -15,6 +16,7 @@ import * as React from 'react'
|
||||
import { SelectDevice } from './SelectDevice'
|
||||
import { SettingsButton } from './SettingsButton'
|
||||
import { SettingsDialogExtendedKey } from '@/features/settings/type'
|
||||
import { TrackSource } from '@livekit/protocol'
|
||||
|
||||
const EffectsButton = ({ onPress }: { onPress: () => void }) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'selectDevice' })
|
||||
@@ -95,6 +97,7 @@ export const VideoDeviceControl = ({
|
||||
}
|
||||
|
||||
const selectLabel = t(`settings.${SettingsDialogExtendedKey.VIDEO}`)
|
||||
const canPublishTrack = useCanPublishTrack(TrackSource.CAMERA)
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -105,6 +108,7 @@ export const VideoDeviceControl = ({
|
||||
>
|
||||
<ToggleDevice
|
||||
{...trackProps}
|
||||
isDisabled={!canPublishTrack}
|
||||
kind={kind}
|
||||
toggle={toggleWithProcessor}
|
||||
overrideToggleButtonProps={{
|
||||
@@ -123,7 +127,9 @@ export const VideoDeviceControl = ({
|
||||
groupPosition="right"
|
||||
square
|
||||
variant={
|
||||
trackProps.enabled && !cannotUseDevice ? 'primaryDark' : 'error2'
|
||||
!canPublishTrack || !trackProps.enabled || cannotUseDevice
|
||||
? 'error2'
|
||||
: 'primaryDark'
|
||||
}
|
||||
>
|
||||
<RiArrowUpSLine />
|
||||
|
||||
@@ -6,6 +6,8 @@ import { Track } from 'livekit-client'
|
||||
import React from 'react'
|
||||
import { type ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
import { TrackSource } from '@livekit/protocol'
|
||||
import { useCanPublishTrack } from '@/features/rooms/livekit/hooks/useCanPublishTrack'
|
||||
|
||||
type Props = Omit<
|
||||
UseTrackToggleProps<Track.Source.ScreenShare>,
|
||||
@@ -29,10 +31,13 @@ export const ScreenShareToggle = ({
|
||||
const tooltipLabel = enabled ? 'stop' : 'start'
|
||||
const Icon = enabled ? RiCloseFill : RiArrowUpLine
|
||||
|
||||
const canShareScreen = useCanPublishTrack(TrackSource.SCREEN_SHARE)
|
||||
|
||||
// fixme - remove ToggleButton custom styles when we design a proper icon
|
||||
return (
|
||||
<ToggleButton
|
||||
isSelected={enabled}
|
||||
isDisabled={!canShareScreen}
|
||||
square
|
||||
variant={variant}
|
||||
tooltip={t(tooltipLabel)}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { TrackSource } from '@livekit/protocol'
|
||||
import {
|
||||
useLocalParticipant,
|
||||
useParticipantPermissions,
|
||||
} from '@livekit/components-react'
|
||||
|
||||
export function useCanPublishTrack(trackSource: TrackSource): boolean {
|
||||
const { localParticipant } = useLocalParticipant()
|
||||
const permissions = useParticipantPermissions({
|
||||
participant: localParticipant,
|
||||
})
|
||||
|
||||
return Boolean(
|
||||
permissions?.canPublish &&
|
||||
permissions?.canPublishSources?.includes(trackSource)
|
||||
)
|
||||
}
|
||||
@@ -148,6 +148,10 @@ export const buttonRecipe = cva({
|
||||
backgroundColor: 'primaryDark.900',
|
||||
color: 'primaryDark.100',
|
||||
},
|
||||
'&[data-disabled]': {
|
||||
backgroundColor: 'primaryDark.75',
|
||||
color: 'primaryDark.300',
|
||||
},
|
||||
'&[data-hovered]': {
|
||||
backgroundColor: 'primaryDark.300',
|
||||
color: 'white',
|
||||
@@ -247,8 +251,8 @@ export const buttonRecipe = cva({
|
||||
color: 'error.100 !important',
|
||||
},
|
||||
'&[data-disabled]': {
|
||||
backgroundColor: 'error.200',
|
||||
color: 'error.300',
|
||||
backgroundColor: 'error.200 !important',
|
||||
color: 'error.300 !important',
|
||||
},
|
||||
},
|
||||
errorCircle: {
|
||||
|
||||
Reference in New Issue
Block a user