99 lines
3.5 KiB
TypeScript
99 lines
3.5 KiB
TypeScript
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"
|
||
|
|
description="Set the maximum number of participants allowed in this room's voice call. Set to 0 for no limit. Enforced locally by Lotus Chat clients."
|
||
|
|
>
|
||
|
|
<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>
|
||
|
|
);
|
||
|
|
}
|