mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-04 22:40:29 +03:00
add create space
This commit is contained in:
parent
8a72f40514
commit
ae5ebb5af5
10 changed files with 420 additions and 15 deletions
249
src/app/features/create-space/CreateSpace.tsx
Normal file
249
src/app/features/create-space/CreateSpace.tsx
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
import React, { FormEventHandler, useCallback, useState } from 'react';
|
||||||
|
import { MatrixError, Room } 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 { 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 {
|
||||||
|
createRoom,
|
||||||
|
CreateRoomAliasInput,
|
||||||
|
CreateRoomData,
|
||||||
|
CreateRoomKind,
|
||||||
|
CreateRoomKindSelector,
|
||||||
|
RoomVersionSelector,
|
||||||
|
} from '../../components/create-room';
|
||||||
|
import { RoomType } from '../../../types/matrix/room';
|
||||||
|
|
||||||
|
const getCreateSpaceKindToIcon = (kind: CreateRoomKind) => {
|
||||||
|
if (kind === CreateRoomKind.Private) return Icons.SpaceLock;
|
||||||
|
if (kind === CreateRoomKind.Restricted) return Icons.Space;
|
||||||
|
return Icons.SpaceGlobe;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CreateSpaceFormProps = {
|
||||||
|
defaultKind?: CreateRoomKind;
|
||||||
|
space?: Room;
|
||||||
|
onCreate?: (roomId: string) => void;
|
||||||
|
};
|
||||||
|
export function CreateSpaceForm({ defaultKind, space, onCreate }: CreateSpaceFormProps) {
|
||||||
|
const mx = useMatrixClient();
|
||||||
|
const alive = useAlive();
|
||||||
|
|
||||||
|
const capabilities = useCapabilities();
|
||||||
|
const roomVersions = capabilities['m.room_versions'];
|
||||||
|
const [selectedRoomVersion, selectRoomVersion] = useState(roomVersions?.default ?? '1');
|
||||||
|
|
||||||
|
const allowRestricted = space && restrictedSupported(selectedRoomVersion);
|
||||||
|
|
||||||
|
const [kind, setKind] = useState(
|
||||||
|
defaultKind ?? allowRestricted ? CreateRoomKind.Restricted : CreateRoomKind.Private
|
||||||
|
);
|
||||||
|
const [federation, setFederation] = useState(true);
|
||||||
|
const [knock, setKnock] = useState(false);
|
||||||
|
const [advance, setAdvance] = useState(false);
|
||||||
|
|
||||||
|
const allowKnock = kind === CreateRoomKind.Private && knockSupported(selectedRoomVersion);
|
||||||
|
const allowKnockRestricted =
|
||||||
|
kind === CreateRoomKind.Restricted && knockRestrictedSupported(selectedRoomVersion);
|
||||||
|
|
||||||
|
const handleRoomVersionChange = (version: string) => {
|
||||||
|
if (!restrictedSupported(version)) {
|
||||||
|
setKind(CreateRoomKind.Private);
|
||||||
|
}
|
||||||
|
selectRoomVersion(version);
|
||||||
|
};
|
||||||
|
|
||||||
|
const [createState, create] = useAsyncCallback<string, Error | MatrixError, [CreateRoomData]>(
|
||||||
|
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<HTMLFormElement> = (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 = kind === CreateRoomKind.Public;
|
||||||
|
let roomKnock = false;
|
||||||
|
if (allowKnock && kind === CreateRoomKind.Private) {
|
||||||
|
roomKnock = knock;
|
||||||
|
}
|
||||||
|
if (allowKnockRestricted && kind === CreateRoomKind.Restricted) {
|
||||||
|
roomKnock = knock;
|
||||||
|
}
|
||||||
|
|
||||||
|
create({
|
||||||
|
version: selectedRoomVersion,
|
||||||
|
type: RoomType.Space,
|
||||||
|
parent: space,
|
||||||
|
kind,
|
||||||
|
name: roomName,
|
||||||
|
topic: roomTopic || undefined,
|
||||||
|
aliasLocalPart: publicRoom ? aliasLocalPart : undefined,
|
||||||
|
knock: roomKnock,
|
||||||
|
allowFederation: federation,
|
||||||
|
}).then((roomId) => {
|
||||||
|
if (alive()) {
|
||||||
|
onCreate?.(roomId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box as="form" onSubmit={handleSubmit} grow="Yes" direction="Column" gap="500">
|
||||||
|
<Box direction="Column" gap="100">
|
||||||
|
<Text size="L400">Access</Text>
|
||||||
|
<CreateRoomKindSelector
|
||||||
|
value={kind}
|
||||||
|
onSelect={setKind}
|
||||||
|
canRestrict={allowRestricted}
|
||||||
|
disabled={disabled}
|
||||||
|
getIcon={getCreateSpaceKindToIcon}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box shrink="No" direction="Column" gap="100">
|
||||||
|
<Text size="L400">Name</Text>
|
||||||
|
<Input
|
||||||
|
required
|
||||||
|
before={<Icon size="100" src={getCreateSpaceKindToIcon(kind)} />}
|
||||||
|
name="nameInput"
|
||||||
|
autoFocus
|
||||||
|
size="500"
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
radii="400"
|
||||||
|
autoComplete="off"
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box shrink="No" direction="Column" gap="100">
|
||||||
|
<Text size="L400">Topic (Optional)</Text>
|
||||||
|
<TextArea
|
||||||
|
name="topicTextAria"
|
||||||
|
size="500"
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
radii="400"
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{kind === CreateRoomKind.Public && <CreateRoomAliasInput disabled={disabled} />}
|
||||||
|
|
||||||
|
<Box shrink="No" direction="Column" gap="100">
|
||||||
|
<Box gap="200" alignItems="End">
|
||||||
|
<Text size="L400">Options</Text>
|
||||||
|
<Box grow="Yes" justifyContent="End">
|
||||||
|
<Chip
|
||||||
|
radii="Pill"
|
||||||
|
before={<Icon src={advance ? Icons.ChevronTop : Icons.ChevronBottom} size="50" />}
|
||||||
|
onClick={() => setAdvance(!advance)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Text size="T200">Advance Options</Text>
|
||||||
|
</Chip>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{kind !== CreateRoomKind.Public && advance && (allowKnock || allowKnockRestricted) && (
|
||||||
|
<SequenceCard
|
||||||
|
style={{ padding: config.space.S300 }}
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
direction="Column"
|
||||||
|
gap="500"
|
||||||
|
>
|
||||||
|
<SettingTile
|
||||||
|
title="Knock to Join"
|
||||||
|
description="Anyone can send request to join this space."
|
||||||
|
after={
|
||||||
|
<Switch variant="Primary" value={knock} onChange={setKnock} disabled={disabled} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SequenceCard>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SequenceCard
|
||||||
|
style={{ padding: config.space.S300 }}
|
||||||
|
variant="SurfaceVariant"
|
||||||
|
direction="Column"
|
||||||
|
gap="500"
|
||||||
|
>
|
||||||
|
<SettingTile
|
||||||
|
title="Allow Federation"
|
||||||
|
description="Users from other servers can join."
|
||||||
|
after={
|
||||||
|
<Switch
|
||||||
|
variant="Primary"
|
||||||
|
value={federation}
|
||||||
|
onChange={setFederation}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SequenceCard>
|
||||||
|
{advance && (
|
||||||
|
<RoomVersionSelector
|
||||||
|
versions={roomVersions?.available ? Object.keys(roomVersions.available) : ['1']}
|
||||||
|
value={selectedRoomVersion}
|
||||||
|
onChange={handleRoomVersionChange}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<Box style={{ color: color.Critical.Main }} alignItems="Center" gap="200">
|
||||||
|
<Icon src={Icons.Warning} filled size="100" />
|
||||||
|
<Text size="T300" style={{ color: color.Critical.Main }}>
|
||||||
|
<b>
|
||||||
|
{error instanceof MatrixError && error.name === ErrorCode.M_LIMIT_EXCEEDED
|
||||||
|
? `Server rate-limited your request for ${millisecondsToMinutes(
|
||||||
|
(error.data.retry_after_ms as number | undefined) ?? 0
|
||||||
|
)} minutes!`
|
||||||
|
: error.message}
|
||||||
|
</b>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Box shrink="No" direction="Column" gap="200">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
size="500"
|
||||||
|
variant="Primary"
|
||||||
|
radii="400"
|
||||||
|
disabled={disabled}
|
||||||
|
before={loading && <Spinner variant="Primary" fill="Solid" size="200" />}
|
||||||
|
>
|
||||||
|
<Text size="B500">Create</Text>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/app/features/create-space/index.ts
Normal file
1
src/app/features/create-space/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './CreateSpace';
|
||||||
12
src/app/hooks/router/useCreateSelected.ts
Normal file
12
src/app/hooks/router/useCreateSelected.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { useMatch } from 'react-router-dom';
|
||||||
|
import { getCreatePath } from '../../pages/pathUtils';
|
||||||
|
|
||||||
|
export const useCreateSelected = (): boolean => {
|
||||||
|
const match = useMatch({
|
||||||
|
path: getCreatePath(),
|
||||||
|
caseSensitive: true,
|
||||||
|
end: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return !!match;
|
||||||
|
};
|
||||||
|
|
@ -28,6 +28,7 @@ import {
|
||||||
_ROOM_PATH,
|
_ROOM_PATH,
|
||||||
_SEARCH_PATH,
|
_SEARCH_PATH,
|
||||||
_SERVER_PATH,
|
_SERVER_PATH,
|
||||||
|
CREATE_PATH,
|
||||||
} from './paths';
|
} from './paths';
|
||||||
import { isAuthenticated } from '../../client/state/auth';
|
import { isAuthenticated } from '../../client/state/auth';
|
||||||
import {
|
import {
|
||||||
|
|
@ -63,6 +64,7 @@ import { ClientRoomsNotificationPreferences } from './client/ClientRoomsNotifica
|
||||||
import { SpaceSettingsRenderer } from '../features/space-settings';
|
import { SpaceSettingsRenderer } from '../features/space-settings';
|
||||||
import { CreateRoomModalRenderer } from '../features/create-room';
|
import { CreateRoomModalRenderer } from '../features/create-room';
|
||||||
import { HomeCreateRoom } from './client/home/CreateRoom';
|
import { HomeCreateRoom } from './client/home/CreateRoom';
|
||||||
|
import { Create } from './client/create';
|
||||||
|
|
||||||
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
|
||||||
const { hashRouter } = clientConfig;
|
const { hashRouter } = clientConfig;
|
||||||
|
|
@ -256,6 +258,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
||||||
<Route path={_FEATURED_PATH} element={<FeaturedRooms />} />
|
<Route path={_FEATURED_PATH} element={<FeaturedRooms />} />
|
||||||
<Route path={_SERVER_PATH} element={<PublicRooms />} />
|
<Route path={_SERVER_PATH} element={<PublicRooms />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path={CREATE_PATH} element={<Create />} />
|
||||||
<Route
|
<Route
|
||||||
path={INBOX_PATH}
|
path={INBOX_PATH}
|
||||||
element={
|
element={
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ import {
|
||||||
SettingsTab,
|
SettingsTab,
|
||||||
UnverifiedTab,
|
UnverifiedTab,
|
||||||
} from './sidebar';
|
} from './sidebar';
|
||||||
import { openCreateRoom, openSearch } from '../../../client/action/navigation';
|
import { openSearch } from '../../../client/action/navigation';
|
||||||
|
import { CreateTab } from './sidebar/CreateTab';
|
||||||
|
|
||||||
export function SidebarNav() {
|
export function SidebarNav() {
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
@ -37,20 +38,7 @@ export function SidebarNav() {
|
||||||
<SidebarStackSeparator />
|
<SidebarStackSeparator />
|
||||||
<SidebarStack>
|
<SidebarStack>
|
||||||
<ExploreTab />
|
<ExploreTab />
|
||||||
<SidebarItem>
|
<CreateTab />
|
||||||
<SidebarItemTooltip tooltip="Create Space">
|
|
||||||
{(triggerRef) => (
|
|
||||||
<SidebarAvatar
|
|
||||||
as="button"
|
|
||||||
ref={triggerRef}
|
|
||||||
outlined
|
|
||||||
onClick={() => openCreateRoom(true)}
|
|
||||||
>
|
|
||||||
<Icon src={Icons.Plus} />
|
|
||||||
</SidebarAvatar>
|
|
||||||
)}
|
|
||||||
</SidebarItemTooltip>
|
|
||||||
</SidebarItem>
|
|
||||||
</SidebarStack>
|
</SidebarStack>
|
||||||
</Scroll>
|
</Scroll>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
src/app/pages/client/create/Create.tsx
Normal file
35
src/app/pages/client/create/Create.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
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';
|
||||||
|
|
||||||
|
export function Create() {
|
||||||
|
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 />
|
||||||
|
</Box>
|
||||||
|
</PageHeroSection>
|
||||||
|
</PageContentCenter>
|
||||||
|
</PageContent>
|
||||||
|
</Scroll>
|
||||||
|
</Box>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/app/pages/client/create/index.ts
Normal file
1
src/app/pages/client/create/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './Create';
|
||||||
111
src/app/pages/client/sidebar/CreateTab.tsx
Normal file
111
src/app/pages/client/sidebar/CreateTab.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
SPACE_PATH,
|
SPACE_PATH,
|
||||||
SPACE_ROOM_PATH,
|
SPACE_ROOM_PATH,
|
||||||
SPACE_SEARCH_PATH,
|
SPACE_SEARCH_PATH,
|
||||||
|
CREATE_PATH,
|
||||||
} from './paths';
|
} from './paths';
|
||||||
import { trimLeadingSlash, trimTrailingSlash } from '../utils/common';
|
import { trimLeadingSlash, trimTrailingSlash } from '../utils/common';
|
||||||
import { HashRouterConfig } from '../hooks/useClientConfig';
|
import { HashRouterConfig } from '../hooks/useClientConfig';
|
||||||
|
|
@ -152,6 +153,8 @@ export const getExploreServerPath = (server: string): string => {
|
||||||
return generatePath(EXPLORE_SERVER_PATH, params);
|
return generatePath(EXPLORE_SERVER_PATH, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCreatePath = (): string => CREATE_PATH;
|
||||||
|
|
||||||
export const getInboxPath = (): string => INBOX_PATH;
|
export const getInboxPath = (): string => INBOX_PATH;
|
||||||
export const getInboxNotificationsPath = (): string => INBOX_NOTIFICATIONS_PATH;
|
export const getInboxNotificationsPath = (): string => INBOX_NOTIFICATIONS_PATH;
|
||||||
export const getInboxInvitesPath = (): string => INBOX_INVITES_PATH;
|
export const getInboxInvitesPath = (): string => INBOX_INVITES_PATH;
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ export type ExploreServerPathSearchParams = {
|
||||||
};
|
};
|
||||||
export const EXPLORE_SERVER_PATH = `/explore/${_SERVER_PATH}`;
|
export const EXPLORE_SERVER_PATH = `/explore/${_SERVER_PATH}`;
|
||||||
|
|
||||||
|
export const CREATE_PATH = '/create';
|
||||||
|
|
||||||
export const _NOTIFICATIONS_PATH = 'notifications/';
|
export const _NOTIFICATIONS_PATH = 'notifications/';
|
||||||
export const _INVITES_PATH = 'invites/';
|
export const _INVITES_PATH = 'invites/';
|
||||||
export const INBOX_PATH = '/inbox/';
|
export const INBOX_PATH = '/inbox/';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue