localize Add Space

This commit is contained in:
v.lagerev 2026-04-14 02:18:51 +03:00
parent 456526315a
commit 45dc4fa8cf
13 changed files with 177 additions and 48 deletions

View file

@ -554,5 +554,58 @@
"join_error_unknown": "Failed to join. Unknown Error.", "join_error_unknown": "Failed to join. Unknown Error.",
"view_error": "View Error", "view_error": "View Error",
"cancel": "Cancel" "cancel": "Cancel"
},
"Create": {
"add_space": "Add Space",
"create_space": "Create Space",
"create_space_desc": "Build a space for your community.",
"join_with_address": "Join with Address",
"join_with_address_desc": "Join an existing community.",
"new_space": "New Space",
"access": "Access",
"name": "Name",
"topic_optional": "Topic (Optional)",
"options": "Options",
"advanced_options": "Advanced Options",
"knock_to_join": "Knock to Join",
"knock_to_join_desc": "Anyone can send a request to join this space.",
"allow_federation": "Allow Federation",
"allow_federation_desc": "Users from other servers can join.",
"create": "Create",
"rate_limited": "Server rate-limited your request for {{minutes}} minutes!",
"access_restricted": "Restricted",
"access_restricted_desc": "Only members of the parent space can join.",
"access_private": "Private",
"access_private_desc": "Only people with an invite can join.",
"access_public": "Public",
"access_public_desc": "Anyone with the address can join.",
"address_optional": "Address (Optional)",
"address_hint": "Pick a unique address to make it discoverable.",
"address_taken": "This address is already taken. Please choose a different one.",
"founders": "Founders",
"founders_desc": "Privileged users assigned during creation. They have elevated control and can only be changed during an upgrade.",
"enter": "Enter",
"no_suggestions": "No Suggestions",
"no_suggestions_desc": "Enter a user ID and press Enter.",
"version": "Version",
"versions": "Versions",
"chat_room": "Chat Room",
"chat_room_desc": "Messages, photos, and videos.",
"voice_room": "Voice Room",
"voice_room_desc": "Live audio and video conversations.",
"new_chat_room": "New Chat Room",
"new_voice_room": "New Voice Room",
"existing_space": "Existing Space",
"add_room": "Add Room",
"existing_room": "Existing Room"
} }
} }

View file

@ -556,5 +556,58 @@
"join_error_unknown": "Не удалось присоединиться. Неизвестная ошибка.", "join_error_unknown": "Не удалось присоединиться. Неизвестная ошибка.",
"view_error": "Подробности", "view_error": "Подробности",
"cancel": "Отмена" "cancel": "Отмена"
},
"Create": {
"add_space": "Добавить пространство",
"create_space": "Создать пространство",
"create_space_desc": "Создайте пространство для вашего сообщества.",
"join_with_address": "Присоединиться по адресу",
"join_with_address_desc": "Присоединиться к существующему сообществу.",
"new_space": "Новое пространство",
"access": "Доступ",
"name": "Название",
"topic_optional": "Тема (необязательно)",
"options": "Параметры",
"advanced_options": "Дополнительные параметры",
"knock_to_join": "Запрос на вступление",
"knock_to_join_desc": "Любой может отправить запрос на вступление в это пространство.",
"allow_federation": "Разрешить федерацию",
"allow_federation_desc": "Пользователи с других серверов смогут присоединиться.",
"create": "Создать",
"rate_limited": "Сервер ограничил ваш запрос на {{minutes}} мин.!",
"access_restricted": "Ограниченный",
"access_restricted_desc": "Могут присоединиться только участники родительского пространства.",
"access_private": "Приватный",
"access_private_desc": "Могут присоединиться только приглашённые.",
"access_public": "Публичный",
"access_public_desc": "Любой, у кого есть адрес, может присоединиться.",
"address_optional": "Адрес (необязательно)",
"address_hint": "Выберите уникальный адрес, чтобы пространство можно было найти.",
"address_taken": "Этот адрес уже занят. Выберите другой.",
"founders": "Основатели",
"founders_desc": "Привилегированные пользователи, назначенные при создании. Они имеют расширенные полномочия; изменить их можно только при обновлении пространства.",
"enter": "Добавить",
"no_suggestions": "Нет предложений",
"no_suggestions_desc": "Введите ID пользователя и нажмите Добавить.",
"version": "Версия",
"versions": "Версии",
"chat_room": "Чат-комната",
"chat_room_desc": "Сообщения, фото и видео.",
"voice_room": "Голосовая комната",
"voice_room_desc": "Голосовые и видеозвонки в реальном времени.",
"new_chat_room": "Новая чат-комната",
"new_voice_room": "Новая голосовая комната",
"existing_space": "Существующее пространство",
"add_room": "Добавить комнату",
"existing_room": "Существующая комната"
} }
} }

