import React, { FormEventHandler, useCallback, useEffect, useState } from 'react'; import { MatrixError, Room, JoinRule } from 'matrix-js-sdk'; import { Box, Button, Chip, color, config, Icon, Icons, Input, Spinner, Switch, Text, TextArea, } from 'folds'; import { SettingTile } from '../../components/setting-tile'; import { SequenceCard } from '../../components/sequence-card'; import { creatorsSupported, knockRestrictedSupported, knockSupported, restrictedSupported, } from '../../utils/matrix'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { millisecondsToMinutes, replaceSpaceWithDash } from '../../utils/common'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { useCapabilities } from '../../hooks/useCapabilities'; import { useAlive } from '../../hooks/useAlive'; import { ErrorCode } from '../../cs-errorcode'; import { AdditionalCreatorInput, createRoom, CreateRoomAliasInput, CreateRoomData, CreateRoomAccess, CreateRoomAccessSelector, RoomVersionSelector, useAdditionalCreators, CreateRoomType, } from '../../components/create-room'; import { RoomType } from '../../../types/matrix/room'; import { CreateRoomTypeSelector } from '../../components/create-room/CreateRoomTypeSelector'; import { getRoomIconSrc } from '../../utils/room'; const getCreateRoomAccessToIcon = (access: CreateRoomAccess, type?: CreateRoomType) => { const isVoiceRoom = type === CreateRoomType.VoiceRoom; let joinRule: JoinRule = JoinRule.Public; if (access === CreateRoomAccess.Restricted) joinRule = JoinRule.Restricted; if (access === CreateRoomAccess.Private) joinRule = JoinRule.Knock; return getRoomIconSrc(Icons, isVoiceRoom ? RoomType.Call : undefined, joinRule); }; const getCreateRoomTypeToIcon = (type: CreateRoomType) => { if (type === CreateRoomType.VoiceRoom) return Icons.VolumeHigh; return Icons.Hash; }; type CreateRoomFormProps = { defaultAccess?: CreateRoomAccess; defaultType?: CreateRoomType; space?: Room; onCreate?: (roomId: string) => void; }; export function CreateRoomForm({ defaultAccess, defaultType, space, onCreate, }: CreateRoomFormProps) { const mx = useMatrixClient(); const alive = useAlive(); const capabilities = useCapabilities(); const roomVersions = capabilities['m.room_versions']; const [selectedRoomVersion, selectRoomVersion] = useState(roomVersions?.default ?? '1'); useEffect(() => { // capabilities load async selectRoomVersion(roomVersions?.default ?? '1'); }, [roomVersions?.default]); const allowRestricted = space && restrictedSupported(selectedRoomVersion); const [type, setType] = useState(defaultType ?? CreateRoomType.TextRoom); const [access, setAccess] = useState( defaultAccess ?? (allowRestricted ? CreateRoomAccess.Restricted : CreateRoomAccess.Private) ); const allowAdditionalCreators = creatorsSupported(selectedRoomVersion); const { additionalCreators, addAdditionalCreator, removeAdditionalCreator } = useAdditionalCreators(); const [federation, setFederation] = useState(true); const [encryption, setEncryption] = useState(false); const [knock, setKnock] = useState(false); const [advance, setAdvance] = useState(false); const allowKnock = access === CreateRoomAccess.Private && knockSupported(selectedRoomVersion); const allowKnockRestricted = access === CreateRoomAccess.Restricted && knockRestrictedSupported(selectedRoomVersion); const handleRoomVersionChange = (version: string) => { if (!restrictedSupported(version)) { setAccess(CreateRoomAccess.Private); } selectRoomVersion(version); }; const [createState, create] = useAsyncCallback( useCallback((data) => createRoom(mx, data), [mx]) ); const loading = createState.status === AsyncStatus.Loading; const error = createState.status === AsyncStatus.Error ? createState.error : undefined; const disabled = createState.status === AsyncStatus.Loading; const handleSubmit: FormEventHandler = (evt) => { evt.preventDefault(); if (disabled) return; const form = evt.currentTarget; const nameInput = form.nameInput as HTMLInputElement | undefined; const topicTextArea = form.topicTextAria as HTMLTextAreaElement | undefined; const aliasInput = form.aliasInput as HTMLInputElement | undefined; const roomName = nameInput?.value.trim(); const roomTopic = topicTextArea?.value.trim(); const aliasLocalPart = aliasInput && aliasInput.value ? replaceSpaceWithDash(aliasInput.value) : undefined; if (!roomName) return; const publicRoom = access === CreateRoomAccess.Public; let roomKnock = false; if (allowKnock && access === CreateRoomAccess.Private) { roomKnock = knock; } if (allowKnockRestricted && access === CreateRoomAccess.Restricted) { roomKnock = knock; } let roomType: RoomType | undefined; if (type === CreateRoomType.VoiceRoom) roomType = RoomType.Call; create({ version: selectedRoomVersion, type: roomType, parent: space, access, name: roomName, topic: roomTopic || undefined, aliasLocalPart: publicRoom ? aliasLocalPart : undefined, encryption: publicRoom ? false : encryption, knock: roomKnock, allowFederation: federation, additionalCreators: allowAdditionalCreators ? additionalCreators : undefined, }).then((roomId) => { if (alive()) { onCreate?.(roomId); } }); }; return ( {!space && ( Type )} Access getCreateRoomAccessToIcon(roomAccess, type)} /> Name } name="nameInput" autoFocus size="500" variant="SurfaceVariant" radii="400" autoComplete="off" disabled={disabled} /> Topic (Optional)