more more reusable create room logic to components

This commit is contained in:
Ajay Bura 2025-08-04 14:04:15 +05:30
parent 2555a5a704
commit 8a72f40514
7 changed files with 400 additions and 347 deletions

View file

@ -0,0 +1,118 @@
import React, {
FormEventHandler,
KeyboardEventHandler,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import { MatrixError } from 'matrix-js-sdk';
import { Box, color, Icon, Icons, Input, Spinner, Text, toRem } from 'folds';
import { isKeyHotkey } from 'is-hotkey';
import { getMxIdServer } from '../../utils/matrix';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { replaceSpaceWithDash } from '../../utils/common';
import { AsyncState, AsyncStatus, useAsync } from '../../hooks/useAsyncCallback';
import { useDebounce } from '../../hooks/useDebounce';
export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) {
const mx = useMatrixClient();
const aliasInputRef = useRef<HTMLInputElement>(null);
const [aliasAvail, setAliasAvail] = useState<AsyncState<boolean, Error>>({
status: AsyncStatus.Idle,
});
useEffect(() => {
if (aliasAvail.status === AsyncStatus.Success && aliasInputRef.current?.value === '') {
setAliasAvail({ status: AsyncStatus.Idle });
}
}, [aliasAvail]);
const checkAliasAvail = useAsync(
useCallback(
async (aliasLocalPart: string) => {
const roomAlias = `#${aliasLocalPart}:${getMxIdServer(mx.getSafeUserId())}`;
try {
const result = await mx.getRoomIdForAlias(roomAlias);
return typeof result.room_id !== 'string';
} catch (e) {
if (e instanceof MatrixError && e.httpStatus === 404) {
return true;
}
throw e;
}
},
[mx]
),
setAliasAvail
);
const aliasAvailable: boolean | undefined =
aliasAvail.status === AsyncStatus.Success ? aliasAvail.data : undefined;
const debounceCheckAliasAvail = useDebounce(checkAliasAvail, { wait: 500 });
const handleAliasChange: FormEventHandler<HTMLInputElement> = (evt) => {
const aliasInput = evt.currentTarget;
const aliasLocalPart = replaceSpaceWithDash(aliasInput.value);
if (aliasLocalPart) {
aliasInput.value = aliasLocalPart;
debounceCheckAliasAvail(aliasLocalPart);
} else {
setAliasAvail({ status: AsyncStatus.Idle });
}
};
const handleAliasKeyDown: KeyboardEventHandler<HTMLInputElement> = (evt) => {
if (isKeyHotkey('enter', evt)) {
evt.preventDefault();
const aliasInput = evt.currentTarget;
const aliasLocalPart = replaceSpaceWithDash(aliasInput.value);
if (aliasLocalPart) {
checkAliasAvail(aliasLocalPart);
} else {
setAliasAvail({ status: AsyncStatus.Idle });
}
}
};
return (
<Box shrink="No" direction="Column" gap="100">
<Text size="L400">Address (Optional)</Text>
<Text size="T200" priority="300">
Pick an unique address to make your community discoverable to public.
</Text>
<Input
ref={aliasInputRef}
onChange={handleAliasChange}
before={
aliasAvail.status === AsyncStatus.Loading ? (
<Spinner size="100" variant="Secondary" />
) : (
<Icon size="100" src={Icons.Hash} />
)
}
after={
<Text style={{ maxWidth: toRem(150) }} truncate>
:{getMxIdServer(mx.getSafeUserId())}
</Text>
}
onKeyDown={handleAliasKeyDown}
name="aliasInput"
size="500"
variant={aliasAvailable === true ? 'Success' : 'SurfaceVariant'}
radii="400"
autoComplete="off"
disabled={disabled}
/>
{aliasAvailable === false && (
<Box style={{ color: color.Critical.Main }} alignItems="Center" gap="100">
<Icon src={Icons.Warning} filled size="50" />
<Text size="T200">
<b>This address is already taken. Please select a different one.</b>
</Text>
</Box>
)}
</Box>
);
}

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { Box, Text, Icon, Icons, config } from 'folds'; import { Box, Text, Icon, Icons, config, IconSrc } from 'folds';
import { SettingTile } from './setting-tile'; import { SequenceCard } from '../sequence-card';
import { SequenceCard } from './sequence-card'; import { SettingTile } from '../setting-tile';
export enum CreateRoomKind { export enum CreateRoomKind {
Private = 'private', Private = 'private',
@ -13,12 +13,14 @@ type CreateRoomKindSelectorProps = {
onSelect: (value: CreateRoomKind) => void; onSelect: (value: CreateRoomKind) => void;
canRestrict?: boolean; canRestrict?: boolean;
disabled?: boolean; disabled?: boolean;
getIcon: (kind: CreateRoomKind) => IconSrc;
}; };
export function CreateRoomKindSelector({ export function CreateRoomKindSelector({
value, value,
onSelect, onSelect,
canRestrict, canRestrict,
disabled, disabled,
getIcon,
}: CreateRoomKindSelectorProps) { }: CreateRoomKindSelectorProps) {
return ( return (
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
@ -35,7 +37,7 @@ export function CreateRoomKindSelector({
disabled={disabled} disabled={disabled}
> >
<SettingTile <SettingTile
before={<Icon size="400" src={Icons.Hash} />} before={<Icon size="400" src={getIcon(CreateRoomKind.Restricted)} />}
after={value === CreateRoomKind.Restricted && <Icon src={Icons.Check} />} after={value === CreateRoomKind.Restricted && <Icon src={Icons.Check} />}
> >
<Text size="H6">Restricted</Text> <Text size="H6">Restricted</Text>
@ -57,7 +59,7 @@ export function CreateRoomKindSelector({
disabled={disabled} disabled={disabled}
> >
<SettingTile <SettingTile
before={<Icon size="400" src={Icons.HashLock} />} before={<Icon size="400" src={getIcon(CreateRoomKind.Private)} />}
after={value === CreateRoomKind.Private && <Icon src={Icons.Check} />} after={value === CreateRoomKind.Private && <Icon src={Icons.Check} />}
> >
<Text size="H6">Private</Text> <Text size="H6">Private</Text>
@ -78,7 +80,7 @@ export function CreateRoomKindSelector({
disabled={disabled} disabled={disabled}
> >
<SettingTile <SettingTile
before={<Icon size="400" src={Icons.HashGlobe} />} before={<Icon size="400" src={getIcon(CreateRoomKind.Public)} />}
after={value === CreateRoomKind.Public && <Icon src={Icons.Check} />} after={value === CreateRoomKind.Public && <Icon src={Icons.Check} />}
> >
<Text size="H6">Public</Text> <Text size="H6">Public</Text>

View file

@ -0,0 +1,117 @@
import React, { MouseEventHandler, useState } from 'react';
import {
Box,
Button,
Chip,
config,
Icon,
Icons,
Menu,
PopOut,
RectCords,
Text,
toRem,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { SettingTile } from '../setting-tile';
import { SequenceCard } from '../sequence-card';
import { stopPropagation } from '../../utils/keyboard';
export function RoomVersionSelector({
versions,
value,
onChange,
disabled,
}: {
versions: string[];
value: string;
onChange: (value: string) => void;
disabled?: boolean;
}) {
const [menuCords, setMenuCords] = useState<RectCords>();
const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
setMenuCords(evt.currentTarget.getBoundingClientRect());
};
const handleSelect = (version: string) => {
setMenuCords(undefined);
onChange(version);
};
return (
<SequenceCard
style={{ padding: config.space.S300 }}
variant="SurfaceVariant"
direction="Column"
gap="500"
>
<SettingTile
title="Room Version"
after={
<PopOut
anchor={menuCords}
offset={5}
position="Bottom"
align="End"
content={
<FocusTrap
focusTrapOptions={{
initialFocus: false,
onDeactivate: () => setMenuCords(undefined),
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) =>
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
isKeyBackward: (evt: KeyboardEvent) =>
evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
escapeDeactivates: stopPropagation,
}}
>
<Menu>
<Box
direction="Column"
gap="200"
style={{ padding: config.space.S200, maxWidth: toRem(300) }}
>
<Text size="L400">Versions</Text>
<Box wrap="Wrap" gap="100">
{versions.map((version) => (
<Chip
key={version}
variant={value === version ? 'Primary' : 'SurfaceVariant'}
aria-pressed={value === version}
outlined={value === version}
radii="300"
onClick={() => handleSelect(version)}
type="button"
>
<Text truncate size="T300">
{version}
</Text>
</Chip>
))}
</Box>
</Box>
</Menu>
</FocusTrap>
}
>
<Button
type="button"
onClick={handleMenu}
size="300"
variant="Secondary"
fill="Soft"
radii="300"
aria-pressed={!!menuCords}
before={<Icon size="50" src={menuCords ? Icons.ChevronTop : Icons.ChevronBottom} />}
disabled={disabled}
>
<Text size="B300">{value}</Text>
</Button>
</PopOut>
}
/>
</SequenceCard>
);
}

View file

@ -0,0 +1,4 @@
export * from './CreateRoomKindSelector';
export * from './CreateRoomAliasInput';
export * from './RoomVersionSelector';
export * from './utils';

View file

@ -0,0 +1,131 @@
import {
ICreateRoomOpts,
ICreateRoomStateEvent,
JoinRule,
MatrixClient,
RestrictedAllowType,
Room,
} from 'matrix-js-sdk';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import { CreateRoomKind } from './CreateRoomKindSelector';
import { RoomType, StateEvent } from '../../../types/matrix/room';
import { getViaServers } from '../../plugins/via-servers';
import { getMxIdServer } from '../../utils/matrix';
export const createRoomCreationContent = (
type: RoomType | undefined,
allowFederation: boolean
): object => {
const content: Record<string, any> = {};
if (typeof type === 'string') {
content.type = type;
}
if (allowFederation === false) {
content['m.federate'] = false;
}
return content;
};
export const createRoomJoinRulesState = (
kind: CreateRoomKind,
parent: Room | undefined,
knock: boolean
) => {
let content: RoomJoinRulesEventContent = {
join_rule: knock ? JoinRule.Knock : JoinRule.Invite,
};
if (kind === CreateRoomKind.Public) {
content = {
join_rule: JoinRule.Public,
};
}
if (kind === CreateRoomKind.Restricted && parent) {
content = {
join_rule: knock ? ('knock_restricted' as JoinRule) : JoinRule.Restricted,
allow: [
{
type: RestrictedAllowType.RoomMembership,
room_id: parent.roomId,
},
],
};
}
return {
type: StateEvent.RoomJoinRules,
state_key: '',
content,
};
};
export const createRoomParentState = (parent: Room) => ({
type: StateEvent.SpaceParent,
state_key: parent.roomId,
content: {
canonical: true,
via: getViaServers(parent),
},
});
export const createRoomEncryptionState = () => ({
type: 'm.room.encryption',
state_key: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
});
export type CreateRoomData = {
version: string;
type?: RoomType;
parent?: Room;
kind: CreateRoomKind;
name: string;
topic?: string;
aliasLocalPart?: string;
encryption?: boolean;
knock: boolean;
allowFederation: boolean;
};
export const createRoom = async (mx: MatrixClient, data: CreateRoomData): Promise<string> => {
const initialState: ICreateRoomStateEvent[] = [];
if (data.encryption) {
initialState.push(createRoomEncryptionState());
}
if (data.parent) {
initialState.push(createRoomParentState(data.parent));
}
initialState.push(createRoomJoinRulesState(data.kind, data.parent, data.knock));
const options: ICreateRoomOpts = {
room_version: data.version,
name: data.name,
topic: data.topic,
room_alias_name: data.aliasLocalPart,
creation_content: createRoomCreationContent(data.type, data.allowFederation),
initial_state: initialState,
};
const result = await mx.createRoom(options);
if (data.parent) {
await mx.sendStateEvent(
data.parent.roomId,
StateEvent.SpaceChild as any,
{
auto_join: false,
suggested: false,
via: [getMxIdServer(mx.getUserId() ?? '') ?? ''],
},
result.room_id
);
}
return result.room_id;
};

View file

@ -1,21 +1,5 @@
import React, { import React, { FormEventHandler, useCallback, useState } from 'react';
FormEventHandler, import { MatrixError, Room } from 'matrix-js-sdk';
KeyboardEventHandler,
MouseEventHandler,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import {
ICreateRoomOpts,
ICreateRoomStateEvent,
JoinRule,
MatrixClient,
MatrixError,
RestrictedAllowType,
Room,
} from 'matrix-js-sdk';
import { import {
Box, Box,
Button, Button,
@ -25,333 +9,28 @@ import {
Icon, Icon,
Icons, Icons,
Input, Input,
Menu,
PopOut,
RectCords,
Spinner, Spinner,
Switch, Switch,
Text, Text,
TextArea, TextArea,
toRem,
} from 'folds'; } from 'folds';
import FocusTrap from 'focus-trap-react';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import { isKeyHotkey } from 'is-hotkey';
import { SettingTile } from '../../components/setting-tile'; import { SettingTile } from '../../components/setting-tile';
import { SequenceCard } from '../../components/sequence-card'; import { SequenceCard } from '../../components/sequence-card';
import { import { knockRestrictedSupported, knockSupported, restrictedSupported } from '../../utils/matrix';
getMxIdServer,
knockRestrictedSupported,
knockSupported,
restrictedSupported,
} from '../../utils/matrix';
import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMatrixClient } from '../../hooks/useMatrixClient';
import { millisecondsToMinutes, replaceSpaceWithDash } from '../../utils/common'; import { millisecondsToMinutes, replaceSpaceWithDash } from '../../utils/common';
import { AsyncState, AsyncStatus, useAsync, useAsyncCallback } from '../../hooks/useAsyncCallback'; import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { useDebounce } from '../../hooks/useDebounce';
import { useCapabilities } from '../../hooks/useCapabilities'; import { useCapabilities } from '../../hooks/useCapabilities';
import { stopPropagation } from '../../utils/keyboard';
import { getViaServers } from '../../plugins/via-servers';
import { StateEvent } from '../../../types/matrix/room';
import { getIdServer } from '../../../util/matrixUtil';
import { useAlive } from '../../hooks/useAlive'; import { useAlive } from '../../hooks/useAlive';
import { ErrorCode } from '../../cs-errorcode'; import { ErrorCode } from '../../cs-errorcode';
import { CreateRoomKind, CreateRoomKindSelector } from '../../components/CreateRoomKindSelector'; import {
createRoom,
export function AliasInput({ disabled }: { disabled?: boolean }) { CreateRoomAliasInput,
const mx = useMatrixClient(); CreateRoomData,
const aliasInputRef = useRef<HTMLInputElement>(null); CreateRoomKind,
const [aliasAvail, setAliasAvail] = useState<AsyncState<boolean, Error>>({ CreateRoomKindSelector,
status: AsyncStatus.Idle, RoomVersionSelector,
}); } from '../../components/create-room';
useEffect(() => {
if (aliasAvail.status === AsyncStatus.Success && aliasInputRef.current?.value === '') {
setAliasAvail({ status: AsyncStatus.Idle });
}
}, [aliasAvail]);
const checkAliasAvail = useAsync(
useCallback(
async (aliasLocalPart: string) => {
const roomAlias = `#${aliasLocalPart}:${getMxIdServer(mx.getSafeUserId())}`;
try {
const result = await mx.getRoomIdForAlias(roomAlias);
return typeof result.room_id !== 'string';
} catch (e) {
if (e instanceof MatrixError && e.httpStatus === 404) {
return true;
}
throw e;
}
},
[mx]
),
setAliasAvail
);
const aliasAvailable: boolean | undefined =
aliasAvail.status === AsyncStatus.Success ? aliasAvail.data : undefined;
const debounceCheckAliasAvail = useDebounce(checkAliasAvail, { wait: 500 });
const handleAliasChange: FormEventHandler<HTMLInputElement> = (evt) => {
const aliasInput = evt.currentTarget;
const aliasLocalPart = replaceSpaceWithDash(aliasInput.value);
if (aliasLocalPart) {
aliasInput.value = aliasLocalPart;
debounceCheckAliasAvail(aliasLocalPart);
} else {
setAliasAvail({ status: AsyncStatus.Idle });
}
};
const handleAliasKeyDown: KeyboardEventHandler<HTMLInputElement> = (evt) => {
if (isKeyHotkey('enter', evt)) {
evt.preventDefault();
const aliasInput = evt.currentTarget;
const aliasLocalPart = replaceSpaceWithDash(aliasInput.value);
if (aliasLocalPart) {
checkAliasAvail(aliasLocalPart);
} else {
setAliasAvail({ status: AsyncStatus.Idle });
}
}
};
return (
<Box shrink="No" direction="Column" gap="100">
<Text size="L400">Address (Optional)</Text>
<Text size="T200" priority="300">
Pick an unique address to make your room discoverable to public.
</Text>
<Input
ref={aliasInputRef}
onChange={handleAliasChange}
before={
aliasAvail.status === AsyncStatus.Loading ? (
<Spinner size="100" variant="Secondary" />
) : (
<Icon size="100" src={Icons.Hash} />
)
}
after={
<Text style={{ maxWidth: toRem(150) }} truncate>
:{getMxIdServer(mx.getSafeUserId())}
</Text>
}
onKeyDown={handleAliasKeyDown}
name="aliasInput"
size="500"
variant={aliasAvailable === true ? 'Success' : 'SurfaceVariant'}
radii="400"
disabled={disabled}
/>
{aliasAvailable === false && (
<Box style={{ color: color.Critical.Main }} alignItems="Center" gap="100">
<Icon src={Icons.Warning} filled size="50" />
<Text size="T200">
<b>This address is already taken. Please select a different one.</b>
</Text>
</Box>
)}
</Box>
);
}
export function RoomVersionSelector({
versions,
value,
onChange,
disabled,
}: {
versions: string[];
value: string;
onChange: (value: string) => void;
disabled?: boolean;
}) {
const [menuCords, setMenuCords] = useState<RectCords>();
const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
setMenuCords(evt.currentTarget.getBoundingClientRect());
};
const handleSelect = (version: string) => {
setMenuCords(undefined);
onChange(version);
};
return (
<SequenceCard
style={{ padding: config.space.S300 }}
variant="SurfaceVariant"
direction="Column"
gap="500"
>
<SettingTile
title="Room Version"
after={
<PopOut
anchor={menuCords}
offset={5}
position="Bottom"
align="End"
content={
<FocusTrap
focusTrapOptions={{
initialFocus: false,
onDeactivate: () => setMenuCords(undefined),
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) =>
evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
isKeyBackward: (evt: KeyboardEvent) =>
evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
escapeDeactivates: stopPropagation,
}}
>
<Menu>
<Box
direction="Column"
gap="200"
style={{ padding: config.space.S200, maxWidth: toRem(300) }}
>
<Text size="L400">Room Versions</Text>
<Box wrap="Wrap" gap="100">
{versions.map((version) => (
<Chip
key={version}
variant={value === version ? 'Primary' : 'SurfaceVariant'}
aria-pressed={value === version}
outlined={value === version}
radii="300"
onClick={() => handleSelect(version)}
type="button"
>
<Text truncate size="T300">
{version}
</Text>
</Chip>
))}
</Box>
</Box>
</Menu>
</FocusTrap>
}
>
<Button
type="button"
onClick={handleMenu}
size="300"
variant="Secondary"
fill="Soft"
radii="300"
aria-pressed={!!menuCords}
before={<Icon size="50" src={menuCords ? Icons.ChevronTop : Icons.ChevronBottom} />}
disabled={disabled}
>
<Text size="B300">{value}</Text>
</Button>
</PopOut>
}
/>
</SequenceCard>
);
}
type CreateRoomData = {
version: string;
parent?: Room;
kind: CreateRoomKind;
name: string;
topic?: string;
aliasLocalPart?: string;
encryption: boolean;
knock: boolean;
allowFederation: boolean;
};
const createRoom = async (mx: MatrixClient, data: CreateRoomData): Promise<string> => {
const creationContent = {
'm.federate': data.allowFederation,
};
const initialState: ICreateRoomStateEvent[] = [];
if (data.encryption) {
initialState.push({
type: 'm.room.encryption',
state_key: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
});
}
if (data.parent) {
initialState.push({
type: StateEvent.SpaceParent,
state_key: data.parent.roomId,
content: {
canonical: true,
via: getViaServers(data.parent),
},
});
}
const getJoinRuleContent = (): RoomJoinRulesEventContent => {
if (data.kind === CreateRoomKind.Public) {
return {
join_rule: JoinRule.Public,
};
}
if (data.kind === CreateRoomKind.Restricted && data.parent) {
return {
join_rule: data.knock ? ('knock_restricted' as JoinRule) : JoinRule.Restricted,
allow: [
{
type: RestrictedAllowType.RoomMembership,
room_id: data.parent.roomId,
},
],
};
}
return {
join_rule: data.knock ? JoinRule.Knock : JoinRule.Invite,
};
};
initialState.push({
type: StateEvent.RoomJoinRules,
content: getJoinRuleContent(),
});
const options: ICreateRoomOpts = {
room_version: data.version,
name: data.name,
topic: data.topic,
room_alias_name: data.aliasLocalPart,
creation_content: creationContent,
initial_state: initialState,
};
const result = await mx.createRoom(options);
if (data.parent) {
await mx.sendStateEvent(
data.parent.roomId,
StateEvent.SpaceChild as any,
{
auto_join: false,
suggested: false,
via: [getIdServer(mx.getUserId())],
},
result.room_id
);
}
return result.room_id;
};
const getCreateRoomKindToIcon = (kind: CreateRoomKind) => { const getCreateRoomKindToIcon = (kind: CreateRoomKind) => {
if (kind === CreateRoomKind.Private) return Icons.HashLock; if (kind === CreateRoomKind.Private) return Icons.HashLock;
@ -369,8 +48,9 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
const alive = useAlive(); const alive = useAlive();
const capabilities = useCapabilities(); const capabilities = useCapabilities();
const roomVersion = capabilities['m.room_versions']; const roomVersions = capabilities['m.room_versions'];
const [selectedRoomVersion, selectRoomVersion] = useState(roomVersion?.default ?? '1'); const [selectedRoomVersion, selectRoomVersion] = useState(roomVersions?.default ?? '1');
const allowRestricted = space && restrictedSupported(selectedRoomVersion); const allowRestricted = space && restrictedSupported(selectedRoomVersion);
const [kind, setKind] = useState( const [kind, setKind] = useState(
@ -448,6 +128,7 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
onSelect={setKind} onSelect={setKind}
canRestrict={allowRestricted} canRestrict={allowRestricted}
disabled={disabled} disabled={disabled}
getIcon={getCreateRoomKindToIcon}
/> />
</Box> </Box>
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
@ -460,6 +141,7 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
size="500" size="500"
variant="SurfaceVariant" variant="SurfaceVariant"
radii="400" radii="400"
autoComplete="off"
disabled={disabled} disabled={disabled}
/> />
</Box> </Box>
@ -474,7 +156,7 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
/> />
</Box> </Box>
{kind === CreateRoomKind.Public && <AliasInput />} {kind === CreateRoomKind.Public && <CreateRoomAliasInput disabled={disabled} />}
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
<Box gap="200" alignItems="End"> <Box gap="200" alignItems="End">
@ -556,7 +238,7 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
</SequenceCard> </SequenceCard>
{advance && ( {advance && (
<RoomVersionSelector <RoomVersionSelector
versions={roomVersion?.available ? Object.keys(roomVersion.available) : ['1']} versions={roomVersions?.available ? Object.keys(roomVersions.available) : ['1']}
value={selectedRoomVersion} value={selectedRoomVersion}
onChange={handleRoomVersionChange} onChange={handleRoomVersionChange}
disabled={disabled} disabled={disabled}

View file

@ -1,4 +1,4 @@
import React, { useRef } from 'react'; import React from 'react';
import { Box, Icon, Icons, Scroll, IconButton } from 'folds'; import { Box, Icon, Icons, Scroll, IconButton } from 'folds';
import { import {
Page, Page,
@ -14,7 +14,6 @@ import { CreateRoomForm } from '../../../features/create-room';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate'; import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
export function HomeCreateRoom() { export function HomeCreateRoom() {
const scrollRef = useRef<HTMLDivElement>(null);
const screenSize = useScreenSizeContext(); const screenSize = useScreenSizeContext();
const { navigateRoom } = useRoomNavigate(); const { navigateRoom } = useRoomNavigate();
@ -34,8 +33,8 @@ export function HomeCreateRoom() {
</Box> </Box>
</PageHeader> </PageHeader>
)} )}
<Box style={{ position: 'relative' }} grow="Yes"> <Box grow="Yes">
<Scroll ref={scrollRef} hideTrack visibility="Hover"> <Scroll hideTrack visibility="Hover">
<PageContent> <PageContent>
<PageContentCenter> <PageContentCenter>
<PageHeroSection> <PageHeroSection>