localize Explore Community
This commit is contained in:
parent
ef0fa23642
commit
f4e94a48cc
7 changed files with 157 additions and 46 deletions
|
|
@ -507,5 +507,52 @@
|
||||||
|
|
||||||
"unverified_device": "Unverified Device",
|
"unverified_device": "Unverified Device",
|
||||||
"unverified_devices": "Unverified Devices"
|
"unverified_devices": "Unverified Devices"
|
||||||
|
},
|
||||||
|
|
||||||
|
"Explore": {
|
||||||
|
"explore_community": "Explore Community",
|
||||||
|
|
||||||
|
"add_server": "Add Server",
|
||||||
|
"add_server_desc": "Add server name to explore public communities.",
|
||||||
|
"server_name": "Server Name",
|
||||||
|
"failed_load_public_rooms": "Failed to load public rooms. Please try again.",
|
||||||
|
"view": "View",
|
||||||
|
"featured": "Featured",
|
||||||
|
"servers": "Servers",
|
||||||
|
|
||||||
|
"featured_by_client": "Featured by Client",
|
||||||
|
"featured_by_client_desc": "Public rooms and spaces hand-picked by this client.",
|
||||||
|
"featured_spaces": "Featured Spaces",
|
||||||
|
"featured_rooms": "Featured Rooms",
|
||||||
|
"no_featured": "No featured rooms or spaces yet.",
|
||||||
|
|
||||||
|
"search": "Search",
|
||||||
|
"search_placeholder": "Search for keyword",
|
||||||
|
"clear": "Clear",
|
||||||
|
"enter": "Enter",
|
||||||
|
"protocols": "Protocols",
|
||||||
|
"presets": "Presets",
|
||||||
|
"custom_limit": "Custom Limit",
|
||||||
|
"per_page_limit": "Per Page Item Limit",
|
||||||
|
"change_limit": "Change Limit",
|
||||||
|
"page_limit": "Page Limit: {{limit}}",
|
||||||
|
"results_for": "Results for \"{{term}}\"",
|
||||||
|
"popular_communities": "Popular Communities",
|
||||||
|
"filter_all": "All",
|
||||||
|
"filter_spaces": "Spaces",
|
||||||
|
"filter_rooms": "Rooms",
|
||||||
|
"previous_page": "Previous Page",
|
||||||
|
"next_page": "Next Page",
|
||||||
|
"no_communities": "No communities found!",
|
||||||
|
|
||||||
|
"space_badge": "Space",
|
||||||
|
"members_count": "{{count}} Members",
|
||||||
|
"join": "Join",
|
||||||
|
"joining": "Joining",
|
||||||
|
"retry": "Retry",
|
||||||
|
"join_error": "Join Error",
|
||||||
|
"join_error_unknown": "Failed to join. Unknown Error.",
|
||||||
|
"view_error": "View Error",
|
||||||
|
"cancel": "Cancel"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -509,5 +509,52 @@
|
||||||
|
|
||||||
"unverified_device": "Неподтверждённое устройство",
|
"unverified_device": "Неподтверждённое устройство",
|
||||||
"unverified_devices": "Неподтверждённые устройства"
|
"unverified_devices": "Неподтверждённые устройства"
|
||||||
|
},
|
||||||
|
|
||||||
|
"Explore": {
|
||||||
|
"explore_community": "Обзор сообществ",
|
||||||
|
|
||||||
|
"add_server": "Добавить сервер",
|
||||||
|
"add_server_desc": "Укажите имя сервера для обзора публичных сообществ.",
|
||||||
|
"server_name": "Имя сервера",
|
||||||
|
"failed_load_public_rooms": "Не удалось загрузить публичные комнаты. Попробуйте ещё раз.",
|
||||||
|
"view": "Открыть",
|
||||||
|
"featured": "Рекомендуемые",
|
||||||
|
"servers": "Серверы",
|
||||||
|
|
||||||
|
"featured_by_client": "Рекомендации клиента",
|
||||||
|
"featured_by_client_desc": "Подборка публичных комнат и пространств от этого клиента.",
|
||||||
|
"featured_spaces": "Рекомендуемые пространства",
|
||||||
|
"featured_rooms": "Рекомендуемые комнаты",
|
||||||
|
"no_featured": "Рекомендуемых комнат и пространств пока нет.",
|
||||||
|
|
||||||
|
"search": "Поиск",
|
||||||
|
"search_placeholder": "Поиск по ключевому слову",
|
||||||
|
"clear": "Очистить",
|
||||||
|
"enter": "Найти",
|
||||||
|
"protocols": "Протоколы",
|
||||||
|
"presets": "Готовые значения",
|
||||||
|
"custom_limit": "Своё значение",
|
||||||
|
"per_page_limit": "Элементов на странице",
|
||||||
|
"change_limit": "Применить",
|
||||||
|
"page_limit": "На странице: {{limit}}",
|
||||||
|
"results_for": "Результаты для «{{term}}»",
|
||||||
|
"popular_communities": "Популярные сообщества",
|
||||||
|
"filter_all": "Все",
|
||||||
|
"filter_spaces": "Пространства",
|
||||||
|
"filter_rooms": "Комнаты",
|
||||||
|
"previous_page": "Предыдущая",
|
||||||
|
"next_page": "Следующая",
|
||||||
|
"no_communities": "Сообщества не найдены!",
|
||||||
|
|
||||||
|
"space_badge": "Пространство",
|
||||||
|
"members_count": "{{count}} участников",
|
||||||
|
"join": "Присоединиться",
|
||||||
|
"joining": "Вступление…",
|
||||||
|
"retry": "Повторить",
|
||||||
|
"join_error": "Не удалось вступить",
|
||||||
|
"join_error_unknown": "Не удалось присоединиться. Неизвестная ошибка.",
|
||||||
|
"view_error": "Подробности",
|
||||||
|
"cancel": "Отмена"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import {
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import * as css from './style.css';
|
import * as css from './style.css';
|
||||||
import { RoomAvatar } from '../room-avatar';
|
import { RoomAvatar } from '../room-avatar';
|
||||||
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
|
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
|
||||||
|
|
@ -94,6 +95,7 @@ function ErrorDialog({
|
||||||
message: string;
|
message: string;
|
||||||
children: (openError: () => void) => ReactNode;
|
children: (openError: () => void) => ReactNode;
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [viewError, setViewError] = useState(false);
|
const [viewError, setViewError] = useState(false);
|
||||||
const closeError = () => setViewError(false);
|
const closeError = () => setViewError(false);
|
||||||
const openError = () => setViewError(true);
|
const openError = () => setViewError(true);
|
||||||
|
|
@ -120,7 +122,7 @@ function ErrorDialog({
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Button size="400" variant="Secondary" fill="Soft" onClick={closeError}>
|
<Button size="400" variant="Secondary" fill="Soft" onClick={closeError}>
|
||||||
<Text size="B400">Cancel</Text>
|
<Text size="B400">{t('Explore.cancel')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
@ -161,6 +163,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const useAuthentication = useMediaAuthentication();
|
const useAuthentication = useMediaAuthentication();
|
||||||
const joinedRoomId = useJoinedRoomId(allRooms, roomIdOrAlias);
|
const joinedRoomId = useJoinedRoomId(allRooms, roomIdOrAlias);
|
||||||
|
|
@ -224,7 +227,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
</Avatar>
|
</Avatar>
|
||||||
{(roomType === RoomType.Space || joinedRoom?.isSpaceRoom()) && (
|
{(roomType === RoomType.Space || joinedRoom?.isSpaceRoom()) && (
|
||||||
<Badge variant="Secondary" fill="Soft" outlined>
|
<Badge variant="Secondary" fill="Soft" outlined>
|
||||||
<Text size="L400">Space</Text>
|
<Text size="L400">{t('Explore.space_badge')}</Text>
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -252,7 +255,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
{typeof joinedMemberCount === 'number' && (
|
{typeof joinedMemberCount === 'number' && (
|
||||||
<Box gap="100">
|
<Box gap="100">
|
||||||
<Icon size="50" src={Icons.User} />
|
<Icon size="50" src={Icons.User} />
|
||||||
<Text size="T200">{`${millify(joinedMemberCount)} Members`}</Text>
|
<Text size="T200">{t('Explore.members_count', { count: millify(joinedMemberCount) })}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{typeof joinedRoomId === 'string' && (
|
{typeof joinedRoomId === 'string' && (
|
||||||
|
|
@ -263,7 +266,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
size="300"
|
size="300"
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
View
|
{t('Explore.view')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
@ -276,7 +279,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
before={joining && <Spinner size="50" variant="Secondary" fill="Soft" />}
|
before={joining && <Spinner size="50" variant="Secondary" fill="Soft" />}
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
{joining ? 'Joining' : 'Join'}
|
{joining ? t('Explore.joining') : t('Explore.join')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
@ -290,12 +293,12 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
size="300"
|
size="300"
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
Retry
|
{t('Explore.retry')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<ErrorDialog
|
<ErrorDialog
|
||||||
title="Join Error"
|
title={t('Explore.join_error')}
|
||||||
message={joinState.error.message || 'Failed to join. Unknown Error.'}
|
message={joinState.error.message || t('Explore.join_error_unknown')}
|
||||||
>
|
>
|
||||||
{(openError) => (
|
{(openError) => (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -307,7 +310,7 @@ export const RoomCard = as<'div', RoomCardProps>(
|
||||||
size="300"
|
size="300"
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
View Error
|
{t('Explore.view_error')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { FormEventHandler, useCallback, useRef, useState } from 'react';
|
import React, { FormEventHandler, useCallback, useRef, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
|
|
@ -39,6 +40,7 @@ import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page
|
||||||
import { stopPropagation } from '../../../utils/keyboard';
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
|
|
||||||
export function AddServer() {
|
export function AddServer() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [dialog, setDialog] = useState(false);
|
const [dialog, setDialog] = useState(false);
|
||||||
|
|
@ -94,7 +96,7 @@ export function AddServer() {
|
||||||
size="500"
|
size="500"
|
||||||
>
|
>
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
<Text size="H4">Add Server</Text>
|
<Text size="H4">{t('Explore.add_server')}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<IconButton size="300" onClick={() => setDialog(false)} radii="300">
|
<IconButton size="300" onClick={() => setDialog(false)} radii="300">
|
||||||
<Icon src={Icons.Cross} />
|
<Icon src={Icons.Cross} />
|
||||||
|
|
@ -107,13 +109,13 @@ export function AddServer() {
|
||||||
direction="Column"
|
direction="Column"
|
||||||
gap="400"
|
gap="400"
|
||||||
>
|
>
|
||||||
<Text priority="400">Add server name to explore public communities.</Text>
|
<Text priority="400">{t('Explore.add_server_desc')}</Text>
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Server Name</Text>
|
<Text size="L400">{t('Explore.server_name')}</Text>
|
||||||
<Input ref={serverInputRef} name="serverInput" variant="Background" required />
|
<Input ref={serverInputRef} name="serverInput" variant="Background" required />
|
||||||
{exploreState.status === AsyncStatus.Error && (
|
{exploreState.status === AsyncStatus.Error && (
|
||||||
<Text style={{ color: color.Critical.Main }} size="T300">
|
<Text style={{ color: color.Critical.Main }} size="T300">
|
||||||
Failed to load public rooms. Please try again.
|
{t('Explore.failed_load_public_rooms')}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -132,7 +134,7 @@ export function AddServer() {
|
||||||
</Button> */}
|
</Button> */}
|
||||||
|
|
||||||
<Button type="submit" onClick={handleView} variant="Secondary" fill="Soft">
|
<Button type="submit" onClick={handleView} variant="Secondary" fill="Soft">
|
||||||
<Text size="B400">View</Text>
|
<Text size="B400">{t('Explore.view')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -148,7 +150,7 @@ export function AddServer() {
|
||||||
onClick={() => setDialog(true)}
|
onClick={() => setDialog(true)}
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
Add Server
|
{t('Explore.add_server')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|
@ -156,6 +158,7 @@ export function AddServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Explore() {
|
export function Explore() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
useNavToActivePathMapper('explore');
|
useNavToActivePathMapper('explore');
|
||||||
const userId = mx.getUserId();
|
const userId = mx.getUserId();
|
||||||
|
|
@ -173,7 +176,7 @@ export function Explore() {
|
||||||
<Box grow="Yes" gap="300">
|
<Box grow="Yes" gap="300">
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
<Text size="H4" truncate>
|
<Text size="H4" truncate>
|
||||||
Explore Community
|
{t('Explore.explore_community')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -191,7 +194,7 @@ export function Explore() {
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box as="span" grow="Yes">
|
<Box as="span" grow="Yes">
|
||||||
<Text as="span" size="Inherit" truncate>
|
<Text as="span" size="Inherit" truncate>
|
||||||
Featured
|
{t('Explore.featured')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -229,7 +232,7 @@ export function Explore() {
|
||||||
<NavCategory>
|
<NavCategory>
|
||||||
<NavCategoryHeader>
|
<NavCategoryHeader>
|
||||||
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
|
<Text size="O400" style={{ paddingLeft: config.space.S200 }}>
|
||||||
Servers
|
{t('Explore.servers')}
|
||||||
</Text>
|
</Text>
|
||||||
</NavCategoryHeader>
|
</NavCategoryHeader>
|
||||||
{servers.map((server) => (
|
{servers.map((server) => (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Icon, IconButton, Icons, Scroll, Text } from 'folds';
|
import { Box, Icon, IconButton, Icons, Scroll, Text } from 'folds';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { useClientConfig } from '../../../hooks/useClientConfig';
|
import { useClientConfig } from '../../../hooks/useClientConfig';
|
||||||
import { RoomCard, RoomCardGrid } from '../../../components/room-card';
|
import { RoomCard, RoomCardGrid } from '../../../components/room-card';
|
||||||
|
|
@ -20,6 +21,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
||||||
import { BackRouteHandler } from '../../../components/BackRouteHandler';
|
import { BackRouteHandler } from '../../../components/BackRouteHandler';
|
||||||
|
|
||||||
export function FeaturedRooms() {
|
export function FeaturedRooms() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { featuredCommunities } = useClientConfig();
|
const { featuredCommunities } = useClientConfig();
|
||||||
const { rooms, spaces } = featuredCommunities ?? {};
|
const { rooms, spaces } = featuredCommunities ?? {};
|
||||||
const allRooms = useAtomValue(allRoomsAtom);
|
const allRooms = useAtomValue(allRoomsAtom);
|
||||||
|
|
@ -49,14 +51,14 @@ export function FeaturedRooms() {
|
||||||
<PageHeroSection>
|
<PageHeroSection>
|
||||||
<PageHero
|
<PageHero
|
||||||
icon={<Icon size="600" src={Icons.Bulb} />}
|
icon={<Icon size="600" src={Icons.Bulb} />}
|
||||||
title="Featured by Client"
|
title={t('Explore.featured_by_client')}
|
||||||
subTitle="Find and explore public rooms and spaces featured by client provider."
|
subTitle={t('Explore.featured_by_client_desc')}
|
||||||
/>
|
/>
|
||||||
</PageHeroSection>
|
</PageHeroSection>
|
||||||
<Box direction="Column" gap="700">
|
<Box direction="Column" gap="700">
|
||||||
{spaces && spaces.length > 0 && (
|
{spaces && spaces.length > 0 && (
|
||||||
<Box direction="Column" gap="400">
|
<Box direction="Column" gap="400">
|
||||||
<Text size="H4">Featured Spaces</Text>
|
<Text size="H4">{t('Explore.featured_spaces')}</Text>
|
||||||
<RoomCardGrid>
|
<RoomCardGrid>
|
||||||
{spaces.map((roomIdOrAlias) => (
|
{spaces.map((roomIdOrAlias) => (
|
||||||
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
||||||
|
|
@ -85,7 +87,7 @@ export function FeaturedRooms() {
|
||||||
)}
|
)}
|
||||||
{rooms && rooms.length > 0 && (
|
{rooms && rooms.length > 0 && (
|
||||||
<Box direction="Column" gap="400">
|
<Box direction="Column" gap="400">
|
||||||
<Text size="H4">Featured Rooms</Text>
|
<Text size="H4">{t('Explore.featured_rooms')}</Text>
|
||||||
<RoomCardGrid>
|
<RoomCardGrid>
|
||||||
{rooms.map((roomIdOrAlias) => (
|
{rooms.map((roomIdOrAlias) => (
|
||||||
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
<RoomSummaryLoader key={roomIdOrAlias} roomIdOrAlias={roomIdOrAlias}>
|
||||||
|
|
@ -123,7 +125,7 @@ export function FeaturedRooms() {
|
||||||
>
|
>
|
||||||
<Icon size="400" src={Icons.Info} />
|
<Icon size="400" src={Icons.Info} />
|
||||||
<Text size="T300" align="Center">
|
<Text size="T300" align="Center">
|
||||||
No rooms or spaces featured by client provider.
|
{t('Explore.no_featured')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import {
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { MatrixClient, Method, RoomType } from 'matrix-js-sdk';
|
import { MatrixClient, Method, RoomType } from 'matrix-js-sdk';
|
||||||
|
|
@ -62,24 +63,26 @@ type RoomTypeFilter = {
|
||||||
title: string;
|
title: string;
|
||||||
value: string | undefined;
|
value: string | undefined;
|
||||||
};
|
};
|
||||||
const useRoomTypeFilters = (): RoomTypeFilter[] =>
|
const useRoomTypeFilters = (): RoomTypeFilter[] => {
|
||||||
useMemo(
|
const { t } = useTranslation();
|
||||||
|
return useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
title: 'All',
|
title: t('Explore.filter_all'),
|
||||||
value: undefined,
|
value: undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Spaces',
|
title: t('Explore.filter_spaces'),
|
||||||
value: RoomType.Space,
|
value: RoomType.Space,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Rooms',
|
title: t('Explore.filter_rooms'),
|
||||||
value: 'null',
|
value: 'null',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[]
|
[t]
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const FALLBACK_ROOMS_LIMIT = 24;
|
const FALLBACK_ROOMS_LIMIT = 24;
|
||||||
|
|
||||||
|
|
@ -91,6 +94,7 @@ type SearchProps = {
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
function Search({ active, loading, searchInputRef, onSearch, onReset }: SearchProps) {
|
function Search({ active, loading, searchInputRef, onSearch, onReset }: SearchProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const handleSearchSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
const handleSearchSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
const { searchInput } = evt.target as HTMLFormElement & {
|
const { searchInput } = evt.target as HTMLFormElement & {
|
||||||
|
|
@ -106,14 +110,14 @@ function Search({ active, loading, searchInputRef, onSearch, onReset }: SearchPr
|
||||||
return (
|
return (
|
||||||
<Box as="form" direction="Column" gap="100" onSubmit={handleSearchSubmit}>
|
<Box as="form" direction="Column" gap="100" onSubmit={handleSearchSubmit}>
|
||||||
<span data-spacing-node />
|
<span data-spacing-node />
|
||||||
<Text size="L400">Search</Text>
|
<Text size="L400">{t('Explore.search')}</Text>
|
||||||
<Input
|
<Input
|
||||||
ref={searchInputRef}
|
ref={searchInputRef}
|
||||||
style={{ paddingRight: config.space.S300 }}
|
style={{ paddingRight: config.space.S300 }}
|
||||||
name="searchInput"
|
name="searchInput"
|
||||||
size="500"
|
size="500"
|
||||||
variant="Background"
|
variant="Background"
|
||||||
placeholder="Search for keyword"
|
placeholder={t('Explore.search_placeholder')}
|
||||||
before={
|
before={
|
||||||
active && loading ? (
|
active && loading ? (
|
||||||
<Spinner variant="Secondary" size="200" />
|
<Spinner variant="Secondary" size="200" />
|
||||||
|
|
@ -132,11 +136,11 @@ function Search({ active, loading, searchInputRef, onSearch, onReset }: SearchPr
|
||||||
after={<Icon size="50" src={Icons.Cross} />}
|
after={<Icon size="50" src={Icons.Cross} />}
|
||||||
onClick={onReset}
|
onClick={onReset}
|
||||||
>
|
>
|
||||||
<Text size="B300">Clear</Text>
|
<Text size="B300">{t('Explore.clear')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
) : (
|
) : (
|
||||||
<Chip type="submit" variant="Primary" size="400" radii="Pill" outlined>
|
<Chip type="submit" variant="Primary" size="400" radii="Pill" outlined>
|
||||||
<Text size="B300">Enter</Text>
|
<Text size="B300">{t('Explore.enter')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +157,7 @@ function ThirdPartyProtocolsSelector({
|
||||||
instanceId?: string;
|
instanceId?: string;
|
||||||
onChange: (instanceId?: string) => void;
|
onChange: (instanceId?: string) => void;
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
|
|
@ -196,7 +201,7 @@ function ThirdPartyProtocolsSelector({
|
||||||
style={{ padding: config.space.S100, minWidth: toRem(100) }}
|
style={{ padding: config.space.S100, minWidth: toRem(100) }}
|
||||||
>
|
>
|
||||||
<Text style={{ padding: config.space.S100 }} size="L400" truncate>
|
<Text style={{ padding: config.space.S100 }} size="L400" truncate>
|
||||||
Protocols
|
{t('Explore.protocols')}
|
||||||
</Text>
|
</Text>
|
||||||
<Box direction="Column">
|
<Box direction="Column">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
@ -252,6 +257,7 @@ type LimitButtonProps = {
|
||||||
onLimitChange: (limit: string) => void;
|
onLimitChange: (limit: string) => void;
|
||||||
};
|
};
|
||||||
function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
const [menuAnchor, setMenuAnchor] = useState<RectCords>();
|
||||||
|
|
||||||
const handleLimitSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
const handleLimitSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
||||||
|
|
@ -288,7 +294,7 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
<Menu variant="Surface">
|
<Menu variant="Surface">
|
||||||
<Box direction="Column" gap="400" style={{ padding: config.space.S300 }}>
|
<Box direction="Column" gap="400" style={{ padding: config.space.S300 }}>
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Presets</Text>
|
<Text size="L400">{t('Explore.presets')}</Text>
|
||||||
<Box gap="100" wrap="Wrap">
|
<Box gap="100" wrap="Wrap">
|
||||||
<Chip variant="SurfaceVariant" onClick={() => setLimit('24')} radii="Pill">
|
<Chip variant="SurfaceVariant" onClick={() => setLimit('24')} radii="Pill">
|
||||||
<Text size="T200">24</Text>
|
<Text size="T200">24</Text>
|
||||||
|
|
@ -303,7 +309,7 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
</Box>
|
</Box>
|
||||||
<Box as="form" onSubmit={handleLimitSubmit} direction="Column" gap="300">
|
<Box as="form" onSubmit={handleLimitSubmit} direction="Column" gap="300">
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Custom Limit</Text>
|
<Text size="L400">{t('Explore.custom_limit')}</Text>
|
||||||
<Input
|
<Input
|
||||||
name="limitInput"
|
name="limitInput"
|
||||||
size="300"
|
size="300"
|
||||||
|
|
@ -314,11 +320,11 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
outlined
|
outlined
|
||||||
type="number"
|
type="number"
|
||||||
radii="400"
|
radii="400"
|
||||||
aria-label="Per Page Item Limit"
|
aria-label={t('Explore.per_page_limit')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Button type="submit" size="300" variant="Primary" radii="400">
|
<Button type="submit" size="300" variant="Primary" radii="400">
|
||||||
<Text size="B300">Change Limit</Text>
|
<Text size="B300">{t('Explore.change_limit')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -334,13 +340,14 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) {
|
||||||
variant="SurfaceVariant"
|
variant="SurfaceVariant"
|
||||||
after={<Icon size="100" src={Icons.ChevronBottom} />}
|
after={<Icon size="100" src={Icons.ChevronBottom} />}
|
||||||
>
|
>
|
||||||
<Text size="T200" truncate>{`Page Limit: ${limit}`}</Text>
|
<Text size="T200" truncate>{t('Explore.page_limit', { limit })}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
</PopOut>
|
</PopOut>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PublicRooms() {
|
export function PublicRooms() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { server } = useParams();
|
const { server } = useParams();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const userId = mx.getUserId();
|
const userId = mx.getUserId();
|
||||||
|
|
@ -488,7 +495,7 @@ export function PublicRooms() {
|
||||||
<Box grow="No" justifyContent="Center" alignItems="Center" gap="200">
|
<Box grow="No" justifyContent="Center" alignItems="Center" gap="200">
|
||||||
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Search} />}
|
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Search} />}
|
||||||
<Text size="H3" truncate>
|
<Text size="H3" truncate>
|
||||||
Search
|
{t('Explore.search')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box grow="Yes" basis="No" />
|
<Box grow="Yes" basis="No" />
|
||||||
|
|
@ -532,9 +539,9 @@ export function PublicRooms() {
|
||||||
<Box direction="Column" gap="400">
|
<Box direction="Column" gap="400">
|
||||||
<Box direction="Column" gap="300">
|
<Box direction="Column" gap="300">
|
||||||
{isSearch ? (
|
{isSearch ? (
|
||||||
<Text size="H4">{`Results for "${serverSearchParams.term}"`}</Text>
|
<Text size="H4">{t('Explore.results_for', { term: serverSearchParams.term })}</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text size="H4">Popular Communities</Text>
|
<Text size="H4">{t('Explore.popular_communities')}</Text>
|
||||||
)}
|
)}
|
||||||
<Box gap="200">
|
<Box gap="200">
|
||||||
{roomTypeFilters.map((filter) => (
|
{roomTypeFilters.map((filter) => (
|
||||||
|
|
@ -624,7 +631,7 @@ export function PublicRooms() {
|
||||||
disabled={!data.prev_batch}
|
disabled={!data.prev_batch}
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
Previous Page
|
{t('Explore.previous_page')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Box data-spacing-node grow="Yes" />
|
<Box data-spacing-node grow="Yes" />
|
||||||
|
|
@ -635,7 +642,7 @@ export function PublicRooms() {
|
||||||
disabled={!data.next_batch}
|
disabled={!data.next_batch}
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
Next Page
|
{t('Explore.next_page')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -651,7 +658,7 @@ export function PublicRooms() {
|
||||||
>
|
>
|
||||||
<Icon size="400" src={Icons.Info} />
|
<Icon size="400" src={Icons.Info} />
|
||||||
<Text size="T300" align="Center">
|
<Text size="T300" align="Center">
|
||||||
No communities found!
|
{t('Explore.no_communities')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Icon, Icons } from 'folds';
|
import { Icon, Icons } from 'folds';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar';
|
import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar';
|
||||||
import { useExploreSelected } from '../../../hooks/router/useExploreSelected';
|
import { useExploreSelected } from '../../../hooks/router/useExploreSelected';
|
||||||
|
|
@ -17,6 +18,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
||||||
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
||||||
|
|
||||||
export function ExploreTab() {
|
export function ExploreTab() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const screenSize = useScreenSizeContext();
|
const screenSize = useScreenSizeContext();
|
||||||
const clientConfig = useClientConfig();
|
const clientConfig = useClientConfig();
|
||||||
|
|
@ -52,7 +54,7 @@ export function ExploreTab() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarItem active={exploreSelected}>
|
<SidebarItem active={exploreSelected}>
|
||||||
<SidebarItemTooltip tooltip="Explore Community">
|
<SidebarItemTooltip tooltip={t('Explore.explore_community')}>
|
||||||
{(triggerRef) => (
|
{(triggerRef) => (
|
||||||
<SidebarAvatar as="button" ref={triggerRef} outlined onClick={handleExploreClick}>
|
<SidebarAvatar as="button" ref={triggerRef} outlined onClick={handleExploreClick}>
|
||||||
<Icon src={Icons.Explore} filled={exploreSelected} />
|
<Icon src={Icons.Explore} filled={exploreSelected} />
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue