2026-06-12 22:20:22 -04:00
import React , { FormEventHandler , useCallback } from 'react' ;
import { Box , Button , color , Input , Spinner , Text } from 'folds' ;
import { MatrixError } from 'matrix-js-sdk' ;
import { SequenceCard } from '../../../components/sequence-card' ;
import { SequenceCardStyle } from '../../room-settings/styles.css' ;
import { SettingTile } from '../../../components/setting-tile' ;
import { useMatrixClient } from '../../../hooks/useMatrixClient' ;
import { useRoom } from '../../../hooks/useRoom' ;
import { StateEvent } from '../../../../types/matrix/room' ;
import { AsyncStatus , useAsyncCallback } from '../../../hooks/useAsyncCallback' ;
import { useStateEvent } from '../../../hooks/useStateEvent' ;
import { RoomPermissionsAPI } from '../../../hooks/useRoomPermissions' ;
export type VoiceLimitContent = {
max_users? : number ;
};
type RoomVoiceLimitProps = {
permissions : RoomPermissionsAPI ;
};
export function RoomVoiceLimit ({ permissions } : RoomVoiceLimitProps ) {
const mx = useMatrixClient ();
const room = useRoom ();
const canEdit = permissions . stateEvent ( StateEvent . LotusVoiceLimit , mx . getSafeUserId ());
const limitEvent = useStateEvent ( room , StateEvent . LotusVoiceLimit );
const maxUsers = limitEvent ? . getContent < VoiceLimitContent >(). max_users ?? 0 ;
const [ submitState , submit ] = useAsyncCallback (
useCallback (
async ( value : number ) => {
const content : VoiceLimitContent = value > 0 ? { max_users : value } : {};
await mx . sendStateEvent ( room . roomId , StateEvent . LotusVoiceLimit as any , content );
},
[ mx , room . roomId ],
),
);
const submitting = submitState . status === AsyncStatus . Loading ;
const handleSubmit : FormEventHandler < HTMLFormElement > = ( evt ) => {
evt . preventDefault ();
const target = evt . target as HTMLFormElement ;
const limitInput = target . elements . namedItem ( 'limitInput' ) as HTMLInputElement | null ;
if ( ! limitInput ) return ;
const parsed = parseInt ( limitInput . value , 10 );
const value = Number . isNaN ( parsed ) || parsed < 0 ? 0 : parsed ;
submit ( value );
};
return (
< SequenceCard
className = { SequenceCardStyle }
variant = "SurfaceVariant"
direction = "Column"
gap = "400"
>
< SettingTile
title = "Voice Channel Limit"
2026-06-13 13:41:22 -04:00
description = "Set the maximum number of participants allowed in this room's voice call. Set to 0 for no limit. Enforced on the server for all Matrix clients."
2026-06-12 22:20:22 -04:00
>
< Box as = "form" onSubmit = { handleSubmit } gap = "200" alignItems = "Center" >
< Box style = {{ maxWidth : '100px' }} grow = "Yes" >
< Input
key = { maxUsers }
name = "limitInput"
defaultValue = { maxUsers }
type = "number"
min = { 0 }
max = { 99 }
size = "300"
variant = "Secondary"
radii = "300"
readOnly = { ! canEdit }
disabled = { ! canEdit }
/>
</ Box >
< Button
type = "submit"
size = "300"
variant = "Primary"
fill = "Solid"
radii = "300"
disabled = { ! canEdit || submitting }
before = { submitting ? < Spinner size = "100" variant = "Primary" fill = "Solid" /> : undefined }
>
< Text size = "B300" > Save </ Text >
</ Button >
</ Box >
{ submitState . status === AsyncStatus . Error && (
< Text style = {{ color : color.Critical.Main }} size = "T200" >
{( submitState . error as MatrixError ). message }
</ Text >
)}
</ SettingTile >
</ SequenceCard >
);
}