View file

@ -17,6 +17,7 @@ import {
} from 'folds'; } from 'folds';
import { isKeyHotkey } from 'is-hotkey'; import { isKeyHotkey } from 'is-hotkey';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { useTranslation } from 'react-i18next';
import React, { import React, {
ChangeEventHandler, ChangeEventHandler,
KeyboardEventHandler, KeyboardEventHandler,
@ -83,6 +84,7 @@ export function AdditionalCreatorInput({
onRemove, onRemove,
disabled, disabled,
}: AdditionalCreatorInputProps) { }: AdditionalCreatorInputProps) {
const { t } = useTranslation();
const mx = useMatrixClient(); const mx = useMatrixClient();
const [menuCords, setMenuCords] = useState<RectCords>(); const [menuCords, setMenuCords] = useState<RectCords>();
const directUsers = useDirectUsers(); const directUsers = useDirectUsers();
@ -150,8 +152,8 @@ export function AdditionalCreatorInput({
return ( return (
<SettingTile <SettingTile
title="Founders" title={t('Create.founders')}
description="Special privileged users can be assigned during creation. These users have elevated control and can only be modified during a upgrade." description={t('Create.founders_desc')}
> >
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
<Box gap="200" wrap="Wrap"> <Box gap="200" wrap="Wrap">
@ -213,7 +215,7 @@ export function AdditionalCreatorInput({
onClick={handleEnterClick} onClick={handleEnterClick}
disabled={!validUserId} disabled={!validUserId}
> >
<Text size="B400">Enter</Text> <Text size="B400">{t('Create.enter')}</Text>
</Button> </Button>
</Box> </Box>
<Line size="300" /> <Line size="300" />
@ -263,10 +265,10 @@ export function AdditionalCreatorInput({
gap="100" gap="100"
> >
<Text size="H6" align="Center"> <Text size="H6" align="Center">
No Suggestions {t('Create.no_suggestions')}
</Text> </Text>
<Text size="T200" align="Center"> <Text size="T200" align="Center">
Please provide the user ID and hit Enter. {t('Create.no_suggestions_desc')}
</Text> </Text>
</Box> </Box>
)} )}

View file

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Box, Text, Icon, Icons, config, IconSrc } from 'folds'; import { Box, Text, Icon, Icons, config, IconSrc } from 'folds';
import { useTranslation } from 'react-i18next';
import { SequenceCard } from '../sequence-card'; import { SequenceCard } from '../sequence-card';
import { SettingTile } from '../setting-tile'; import { SettingTile } from '../setting-tile';
import { CreateRoomAccess } from './types'; import { CreateRoomAccess } from './types';
@ -18,6 +19,7 @@ export function CreateRoomAccessSelector({
disabled, disabled,
getIcon, getIcon,
}: CreateRoomAccessSelectorProps) { }: CreateRoomAccessSelectorProps) {
const { t } = useTranslation();
return ( return (
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
{canRestrict && ( {canRestrict && (
@ -36,9 +38,9 @@ export function CreateRoomAccessSelector({
before={<Icon size="400" src={getIcon(CreateRoomAccess.Restricted)} />} before={<Icon size="400" src={getIcon(CreateRoomAccess.Restricted)} />}
after={value === CreateRoomAccess.Restricted && <Icon src={Icons.Check} />} after={value === CreateRoomAccess.Restricted && <Icon src={Icons.Check} />}
> >
<Text size="H6">Restricted</Text> <Text size="H6">{t('Create.access_restricted')}</Text>
<Text size="T300" priority="300"> <Text size="T300" priority="300">
Only member of parent space can join. {t('Create.access_restricted_desc')}
</Text> </Text>
</SettingTile> </SettingTile>
</SequenceCard> </SequenceCard>
@ -58,9 +60,9 @@ export function CreateRoomAccessSelector({
before={<Icon size="400" src={getIcon(CreateRoomAccess.Private)} />} before={<Icon size="400" src={getIcon(CreateRoomAccess.Private)} />}
after={value === CreateRoomAccess.Private && <Icon src={Icons.Check} />} after={value === CreateRoomAccess.Private && <Icon src={Icons.Check} />}
> >
<Text size="H6">Private</Text> <Text size="H6">{t('Create.access_private')}</Text>
<Text size="T300" priority="300"> <Text size="T300" priority="300">
Only people with invite can join. {t('Create.access_private_desc')}
</Text> </Text>
</SettingTile> </SettingTile>
</SequenceCard> </SequenceCard>
@ -79,9 +81,9 @@ export function CreateRoomAccessSelector({
before={<Icon size="400" src={getIcon(CreateRoomAccess.Public)} />} before={<Icon size="400" src={getIcon(CreateRoomAccess.Public)} />}
after={value === CreateRoomAccess.Public && <Icon src={Icons.Check} />} after={value === CreateRoomAccess.Public && <Icon src={Icons.Check} />}
> >
<Text size="H6">Public</Text> <Text size="H6">{t('Create.access_public')}</Text>
<Text size="T300" priority="300"> <Text size="T300" priority="300">
Anyone with the address can join. {t('Create.access_public_desc')}
</Text> </Text>
</SettingTile> </SettingTile>
</SequenceCard> </SequenceCard>

View file

@ -9,6 +9,7 @@ import React, {
import { MatrixError } from 'matrix-js-sdk'; import { MatrixError } from 'matrix-js-sdk';
import { Box, color, Icon, Icons, Input, Spinner, Text, toRem } from 'folds'; import { Box, color, Icon, Icons, Input, Spinner, Text, toRem } from 'folds';
import { isKeyHotkey } from 'is-hotkey'; import { isKeyHotkey } from 'is-hotkey';
import { useTranslation } from 'react-i18next';
import { getMxIdServer } from '../../utils/matrix'; import { getMxIdServer } from '../../utils/matrix';
import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useMatrixClient } from '../../hooks/useMatrixClient';
import { replaceSpaceWithDash } from '../../utils/common'; import { replaceSpaceWithDash } from '../../utils/common';
@ -16,6 +17,7 @@ import { AsyncState, AsyncStatus, useAsync } from '../../hooks/useAsyncCallback'
import { useDebounce } from '../../hooks/useDebounce'; import { useDebounce } from '../../hooks/useDebounce';
export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) { export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) {
const { t } = useTranslation();
const mx = useMatrixClient(); const mx = useMatrixClient();
const aliasInputRef = useRef<HTMLInputElement>(null); const aliasInputRef = useRef<HTMLInputElement>(null);
const [aliasAvail, setAliasAvail] = useState<AsyncState<boolean, Error>>({ const [aliasAvail, setAliasAvail] = useState<AsyncState<boolean, Error>>({
@ -78,9 +80,9 @@ export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) {
return ( return (
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
<Text size="L400">Address (Optional)</Text> <Text size="L400">{t('Create.address_optional')}</Text>
<Text size="T200" priority="300"> <Text size="T200" priority="300">
Pick an unique address to make it discoverable. {t('Create.address_hint')}
</Text> </Text>
<Input <Input
ref={aliasInputRef} ref={aliasInputRef}
@ -109,7 +111,7 @@ export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) {
<Box style={{ color: color.Critical.Main }} alignItems="Center" gap="100"> <Box style={{ color: color.Critical.Main }} alignItems="Center" gap="100">
<Icon src={Icons.Warning} filled size="50" /> <Icon src={Icons.Warning} filled size="50" />
<Text size="T200"> <Text size="T200">
<b>This address is already taken. Please select a different one.</b> <b>{t('Create.address_taken')}</b>
</Text> </Text>
</Box> </Box>
)} )}

View file

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Box, Text, Icon, Icons, config, IconSrc } from 'folds'; import { Box, Text, Icon, Icons, config, IconSrc } from 'folds';
import { useTranslation } from 'react-i18next';
import { SequenceCard } from '../sequence-card'; import { SequenceCard } from '../sequence-card';
import { SettingTile } from '../setting-tile'; import { SettingTile } from '../setting-tile';
import { CreateRoomType } from './types'; import { CreateRoomType } from './types';
@ -17,6 +18,7 @@ export function CreateRoomTypeSelector({
disabled, disabled,
getIcon, getIcon,
}: CreateRoomTypeSelectorProps) { }: CreateRoomTypeSelectorProps) {
const { t } = useTranslation();
return ( return (
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
<SequenceCard <SequenceCard
@ -36,10 +38,10 @@ export function CreateRoomTypeSelector({
> >
<Box gap="200" alignItems="Baseline"> <Box gap="200" alignItems="Baseline">
<Text size="H6" style={{ flexShrink: 0 }}> <Text size="H6" style={{ flexShrink: 0 }}>
Chat Room {t('Create.chat_room')}
</Text> </Text>
<Text size="T300" priority="300" truncate> <Text size="T300" priority="300" truncate>
- Messages, photos, and videos. - {t('Create.chat_room_desc')}
</Text> </Text>
</Box> </Box>
</SettingTile> </SettingTile>
@ -61,10 +63,10 @@ export function CreateRoomTypeSelector({
> >
<Box gap="200" alignItems="Baseline"> <Box gap="200" alignItems="Baseline">
<Text size="H6" style={{ flexShrink: 0 }}> <Text size="H6" style={{ flexShrink: 0 }}>
Voice Room {t('Create.voice_room')}
</Text> </Text>
<Text size="T300" priority="300" truncate> <Text size="T300" priority="300" truncate>
- Live audio and video conversations. - {t('Create.voice_room_desc')}
</Text> </Text>
<BetaNoticeBadge /> <BetaNoticeBadge />
</Box> </Box>

View file

@ -1,4 +1,5 @@
import React, { MouseEventHandler, useState } from 'react'; import React, { MouseEventHandler, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { import {
Box, Box,
Button, Button,
@ -28,6 +29,7 @@ export function RoomVersionSelector({
onChange: (value: string) => void; onChange: (value: string) => void;
disabled?: boolean; disabled?: boolean;
}) { }) {
const { t } = useTranslation();
const [menuCords, setMenuCords] = useState<RectCords>(); const [menuCords, setMenuCords] = useState<RectCords>();
const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => { const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
@ -47,7 +49,7 @@ export function RoomVersionSelector({
gap="500" gap="500"
> >
<SettingTile <SettingTile
title="Version" title={t('Create.version')}
after={ after={
<PopOut <PopOut
anchor={menuCords} anchor={menuCords}
@ -73,7 +75,7 @@ export function RoomVersionSelector({
gap="200" gap="200"
style={{ padding: config.space.S200, maxWidth: toRem(300) }} style={{ padding: config.space.S200, maxWidth: toRem(300) }}
> >
<Text size="L400">Versions</Text> <Text size="L400">{t('Create.versions')}</Text>
<Box wrap="Wrap" gap="100"> <Box wrap="Wrap" gap="100">
{versions.map((version) => ( {versions.map((version) => (
<Chip <Chip

View file

@ -14,6 +14,7 @@ import {
Text, Text,
} from 'folds'; } from 'folds';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { useTranslation } from 'react-i18next';
import { useAllJoinedRoomsSet, useGetRoom } from '../../hooks/useGetRoom'; import { useAllJoinedRoomsSet, useGetRoom } from '../../hooks/useGetRoom';
import { SpaceProvider } from '../../hooks/useSpace'; import { SpaceProvider } from '../../hooks/useSpace';
import { CreateRoomForm } from './CreateRoom'; import { CreateRoomForm } from './CreateRoom';
@ -29,6 +30,7 @@ type CreateRoomModalProps = {
state: CreateRoomModalState; state: CreateRoomModalState;
}; };
function CreateRoomModal({ state }: CreateRoomModalProps) { function CreateRoomModal({ state }: CreateRoomModalProps) {
const { t } = useTranslation();
const { spaceId, type } = state; const { spaceId, type } = state;
const closeDialog = useCloseCreateRoomModal(); const closeDialog = useCloseCreateRoomModal();
@ -59,7 +61,7 @@ function CreateRoomModal({ state }: CreateRoomModalProps) {
> >
<Box grow="Yes"> <Box grow="Yes">
<Text size="H4"> <Text size="H4">
{type === CreateRoomType.VoiceRoom ? 'New Voice Room' : 'New Chat Room'} {type === CreateRoomType.VoiceRoom ? t('Create.new_voice_room') : t('Create.new_chat_room')}
</Text> </Text>
</Box> </Box>
<Box shrink="No"> <Box shrink="No">

View file

@ -14,6 +14,7 @@ import {
Text, Text,
TextArea, TextArea,
} from 'folds'; } from 'folds';
import { useTranslation } from 'react-i18next';
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 {
@ -52,6 +53,7 @@ type CreateSpaceFormProps = {
onCreate?: (roomId: string) => void; onCreate?: (roomId: string) => void;
}; };
export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceFormProps) { export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceFormProps) {
const { t } = useTranslation();
const mx = useMatrixClient(); const mx = useMatrixClient();
const alive = useAlive(); const alive = useAlive();
@ -138,7 +140,7 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
return ( return (
<Box as="form" onSubmit={handleSubmit} grow="Yes" direction="Column" gap="500"> <Box as="form" onSubmit={handleSubmit} grow="Yes" direction="Column" gap="500">
<Box direction="Column" gap="100"> <Box direction="Column" gap="100">
<Text size="L400">Access</Text> <Text size="L400">{t('Create.access')}</Text>
<CreateRoomAccessSelector <CreateRoomAccessSelector
value={access} value={access}
onSelect={setAccess} onSelect={setAccess}
@ -148,7 +150,7 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
/> />
</Box> </Box>
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
<Text size="L400">Name</Text> <Text size="L400">{t('Create.name')}</Text>
<Input <Input
required required
before={<Icon size="100" src={getCreateSpaceAccessToIcon(access)} />} before={<Icon size="100" src={getCreateSpaceAccessToIcon(access)} />}
@ -162,7 +164,7 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
/> />
</Box> </Box>
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
<Text size="L400">Topic (Optional)</Text> <Text size="L400">{t('Create.topic_optional')}</Text>
<TextArea <TextArea
name="topicTextAria" name="topicTextAria"
size="500" size="500"
@ -176,7 +178,7 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
<Box shrink="No" direction="Column" gap="100"> <Box shrink="No" direction="Column" gap="100">
<Box gap="200" alignItems="End"> <Box gap="200" alignItems="End">
<Text size="L400">Options</Text> <Text size="L400">{t('Create.options')}</Text>
<Box grow="Yes" justifyContent="End"> <Box grow="Yes" justifyContent="End">
<Chip <Chip
radii="Pill" radii="Pill"
@ -184,7 +186,7 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
onClick={() => setAdvance(!advance)} onClick={() => setAdvance(!advance)}
type="button" type="button"
> >
<Text size="T200">Advanced Options</Text> <Text size="T200">{t('Create.advanced_options')}</Text>
</Chip> </Chip>
</Box> </Box>
</Box> </Box>
@ -210,8 +212,8 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
gap="500" gap="500"
> >
<SettingTile <SettingTile
title="Knock to Join" title={t('Create.knock_to_join')}
description="Anyone can send request to join this space." description={t('Create.knock_to_join_desc')}
after={ after={
<Switch variant="Primary" value={knock} onChange={setKnock} disabled={disabled} /> <Switch variant="Primary" value={knock} onChange={setKnock} disabled={disabled} />
} }
@ -226,8 +228,8 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
gap="500" gap="500"
> >
<SettingTile <SettingTile
title="Allow Federation" title={t('Create.allow_federation')}
description="Users from other servers can join." description={t('Create.allow_federation_desc')}
after={ after={
<Switch <Switch
variant="Primary" variant="Primary"
@ -254,9 +256,9 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
<Text size="T300" style={{ color: color.Critical.Main }}> <Text size="T300" style={{ color: color.Critical.Main }}>
<b> <b>
{error instanceof MatrixError && error.name === ErrorCode.M_LIMIT_EXCEEDED {error instanceof MatrixError && error.name === ErrorCode.M_LIMIT_EXCEEDED
? `Server rate-limited your request for ${millisecondsToMinutes( ? t('Create.rate_limited', { minutes: millisecondsToMinutes(
(error.data.retry_after_ms as number | undefined) ?? 0 (error.data.retry_after_ms as number | undefined) ?? 0
)} minutes!` ) })
: error.message} : error.message}
</b> </b>
</Text> </Text>
@ -271,7 +273,7 @@ export function CreateSpaceForm({ defaultAccess, space, onCreate }: CreateSpaceF
disabled={disabled} disabled={disabled}
before={loading && <Spinner variant="Primary" fill="Solid" size="200" />} before={loading && <Spinner variant="Primary" fill="Solid" size="200" />}
> >
<Text size="B500">Create</Text> <Text size="B500">{t('Create.create')}</Text>
</Button> </Button>
</Box> </Box>
</Box> </Box>

View file

@ -14,6 +14,7 @@ import {
Text, Text,
} from 'folds'; } from 'folds';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { useTranslation } from 'react-i18next';
import { useAllJoinedRoomsSet, useGetRoom } from '../../hooks/useGetRoom'; import { useAllJoinedRoomsSet, useGetRoom } from '../../hooks/useGetRoom';
import { SpaceProvider } from '../../hooks/useSpace'; import { SpaceProvider } from '../../hooks/useSpace';
import { CreateSpaceForm } from './CreateSpace'; import { CreateSpaceForm } from './CreateSpace';
@ -28,6 +29,7 @@ type CreateSpaceModalProps = {
state: CreateSpaceModalState; state: CreateSpaceModalState;
}; };
function CreateSpaceModal({ state }: CreateSpaceModalProps) { function CreateSpaceModal({ state }: CreateSpaceModalProps) {
const { t } = useTranslation();
const { spaceId } = state; const { spaceId } = state;
const closeDialog = useCloseCreateSpaceModal(); const closeDialog = useCloseCreateSpaceModal();
@ -58,7 +60,7 @@ function CreateSpaceModal({ state }: CreateSpaceModalProps) {
}} }}
> >
<Box grow="Yes"> <Box grow="Yes">
<Text size="H4">New Space</Text> <Text size="H4">{t('Create.new_space')}</Text>
</Box> </Box>
<Box shrink="No"> <Box shrink="No">
<IconButton size="300" radii="300" onClick={closeDialog}> <IconButton size="300" radii="300" onClick={closeDialog}>

View file

@ -17,6 +17,7 @@ import {
config, config,
} from 'folds'; } from 'folds';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { useTranslation } from 'react-i18next';
import classNames from 'classnames'; import classNames from 'classnames';
import { MatrixError, Room } from 'matrix-js-sdk'; import { MatrixError, Room } from 'matrix-js-sdk';
import { IHierarchyRoom } from 'matrix-js-sdk/lib/@types/spaces'; import { IHierarchyRoom } from 'matrix-js-sdk/lib/@types/spaces';
@ -243,6 +244,7 @@ function RootSpaceProfile({ closed, categoryId, handleClose }: RootSpaceProfileP
} }
function AddRoomButton({ item }: { item: HierarchyItem }) { function AddRoomButton({ item }: { item: HierarchyItem }) {
const { t } = useTranslation();
const [cords, setCords] = useState<RectCords>(); const [cords, setCords] = useState<RectCords>();
const openCreateRoomModal = useOpenCreateRoomModal(); const openCreateRoomModal = useOpenCreateRoomModal();
const [addExisting, setAddExisting] = useState(false); const [addExisting, setAddExisting] = useState(false);
@ -285,7 +287,7 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
fill="None" fill="None"
onClick={() => handleCreateRoom(CreateRoomType.TextRoom)} onClick={() => handleCreateRoom(CreateRoomType.TextRoom)}
> >
<Text size="T300">Chat Room</Text> <Text size="T300">{t('Create.chat_room')}</Text>
</MenuItem> </MenuItem>
<MenuItem <MenuItem
size="300" size="300"
@ -295,10 +297,10 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
onClick={() => handleCreateRoom(CreateRoomType.VoiceRoom)} onClick={() => handleCreateRoom(CreateRoomType.VoiceRoom)}
after={<BetaNoticeBadge />} after={<BetaNoticeBadge />}
> >
<Text size="T300">Voice Room</Text> <Text size="T300">{t('Create.voice_room')}</Text>
</MenuItem> </MenuItem>
<MenuItem size="300" radii="300" fill="None" onClick={handleAddExisting}> <MenuItem size="300" radii="300" fill="None" onClick={handleAddExisting}>
<Text size="T300">Existing Room</Text> <Text size="T300">{t('Create.existing_room')}</Text>
</MenuItem> </MenuItem>
</Menu> </Menu>
</FocusTrap> </FocusTrap>
@ -311,7 +313,7 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
onClick={handleAddRoom} onClick={handleAddRoom}
aria-pressed={!!cords} aria-pressed={!!cords}
> >
<Text size="B300">Add Room</Text> <Text size="B300">{t('Create.add_room')}</Text>
</Chip> </Chip>
{addExisting && ( {addExisting && (
<AddExistingModal parentId={item.roomId} requestClose={() => setAddExisting(false)} /> <AddExistingModal parentId={item.roomId} requestClose={() => setAddExisting(false)} />
@ -321,6 +323,7 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
} }
function AddSpaceButton({ item }: { item: HierarchyItem }) { function AddSpaceButton({ item }: { item: HierarchyItem }) {
const { t } = useTranslation();
const [cords, setCords] = useState<RectCords>(); const [cords, setCords] = useState<RectCords>();
const openCreateSpaceModal = useOpenCreateSpaceModal(); const openCreateSpaceModal = useOpenCreateSpaceModal();
const [addExisting, setAddExisting] = useState(false); const [addExisting, setAddExisting] = useState(false);
@ -362,10 +365,10 @@ function AddSpaceButton({ item }: { item: HierarchyItem }) {
fill="None" fill="None"
onClick={handleCreateSpace} onClick={handleCreateSpace}
> >
<Text size="T300">New Space</Text> <Text size="T300">{t('Create.new_space')}</Text>
</MenuItem> </MenuItem>
<MenuItem size="300" radii="300" fill="None" onClick={handleAddExisting}> <MenuItem size="300" radii="300" fill="None" onClick={handleAddExisting}>
<Text size="T300">Existing Space</Text> <Text size="T300">{t('Create.existing_space')}</Text>
</MenuItem> </MenuItem>
</Menu> </Menu>
</FocusTrap> </FocusTrap>
@ -378,7 +381,7 @@ function AddSpaceButton({ item }: { item: HierarchyItem }) {
onClick={handleAddSpace} onClick={handleAddSpace}
aria-pressed={!!cords} aria-pressed={!!cords}
> >
<Text size="B300">Add Space</Text> <Text size="B300">{t('Create.add_space')}</Text>
</Chip> </Chip>
{addExisting && ( {addExisting && (
<AddExistingModal space parentId={item.roomId} requestClose={() => setAddExisting(false)} /> <AddExistingModal space parentId={item.roomId} requestClose={() => setAddExisting(false)} />

View file

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { Box, Icon, Icons, Scroll } from 'folds'; import { Box, Icon, Icons, Scroll } from 'folds';
import { useTranslation } from 'react-i18next';
import { import {
Page, Page,
PageContent, PageContent,
@ -11,6 +12,7 @@ import { CreateSpaceForm } from '../../../features/create-space';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate'; import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
export function Create() { export function Create() {
const { t } = useTranslation();
const { navigateSpace } = useRoomNavigate(); const { navigateSpace } = useRoomNavigate();
return ( return (
@ -23,8 +25,8 @@ export function Create() {
<Box direction="Column" gap="700"> <Box direction="Column" gap="700">
<PageHero <PageHero
icon={<Icon size="600" src={Icons.Space} />} icon={<Icon size="600" src={Icons.Space} />}
title="Create Space" title={t('Create.create_space')}
subTitle="Build a space for your community." subTitle={t('Create.create_space_desc')}
/> />
<CreateSpaceForm onCreate={navigateSpace} /> <CreateSpaceForm onCreate={navigateSpace} />
</Box> </Box>

View file

@ -2,6 +2,7 @@ import React, { MouseEventHandler, useState } from 'react';
import { Box, config, Icon, Icons, Menu, PopOut, RectCords, Text } from 'folds'; import { Box, config, Icon, Icons, Menu, PopOut, RectCords, Text } from 'folds';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar'; import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar';
import { stopPropagation } from '../../../utils/keyboard'; import { stopPropagation } from '../../../utils/keyboard';
import { SequenceCard } from '../../../components/sequence-card'; import { SequenceCard } from '../../../components/sequence-card';
@ -18,6 +19,7 @@ import { JoinAddressPrompt } from '../../../components/join-address-prompt';
import { _RoomSearchParams } from '../../paths'; import { _RoomSearchParams } from '../../paths';
export function CreateTab() { export function CreateTab() {
const { t } = useTranslation();
const createSelected = useCreateSelected(); const createSelected = useCreateSelected();
const navigate = useNavigate(); const navigate = useNavigate();
@ -40,7 +42,7 @@ export function CreateTab() {
return ( return (
<SidebarItem active={createSelected}> <SidebarItem active={createSelected}>
<SidebarItemTooltip tooltip="Add Space"> <SidebarItemTooltip tooltip={t('Create.add_space')}>
{(triggerRef) => ( {(triggerRef) => (
<PopOut <PopOut
anchor={menuCords} anchor={menuCords}
@ -73,9 +75,9 @@ export function CreateTab() {
onClick={handleCreateSpace} onClick={handleCreateSpace}
> >
<SettingTile before={<Icon size="400" src={Icons.Space} />}> <SettingTile before={<Icon size="400" src={Icons.Space} />}>
<Text size="H6">Create Space</Text> <Text size="H6">{t('Create.create_space')}</Text>
<Text size="T300" priority="300"> <Text size="T300" priority="300">
Build a space for your community. {t('Create.create_space_desc')}
</Text> </Text>
</SettingTile> </SettingTile>
</SequenceCard> </SequenceCard>
@ -90,9 +92,9 @@ export function CreateTab() {
onClick={handleJoinWithAddress} onClick={handleJoinWithAddress}
> >
<SettingTile before={<Icon size="400" src={Icons.Link} />}> <SettingTile before={<Icon size="400" src={Icons.Link} />}>
<Text size="H6">Join with Address</Text> <Text size="H6">{t('Create.join_with_address')}</Text>
<Text size="T300" priority="300"> <Text size="T300" priority="300">
Become a part of existing community. {t('Create.join_with_address_desc')}
</Text> </Text>
</SettingTile> </SettingTile>
</SequenceCard> </SequenceCard>