localize Inbox/Notifications
This commit is contained in:
parent
77fda29820
commit
ef0fa23642
9 changed files with 190 additions and 54 deletions
|
|
@ -451,5 +451,61 @@
|
||||||
"broken_message": "Broken message",
|
"broken_message": "Broken message",
|
||||||
"empty_message": "Empty message",
|
"empty_message": "Empty message",
|
||||||
"edited": " (edited)"
|
"edited": " (edited)"
|
||||||
|
},
|
||||||
|
"Inbox": {
|
||||||
|
"inbox": "Inbox",
|
||||||
|
"invites": "Invites",
|
||||||
|
"notifications": "Notifications",
|
||||||
|
|
||||||
|
"notification_messages": "Notifications",
|
||||||
|
"filter": "Filter",
|
||||||
|
"all_notifications": "All Notifications",
|
||||||
|
"highlighted": "Highlighted",
|
||||||
|
"mark_as_read": "Mark as Read",
|
||||||
|
"open": "Open",
|
||||||
|
"no_notifications": "No Notifications",
|
||||||
|
"no_notifications_desc": "You don't have any new notifications to display yet.",
|
||||||
|
"scroll_to_top": "Scroll to Top",
|
||||||
|
|
||||||
|
"encrypted": "Encrypted",
|
||||||
|
"direct_message": "Direct Message",
|
||||||
|
"space": "Space",
|
||||||
|
"decline": "Decline",
|
||||||
|
"accept": "Accept",
|
||||||
|
"from": "From: ",
|
||||||
|
"reason_label": "Reason: ",
|
||||||
|
|
||||||
|
"primary": "Primary",
|
||||||
|
"public": "Public",
|
||||||
|
"spam": "Spam",
|
||||||
|
|
||||||
|
"no_invites": "No Invites",
|
||||||
|
"no_invites_known_desc": "When someone you share a room with sends you an invite, it'll show up here.",
|
||||||
|
"no_invites_unknown_desc": "Invites from people outside your rooms will appear here.",
|
||||||
|
"decline_all": "Decline All",
|
||||||
|
|
||||||
|
"spam_invites_count_one": "{{count}} Spam Invite",
|
||||||
|
"spam_invites_count_other": "{{count}} Spam Invites",
|
||||||
|
"spam_invites_desc": "Some of the following invites may contain harmful content or have been sent by banned users.",
|
||||||
|
"report_all": "Report All",
|
||||||
|
"block_all": "Block All",
|
||||||
|
"hide_all": "Hide All",
|
||||||
|
"view_all": "View All",
|
||||||
|
"no_spam_invites": "No Spam Invites",
|
||||||
|
"no_spam_invites_desc": "Invites detected as spam appear here.",
|
||||||
|
|
||||||
|
"invite_title": "Invite",
|
||||||
|
"user_id": "User ID",
|
||||||
|
"user_id_placeholder": "@username:server",
|
||||||
|
"reason_optional": "Reason (Optional)",
|
||||||
|
"invite_button": "Invite",
|
||||||
|
|
||||||
|
"notif_default": "Default",
|
||||||
|
"notif_all_messages": "All Messages",
|
||||||
|
"notif_mentions_keywords": "Mention & Keywords",
|
||||||
|
"notif_mute": "Mute",
|
||||||
|
|
||||||
|
"unverified_device": "Unverified Device",
|
||||||
|
"unverified_devices": "Unverified Devices"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -451,5 +451,63 @@
|
||||||
"broken_message": "Повреждённое сообщение",
|
"broken_message": "Повреждённое сообщение",
|
||||||
"empty_message": "Пустое сообщение",
|
"empty_message": "Пустое сообщение",
|
||||||
"edited": " (изменено)"
|
"edited": " (изменено)"
|
||||||
|
},
|
||||||
|
"Inbox": {
|
||||||
|
"inbox": "Входящие",
|
||||||
|
"invites": "Приглашения",
|
||||||
|
"notifications": "Уведомления",
|
||||||
|
|
||||||
|
"notification_messages": "Уведомления",
|
||||||
|
"filter": "Фильтр",
|
||||||
|
"all_notifications": "Все уведомления",
|
||||||
|
"highlighted": "Выделенные",
|
||||||
|
"mark_as_read": "Отметить прочитанным",
|
||||||
|
"open": "Открыть",
|
||||||
|
"no_notifications": "Нет уведомлений",
|
||||||
|
"no_notifications_desc": "У вас пока нет новых уведомлений.",
|
||||||
|
"scroll_to_top": "Наверх",
|
||||||
|
|
||||||
|
"encrypted": "Зашифровано",
|
||||||
|
"direct_message": "Личное сообщение",
|
||||||
|
"space": "Пространство",
|
||||||
|
"decline": "Отклонить",
|
||||||
|
"accept": "Принять",
|
||||||
|
"from": "От: ",
|
||||||
|
"reason_label": "Причина: ",
|
||||||
|
|
||||||
|
"primary": "Основные",
|
||||||
|
"public": "Публичные",
|
||||||
|
"spam": "Спам",
|
||||||
|
|
||||||
|
"no_invites": "Нет приглашений",
|
||||||
|
"no_invites_known_desc": "Когда кто-то, с кем вы состоите в одной комнате, отправит вам приглашение, оно появится здесь.",
|
||||||
|
"no_invites_unknown_desc": "Приглашения от людей за пределами ваших комнат появятся здесь.",
|
||||||
|
"decline_all": "Отклонить все",
|
||||||
|
|
||||||
|
"spam_invites_count_one": "{{count}} спам-приглашение",
|
||||||
|
"spam_invites_count_few": "{{count}} спам-приглашения",
|
||||||
|
"spam_invites_count_many": "{{count}} спам-приглашений",
|
||||||
|
"spam_invites_count_other": "{{count}} спам-приглашений",
|
||||||
|
"spam_invites_desc": "Некоторые из этих приглашений могут содержать вредоносный контент или были отправлены забаненными пользователями.",
|
||||||
|
"report_all": "Пожаловаться на все",
|
||||||
|
"block_all": "Заблокировать всех",
|
||||||
|
"hide_all": "Скрыть все",
|
||||||
|
"view_all": "Показать все",
|
||||||
|
"no_spam_invites": "Нет спам-приглашений",
|
||||||
|
"no_spam_invites_desc": "Приглашения, распознанные как спам, появятся здесь.",
|
||||||
|
|
||||||
|
"invite_title": "Пригласить",
|
||||||
|
"user_id": "ID пользователя",
|
||||||
|
"user_id_placeholder": "@username:server",
|
||||||
|
"reason_optional": "Причина (необязательно)",
|
||||||
|
"invite_button": "Пригласить",
|
||||||
|
|
||||||
|
"notif_default": "По умолчанию",
|
||||||
|
"notif_all_messages": "Все сообщения",
|
||||||
|
"notif_mentions_keywords": "Упоминания и ключевые слова",
|
||||||
|
"notif_mute": "Без уведомлений",
|
||||||
|
|
||||||
|
"unverified_device": "Неподтверждённое устройство",
|
||||||
|
"unverified_devices": "Неподтверждённые устройства"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { Box, config, Icon, Menu, MenuItem, PopOut, RectCords, Text } from 'folds';
|
import { Box, config, Icon, Menu, MenuItem, PopOut, RectCords, Text } from 'folds';
|
||||||
import React, { MouseEventHandler, ReactNode, useMemo, useState } from 'react';
|
import React, { MouseEventHandler, ReactNode, useMemo, useState } from 'react';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { stopPropagation } from '../utils/keyboard';
|
import { stopPropagation } from '../utils/keyboard';
|
||||||
import {
|
import {
|
||||||
getRoomNotificationModeIcon,
|
getRoomNotificationModeIcon,
|
||||||
|
|
@ -20,16 +21,18 @@ const useRoomNotificationModes = (): RoomNotificationMode[] =>
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const useRoomNotificationModeStr = (): Record<RoomNotificationMode, string> =>
|
const useRoomNotificationModeStr = (): Record<RoomNotificationMode, string> => {
|
||||||
useMemo(
|
const { t } = useTranslation();
|
||||||
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
[RoomNotificationMode.Unset]: 'Default',
|
[RoomNotificationMode.Unset]: t('Inbox.notif_default'),
|
||||||
[RoomNotificationMode.AllMessages]: 'All Messages',
|
[RoomNotificationMode.AllMessages]: t('Inbox.notif_all_messages'),
|
||||||
[RoomNotificationMode.SpecialMessages]: 'Mention & Keywords',
|
[RoomNotificationMode.SpecialMessages]: t('Inbox.notif_mentions_keywords'),
|
||||||
[RoomNotificationMode.Mute]: 'Mute',
|
[RoomNotificationMode.Mute]: t('Inbox.notif_mute'),
|
||||||
}),
|
}),
|
||||||
[]
|
[t]
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
type NotificationModeSwitcherProps = {
|
type NotificationModeSwitcherProps = {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import {
|
||||||
import { Room } from 'matrix-js-sdk';
|
import { Room } from 'matrix-js-sdk';
|
||||||
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 { stopPropagation } from '../../utils/keyboard';
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
import { useDirectUsers } from '../../hooks/useDirectUsers';
|
import { useDirectUsers } from '../../hooks/useDirectUsers';
|
||||||
import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../utils/matrix';
|
import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../utils/matrix';
|
||||||
|
|
@ -56,6 +57,7 @@ type InviteUserProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
};
|
};
|
||||||
export function InviteUserPrompt({ room, requestClose }: InviteUserProps) {
|
export function InviteUserPrompt({ room, requestClose }: InviteUserProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const alive = useAlive();
|
const alive = useAlive();
|
||||||
|
|
||||||
|
|
@ -169,7 +171,7 @@ export function InviteUserPrompt({ room, requestClose }: InviteUserProps) {
|
||||||
>
|
>
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
<Text size="H4" truncate>
|
<Text size="H4" truncate>
|
||||||
Invite
|
{t('Inbox.invite_title')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box shrink="No">
|
<Box shrink="No">
|
||||||
|
|
@ -187,14 +189,14 @@ export function InviteUserPrompt({ room, requestClose }: InviteUserProps) {
|
||||||
gap="400"
|
gap="400"
|
||||||
>
|
>
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">User ID</Text>
|
<Text size="L400">{t('Inbox.user_id')}</Text>
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
size="500"
|
size="500"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
placeholder="@username:server"
|
placeholder={t('Inbox.user_id_placeholder')}
|
||||||
name="userIdInput"
|
name="userIdInput"
|
||||||
variant="Background"
|
variant="Background"
|
||||||
disabled={inviting}
|
disabled={inviting}
|
||||||
|
|
@ -260,7 +262,7 @@ export function InviteUserPrompt({ room, requestClose }: InviteUserProps) {
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<Text size="L400">Reason (Optional)</Text>
|
<Text size="L400">{t('Inbox.reason_optional')}</Text>
|
||||||
<TextArea
|
<TextArea
|
||||||
size="500"
|
size="500"
|
||||||
name="reasonInput"
|
name="reasonInput"
|
||||||
|
|
@ -279,7 +281,7 @@ export function InviteUserPrompt({ room, requestClose }: InviteUserProps) {
|
||||||
disabled={!validUserId || inviting}
|
disabled={!validUserId || inviting}
|
||||||
before={inviting && <Spinner size="200" variant="Primary" fill="Solid" />}
|
before={inviting && <Spinner size="200" variant="Primary" fill="Solid" />}
|
||||||
>
|
>
|
||||||
<Text size="B400">Invite</Text>
|
<Text size="B400">{t('Inbox.invite_button')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Avatar, Box, Icon, Icons, Text } from 'folds';
|
import { Avatar, Box, Icon, Icons, Text } from 'folds';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { NavCategory, NavItem, NavItemContent, NavLink } from '../../../components/nav';
|
import { NavCategory, NavItem, NavItemContent, NavLink } from '../../../components/nav';
|
||||||
import { getInboxInvitesPath, getInboxNotificationsPath } from '../../pathUtils';
|
import { getInboxInvitesPath, getInboxNotificationsPath } from '../../pathUtils';
|
||||||
|
|
@ -13,6 +14,7 @@ import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMappe
|
||||||
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
|
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
|
||||||
|
|
||||||
function InvitesNavItem() {
|
function InvitesNavItem() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const invitesSelected = useInboxInvitesSelected();
|
const invitesSelected = useInboxInvitesSelected();
|
||||||
const allInvites = useAtomValue(allInvitesAtom);
|
const allInvites = useAtomValue(allInvitesAtom);
|
||||||
const inviteCount = allInvites.length;
|
const inviteCount = allInvites.length;
|
||||||
|
|
@ -32,7 +34,7 @@ function InvitesNavItem() {
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box as="span" grow="Yes">
|
<Box as="span" grow="Yes">
|
||||||
<Text as="span" size="Inherit" truncate>
|
<Text as="span" size="Inherit" truncate>
|
||||||
Invites
|
{t('Inbox.invites')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{inviteCount > 0 && <UnreadBadge highlight count={inviteCount} />}
|
{inviteCount > 0 && <UnreadBadge highlight count={inviteCount} />}
|
||||||
|
|
@ -44,6 +46,7 @@ function InvitesNavItem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Inbox() {
|
export function Inbox() {
|
||||||
|
const { t } = useTranslation();
|
||||||
useNavToActivePathMapper('inbox');
|
useNavToActivePathMapper('inbox');
|
||||||
const notificationsSelected = useInboxNotificationsSelected();
|
const notificationsSelected = useInboxNotificationsSelected();
|
||||||
|
|
||||||
|
|
@ -53,7 +56,7 @@ export function Inbox() {
|
||||||
<Box grow="Yes" gap="300">
|
<Box grow="Yes" gap="300">
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
<Text size="H4" truncate>
|
<Text size="H4" truncate>
|
||||||
Inbox
|
{t('Inbox.inbox')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -71,7 +74,7 @@ export function Inbox() {
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Box as="span" grow="Yes">
|
<Box as="span" grow="Yes">
|
||||||
<Text as="span" size="Inherit" truncate>
|
<Text as="span" size="Inherit" truncate>
|
||||||
Notifications
|
{t('Inbox.notifications')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
config,
|
config,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { RoomTopicEventContent } from 'matrix-js-sdk/lib/types';
|
import { RoomTopicEventContent } from 'matrix-js-sdk/lib/types';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
import { MatrixClient, MatrixError, Room } from 'matrix-js-sdk';
|
import { MatrixClient, MatrixError, Room } from 'matrix-js-sdk';
|
||||||
|
|
@ -159,6 +160,7 @@ function InviteCard({
|
||||||
onNavigate,
|
onNavigate,
|
||||||
hideAvatar,
|
hideAvatar,
|
||||||
}: InviteCardProps) {
|
}: InviteCardProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const userId = mx.getSafeUserId();
|
const userId = mx.getSafeUserId();
|
||||||
|
|
||||||
|
|
@ -200,21 +202,21 @@ function InviteCard({
|
||||||
{invite.isEncrypted && (
|
{invite.isEncrypted && (
|
||||||
<Box shrink="No" alignItems="Center" justifyContent="Center">
|
<Box shrink="No" alignItems="Center" justifyContent="Center">
|
||||||
<Badge variant="Success" fill="Solid" size="400" radii="300">
|
<Badge variant="Success" fill="Solid" size="400" radii="300">
|
||||||
<Text size="L400">Encrypted</Text>
|
<Text size="L400">{t('Inbox.encrypted')}</Text>
|
||||||
</Badge>
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{invite.isDirect && (
|
{invite.isDirect && (
|
||||||
<Box shrink="No" alignItems="Center" justifyContent="Center">
|
<Box shrink="No" alignItems="Center" justifyContent="Center">
|
||||||
<Badge variant="Primary" fill="Solid" size="400" radii="300">
|
<Badge variant="Primary" fill="Solid" size="400" radii="300">
|
||||||
<Text size="L400">Direct Message</Text>
|
<Text size="L400">{t('Inbox.direct_message')}</Text>
|
||||||
</Badge>
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{invite.isSpace && (
|
{invite.isSpace && (
|
||||||
<Box shrink="No" alignItems="Center" justifyContent="Center">
|
<Box shrink="No" alignItems="Center" justifyContent="Center">
|
||||||
<Badge variant="Secondary" fill="Soft" size="400" radii="300">
|
<Badge variant="Secondary" fill="Soft" size="400" radii="300">
|
||||||
<Text size="L400">Space</Text>
|
<Text size="L400">{t('Inbox.space')}</Text>
|
||||||
</Badge>
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
@ -290,7 +292,7 @@ function InviteCard({
|
||||||
disabled={joining || leaving}
|
disabled={joining || leaving}
|
||||||
before={leaving ? <Spinner variant="Secondary" size="100" /> : undefined}
|
before={leaving ? <Spinner variant="Secondary" size="100" /> : undefined}
|
||||||
>
|
>
|
||||||
<Text size="B300">Decline</Text>
|
<Text size="B300">{t('Inbox.decline')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={join}
|
onClick={join}
|
||||||
|
|
@ -302,7 +304,7 @@ function InviteCard({
|
||||||
disabled={joining || leaving}
|
disabled={joining || leaving}
|
||||||
before={joining ? <Spinner variant="Success" fill="Soft" size="100" /> : undefined}
|
before={joining ? <Spinner variant="Success" fill="Soft" size="100" /> : undefined}
|
||||||
>
|
>
|
||||||
<Text size="B300">Accept</Text>
|
<Text size="B300">{t('Inbox.accept')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -311,7 +313,7 @@ function InviteCard({
|
||||||
<Box gap="200" alignItems="Baseline">
|
<Box gap="200" alignItems="Baseline">
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
<Text size="T200" priority="300">
|
<Text size="T200" priority="300">
|
||||||
From: <b>{invite.senderId}</b>
|
{t('Inbox.from')}<b>{invite.senderId}</b>
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{typeof invite.inviteTs === 'number' && invite.inviteTs !== 0 && (
|
{typeof invite.inviteTs === 'number' && invite.inviteTs !== 0 && (
|
||||||
|
|
@ -328,7 +330,7 @@ function InviteCard({
|
||||||
</Box>
|
</Box>
|
||||||
{invite.reason && (
|
{invite.reason && (
|
||||||
<Text size="T200" priority="300">
|
<Text size="T200" priority="300">
|
||||||
Reason: {invite.reason}
|
{t('Inbox.reason_label')}{invite.reason}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -355,6 +357,7 @@ function InviteFilters({
|
||||||
unknownInvites,
|
unknownInvites,
|
||||||
spamInvites,
|
spamInvites,
|
||||||
}: InviteFiltersProps) {
|
}: InviteFiltersProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const isKnown = filter === InviteFilter.Known;
|
const isKnown = filter === InviteFilter.Known;
|
||||||
const isUnknown = filter === InviteFilter.Unknown;
|
const isUnknown = filter === InviteFilter.Unknown;
|
||||||
const isSpam = filter === InviteFilter.Spam;
|
const isSpam = filter === InviteFilter.Spam;
|
||||||
|
|
@ -375,7 +378,7 @@ function InviteFilters({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text size="T200">Primary</Text>
|
<Text size="T200">{t('Inbox.primary')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
<Chip
|
<Chip
|
||||||
variant={isUnknown ? 'Warning' : 'Surface'}
|
variant={isUnknown ? 'Warning' : 'Surface'}
|
||||||
|
|
@ -391,7 +394,7 @@ function InviteFilters({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text size="T200">Public</Text>
|
<Text size="T200">{t('Inbox.public')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
<Chip
|
<Chip
|
||||||
variant={isSpam ? 'Critical' : 'Surface'}
|
variant={isSpam ? 'Critical' : 'Surface'}
|
||||||
|
|
@ -407,7 +410,7 @@ function InviteFilters({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text size="T200">Spam</Text>
|
<Text size="T200">{t('Inbox.spam')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
@ -427,9 +430,10 @@ function KnownInvites({
|
||||||
hour24Clock,
|
hour24Clock,
|
||||||
dateFormatString,
|
dateFormatString,
|
||||||
}: KnownInvitesProps) {
|
}: KnownInvitesProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" gap="200">
|
<Box direction="Column" gap="200">
|
||||||
<Text size="H4">Primary</Text>
|
<Text size="H4">{t('Inbox.primary')}</Text>
|
||||||
{invites.length > 0 ? (
|
{invites.length > 0 ? (
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
{invites.map((invite) => (
|
{invites.map((invite) => (
|
||||||
|
|
@ -449,8 +453,8 @@ function KnownInvites({
|
||||||
<PageHeroSection>
|
<PageHeroSection>
|
||||||
<PageHero
|
<PageHero
|
||||||
icon={<Icon size="600" src={Icons.Mail} />}
|
icon={<Icon size="600" src={Icons.Mail} />}
|
||||||
title="No Invites"
|
title={t('Inbox.no_invites')}
|
||||||
subTitle="When someone you share a room with sends you an invite, it’ll show up here."
|
subTitle={t('Inbox.no_invites_known_desc')}
|
||||||
/>
|
/>
|
||||||
</PageHeroSection>
|
</PageHeroSection>
|
||||||
</PageHeroEmpty>
|
</PageHeroEmpty>
|
||||||
|
|
@ -473,6 +477,7 @@ function UnknownInvites({
|
||||||
hour24Clock,
|
hour24Clock,
|
||||||
dateFormatString,
|
dateFormatString,
|
||||||
}: UnknownInvitesProps) {
|
}: UnknownInvitesProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
|
||||||
const [declineAllStatus, declineAll] = useAsyncCallback(
|
const [declineAllStatus, declineAll] = useAsyncCallback(
|
||||||
|
|
@ -488,7 +493,7 @@ function UnknownInvites({
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" gap="200">
|
<Box direction="Column" gap="200">
|
||||||
<Box gap="200" justifyContent="SpaceBetween" alignItems="Center">
|
<Box gap="200" justifyContent="SpaceBetween" alignItems="Center">
|
||||||
<Text size="H4">Public</Text>
|
<Text size="H4">{t('Inbox.public')}</Text>
|
||||||
<Box>
|
<Box>
|
||||||
{invites.length > 0 && (
|
{invites.length > 0 && (
|
||||||
<Chip
|
<Chip
|
||||||
|
|
@ -498,7 +503,7 @@ function UnknownInvites({
|
||||||
disabled={declining}
|
disabled={declining}
|
||||||
radii="Pill"
|
radii="Pill"
|
||||||
>
|
>
|
||||||
<Text size="T200">Decline All</Text>
|
<Text size="T200">{t('Inbox.decline_all')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -522,8 +527,8 @@ function UnknownInvites({
|
||||||
<PageHeroSection>
|
<PageHeroSection>
|
||||||
<PageHero
|
<PageHero
|
||||||
icon={<Icon size="600" src={Icons.Info} />}
|
icon={<Icon size="600" src={Icons.Info} />}
|
||||||
title="No Invites"
|
title={t('Inbox.no_invites')}
|
||||||
subTitle="Invites from people outside your rooms will appear here."
|
subTitle={t('Inbox.no_invites_unknown_desc')}
|
||||||
/>
|
/>
|
||||||
</PageHeroSection>
|
</PageHeroSection>
|
||||||
</PageHeroEmpty>
|
</PageHeroEmpty>
|
||||||
|
|
@ -546,6 +551,7 @@ function SpamInvites({
|
||||||
hour24Clock,
|
hour24Clock,
|
||||||
dateFormatString,
|
dateFormatString,
|
||||||
}: SpamInvitesProps) {
|
}: SpamInvitesProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [showInvites, setShowInvites] = useState(false);
|
const [showInvites, setShowInvites] = useState(false);
|
||||||
|
|
||||||
|
|
@ -585,7 +591,7 @@ function SpamInvites({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" gap="200">
|
<Box direction="Column" gap="200">
|
||||||
<Text size="H4">Spam</Text>
|
<Text size="H4">{t('Inbox.spam')}</Text>
|
||||||
{invites.length > 0 ? (
|
{invites.length > 0 ? (
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<SequenceCard
|
<SequenceCard
|
||||||
|
|
@ -597,8 +603,8 @@ function SpamInvites({
|
||||||
<PageHeroSection>
|
<PageHeroSection>
|
||||||
<PageHero
|
<PageHero
|
||||||
icon={<Icon size="600" src={Icons.Warning} />}
|
icon={<Icon size="600" src={Icons.Warning} />}
|
||||||
title={`${invites.length} Spam Invites`}
|
title={t('Inbox.spam_invites_count', { count: invites.length })}
|
||||||
subTitle="Some of the following invites may contain harmful content or have been sent by banned users."
|
subTitle={t('Inbox.spam_invites_desc')}
|
||||||
>
|
>
|
||||||
<Box direction="Row" gap="200" justifyContent="Center" wrap="Wrap">
|
<Box direction="Row" gap="200" justifyContent="Center" wrap="Wrap">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -611,7 +617,7 @@ function SpamInvites({
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
Decline All
|
{t('Inbox.decline_all')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
{reportRoomSupported && reportAllStatus.status !== AsyncStatus.Success && (
|
{reportRoomSupported && reportAllStatus.status !== AsyncStatus.Success && (
|
||||||
|
|
@ -625,7 +631,7 @@ function SpamInvites({
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
Report All
|
{t('Inbox.report_all')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
@ -640,7 +646,7 @@ function SpamInvites({
|
||||||
before={blocking && <Spinner size="100" variant="Secondary" fill="Solid" />}
|
before={blocking && <Spinner size="100" variant="Secondary" fill="Solid" />}
|
||||||
>
|
>
|
||||||
<Text size="B300" truncate>
|
<Text size="B300" truncate>
|
||||||
Block All
|
{t('Inbox.block_all')}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
@ -658,7 +664,7 @@ function SpamInvites({
|
||||||
}
|
}
|
||||||
onClick={() => setShowInvites(!showInvites)}
|
onClick={() => setShowInvites(!showInvites)}
|
||||||
>
|
>
|
||||||
<Text size="B300">{showInvites ? 'Hide All' : 'View All'}</Text>
|
<Text size="B300">{showInvites ? t('Inbox.hide_all') : t('Inbox.view_all')}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</PageHero>
|
</PageHero>
|
||||||
</PageHeroSection>
|
</PageHeroSection>
|
||||||
|
|
@ -681,8 +687,8 @@ function SpamInvites({
|
||||||
<PageHeroSection>
|
<PageHeroSection>
|
||||||
<PageHero
|
<PageHero
|
||||||
icon={<Icon size="600" src={Icons.Warning} />}
|
icon={<Icon size="600" src={Icons.Warning} />}
|
||||||
title="No Spam Invites"
|
title={t('Inbox.no_spam_invites')}
|
||||||
subTitle="Invites detected as spam appear here."
|
subTitle={t('Inbox.no_spam_invites_desc')}
|
||||||
/>
|
/>
|
||||||
</PageHeroSection>
|
</PageHeroSection>
|
||||||
</PageHeroEmpty>
|
</PageHeroEmpty>
|
||||||
|
|
@ -692,6 +698,7 @@ function SpamInvites({
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Invites() {
|
export function Invites() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const useAuthentication = useMediaAuthentication();
|
const useAuthentication = useMediaAuthentication();
|
||||||
const { navigateRoom, navigateSpace } = useRoomNavigate();
|
const { navigateRoom, navigateSpace } = useRoomNavigate();
|
||||||
|
|
@ -763,7 +770,7 @@ export function Invites() {
|
||||||
<Box alignItems="Center" gap="200">
|
<Box alignItems="Center" gap="200">
|
||||||
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Mail} />}
|
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Mail} />}
|
||||||
<Text size="H3" truncate>
|
<Text size="H3" truncate>
|
||||||
Invites
|
{t('Inbox.invites')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box grow="Yes" basis="No" />
|
<Box grow="Yes" basis="No" />
|
||||||
|
|
@ -776,7 +783,7 @@ export function Invites() {
|
||||||
<Box ref={containerRef} direction="Column" gap="600">
|
<Box ref={containerRef} direction="Column" gap="600">
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<span data-spacing-node />
|
<span data-spacing-node />
|
||||||
<Text size="L400">Filter</Text>
|
<Text size="L400">{t('Inbox.filter')}</Text>
|
||||||
<InviteFilters
|
<InviteFilters
|
||||||
filter={filter}
|
filter={filter}
|
||||||
onFilter={setFilter}
|
onFilter={setFilter}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import {
|
||||||
toRem,
|
toRem,
|
||||||
} from 'folds';
|
} from 'folds';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
INotification,
|
INotification,
|
||||||
INotificationsResponse,
|
INotificationsResponse,
|
||||||
|
|
@ -222,6 +223,7 @@ function RoomNotificationsGroupComp({
|
||||||
hour24Clock,
|
hour24Clock,
|
||||||
dateFormatString,
|
dateFormatString,
|
||||||
}: RoomNotificationsGroupProps) {
|
}: RoomNotificationsGroupProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const useAuthentication = useMediaAuthentication();
|
const useAuthentication = useMediaAuthentication();
|
||||||
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
||||||
|
|
@ -438,7 +440,7 @@ function RoomNotificationsGroupComp({
|
||||||
onClick={handleMarkAsRead}
|
onClick={handleMarkAsRead}
|
||||||
before={<Icon size="100" src={Icons.CheckTwice} />}
|
before={<Icon size="100" src={Icons.CheckTwice} />}
|
||||||
>
|
>
|
||||||
<Text size="T200">Mark as Read</Text>
|
<Text size="T200">{t('Inbox.mark_as_read')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -524,7 +526,7 @@ function RoomNotificationsGroupComp({
|
||||||
variant="Secondary"
|
variant="Secondary"
|
||||||
radii="400"
|
radii="400"
|
||||||
>
|
>
|
||||||
<Text size="T200">Open</Text>
|
<Text size="T200">{t('Inbox.open')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -562,6 +564,7 @@ const useNotificationsSearchParams = (
|
||||||
const DEFAULT_REFRESH_MS = 7000;
|
const DEFAULT_REFRESH_MS = 7000;
|
||||||
|
|
||||||
export function Notifications() {
|
export function Notifications() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
|
const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
|
||||||
const [mediaAutoLoad] = useSetting(settingsAtom, 'mediaAutoLoad');
|
const [mediaAutoLoad] = useSetting(settingsAtom, 'mediaAutoLoad');
|
||||||
|
|
@ -652,7 +655,7 @@ export function Notifications() {
|
||||||
<Box alignItems="Center" gap="200">
|
<Box alignItems="Center" gap="200">
|
||||||
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Message} />}
|
{screenSize !== ScreenSize.Mobile && <Icon size="400" src={Icons.Message} />}
|
||||||
<Text size="H3" truncate>
|
<Text size="H3" truncate>
|
||||||
Notification Messages
|
{t('Inbox.notification_messages')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box grow="Yes" basis="No" />
|
<Box grow="Yes" basis="No" />
|
||||||
|
|
@ -666,7 +669,7 @@ export function Notifications() {
|
||||||
<Box direction="Column" gap="200">
|
<Box direction="Column" gap="200">
|
||||||
<Box ref={scrollTopAnchorRef} direction="Column" gap="100">
|
<Box ref={scrollTopAnchorRef} direction="Column" gap="100">
|
||||||
<span data-spacing-node />
|
<span data-spacing-node />
|
||||||
<Text size="L400">Filter</Text>
|
<Text size="L400">{t('Inbox.filter')}</Text>
|
||||||
<Box gap="200">
|
<Box gap="200">
|
||||||
<Chip
|
<Chip
|
||||||
onClick={() => setOnlyHighlighted(false)}
|
onClick={() => setOnlyHighlighted(false)}
|
||||||
|
|
@ -675,7 +678,7 @@ export function Notifications() {
|
||||||
before={!onlyHighlight && <Icon size="100" src={Icons.Check} />}
|
before={!onlyHighlight && <Icon size="100" src={Icons.Check} />}
|
||||||
outlined
|
outlined
|
||||||
>
|
>
|
||||||
<Text size="T200">All Notifications</Text>
|
<Text size="T200">{t('Inbox.all_notifications')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
<Chip
|
<Chip
|
||||||
onClick={() => setOnlyHighlighted(true)}
|
onClick={() => setOnlyHighlighted(true)}
|
||||||
|
|
@ -684,7 +687,7 @@ export function Notifications() {
|
||||||
before={onlyHighlight && <Icon size="100" src={Icons.Check} />}
|
before={onlyHighlight && <Icon size="100" src={Icons.Check} />}
|
||||||
outlined
|
outlined
|
||||||
>
|
>
|
||||||
<Text size="T200">Highlighted</Text>
|
<Text size="T200">{t('Inbox.highlighted')}</Text>
|
||||||
</Chip>
|
</Chip>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -699,7 +702,7 @@ export function Notifications() {
|
||||||
radii="Pill"
|
radii="Pill"
|
||||||
outlined
|
outlined
|
||||||
size="300"
|
size="300"
|
||||||
aria-label="Scroll to Top"
|
aria-label={t('Inbox.scroll_to_top')}
|
||||||
>
|
>
|
||||||
<Icon src={Icons.ChevronTop} size="300" />
|
<Icon src={Icons.ChevronTop} size="300" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
@ -752,9 +755,9 @@ export function Notifications() {
|
||||||
direction="Column"
|
direction="Column"
|
||||||
gap="200"
|
gap="200"
|
||||||
>
|
>
|
||||||
<Text>No Notifications</Text>
|
<Text>{t('Inbox.no_notifications')}</Text>
|
||||||
<Text size="T200">
|
<Text size="T200">
|
||||||
You don't have any new notifications to display yet.
|
{t('Inbox.no_notifications_desc')}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Icon, Icons } from 'folds';
|
import { Icon, Icons } from 'folds';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import {
|
import {
|
||||||
SidebarAvatar,
|
SidebarAvatar,
|
||||||
|
|
@ -21,6 +22,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
|
||||||
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
|
||||||
|
|
||||||
export function InboxTab() {
|
export function InboxTab() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const screenSize = useScreenSizeContext();
|
const screenSize = useScreenSizeContext();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const navToActivePath = useAtomValue(useNavToActivePathAtom());
|
const navToActivePath = useAtomValue(useNavToActivePathAtom());
|
||||||
|
|
@ -45,7 +47,7 @@ export function InboxTab() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarItem active={inboxSelected}>
|
<SidebarItem active={inboxSelected}>
|
||||||
<SidebarItemTooltip tooltip="Inbox">
|
<SidebarItemTooltip tooltip={t('Inbox.inbox')}>
|
||||||
{(triggerRef) => (
|
{(triggerRef) => (
|
||||||
<SidebarAvatar as="button" ref={triggerRef} outlined onClick={handleInboxClick}>
|
<SidebarAvatar as="button" ref={triggerRef} outlined onClick={handleInboxClick}>
|
||||||
<Icon src={Icons.Inbox} filled={inboxSelected} />
|
<Icon src={Icons.Inbox} filled={inboxSelected} />
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Badge, color, Icon, Icons, Text } from 'folds';
|
import { Badge, color, Icon, Icons, Text } from 'folds';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
SidebarAvatar,
|
SidebarAvatar,
|
||||||
SidebarItem,
|
SidebarItem,
|
||||||
|
|
@ -19,6 +20,7 @@ import { Modal500 } from '../../../components/Modal500';
|
||||||
import { Settings, SettingsPages } from '../../../features/settings';
|
import { Settings, SettingsPages } from '../../../features/settings';
|
||||||
|
|
||||||
function UnverifiedIndicator() {
|
function UnverifiedIndicator() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
|
||||||
const crypto = mx.getCrypto();
|
const crypto = mx.getCrypto();
|
||||||
|
|
@ -49,7 +51,7 @@ function UnverifiedIndicator() {
|
||||||
<>
|
<>
|
||||||
{hasUnverified && (
|
{hasUnverified && (
|
||||||
<SidebarItem active={settings} className={css.UnverifiedTab}>
|
<SidebarItem active={settings} className={css.UnverifiedTab}>
|
||||||
<SidebarItemTooltip tooltip={unverified ? 'Unverified Device' : 'Unverified Devices'}>
|
<SidebarItemTooltip tooltip={unverified ? t('Inbox.unverified_device') : t('Inbox.unverified_devices')}>
|
||||||
{(triggerRef) => (
|
{(triggerRef) => (
|
||||||
<SidebarAvatar
|
<SidebarAvatar
|
||||||
className={unverified ? css.UnverifiedAvatar : css.UnverifiedOtherAvatar}
|
className={unverified ? css.UnverifiedAvatar : css.UnverifiedOtherAvatar}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue