Redesign space/room creation panel (#2408)

* add new create room

* rename create room modal file

* default restrict access for space children in room create modal

* move create room kind selector to components

* add radii variant to sequence card component

* more more reusable create room logic to components

* add create space

* update address input description

* add new space modal

* fix add room button visible on left room in space lobby
This commit is contained in:
Ajay Bura
2025-08-05 18:37:07 +05:30
committed by GitHub
parent e9798a22c3
commit faa952295f
33 changed files with 1637 additions and 53 deletions

View File

@@ -19,7 +19,8 @@ import {
SettingsTab,
UnverifiedTab,
} from './sidebar';
import { openCreateRoom, openSearch } from '../../../client/action/navigation';
import { openSearch } from '../../../client/action/navigation';
import { CreateTab } from './sidebar/CreateTab';
export function SidebarNav() {
const scrollRef = useRef<HTMLDivElement>(null);
@@ -37,20 +38,7 @@ export function SidebarNav() {
<SidebarStackSeparator />
<SidebarStack>
<ExploreTab />
<SidebarItem>
<SidebarItemTooltip tooltip="Create Space">
{(triggerRef) => (
<SidebarAvatar
as="button"
ref={triggerRef}
outlined
onClick={() => openCreateRoom(true)}
>
<Icon src={Icons.Plus} />
</SidebarAvatar>
)}
</SidebarItemTooltip>
</SidebarItem>
<CreateTab />
</SidebarStack>
</Scroll>
}

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { Box, Icon, Icons, Scroll } from 'folds';
import {
Page,
PageContent,
PageContentCenter,
PageHero,
PageHeroSection,
} from '../../../components/page';
import { CreateSpaceForm } from '../../../features/create-space';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
export function Create() {
const { navigateSpace } = useRoomNavigate();
return (
<Page>
<Box grow="Yes">
<Scroll hideTrack visibility="Hover">
<PageContent>
<PageContentCenter>
<PageHeroSection>
<Box direction="Column" gap="700">
<PageHero
icon={<Icon size="600" src={Icons.Space} />}
title="Create Space"
subTitle="Build a space for your community."
/>
<CreateSpaceForm onCreate={navigateSpace} />
</Box>
</PageHeroSection>
</PageContentCenter>
</PageContent>
</Scroll>
</Box>
</Page>
);
}

View File

@@ -0,0 +1 @@
export * from './Create';

View File

@@ -0,0 +1,56 @@
import React from 'react';
import { Box, Icon, Icons, Scroll, IconButton } from 'folds';
import {
Page,
PageContent,
PageContentCenter,
PageHeader,
PageHero,
PageHeroSection,
} from '../../../components/page';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
import { BackRouteHandler } from '../../../components/BackRouteHandler';
import { CreateRoomForm } from '../../../features/create-room';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
export function HomeCreateRoom() {
const screenSize = useScreenSizeContext();
const { navigateRoom } = useRoomNavigate();
return (
<Page>
{screenSize === ScreenSize.Mobile && (
<PageHeader balance outlined={false}>
<Box grow="Yes" alignItems="Center" gap="200">
<BackRouteHandler>
{(onBack) => (
<IconButton onClick={onBack}>
<Icon src={Icons.ArrowLeft} />
</IconButton>
)}
</BackRouteHandler>
</Box>
</PageHeader>
)}
<Box grow="Yes">
<Scroll hideTrack visibility="Hover">
<PageContent>
<PageContentCenter>
<PageHeroSection>
<Box direction="Column" gap="700">
<PageHero
icon={<Icon size="600" src={Icons.Hash} />}
title="Create Room"
subTitle="Build a Room for Real-Time Conversations"
/>
<CreateRoomForm onCreate={navigateRoom} />
</Box>
</PageHeroSection>
</PageContentCenter>
</PageContent>
</Scroll>
</Box>
</Page>
);
}

View File

@@ -29,10 +29,18 @@ import {
NavItemContent,
NavLink,
} from '../../../components/nav';
import { getExplorePath, getHomeRoomPath, getHomeSearchPath } from '../../pathUtils';
import {
getExplorePath,
getHomeCreatePath,
getHomeRoomPath,
getHomeSearchPath,
} from '../../pathUtils';
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { useHomeSearchSelected } from '../../../hooks/router/useHomeSelected';
import {
useHomeCreateSelected,
useHomeSearchSelected,
} from '../../../hooks/router/useHomeSelected';
import { useHomeRooms } from './useHomeRooms';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { VirtualTile } from '../../../components/virtualizer';
@@ -41,7 +49,7 @@ import { makeNavCategoryId } from '../../../state/closedNavCategories';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
import { openCreateRoom, openJoinAlias } from '../../../../client/action/navigation';
import { openJoinAlias } from '../../../../client/action/navigation';
import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page';
import { useRoomsUnread } from '../../../state/hooks/unread';
import { markAsRead } from '../../../../client/action/notifications';
@@ -174,7 +182,7 @@ function HomeEmpty() {
}
options={
<>
<Button onClick={() => openCreateRoom()} variant="Secondary" size="300">
<Button onClick={() => navigate(getHomeCreatePath())} variant="Secondary" size="300">
<Text size="B300" truncate>
Create Room
</Text>
@@ -204,8 +212,10 @@ export function Home() {
const rooms = useHomeRooms();
const notificationPreferences = useRoomsNotificationPreferencesContext();
const roomToUnread = useAtomValue(roomToUnreadAtom);
const navigate = useNavigate();
const selectedRoomId = useSelectedRoom();
const createRoomSelected = useHomeCreateSelected();
const searchSelected = useHomeSearchSelected();
const noRoomToDisplay = rooms.length === 0;
const [closedCategories, setClosedCategories] = useAtom(useClosedNavCategoriesAtom());
@@ -242,8 +252,8 @@ export function Home() {
<PageNavContent scrollRef={scrollRef}>
<Box direction="Column" gap="300">
<NavCategory>
<NavItem variant="Background" radii="400">
<NavButton onClick={() => openCreateRoom()}>
<NavItem variant="Background" radii="400" aria-selected={createRoomSelected}>
<NavButton onClick={() => navigate(getHomeCreatePath())}>
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400">

View File

@@ -0,0 +1,111 @@
import React, { MouseEventHandler, useState } from 'react';
import { Box, config, Icon, Icons, Menu, PopOut, RectCords, Text } from 'folds';
import FocusTrap from 'focus-trap-react';
import { useNavigate } from 'react-router-dom';
import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar';
import { stopPropagation } from '../../../utils/keyboard';
import { SequenceCard } from '../../../components/sequence-card';
import { SettingTile } from '../../../components/setting-tile';
import { ContainerColor } from '../../../styles/ContainerColor.css';
import { openJoinAlias } from '../../../../client/action/navigation';
import { getCreatePath } from '../../pathUtils';
import { useCreateSelected } from '../../../hooks/router/useCreateSelected';
export function CreateTab() {
const createSelected = useCreateSelected();
const navigate = useNavigate();
const [menuCords, setMenuCords] = useState<RectCords>();
const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
setMenuCords(menuCords ? undefined : evt.currentTarget.getBoundingClientRect());
};
const handleCreateSpace = () => {
navigate(getCreatePath());
setMenuCords(undefined);
};
const handleJoinWithAddress = () => {
openJoinAlias();
setMenuCords(undefined);
};
return (
<SidebarItem active={createSelected}>
<SidebarItemTooltip tooltip="Add Space">
{(triggerRef) => (
<PopOut
anchor={menuCords}
position="Right"
align="Center"
content={
<FocusTrap
focusTrapOptions={{
returnFocusOnDeactivate: false,
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">
<SequenceCard
style={{ padding: config.space.S300 }}
variant="Surface"
direction="Column"
gap="100"
radii="0"
as="button"
type="button"
onClick={handleCreateSpace}
>
<SettingTile before={<Icon size="400" src={Icons.Space} />}>
<Text size="H6">Create Space</Text>
<Text size="T300" priority="300">
Build a space for your community.
</Text>
</SettingTile>
</SequenceCard>
<SequenceCard
style={{ padding: config.space.S300 }}
variant="Surface"
direction="Column"
gap="100"
radii="0"
as="button"
type="button"
onClick={handleJoinWithAddress}
>
<SettingTile before={<Icon size="400" src={Icons.Link} />}>
<Text size="H6">Join with Address</Text>
<Text size="T300" priority="300">
Become a part of existing community.
</Text>
</SettingTile>
</SequenceCard>
</Box>
</Menu>
</FocusTrap>
}
>
<SidebarAvatar
className={menuCords ? ContainerColor({ variant: 'Surface' }) : undefined}
as="button"
ref={triggerRef}
outlined
onClick={handleMenu}
>
<Icon src={Icons.Plus} />
</SidebarAvatar>
</PopOut>
)}
</SidebarItemTooltip>
</SidebarItem>
);
}