client: migrate to lingui.js

This commit is contained in:
Jesse Chan
2021-01-29 11:53:13 +08:00
parent 6bc71c5e26
commit c4bf66fbf6
142 changed files with 5469 additions and 82242 deletions

View File

@@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
node: [15]
check: [check-compiled-i18n, check-source-formatting, check-types, lint]
check: [check-source-formatting, check-types, lint]
steps:
- uses: actions/checkout@v2

51
.linguirc Normal file
View File

@@ -0,0 +1,51 @@
{
"locales": [
"af",
"ar",
"ca",
"cs",
"da",
"de",
"el",
"en",
"es",
"fi",
"fr",
"he",
"hu",
"it",
"ja",
"ko",
"nl",
"no",
"pl",
"pt",
"ro",
"ru",
"sr",
"sv",
"tr",
"uk",
"vi",
"zh-Hans",
"zh-Hant"
],
"sourceLocale": "en",
"fallbackLocales": {
"zh-Hans": "zh-Hant",
"zh-Hant": "zh-Hans",
"default": "en"
},
"catalogs": [
{
"path": "client/src/javascript/i18n/strings/{locale}",
"include": [
"<rootDir>/client/src/javascript"
],
"exclude": [
"**/node_modules/**"
]
}
],
"format": "minimal"
}

View File

@@ -111,7 +111,6 @@ module.exports = {
resolve: {
extensions: ['.cjs', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
alias: {
'react-intl': 'react-intl/react-intl-no-parser.umd.min.js',
'@client': path.resolve('./client/src/javascript'),
'@shared': path.resolve('./shared'),
},

View File

@@ -11,7 +11,6 @@ import AppWrapper from './components/AppWrapper';
import LoadingOverlay from './components/general/LoadingOverlay';
import AsyncIntlProvider from './i18n/languages';
import ConfigStore from './stores/ConfigStore';
import SettingStore from './stores/SettingStore';
import UIStore from './stores/UIStore';
import history from './util/history';
@@ -76,7 +75,7 @@ const FloodApp: FC = observer(() => {
return (
<Suspense fallback={<LoadingOverlay />}>
<AsyncIntlProvider language={SettingStore.floodSettings.language}>
<AsyncIntlProvider>
<Router history={history}>
<QueryParamProvider ReactRouterRoute={Route}>
<AppWrapper className={ConfigStore.preferDark ? 'dark' : undefined}>

View File

@@ -1,7 +1,7 @@
import {FC} from 'react';
import {FormattedMessage} from 'react-intl';
import classnames from 'classnames';
import {observer} from 'mobx-react';
import {Trans} from '@lingui/react';
import AlertStore from '@client/stores/AlertStore';
import {CircleCheckmark, CircleExclamation} from '@client/ui/icons';
@@ -32,7 +32,7 @@ const Alert: FC<AlertProps> = observer((props: AlertProps) => {
<li className={alertClasses}>
{icon}
<span className="alert__content">
<FormattedMessage
<Trans
id={id}
values={{
count,

View File

@@ -1,5 +1,5 @@
import {FC, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Button, Form, FormError, FormRow, Panel, PanelContent, PanelHeader, PanelFooter, Textbox} from '@client/ui';
import AuthActions from '@client/actions/AuthActions';
@@ -20,7 +20,7 @@ interface AuthFormProps {
}
const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const formRef = useRef<Form>(null);
const clientConnectionSettingsRef = useRef<ClientConnectionSettings | null>(null);
const [errorMessage, setErrorMessage] = useState<string | {id: string} | undefined>(undefined);
@@ -102,30 +102,16 @@ const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
}}
ref={formRef}>
<PanelHeader>
<h1>
{isLoginMode
? intl.formatMessage({
id: 'auth.login',
})
: intl.formatMessage({
id: 'auth.create.an.account',
})}
</h1>
<h1>{isLoginMode ? i18n._('auth.login') : i18n._('auth.create.an.account')}</h1>
</PanelHeader>
<PanelContent>
<p className="copy--lead">
{isLoginMode
? intl.formatMessage({
id: 'auth.login.intro',
})
: intl.formatMessage({
id: 'auth.create.an.account.intro',
})}
{isLoginMode ? i18n._('auth.login.intro') : i18n._('auth.create.an.account.intro')}
</p>
{errorMessage != null ? (
<FormRow>
<FormError isLoading={isSubmitting}>
{typeof errorMessage === 'string' ? errorMessage : intl.formatMessage(errorMessage)}
{typeof errorMessage === 'string' ? errorMessage : i18n._(errorMessage)}
</FormError>
</FormRow>
) : null}
@@ -159,18 +145,10 @@ const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
formRef.current.resetForm();
}
}}>
{intl.formatMessage({
id: 'auth.input.clear',
})}
{i18n._('auth.input.clear')}
</Button>
<Button isLoading={isSubmitting} type="submit">
{isLoginMode
? intl.formatMessage({
id: 'auth.log.in',
})
: intl.formatMessage({
id: 'auth.create.account',
})}
{isLoginMode ? i18n._('auth.log.in') : i18n._('auth.create.account')}
</Button>
</FormRow>
</PanelFooter>

View File

@@ -1,6 +1,6 @@
import {FC, ReactText, useRef, useState} from 'react';
import {FormattedMessage} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans} from '@lingui/react';
import {
Button,
@@ -74,14 +74,14 @@ const ClientConnectionInterruption: FC = observer(() => {
}}>
<PanelHeader>
<h1>
<FormattedMessage id="connection-interruption.heading" />
<Trans id="connection-interruption.heading" />
</h1>
</PanelHeader>
<PanelContent>
{error && (
<FormRow>
<FormError>
<FormattedMessage id={error} />
<Trans id={error} />
</FormError>
</FormRow>
)}
@@ -89,16 +89,16 @@ const ClientConnectionInterruption: FC = observer(() => {
<FormRow>
<Select id="action" onSelect={setSelection} defaultID="retry">
<SelectItem key="retry" id="retry">
<FormattedMessage id="connection-interruption.action.selection.retry" />
<Trans id="connection-interruption.action.selection.retry" />
</SelectItem>
<SelectItem key="config" id="config">
<FormattedMessage id="connection-interruption.action.selection.config" />
<Trans id="connection-interruption.action.selection.config" />
</SelectItem>
</Select>
</FormRow>
) : (
<p className="copy--lead">
<FormattedMessage id="connection-interruption.not.admin" />
<Trans id="connection-interruption.not.admin" />
</p>
)}
{selection === 'config' && (
@@ -113,12 +113,12 @@ const ClientConnectionInterruption: FC = observer(() => {
<FormRow justify="end">
{selection === 'retry' && (
<Button type="submit" isLoading={isSubmitting}>
<FormattedMessage id="button.retry" />
<Trans id="button.retry" />
</Button>
)}
{selection === 'config' && (
<Button type="submit" isLoading={isSubmitting}>
<FormattedMessage id="button.save" />
<Trans id="button.save" />
</Button>
)}
</FormRow>

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {FC} from 'react';
import {observer} from 'mobx-react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {useKeyPressEvent} from 'react-use';
import {ContextMenu} from '@client/ui';
@@ -22,7 +22,7 @@ const ContextMenuMountPoint: FC<ContextMenuMountPointProps> = observer(({id}: Co
y: 0,
};
const intl = useIntl();
const {i18n} = useLingui();
useKeyPressEvent('Escape', () => UIActions.dismissContextMenu(id));
@@ -51,7 +51,7 @@ const ContextMenuMountPoint: FC<ContextMenuMountPointProps> = observer(({id}: Co
className={classnames('menu__item__label--primary', {
'has-action': item.labelAction,
})}>
<span className="menu__item__label">{intl.formatMessage({id: item.label})}</span>
<span className="menu__item__label">{i18n._(item.label)}</span>
{item.labelAction ? (
<span className="menu__item__label__action">
<item.labelAction />

View File

@@ -1,5 +1,5 @@
import {FC, ReactNode} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
const secondsToDuration = (
cumSeconds: number,
@@ -59,19 +59,19 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
const duration = value === -1 ? -1 : secondsToDuration(value);
if (duration === -1) {
content = <FormattedMessage id="unit.time.infinity" />;
content = <Trans id="unit.time.infinity" />;
} else if (duration.years != null && duration.years > 0) {
content = [
<span className="duration--segment" key="years">
{duration.years}
<em className="unit">
<FormattedMessage id="unit.time.year" />
<Trans id="unit.time.year" />
</em>
</span>,
<span className="duration--segment" key="weeks">
{duration.weeks}
<em className="unit">
<FormattedMessage id="unit.time.week" />
<Trans id="unit.time.week" />
</em>
</span>,
];
@@ -80,13 +80,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
<span className="duration--segment" key="weeks">
{duration.weeks}
<em className="unit">
<FormattedMessage id="unit.time.week" />
<Trans id="unit.time.week" />
</em>
</span>,
<span className="duration--segment" key="days">
{duration.days}
<em className="unit">
<FormattedMessage id="unit.time.day" />
<Trans id="unit.time.day" />
</em>
</span>,
];
@@ -95,13 +95,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
<span className="duration--segment" key="days">
{duration.days}
<em className="unit">
<FormattedMessage id="unit.time.day" />
<Trans id="unit.time.day" />
</em>
</span>,
<span className="duration--segment" key="hours">
{duration.hours}
<em className="unit">
<FormattedMessage id="unit.time.hour" />
<Trans id="unit.time.hour" />
</em>
</span>,
];
@@ -110,13 +110,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
<span className="duration--segment" key="hours">
{duration.hours}
<em className="unit">
<FormattedMessage id="unit.time.hour" />
<Trans id="unit.time.hour" />
</em>
</span>,
<span className="duration--segment" key="minutes">
{duration.minutes}
<em className="unit">
<FormattedMessage id="unit.time.minute" />
<Trans id="unit.time.minute" />
</em>
</span>,
];
@@ -125,13 +125,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
<span className="duration--segment" key="minutes">
{duration.minutes}
<em className="unit">
<FormattedMessage id="unit.time.minute" />
<Trans id="unit.time.minute" />
</em>
</span>,
<span className="duration--segment" key="seconds">
{duration.seconds}
<em className="unit">
<FormattedMessage id="unit.time.second" />
<Trans id="unit.time.second" />
</em>
</span>,
];
@@ -140,7 +140,7 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
<span className="duration--segment">
{duration.seconds}
<em className="unit">
<FormattedMessage id="unit.time.second" />
<Trans id="unit.time.second" />
</em>
</span>
);

View File

@@ -1,6 +1,6 @@
import classnames from 'classnames';
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {CheckmarkThick} from '@client/ui/icons';
@@ -13,7 +13,7 @@ const ICONS = {
};
const LoadingDependencyList: FC<{dependencies: Dependencies}> = ({dependencies}: {dependencies: Dependencies}) => {
const intl = useIntl();
const {i18n} = useLingui();
return (
<ul className="dependency-list">
@@ -28,7 +28,7 @@ const LoadingDependencyList: FC<{dependencies: Dependencies}> = ({dependencies}:
<li className={classes} key={id}>
{satisfied != null ? <span className="dependency-list__dependency__icon">{statusIcon}</span> : null}
<span className="dependency-list__dependency__message">
{typeof message === 'string' ? message : intl.formatMessage(message)}
{typeof message === 'string' ? message : i18n._(message)}
</span>
</li>
);

View File

@@ -1,5 +1,5 @@
import {FC, ReactNode, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import PriorityLevels from '../../constants/PriorityLevels';
@@ -24,7 +24,7 @@ const PriorityMeter: FC<PriorityMeterProps> = ({
changePriorityFuncRef,
onChange,
}: PriorityMeterProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const [priorityLevel, setPriorityLevel] = useState<number>(level);
const changePriority = () => {
@@ -54,24 +54,16 @@ const PriorityMeter: FC<PriorityMeterProps> = ({
let priorityLevelElement: ReactNode;
switch (priorityLevels[priorityLevel as keyof typeof priorityLevels]) {
case 'DONT_DOWNLOAD':
priorityLevelElement = intl.formatMessage({
id: 'priority.dont.download',
});
priorityLevelElement = i18n._('priority.dont.download');
break;
case 'HIGH':
priorityLevelElement = intl.formatMessage({
id: 'priority.high',
});
priorityLevelElement = i18n._('priority.high');
break;
case 'LOW':
priorityLevelElement = intl.formatMessage({
id: 'priority.low',
});
priorityLevelElement = i18n._('priority.low');
break;
default:
priorityLevelElement = intl.formatMessage({
id: 'priority.normal',
});
priorityLevelElement = i18n._('priority.normal');
break;
}

View File

@@ -1,16 +1,8 @@
import {FC} from 'react';
import {FormattedNumber, useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {compute, getTranslationString} from '../../util/size';
const renderNumber = (computedNumber: ReturnType<typeof compute>) => {
if (Number.isNaN(computedNumber.value)) {
return '—';
}
return <FormattedNumber value={computedNumber.value} />;
};
interface SizeProps {
value: number;
precision?: number;
@@ -20,26 +12,19 @@ interface SizeProps {
const Size: FC<SizeProps> = ({value, isSpeed, className, precision}: SizeProps) => {
const computed = compute(value, precision);
const intl = useIntl();
const {i18n} = useLingui();
let translatedUnit = intl.formatMessage({
id: getTranslationString(computed.unit),
});
let translatedUnit = i18n._(getTranslationString(computed.unit));
if (isSpeed) {
translatedUnit = intl.formatMessage(
{
id: 'unit.speed',
},
{
baseUnit: translatedUnit,
},
);
translatedUnit = i18n._('unit.speed', {
baseUnit: translatedUnit,
});
}
return (
<span className={className}>
{renderNumber(computed)}
{Number.isNaN(computed.value) ? '—' : i18n.number(computed.value)}
<em className="unit">{translatedUnit}</em>
</span>
);

View File

@@ -1,13 +1,13 @@
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import {useLingui} from '@lingui/react';
import {compute, getTranslationString} from '../../util/size';
import TransferDataStore from '../../stores/TransferDataStore';
const WindowTitle: FC = observer(() => {
const {transferSummary: summary} = TransferDataStore;
const intl = useIntl();
const {i18n} = useLingui();
let title = 'Flood';
@@ -15,25 +15,15 @@ const WindowTitle: FC = observer(() => {
const down = compute(summary.downRate);
const up = compute(summary.upRate);
const formattedDownSpeed = intl.formatNumber(down.value);
const formattedUpSpeed = intl.formatNumber(up.value);
const formattedDownSpeed = i18n.number(down.value);
const formattedUpSpeed = i18n.number(up.value);
const translatedDownUnit = intl.formatMessage(
{
id: 'unit.speed',
},
{
baseUnit: intl.formatMessage({id: getTranslationString(down.unit)}),
},
);
const translatedUpUnit = intl.formatMessage(
{
id: 'unit.speed',
},
{
baseUnit: intl.formatMessage({id: getTranslationString(up.unit)}),
},
);
const translatedDownUnit = i18n._('unit.speed', {
baseUnit: i18n._(getTranslationString(down.unit)),
});
const translatedUpUnit = i18n._('unit.speed', {
baseUnit: i18n._(getTranslationString(up.unit)),
});
title = `${formattedDownSpeed} ${translatedDownUnit}${formattedUpSpeed} ${translatedUpUnit} - Flood`;
}

View File

@@ -1,5 +1,5 @@
import {FC, ReactNode, useEffect, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {FormRow, Select, SelectItem} from '@client/ui';
@@ -20,7 +20,7 @@ interface ClientConnectionSettingsFormProps {
const ClientConnectionSettingsForm: FC<ClientConnectionSettingsFormProps> = ({
onSettingsChange,
}: ClientConnectionSettingsFormProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const [selectedClient, setSelectedClient] = useState<ClientConnectionSettings['client']>(DEFAULT_SELECTION);
useEffect(() => {
@@ -47,16 +47,14 @@ const ClientConnectionSettingsForm: FC<ClientConnectionSettingsFormProps> = ({
<FormRow>
<Select
id="client"
label={intl.formatMessage({
id: 'connection.settings.client.select',
})}
label={i18n._('connection.settings.client.select')}
onSelect={(newSelectedClient) => {
setSelectedClient(newSelectedClient as ClientConnectionSettings['client']);
}}
defaultID={DEFAULT_SELECTION}>
{SUPPORTED_CLIENTS.map((client) => (
<SelectItem key={client} id={client}>
<FormattedMessage id={`connection.settings.${client.toLowerCase()}`} />
<Trans id={`connection.settings.${client.toLowerCase()}`} />
</SelectItem>
))}
</Select>

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {FormGroup, FormRow, Textbox} from '@client/ui';
@@ -12,7 +12,7 @@ export interface QBittorrentConnectionSettingsProps {
const QBittorrentConnectionSettingsForm: FC<QBittorrentConnectionSettingsProps> = ({
onSettingsChange,
}: QBittorrentConnectionSettingsProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const [settings, setSettings] = useState<QBittorrentConnectionSettings>({
client: 'qBittorrent',
type: 'web',
@@ -44,29 +44,23 @@ const QBittorrentConnectionSettingsForm: FC<QBittorrentConnectionSettingsProps>
<Textbox
onChange={(e) => handleFormChange('url', e.target.value)}
id="url"
label={<FormattedMessage id="connection.settings.qbittorrent.url" />}
placeholder={intl.formatMessage({
id: 'connection.settings.qbittorrent.url.input.placeholder',
})}
label={<Trans id="connection.settings.qbittorrent.url" />}
placeholder={i18n._('connection.settings.qbittorrent.url.input.placeholder')}
/>
</FormRow>
<FormRow>
<Textbox
onChange={(e) => handleFormChange('username', e.target.value)}
id="qbt-username"
label={<FormattedMessage id="connection.settings.qbittorrent.username" />}
placeholder={intl.formatMessage({
id: 'connection.settings.qbittorrent.username.input.placeholder',
})}
label={<Trans id="connection.settings.qbittorrent.username" />}
placeholder={i18n._('connection.settings.qbittorrent.username.input.placeholder')}
autoComplete="off"
/>
<Textbox
onChange={(e) => handleFormChange('password', e.target.value)}
id="qbt-password"
label={<FormattedMessage id="connection.settings.qbittorrent.password" />}
placeholder={intl.formatMessage({
id: 'connection.settings.qbittorrent.password.input.placeholder',
})}
label={<Trans id="connection.settings.qbittorrent.password" />}
placeholder={i18n._('connection.settings.qbittorrent.password.input.placeholder')}
autoComplete="off"
type="password"
/>

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {FormError, FormGroup, FormRow, FormRowGroup, Radio, Textbox} from '@client/ui';
@@ -12,7 +12,7 @@ export interface RTorrentConnectionSettingsProps {
const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
onSettingsChange,
}: RTorrentConnectionSettingsProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const [type, setType] = useState<'tcp' | 'socket'>('socket');
const [settings, setSettings] = useState<RTorrentConnectionSettings | null>(null);
@@ -47,10 +47,7 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
<FormRow>
<FormGroup>
<FormRow>
<FormGroup
label={intl.formatMessage({
id: 'connection.settings.rtorrent.type',
})}>
<FormGroup label={i18n._('connection.settings.rtorrent.type')}>
<FormRow>
<Radio
onClick={() => {
@@ -60,7 +57,7 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
id="socket"
grow={false}
defaultChecked={type === 'socket'}>
<FormattedMessage id="connection.settings.rtorrent.type.socket" />
<Trans id="connection.settings.rtorrent.type.socket" />
</Radio>
<Radio
onClick={() => {
@@ -70,7 +67,7 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
id="tcp"
grow={false}
defaultChecked={type === 'tcp'}>
<FormattedMessage id="connection.settings.rtorrent.type.tcp" />
<Trans id="connection.settings.rtorrent.type.tcp" />
</Radio>
</FormRow>
</FormGroup>
@@ -78,28 +75,20 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
{type === 'tcp' ? (
<FormRowGroup>
<FormRow>
<FormError>
{intl.formatMessage({
id: 'connection.settings.rtorrent.type.tcp.warning',
})}
</FormError>
<FormError>{i18n._('connection.settings.rtorrent.type.tcp.warning')}</FormError>
</FormRow>
<FormRow>
<Textbox
onChange={(e) => handleFormChange('host', e.target.value)}
id="host"
label={<FormattedMessage id="connection.settings.rtorrent.host" />}
placeholder={intl.formatMessage({
id: 'connection.settings.rtorrent.host.input.placeholder',
})}
label={<Trans id="connection.settings.rtorrent.host" />}
placeholder={i18n._('connection.settings.rtorrent.host.input.placeholder')}
/>
<Textbox
onChange={(e) => handleFormChange('port', Number(e.target.value))}
id="port"
label={<FormattedMessage id="connection.settings.rtorrent.port" />}
placeholder={intl.formatMessage({
id: 'connection.settings.rtorrent.port.input.placeholder',
})}
label={<Trans id="connection.settings.rtorrent.port" />}
placeholder={i18n._('connection.settings.rtorrent.port.input.placeholder')}
/>
</FormRow>
</FormRowGroup>
@@ -108,10 +97,8 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
<Textbox
onChange={(e) => handleFormChange('socket', e.target.value)}
id="socket"
label={<FormattedMessage id="connection.settings.rtorrent.socket" />}
placeholder={intl.formatMessage({
id: 'connection.settings.rtorrent.socket.input.placeholder',
})}
label={<Trans id="connection.settings.rtorrent.socket" />}
placeholder={i18n._('connection.settings.rtorrent.socket.input.placeholder')}
/>
</FormRow>
)}

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {FormGroup, FormRow, Textbox} from '@client/ui';
@@ -12,7 +12,7 @@ export interface TransmissionConnectionSettingsProps {
const TransmissionConnectionSettingsForm: FC<TransmissionConnectionSettingsProps> = ({
onSettingsChange,
}: TransmissionConnectionSettingsProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const [settings, setSettings] = useState<TransmissionConnectionSettings>({
client: 'Transmission',
type: 'rpc',
@@ -44,29 +44,23 @@ const TransmissionConnectionSettingsForm: FC<TransmissionConnectionSettingsProps
<Textbox
onChange={(e) => handleFormChange('url', e.target.value)}
id="url"
label={<FormattedMessage id="connection.settings.transmission.url" />}
placeholder={intl.formatMessage({
id: 'connection.settings.transmission.url.input.placeholder',
})}
label={<Trans id="connection.settings.transmission.url" />}
placeholder={i18n._('connection.settings.transmission.url.input.placeholder')}
/>
</FormRow>
<FormRow>
<Textbox
onChange={(e) => handleFormChange('username', e.target.value)}
id="transmission-username"
label={<FormattedMessage id="connection.settings.transmission.username" />}
placeholder={intl.formatMessage({
id: 'connection.settings.transmission.username.input.placeholder',
})}
label={<Trans id="connection.settings.transmission.username" />}
placeholder={i18n._('connection.settings.transmission.username.input.placeholder')}
autoComplete="off"
/>
<Textbox
onChange={(e) => handleFormChange('password', e.target.value)}
id="transmission-password"
label={<FormattedMessage id="connection.settings.transmission.password" />}
placeholder={intl.formatMessage({
id: 'connection.settings.transmission.password.input.placeholder',
})}
label={<Trans id="connection.settings.transmission.password" />}
placeholder={i18n._('connection.settings.transmission.password.input.placeholder')}
autoComplete="off"
type="password"
/>

View File

@@ -1,28 +1,15 @@
import {defineMessages, WrappedComponentProps} from 'react-intl';
import {PureComponent, ReactNodeArray} from 'react';
import {Trans} from '@lingui/react';
import {Arrow, File, FolderClosedSolid} from '@client/ui/icons';
import FloodActions from '@client/actions/FloodActions';
const MESSAGES = defineMessages({
EACCES: {
id: 'filesystem.error.eacces',
},
ENOENT: {
id: 'filesystem.error.enoent',
},
emptyDirectory: {
id: 'filesystem.empty.directory',
},
fetching: {
id: 'filesystem.fetching',
},
unknownError: {
id: 'filesystem.error.unknown',
},
});
const MESSAGES = {
EACCES: 'filesystem.error.eacces',
ENOENT: 'filesystem.error.enoent',
};
interface FilesystemBrowserProps extends WrappedComponentProps {
interface FilesystemBrowserProps {
selectable?: 'files' | 'directories';
directory: string;
onItemSelection?: (newDestination: string, isDirectory?: boolean) => void;
@@ -108,7 +95,7 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
};
render() {
const {intl, selectable} = this.props;
const {selectable} = this.props;
const {directories, errorResponse, files} = this.state;
let errorMessage = null;
let listItems = null;
@@ -119,7 +106,9 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
shouldShowDirectoryList = false;
errorMessage = (
<div className="filesystem__directory-list__item filesystem__directory-list__item--message">
<em>{intl.formatMessage(MESSAGES.fetching)}</em>
<em>
<Trans id="filesystem.fetching" />
</em>
</div>
);
}
@@ -127,11 +116,11 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
if (errorResponse && errorResponse.data && errorResponse.data.code) {
shouldShowDirectoryList = false;
const messageConfig = MESSAGES[errorResponse.data.code as keyof typeof MESSAGES] || MESSAGES.unknownError;
errorMessage = (
<div className="filesystem__directory-list__item filesystem__directory-list__item--message">
<em>{intl.formatMessage(messageConfig)}</em>
<em>
<Trans id={MESSAGES[errorResponse.data.code as keyof typeof MESSAGES] || 'filesystem.error.unknown'} />
</em>
</div>
);
}
@@ -141,9 +130,7 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
className="filesystem__directory-list__item filesystem__directory-list__item--parent"
onClick={this.handleParentDirectoryClick}>
<Arrow />
{intl.formatMessage({
id: 'filesystem.parent.directory',
})}
<Trans id="filesystem.parent.directory" />
</li>
);
@@ -188,7 +175,9 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
if ((!listItems || listItems.length === 0) && !errorMessage) {
errorMessage = (
<div className="filesystem__directory-list__item filesystem__directory-list__item--message">
<em>{intl.formatMessage(MESSAGES.emptyDirectory)}</em>
<em>
<Trans id="filesystem.empty.directory" />
</em>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import Dropzone from 'react-dropzone';
import {FormattedMessage} from 'react-intl';
import {FC, useEffect, useState} from 'react';
import {Trans} from '@lingui/react';
import {Close, File, Files} from '@client/ui/icons';
import {FormRowItem} from '@client/ui';
@@ -21,7 +21,7 @@ const FileDropzone: FC<FileDropzoneProps> = ({onFilesChanged}: FileDropzoneProps
return (
<FormRowItem>
<label className="form__element__label">
<FormattedMessage id="torrents.add.torrents.label" />
<Trans id="torrents.add.torrents.label" />
</label>
{files.length > 0 ? (
<ul
@@ -76,9 +76,9 @@ const FileDropzone: FC<FileDropzoneProps> = ({onFilesChanged}: FileDropzoneProps
<div className="dropzone__icon">
<Files />
</div>
<FormattedMessage id="torrents.add.tab.file.drop" />{' '}
<Trans id="torrents.add.tab.file.drop" />{' '}
<span className="dropzone__browse-button">
<FormattedMessage id="torrents.add.tab.file.browse" />
<Trans id="torrents.add.tab.file.browse" />
</span>
.
</div>

View File

@@ -1,6 +1,6 @@
import debounce from 'lodash/debounce';
import {FormattedMessage, useIntl} from 'react-intl';
import {forwardRef, MutableRefObject, ReactNode, useEffect, useRef, useState} from 'react';
import {Trans, useLingui} from '@lingui/react';
import {useEnsuredForwardedRef} from 'react-use';
import {Checkbox, ContextMenu, FormElementAddon, FormRow, FormRowGroup, Portal, Textbox} from '@client/ui';
@@ -45,7 +45,7 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
const formRowRef = useRef<HTMLDivElement>(null);
const textboxRef = useEnsuredForwardedRef(ref as MutableRefObject<HTMLInputElement>);
const intl = useIntl();
const {i18n} = useLingui();
useEffect(() => {
const closeDirectoryList = (): void => {
@@ -71,14 +71,14 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
if (showBasePathToggle) {
toggles.push(
<Checkbox grow={false} id="isBasePath" key="isBasePath">
<FormattedMessage id="torrents.destination.base_path" />
<Trans id="torrents.destination.base_path" />
</Checkbox>,
);
}
if (showCompletedToggle) {
toggles.push(
<Checkbox grow={false} id="isCompleted" key="isCompleted">
<FormattedMessage id="torrents.destination.completed" />
<Trans id="torrents.destination.completed" />
</Checkbox>,
);
}
@@ -86,7 +86,7 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
// TODO: this is getting bloated. toggles can be moved to their own elements...
toggles.push(
<Checkbox grow={false} id="isSequential" key="isSequential">
<FormattedMessage id="torrents.destination.sequential" />
<Trans id="torrents.destination.sequential" />
</Checkbox>,
);
}
@@ -118,9 +118,7 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
{leading: true},
)}
onClick={(event) => event.nativeEvent.stopImmediatePropagation()}
placeholder={intl.formatMessage({
id: 'torrents.add.destination.placeholder',
})}
placeholder={i18n._('torrents.add.destination.placeholder')}
ref={textboxRef}>
<FormElementAddon
onClick={() => {
@@ -140,7 +138,6 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
triggerRef={textboxRef}>
<FilesystemBrowser
directory={destination}
intl={intl}
selectable={selectable}
onItemSelection={(newDestination: string, isDirectory = true) => {
if (textboxRef.current != null) {

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {FC, ReactNode, ReactNodeArray, useEffect, useRef, useState} from 'react';
import {FormattedMessage} from 'react-intl';
import sort from 'fast-sort';
import {Trans} from '@lingui/react';
import {useKeyPressEvent} from 'react-use';
import {ContextMenu, FormElementAddon, FormRowItem, Portal, SelectItem, Textbox} from '@client/ui';
@@ -130,7 +130,7 @@ const TagSelect: FC<TagSelectProps> = ({defaultValue, placeholder, id, label, on
setSelectedTags([...selectedTags.filter((key) => key !== ''), tag]);
}
}}>
{tag === 'untagged' ? <FormattedMessage id="filter.untagged" /> : tag}
{tag === 'untagged' ? <Trans id="filter.untagged" /> : tag}
</SelectItem>,
);
return accumulator;

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import ModalActions from '../ModalActions';
import SettingStore from '../../../stores/SettingStore';
@@ -13,33 +13,27 @@ const AddTorrentsActions: FC<AddTorrentsActionsProps> = ({
isAddingTorrents,
onAddTorrentsClick,
}: AddTorrentsActionsProps) => {
const intl = useIntl();
const {i18n} = useLingui();
return (
<ModalActions
actions={[
{
checked: Boolean(SettingStore.floodSettings.startTorrentsOnLoad),
clickHandler: null,
content: intl.formatMessage({
id: 'torrents.add.start.label',
}),
content: i18n._('torrents.add.start.label'),
id: 'start',
triggerDismiss: false,
type: 'checkbox',
},
{
clickHandler: null,
content: intl.formatMessage({
id: 'button.cancel',
}),
content: i18n._('button.cancel'),
triggerDismiss: true,
type: 'tertiary',
},
{
clickHandler: onAddTorrentsClick,
content: intl.formatMessage({
id: 'torrents.add.button.add',
}),
content: i18n._('torrents.add.button.add'),
isLoading: isAddingTorrents,
submit: true,
triggerDismiss: false,

View File

@@ -1,5 +1,5 @@
import {FC, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Checkbox, Form, FormRow, Textbox} from '@client/ui';
import {saveAddTorrentsUserPreferences} from '@client/util/userPreferences';
@@ -25,74 +25,49 @@ type AddTorrentsByCreationFormData = {
const AddTorrentsByCreation: FC = () => {
const formRef = useRef<Form>(null);
const intl = useIntl();
const {i18n} = useLingui();
const [isCreatingTorrents, setIsCreatingTorrents] = useState<boolean>(false);
return (
<Form className="inverse" ref={formRef}>
<FilesystemBrowserTextbox
id="sourcePath"
label={intl.formatMessage({
id: 'torrents.create.source.path.label',
})}
/>
<FilesystemBrowserTextbox id="sourcePath" label={i18n._('torrents.create.source.path.label')} />
<TextboxRepeater
id="trackers"
label={intl.formatMessage({
id: 'torrents.create.trackers.label',
})}
placeholder={intl.formatMessage({
id: 'torrents.create.tracker.input.placeholder',
})}
label={i18n._('torrents.create.trackers.label')}
placeholder={i18n._('torrents.create.tracker.input.placeholder')}
defaultValues={[{id: 0, value: ''}]}
/>
<FormRow>
<Textbox
id="name"
label={intl.formatMessage({
id: 'torrents.create.base.name.label',
})}
placeholder={intl.formatMessage({
id: 'torrents.create.base.name.input.placeholder',
})}
label={i18n._('torrents.create.base.name.label')}
placeholder={i18n._('torrents.create.base.name.input.placeholder')}
/>
</FormRow>
<FormRow>
<Textbox
id="comment"
label={intl.formatMessage({
id: 'torrents.create.comment.label',
})}
placeholder={intl.formatMessage({
id: 'torrents.create.comment.input.placeholder',
})}
label={i18n._('torrents.create.comment.label')}
placeholder={i18n._('torrents.create.comment.input.placeholder')}
/>
</FormRow>
<FormRow>
<Textbox
id="infoSource"
label={intl.formatMessage({
id: 'torrents.create.info.source.label',
})}
placeholder={intl.formatMessage({
id: 'torrents.create.info.source.input.placeholder',
})}
label={i18n._('torrents.create.info.source.label')}
placeholder={i18n._('torrents.create.info.source.input.placeholder')}
/>
</FormRow>
<FormRow>
<Checkbox grow={false} id="isPrivate">
{intl.formatMessage({id: 'torrents.create.is.private.label'})}
{i18n._('torrents.create.is.private.label')}
</Checkbox>
</FormRow>
<FormRow>
<TagSelect
id="tags"
label={intl.formatMessage({
id: 'torrents.add.tags',
})}
placeholder={intl.formatMessage({
id: 'torrents.create.tags.input.placeholder',
})}
label={i18n._('torrents.add.tags')}
placeholder={i18n._('torrents.create.tags.input.placeholder')}
/>
</FormRow>
<AddTorrentsActions

View File

@@ -1,5 +1,5 @@
import {FC, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Form, FormRow} from '@client/ui';
import {saveAddTorrentsUserPreferences} from '@client/util/userPreferences';
@@ -29,7 +29,7 @@ const AddTorrentsByFile: FC = () => {
const textboxRef = useRef<HTMLInputElement>(null);
const [isAddingTorrents, setIsAddingTorrents] = useState<boolean>(false);
const intl = useIntl();
const {i18n} = useLingui();
return (
<Form className="inverse" ref={formRef}>
@@ -42,9 +42,7 @@ const AddTorrentsByFile: FC = () => {
</FormRow>
<FormRow>
<TagSelect
label={intl.formatMessage({
id: 'torrents.add.tags',
})}
label={i18n._('torrents.add.tags')}
id="tags"
onTagSelected={(tags) => {
if (textboxRef.current != null) {
@@ -59,9 +57,7 @@ const AddTorrentsByFile: FC = () => {
</FormRow>
<FilesystemBrowserTextbox
id="destination"
label={intl.formatMessage({
id: 'torrents.add.destination.label',
})}
label={i18n._('torrents.add.destination.label')}
ref={textboxRef}
selectable="directories"
showBasePathToggle

View File

@@ -1,5 +1,5 @@
import {FC, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import ConfigStore from '@client/stores/ConfigStore';
import {Form, FormRow} from '@client/ui';
@@ -31,7 +31,7 @@ const AddTorrentsByURL: FC = () => {
const textboxRef = useRef<HTMLInputElement>(null);
const [isAddingTorrents, setIsAddingTorrents] = useState<boolean>(false);
const intl = useIntl();
const {i18n} = useLingui();
return (
<Form className="inverse" ref={formRef}>
@@ -39,9 +39,7 @@ const AddTorrentsByURL: FC = () => {
id="urls"
label={
<div>
{intl.formatMessage({
id: 'torrents.add.torrents.label',
})}
{i18n._('torrents.add.torrents.label')}
{typeof navigator.registerProtocolHandler === 'function' && (
<em
style={{cursor: 'pointer', fontSize: '0.8em', float: 'right'}}
@@ -54,35 +52,25 @@ const AddTorrentsByURL: FC = () => {
);
}
}}>
{intl.formatMessage({
id: 'torrents.add.tab.url.register.magnet.handler',
})}
{i18n._('torrents.add.tab.url.register.magnet.handler')}
</em>
)}
</div>
}
placeholder={intl.formatMessage({
id: 'torrents.add.tab.url.input.placeholder',
})}
placeholder={i18n._('torrents.add.tab.url.input.placeholder')}
defaultValues={
(UIStore.activeModal?.id === 'add-torrents' && UIStore.activeModal?.initialURLs) || [{id: 0, value: ''}]
}
/>
<TextboxRepeater
id="cookies"
label={intl.formatMessage({
id: 'torrents.add.cookies.label',
})}
placeholder={intl.formatMessage({
id: 'torrents.add.cookies.input.placeholder',
})}
label={i18n._('torrents.add.cookies.label')}
placeholder={i18n._('torrents.add.cookies.input.placeholder')}
/>
<FormRow>
<TagSelect
id="tags"
label={intl.formatMessage({
id: 'torrents.add.tags',
})}
label={i18n._('torrents.add.tags')}
onTagSelected={(tags) => {
if (textboxRef.current != null) {
const suggestedPath = SettingStore.floodSettings.torrentDestinations?.[tags[0]];
@@ -96,9 +84,7 @@ const AddTorrentsByURL: FC = () => {
</FormRow>
<FilesystemBrowserTextbox
id="destination"
label={intl.formatMessage({
id: 'torrents.add.destination.label',
})}
label={i18n._('torrents.add.destination.label')}
ref={textboxRef}
selectable="directories"
showBasePathToggle

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import AddTorrentsByCreation from './AddTorrentsByCreation';
import AddTorrentsByFile from './AddTorrentsByFile';
@@ -9,26 +9,20 @@ import SettingStore from '../../../stores/SettingStore';
import UIStore from '../../../stores/UIStore';
const AddTorrentsModal: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
const tabs = {
'by-url': {
content: AddTorrentsByURL,
label: intl.formatMessage({
id: 'torrents.add.tab.url.title',
}),
label: i18n._('torrents.add.tab.url.title'),
},
'by-file': {
content: AddTorrentsByFile,
label: intl.formatMessage({
id: 'torrents.add.tab.file.title',
}),
label: i18n._('torrents.add.tab.file.title'),
},
'by-creation': {
content: AddTorrentsByCreation,
label: intl.formatMessage({
id: 'torrents.add.tab.create.title',
}),
label: i18n._('torrents.add.tab.create.title'),
},
};
@@ -41,15 +35,7 @@ const AddTorrentsModal: FC = () => {
initialTabId = SettingStore.floodSettings.UITorrentsAddTab ?? initialTabId;
}
return (
<Modal
heading={intl.formatMessage({
id: 'torrents.add.heading',
})}
tabs={tabs}
initialTabId={initialTabId}
/>
);
return <Modal heading={i18n._('torrents.add.heading')} tabs={tabs} initialTabId={initialTabId} />;
};
export default AddTorrentsModal;

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {
Button,
@@ -34,30 +34,22 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
onCancel,
}: DownloadRuleFormProps) => {
const {feeds} = FeedStore;
const intl = useIntl();
const {i18n} = useLingui();
return (
<FormRowGroup>
<FormRow>
<Textbox
id="label"
label={intl.formatMessage({
id: 'feeds.label',
})}
defaultValue={rule.label}
/>
<Textbox id="label" label={i18n._('feeds.label')} defaultValue={rule.label} />
<Select
disabled={!feeds.length}
id="feedID"
label={intl.formatMessage({
id: 'feeds.applicable.feed',
})}
label={i18n._('feeds.applicable.feed')}
defaultID={rule.feedIDs?.[0]}>
{feeds.length === 0
? [
<SelectItem key="empty" id="placeholder" isPlaceholder>
<em>
<FormattedMessage id="feeds.no.feeds.available" />
<Trans id="feeds.no.feeds.available" />
</em>
</SelectItem>,
]
@@ -71,7 +63,7 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
[
<SelectItem key="select-feed" id="placeholder" isPlaceholder>
<em>
<FormattedMessage id="feeds.select.feed" />
<Trans id="feeds.select.feed" />
</em>
</SelectItem>,
],
@@ -81,23 +73,15 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
<FormRow>
<Textbox
id="match"
label={intl.formatMessage({
id: 'feeds.match.pattern',
})}
placeholder={intl.formatMessage({
id: 'feeds.regEx',
})}
label={i18n._('feeds.match.pattern')}
placeholder={i18n._('feeds.regEx')}
defaultValue={rule.match}
width="three-eighths"
/>
<Textbox
id="exclude"
label={intl.formatMessage({
id: 'feeds.exclude.pattern',
})}
placeholder={intl.formatMessage({
id: 'feeds.regEx',
})}
label={i18n._('feeds.exclude.pattern')}
placeholder={i18n._('feeds.regEx')}
defaultValue={rule.exclude}
width="three-eighths"
/>
@@ -106,12 +90,8 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
<Textbox
addonPlacement="after"
id="check"
label={intl.formatMessage({
id: 'feeds.test.match',
})}
placeholder={intl.formatMessage({
id: 'feeds.check',
})}>
label={i18n._('feeds.test.match')}
placeholder={i18n._('feeds.check')}>
{isPatternMatched && (
<FormElementAddon>
<CheckmarkThick />
@@ -123,9 +103,7 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
<FormRowItem>
<FilesystemBrowserTextbox
id="destination"
label={intl.formatMessage({
id: 'feeds.torrent.destination',
})}
label={i18n._('feeds.torrent.destination')}
selectable="directories"
suggested={rule.destination}
showBasePathToggle
@@ -133,25 +111,21 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
</FormRowItem>
<TagSelect
id="tags"
label={intl.formatMessage({
id: 'feeds.apply.tags',
})}
placeholder={intl.formatMessage({
id: 'feeds.tags',
})}
label={i18n._('feeds.apply.tags')}
placeholder={i18n._('feeds.tags')}
defaultValue={rule.tags}
/>
</FormRow>
<FormRow>
<br />
<Checkbox id="startOnLoad" defaultChecked={rule.startOnLoad} matchTextboxHeight>
<FormattedMessage id="feeds.start.on.load" />
<Trans id="feeds.start.on.load" />
</Checkbox>
<Button onClick={onCancel}>
<FormattedMessage id="button.cancel" />
<Trans id="button.cancel" />
</Button>
<Button type="submit" isLoading={isSubmitting}>
<FormattedMessage id="button.save.feed" />
<Trans id="button.save.feed" />
</Button>
</FormRow>
</FormRowGroup>

View File

@@ -1,6 +1,6 @@
import {FC} from 'react';
import {FormattedMessage} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans} from '@lingui/react';
import {Close, Edit} from '@client/ui/icons';
@@ -22,7 +22,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
return (
<ul className="interactive-list">
<li className="interactive-list__item">
<FormattedMessage id="feeds.no.rules.defined" />
<Trans id="feeds.no.rules.defined" />
</li>
</ul>
);
@@ -40,7 +40,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
<li
className="interactive-list__detail-list__item
interactive-list__detail interactive-list__detail--tertiary">
<FormattedMessage id="feeds.exclude" />
<Trans id="feeds.exclude" />
{': '}
{rule.exclude}
</li>
@@ -56,7 +56,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
tags = (
<li className="interactive-list__detail-list__item interactive-list__detail interactive-list__detail--tertiary">
<FormattedMessage id="feeds.tags" />
<Trans id="feeds.tags" />
{': '}
{tagNodes}
</li>
@@ -76,7 +76,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
className="interactive-list__detail-list__item
interactive-list__detail-list__item--overflow
interactive-list__detail interactive-list__detail--secondary">
<FormattedMessage id="feeds.match.count" values={{count: matchedCount}} />
<Trans id="feeds.match.count" values={{count: matchedCount}} />
</li>
{rule === currentRule && (
<li
@@ -95,7 +95,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
overflow: 'hidden',
textOverflow: 'ellipsis',
}}>
<FormattedMessage id="feeds.match" />
<Trans id="feeds.match" />
{': '}
{rule.match}
</li>

View File

@@ -1,5 +1,5 @@
import {FC, ReactNodeArray, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {Button, Form, FormError, FormRow, FormRowItem} from '@client/ui';
import FeedActions from '@client/actions/FeedActions';
@@ -71,7 +71,7 @@ interface RuleFormData {
const DownloadRulesTab: FC = () => {
const formRef = useRef<Form>(null);
const intl = useIntl();
const {i18n} = useLingui();
const [currentRule, setCurrentRule] = useState<Rule | null>(null);
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
@@ -161,13 +161,13 @@ const DownloadRulesTab: FC = () => {
}}
ref={formRef}>
<ModalFormSectionHeader>
<FormattedMessage id="feeds.existing.rules" />
<Trans id="feeds.existing.rules" />
</ModalFormSectionHeader>
{Object.keys(errors).reduce((memo: ReactNodeArray, key) => {
if (errors[key as ValidatedField] != null) {
memo.push(
<FormRow key={`error-${key}`}>
<FormError>{intl.formatMessage({id: errors?.[key as ValidatedField]})}</FormError>
<FormError>{i18n._(errors?.[key as ValidatedField] as string)}</FormError>
</FormRow>,
);
}
@@ -212,7 +212,7 @@ const DownloadRulesTab: FC = () => {
onClick={() => {
setIsEditing(true);
}}>
<FormattedMessage id="button.new" />
<Trans id="button.new" />
</Button>
</FormRow>
)}

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {Button, FormRow, FormRowGroup, Select, SelectItem, Textbox} from '@client/ui';
@@ -20,7 +20,7 @@ const FeedForm: FC<FeedFormProps> = ({
isSubmitting,
onCancel,
}: FeedFormProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const feedInterval = currentFeed?.interval ?? defaultFeed.interval;
let defaultIntervalTextValue = feedInterval;
@@ -40,29 +40,21 @@ const FeedForm: FC<FeedFormProps> = ({
<FormRow>
<Textbox
id="label"
label={intl.formatMessage({
id: 'feeds.label',
})}
placeholder={intl.formatMessage({
id: 'feeds.label',
})}
label={i18n._('feeds.label')}
placeholder={i18n._('feeds.label')}
defaultValue={currentFeed?.label ?? defaultFeed.label}
/>
<Textbox
id="interval"
label={intl.formatMessage({
id: 'feeds.select.interval',
})}
placeholder={intl.formatMessage({
id: 'feeds.interval',
})}
label={i18n._('feeds.select.interval')}
placeholder={i18n._('feeds.interval')}
defaultValue={defaultIntervalTextValue}
width="one-eighth"
/>
<Select labelOffset defaultID={defaultIntervalMultiplier} id="intervalMultiplier" width="one-eighth">
{intervalMultipliers.map((interval) => (
<SelectItem key={interval.value} id={interval.value}>
{intl.formatMessage({id: interval.message})}
{i18n._(interval.message)}
</SelectItem>
))}
</Select>
@@ -70,17 +62,15 @@ const FeedForm: FC<FeedFormProps> = ({
<FormRow>
<Textbox
id="url"
label={intl.formatMessage({
id: 'feeds.url',
})}
placeholder={intl.formatMessage({id: 'feeds.url'})}
label={i18n._('feeds.url')}
placeholder={i18n._('feeds.url')}
defaultValue={currentFeed?.url ?? defaultFeed?.url}
/>
<Button labelOffset onClick={onCancel}>
<FormattedMessage id="button.cancel" />
<Trans id="button.cancel" />
</Button>
<Button labelOffset type="submit" isLoading={isSubmitting}>
<FormattedMessage id="button.save.feed" />
<Trans id="button.save.feed" />
</Button>
</FormRow>
</FormRowGroup>

View File

@@ -1,6 +1,6 @@
import {FC, ReactNodeArray} from 'react';
import {FormattedMessage} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans} from '@lingui/react';
import {Checkbox, FormRow} from '@client/ui';
@@ -42,7 +42,7 @@ const FeedItems: FC<FeedItemsProps> = observer(({selectedFeedID}: FeedItemsProps
<ul className="interactive-list">
<li className="interactive-list__item">
<div className="interactive-list__label">
<FormattedMessage id="feeds.no.items.matching" />
<Trans id="feeds.no.items.matching" />
</div>
</li>
</ul>

View File

@@ -1,6 +1,6 @@
import {FC, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans, useLingui} from '@lingui/react';
import {Button, Form, FormRow, Select, SelectItem, Textbox} from '@client/ui';
import FeedActions from '@client/actions/FeedActions';
@@ -11,7 +11,7 @@ import FeedItems from './FeedItems';
import ModalFormSectionHeader from '../ModalFormSectionHeader';
const FeedItemsForm: FC = observer(() => {
const intl = useIntl();
const {i18n} = useLingui();
const manualAddingFormRef = useRef<Form>(null);
const [selectedFeedID, setSelectedFeedID] = useState<string | null>(null);
@@ -55,22 +55,20 @@ const FeedItemsForm: FC = observer(() => {
}}
ref={manualAddingFormRef}>
<ModalFormSectionHeader>
<FormattedMessage id="feeds.browse.feeds" />
<Trans id="feeds.browse.feeds" />
</ModalFormSectionHeader>
<FormRow>
<Select
disabled={!feeds.length}
grow={false}
id="feedID"
label={intl.formatMessage({
id: 'feeds.select.feed',
})}
label={i18n._('feeds.select.feed')}
width="three-eighths">
{!feeds.length
? [
<SelectItem key="empty" id="placeholder" isPlaceholder>
<em>
<FormattedMessage id="feeds.no.feeds.available" />
<Trans id="feeds.no.feeds.available" />
</em>
</SelectItem>,
]
@@ -89,26 +87,18 @@ const FeedItemsForm: FC = observer(() => {
[
<SelectItem key="select-feed" id="placeholder" isPlaceholder>
<em>
<FormattedMessage id="feeds.select.feed" />
<Trans id="feeds.select.feed" />
</em>
</SelectItem>,
],
)}
</Select>
{selectedFeedID && (
<Textbox
id="search"
label={intl.formatMessage({
id: 'feeds.search.term',
})}
placeholder={intl.formatMessage({
id: 'feeds.search',
})}
/>
<Textbox id="search" label={i18n._('feeds.search.term')} placeholder={i18n._('feeds.search')} />
)}
{selectedFeedID && (
<Button key="button" type="submit" labelOffset>
<FormattedMessage id="button.download" />
<Trans id="button.download" />
</Button>
)}
</FormRow>

View File

@@ -1,6 +1,6 @@
import {FC} from 'react';
import {observer} from 'mobx-react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {Close, Edit} from '@client/ui/icons';
@@ -18,13 +18,13 @@ interface FeedListProps {
const FeedList: FC<FeedListProps> = observer(
({currentFeed, intervalMultipliers, onSelect, onRemove}: FeedListProps) => {
const {feeds} = FeedStore;
const intl = useIntl();
const {i18n} = useLingui();
if (feeds.length === 0) {
return (
<ul className="interactive-list">
<li className="interactive-list__item">
<FormattedMessage id="feeds.no.feeds.defined" />
<Trans id="feeds.no.feeds.defined" />
</li>
</ul>
);
@@ -60,7 +60,7 @@ const FeedList: FC<FeedListProps> = observer(
className="interactive-list__detail-list__item
interactive-list__detail-list__item--overflow
interactive-list__detail interactive-list__detail--secondary">
<FormattedMessage id="feeds.match.count" values={{count: matchedCount}} />
<Trans id="feeds.match.count" values={{count: matchedCount}} />
</li>
{feed === currentFeed && (
<li
@@ -74,7 +74,7 @@ const FeedList: FC<FeedListProps> = observer(
<li
className="interactive-list__detail-list__item
interactive-list__detail interactive-list__detail--tertiary">
{`${intervalText} ${intl.formatMessage({id: intervalMultiplierMessage})}`}
{`${intervalText} ${i18n._(intervalMultiplierMessage)}`}
</li>
<li
className="interactive-list__detail-list__item

View File

@@ -1,5 +1,5 @@
import {FC, useEffect} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import DownloadRulesTab from './DownloadRulesTab';
import FeedActions from '../../../actions/FeedActions';
@@ -7,7 +7,7 @@ import FeedsTab from './FeedsTab';
import Modal from '../Modal';
const FeedsModal: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
useEffect(() => {
FeedActions.fetchFeedMonitors();
@@ -16,28 +16,15 @@ const FeedsModal: FC = () => {
const tabs = {
feeds: {
content: FeedsTab,
label: intl.formatMessage({
id: 'feeds.tabs.feeds',
}),
label: i18n._('feeds.tabs.feeds'),
},
downloadRules: {
content: DownloadRulesTab,
label: intl.formatMessage({
id: 'feeds.tabs.download.rules',
}),
label: i18n._('feeds.tabs.download.rules'),
},
};
return (
<Modal
heading={intl.formatMessage({
id: 'feeds.tabs.heading',
})}
orientation="horizontal"
size="large"
tabs={tabs}
/>
);
return <Modal heading={i18n._('feeds.tabs.heading')} orientation="horizontal" size="large" tabs={tabs} />;
};
export default FeedsModal;

View File

@@ -1,5 +1,5 @@
import {FC, ReactNodeArray, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {Button, Form, FormError, FormRow, FormRowItem} from '@client/ui';
import FeedActions from '@client/actions/FeedActions';
@@ -63,7 +63,7 @@ const defaultFeed: AddFeedOptions = {
const FeedsTab: FC = () => {
const formRef = useRef<Form>(null);
const intl = useIntl();
const {i18n} = useLingui();
const [currentFeed, setCurrentFeed] = useState<Feed | null>(null);
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
const [isEditing, setIsEditing] = useState<boolean>(false);
@@ -134,13 +134,13 @@ const FeedsTab: FC = () => {
}}
ref={formRef}>
<ModalFormSectionHeader>
<FormattedMessage id="feeds.existing.feeds" />
<Trans id="feeds.existing.feeds" />
</ModalFormSectionHeader>
{Object.keys(errors).reduce((memo: ReactNodeArray, key) => {
if (errors[key as ValidatedField] != null) {
memo.push(
<FormRow key={`error-${key}`}>
<FormError>{intl.formatMessage({id: errors?.[key as ValidatedField]})}</FormError>
<FormError>{i18n._(errors?.[key as ValidatedField] as string)}</FormError>
</FormRow>,
);
}
@@ -192,7 +192,7 @@ const FeedsTab: FC = () => {
onClick={() => {
setIsEditing(true);
}}>
<FormattedMessage id="button.new" />
<Trans id="button.new" />
</Button>
</FormRow>
)}

View File

@@ -1,5 +1,5 @@
import {FC, useEffect, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {CheckmarkThick, Clipboard} from '@client/ui/icons';
import {Form, FormElementAddon, FormError, FormRow, Textbox} from '@client/ui';
@@ -25,7 +25,7 @@ const generateMagnet = (hash: string, trackers?: Array<string>): string => {
const GenerateMagnetModal: FC = () => {
const magnetTextboxRef = useRef<HTMLInputElement>(null);
const magnetTrackersTextboxRef = useRef<HTMLInputElement>(null);
const intl = useIntl();
const {i18n} = useLingui();
const [isMagnetCopied, setIsMagnetCopied] = useState<boolean>(false);
const [isMagnetTrackersCopied, setIsMagnetTrackersCopied] = useState<boolean>(false);
@@ -55,15 +55,13 @@ const GenerateMagnetModal: FC = () => {
return (
<Modal
heading={intl.formatMessage({
id: 'torrents.generate.magnet.heading',
})}
heading={i18n._('torrents.generate.magnet.heading')}
content={
<div className="modal__content inverse">
<Form>
{TorrentStore.torrents[TorrentStore.selectedTorrents[0]]?.isPrivate ? (
<FormRow>
<FormError>{intl.formatMessage({id: 'torrents.generate.magnet.private.torrent'})}</FormError>
<FormError>{i18n._('torrents.generate.magnet.private.torrent')}</FormError>
</FormRow>
) : null}
<FormRow>
@@ -71,7 +69,7 @@ const GenerateMagnetModal: FC = () => {
id="magnet"
ref={magnetTextboxRef}
addonPlacement="after"
label={intl.formatMessage({id: 'torrents.generate.magnet.magnet'})}
label={i18n._('torrents.generate.magnet.magnet')}
defaultValue={magnetLink}
readOnly>
<FormElementAddon
@@ -94,10 +92,8 @@ const GenerateMagnetModal: FC = () => {
{trackerState.isLoadingTrackers ? (
<Textbox
id="loading"
label={intl.formatMessage({id: 'torrents.generate.magnet.magnet.with.trackers'})}
placeholder={intl.formatMessage({
id: 'torrents.generate.magnet.loading.trackers',
})}
label={i18n._('torrents.generate.magnet.magnet.with.trackers')}
placeholder={i18n._('torrents.generate.magnet.loading.trackers')}
disabled
/>
) : (
@@ -105,7 +101,7 @@ const GenerateMagnetModal: FC = () => {
id="magnet-trackers"
ref={magnetTrackersTextboxRef}
addonPlacement="after"
label={intl.formatMessage({id: 'torrents.generate.magnet.magnet.with.trackers'})}
label={i18n._('torrents.generate.magnet.magnet.with.trackers')}
defaultValue={trackerState.magnetTrackersLink}
readOnly>
<FormElementAddon
@@ -131,9 +127,7 @@ const GenerateMagnetModal: FC = () => {
actions={[
{
clickHandler: null,
content: intl.formatMessage({
id: 'button.close',
}),
content: i18n._('button.close'),
triggerDismiss: true,
type: 'tertiary',
},

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Form} from '@client/ui';
import TorrentActions from '@client/actions/TorrentActions';
@@ -31,14 +31,12 @@ const getSuggestedPath = (sources: Array<string>): string | undefined => {
};
const MoveTorrents: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
const [isSettingDownloadPath, setIsSettingDownloadPath] = useState<boolean>(false);
return (
<Modal
heading={intl.formatMessage({
id: 'torrents.move.heading',
})}
heading={i18n._('torrents.move.heading')}
content={
<div className="modal__content">
<Form
@@ -71,31 +69,23 @@ const MoveTorrents: FC = () => {
actions={[
{
checked: true,
content: intl.formatMessage({
id: 'torrents.move.data.label',
}),
content: i18n._('torrents.move.data.label'),
id: 'moveFiles',
type: 'checkbox',
},
{
checked: false,
content: intl.formatMessage({
id: 'torrents.move.check_hash.label',
}),
content: i18n._('torrents.move.check_hash.label'),
id: 'isCheckHash',
type: 'checkbox',
},
{
content: intl.formatMessage({
id: 'button.cancel',
}),
content: i18n._('button.cancel'),
triggerDismiss: true,
type: 'tertiary',
},
{
content: intl.formatMessage({
id: 'torrents.move.button.set.location',
}),
content: i18n._('torrents.move.button.set.location'),
isLoading: isSettingDownloadPath,
submit: true,
type: 'primary',

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {Form, FormRow} from '@client/ui';
import {saveDeleteTorrentsUserPreferences} from '@client/util/userPreferences';
@@ -12,21 +12,19 @@ import Modal from '../Modal';
import ModalActions from '../ModalActions';
const RemoveTorrentsModal: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
const [isRemoving, setIsRemoving] = useState<boolean>(false);
const {selectedTorrents} = TorrentStore;
if (selectedTorrents.length === 0) {
return (
<Modal
heading={intl.formatMessage({
id: 'torrents.remove',
})}
heading={i18n._('torrents.remove')}
content={
<div className="modal__content inverse">
<Form>
<FormRow>
<FormattedMessage id="torrents.remove.error.no.torrents.selected" />
<Trans id="torrents.remove.error.no.torrents.selected" />
</FormRow>
</Form>
</div>
@@ -34,9 +32,7 @@ const RemoveTorrentsModal: FC = () => {
actions={[
{
clickHandler: null,
content: intl.formatMessage({
id: 'button.ok',
}),
content: i18n._('button.ok'),
triggerDismiss: true,
type: 'primary',
},
@@ -47,9 +43,7 @@ const RemoveTorrentsModal: FC = () => {
return (
<Modal
heading={intl.formatMessage({
id: 'torrents.remove',
})}
heading={i18n._('torrents.remove')}
content={
<div className="modal__content">
<Form
@@ -69,29 +63,23 @@ const RemoveTorrentsModal: FC = () => {
});
}}>
<FormRow>
<FormattedMessage id="torrents.remove.are.you.sure" values={{count: selectedTorrents.length}} />
<Trans id="torrents.remove.are.you.sure" values={{count: selectedTorrents.length}} />
</FormRow>
<ModalActions
actions={[
{
checked: SettingStore.floodSettings.deleteTorrentData,
content: intl.formatMessage({
id: 'torrents.remove.delete.data',
}),
content: i18n._('torrents.remove.delete.data'),
id: 'deleteData',
type: 'checkbox',
},
{
content: intl.formatMessage({
id: 'button.no',
}),
content: i18n._('button.no'),
triggerDismiss: true,
type: 'tertiary',
},
{
content: intl.formatMessage({
id: 'button.yes',
}),
content: i18n._('button.yes'),
isLoading: isRemoving,
submit: true,
type: 'primary',

View File

@@ -1,5 +1,5 @@
import {FC, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Form, FormRow} from '@client/ui';
import TorrentActions from '@client/actions/TorrentActions';
@@ -10,14 +10,12 @@ import TagSelect from '../../general/form-elements/TagSelect';
const SetTagsModal: FC = () => {
const formRef = useRef<Form>(null);
const intl = useIntl();
const {i18n} = useLingui();
const [isSettingTags, setIsSettingTags] = useState<boolean>(false);
return (
<Modal
heading={intl.formatMessage({
id: 'torrents.set.tags.heading',
})}
heading={i18n._('torrents.set.tags.heading')}
content={
<div className="modal__content inverse">
<Form ref={formRef}>
@@ -27,9 +25,7 @@ const SetTagsModal: FC = () => {
.map((hash: string) => TorrentStore.torrents[hash].tags)[0]
.slice()}
id="tags"
placeholder={intl.formatMessage({
id: 'torrents.set.tags.enter.tags',
})}
placeholder={i18n._('torrents.set.tags.enter.tags')}
/>
</FormRow>
</Form>
@@ -37,17 +33,13 @@ const SetTagsModal: FC = () => {
}
actions={[
{
content: intl.formatMessage({
id: 'button.cancel',
}),
content: i18n._('button.cancel'),
clickHandler: null,
triggerDismiss: true,
type: 'tertiary',
},
{
content: intl.formatMessage({
id: 'torrents.set.tags.button.set',
}),
content: i18n._('torrents.set.tags.button.set'),
clickHandler: () => {
if (formRef.current == null) {
return;

View File

@@ -1,5 +1,5 @@
import {FC, useEffect, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Form, FormRow, Textbox} from '@client/ui';
import TorrentActions from '@client/actions/TorrentActions';
@@ -13,7 +13,7 @@ import TextboxRepeater, {getTextArray} from '../../general/form-elements/Textbox
const SetTrackersModal: FC = () => {
const formRef = useRef<Form>(null);
const intl = useIntl();
const {i18n} = useLingui();
const [isSettingTrackers, setIsSettingTrackers] = useState<boolean>(false);
const [trackerState, setTrackerState] = useState<{
isLoadingTrackers: boolean;
@@ -38,28 +38,18 @@ const SetTrackersModal: FC = () => {
return (
<Modal
heading={intl.formatMessage({
id: 'torrents.set.trackers.heading',
})}
heading={i18n._('torrents.set.trackers.heading')}
content={
<div className="modal__content inverse">
<Form ref={formRef}>
{trackerState.isLoadingTrackers ? (
<FormRow>
<Textbox
id="loading"
placeholder={intl.formatMessage({
id: 'torrents.set.trackers.loading.trackers',
})}
disabled
/>
<Textbox id="loading" placeholder={i18n._('torrents.set.trackers.loading.trackers')} disabled />
</FormRow>
) : (
<TextboxRepeater
id="trackers"
placeholder={intl.formatMessage({
id: 'torrents.set.trackers.enter.tracker',
})}
placeholder={i18n._('torrents.set.trackers.enter.tracker')}
defaultValues={
trackerState.trackerURLs.length === 0
? undefined
@@ -76,9 +66,7 @@ const SetTrackersModal: FC = () => {
actions={[
{
clickHandler: null,
content: intl.formatMessage({
id: 'button.cancel',
}),
content: i18n._('button.cancel'),
triggerDismiss: true,
type: 'tertiary',
},
@@ -101,9 +89,7 @@ const SetTrackersModal: FC = () => {
UIStore.dismissModal();
});
},
content: intl.formatMessage({
id: 'torrents.set.trackers.button.set',
}),
content: i18n._('torrents.set.trackers.button.set'),
isLoading: isSettingTrackers || trackerState.isLoadingTrackers,
triggerDismiss: false,
type: 'primary',

View File

@@ -1,8 +1,8 @@
import classnames from 'classnames';
import {CSSTransition, TransitionGroup} from 'react-transition-group';
import {FC, useEffect, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans, useLingui} from '@lingui/react';
import {Button, Checkbox, Form, FormError, FormRowItem, FormRow, LoadingRing, Textbox} from '@client/ui';
import {Close} from '@client/ui/icons';
@@ -29,7 +29,7 @@ const AuthTab: FC = observer(() => {
const [error, setError] = useState<string | null>(null);
const [isUserListFetched, setIsUserListFetched] = useState<boolean>(false);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const intl = useIntl();
const {i18n} = useLingui();
useEffect(() => {
if (AuthStore.currentUser.isAdmin) {
@@ -43,11 +43,11 @@ const AuthTab: FC = observer(() => {
return (
<Form>
<ModalFormSectionHeader>
<FormattedMessage id="auth.user.accounts" />
<Trans id="auth.user.accounts" />
</ModalFormSectionHeader>
<FormRow>
<FormError>
<FormattedMessage id="auth.message.not.admin" />
<Trans id="auth.message.not.admin" />
</FormError>
</FormRow>
</Form>
@@ -106,7 +106,7 @@ const AuthTab: FC = observer(() => {
}}
ref={formRef}>
<ModalFormSectionHeader>
<FormattedMessage id="auth.user.accounts" />
<Trans id="auth.user.accounts" />
</ModalFormSectionHeader>
<FormRow>
<FormRowItem>
@@ -139,7 +139,7 @@ const AuthTab: FC = observer(() => {
} else {
badge = (
<span className="interactive-list__label__tag tag">
<FormattedMessage id="auth.current.user" />
<Trans id="auth.current.user" />
</span>
);
}
@@ -162,32 +162,28 @@ const AuthTab: FC = observer(() => {
</FormRowItem>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="auth.add.user" />
<Trans id="auth.add.user" />
</ModalFormSectionHeader>
{error && (
<FormRow>
<FormError>{intl.formatMessage({id: error})}</FormError>
<FormError>{i18n._(error)}</FormError>
</FormRow>
)}
<FormRow>
<Textbox
id="username"
label={<FormattedMessage id="auth.username" />}
placeholder={intl.formatMessage({
id: 'auth.username',
})}
label={<Trans id="auth.username" />}
placeholder={i18n._('auth.username')}
autoComplete="username"
/>
<Textbox
id="password"
label={<FormattedMessage id="auth.password" />}
placeholder={intl.formatMessage({
id: 'auth.password',
})}
label={<Trans id="auth.password" />}
placeholder={i18n._('auth.password')}
autoComplete="new-password"
/>
<Checkbox grow={false} id="isAdmin" labelOffset matchTextboxHeight>
<FormattedMessage id="auth.admin" />
<Trans id="auth.admin" />
</Checkbox>
</FormRow>
<ClientConnectionSettingsForm
@@ -198,7 +194,7 @@ const AuthTab: FC = observer(() => {
<p />
<FormRow justify="end">
<Button isLoading={isSubmitting} priority="primary" type="submit">
<FormattedMessage id="button.add" />
<Trans id="button.add" />
</Button>
</FormRow>
</Form>

View File

@@ -1,5 +1,5 @@
import {FormattedMessage} from 'react-intl';
import {FC, FormEvent, useState} from 'react';
import {Trans} from '@lingui/react';
import {Form, FormRow, Textbox} from '@client/ui';
import SettingStore from '@client/stores/SettingStore';
@@ -62,7 +62,7 @@ const BandwidthTab: FC<BandwidthTabProps> = ({onSettingsChange, onClientSettings
onClientSettingsChange(newChangedClientSettings);
}}>
<ModalFormSectionHeader>
<FormattedMessage id="settings.bandwidth.transferrate.heading" />
<Trans id="settings.bandwidth.transferrate.heading" />
</ModalFormSectionHeader>
<FormRow>
<Textbox
@@ -71,7 +71,7 @@ const BandwidthTab: FC<BandwidthTabProps> = ({onSettingsChange, onClientSettings
? processSpeedsForDisplay(SettingStore.floodSettings.speedLimits.download)
: 0
}
label={<FormattedMessage id="settings.bandwidth.transferrate.dropdown.preset.download.label" />}
label={<Trans id="settings.bandwidth.transferrate.dropdown.preset.download.label" />}
id="dropdownPresetDownload"
/>
</FormRow>
@@ -82,46 +82,46 @@ const BandwidthTab: FC<BandwidthTabProps> = ({onSettingsChange, onClientSettings
? processSpeedsForDisplay(SettingStore.floodSettings.speedLimits.upload)
: 0
}
label={<FormattedMessage id="settings.bandwidth.transferrate.dropdown.preset.upload.label" />}
label={<Trans id="settings.bandwidth.transferrate.dropdown.preset.upload.label" />}
id="dropdownPresetUpload"
/>
</FormRow>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleGlobalDownSpeed')}
label={<FormattedMessage id="settings.bandwidth.transferrate.global.throttle.download" />}
label={<Trans id="settings.bandwidth.transferrate.global.throttle.download" />}
id="throttleGlobalDownSpeed"
/>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleGlobalUpSpeed')}
label={<FormattedMessage id="settings.bandwidth.transferrate.global.throttle.upload" />}
label={<Trans id="settings.bandwidth.transferrate.global.throttle.upload" />}
id="throttleGlobalUpSpeed"
/>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.bandwidth.slots.heading" />
<Trans id="settings.bandwidth.slots.heading" />
</ModalFormSectionHeader>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxUploads')}
label={<FormattedMessage id="settings.bandwidth.slots.upload.label" />}
label={<Trans id="settings.bandwidth.slots.upload.label" />}
id="throttleMaxUploads"
/>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxUploadsGlobal')}
label={<FormattedMessage id="settings.bandwidth.slots.upload.global.label" />}
label={<Trans id="settings.bandwidth.slots.upload.global.label" />}
id="throttleMaxUploadsGlobal"
/>
</FormRow>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxDownloads')}
label={<FormattedMessage id="settings.bandwidth.slots.download.label" />}
label={<Trans id="settings.bandwidth.slots.download.label" />}
id="throttleMaxDownloads"
/>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxDownloadsGlobal')}
label={<FormattedMessage id="settings.bandwidth.slots.download.global.label" />}
label={<Trans id="settings.bandwidth.slots.download.global.label" />}
id="throttleMaxDownloadsGlobal"
/>
</FormRow>

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import {Checkbox, Form, FormRow, Textbox} from '@client/ui';
@@ -27,13 +27,13 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
onClientSettingsChange(newChangedClientSettings);
}}>
<ModalFormSectionHeader>
<FormattedMessage id="settings.connectivity.incoming.heading" />
<Trans id="settings.connectivity.incoming.heading" />
</ModalFormSectionHeader>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'networkPortRange')}
id="networkPortRange"
label={<FormattedMessage id="settings.connectivity.port.range.label" />}
label={<Trans id="settings.connectivity.port.range.label" />}
width="one-quarter"
/>
<Checkbox
@@ -42,7 +42,7 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
id="networkPortRandom"
labelOffset
matchTextboxHeight>
<FormattedMessage id="settings.connectivity.port.randomize.label" />
<Trans id="settings.connectivity.port.randomize.label" />
</Checkbox>
<Checkbox
defaultChecked={getChangedClientSetting(changedClientSettings, 'networkPortOpen')}
@@ -50,29 +50,29 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
id="networkPortOpen"
labelOffset
matchTextboxHeight>
<FormattedMessage id="settings.connectivity.port.open.label" />
<Trans id="settings.connectivity.port.open.label" />
</Checkbox>
</FormRow>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'networkLocalAddress')}
id="networkLocalAddress"
label={<FormattedMessage id="settings.connectivity.ip.hostname.label" />}
label={<Trans id="settings.connectivity.ip.hostname.label" />}
/>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'networkHttpMaxOpen')}
id="networkHttpMaxOpen"
label={<FormattedMessage id="settings.connectivity.max.http.connections" />}
label={<Trans id="settings.connectivity.max.http.connections" />}
/>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.connectivity.dpd.heading" />
<Trans id="settings.connectivity.dpd.heading" />
</ModalFormSectionHeader>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'dhtPort')}
id="dhtPort"
label={<FormattedMessage id="settings.connectivity.dht.port.label" />}
label={<Trans id="settings.connectivity.dht.port.label" />}
width="one-quarter"
/>
<Checkbox
@@ -81,7 +81,7 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
id="dht"
labelOffset
matchTextboxHeight>
<FormattedMessage id="settings.connectivity.dht.label" />
<Trans id="settings.connectivity.dht.label" />
</Checkbox>
<Checkbox
defaultChecked={getChangedClientSetting(changedClientSettings, 'protocolPex')}
@@ -89,41 +89,41 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
id="protocolPex"
labelOffset
matchTextboxHeight>
<FormattedMessage id="settings.connectivity.peer.exchange.label" />
<Trans id="settings.connectivity.peer.exchange.label" />
</Checkbox>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.connectivity.peers.heading" />
<Trans id="settings.connectivity.peers.heading" />
</ModalFormSectionHeader>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMinPeersNormal')}
id="throttleMinPeersNormal"
label={<FormattedMessage id="settings.connectivity.peers.min.label" />}
label={<Trans id="settings.connectivity.peers.min.label" />}
/>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxPeersNormal')}
id="throttleMaxPeersNormal"
label={<FormattedMessage id="settings.connectivity.peers.max.label" />}
label={<Trans id="settings.connectivity.peers.max.label" />}
/>
</FormRow>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMinPeersSeed')}
id="throttleMinPeersSeed"
label={<FormattedMessage id="settings.connectivity.peers.seeding.min.label" />}
label={<Trans id="settings.connectivity.peers.seeding.min.label" />}
/>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxPeersSeed')}
id="throttleMaxPeersSeed"
label={<FormattedMessage id="settings.connectivity.peers.seeding.max.label" />}
label={<Trans id="settings.connectivity.peers.seeding.max.label" />}
/>
</FormRow>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'trackersNumWant')}
id="trackersNumWant"
label={<FormattedMessage id="settings.connectivity.peers.desired.label" />}
label={<Trans id="settings.connectivity.peers.desired.label" />}
width="one-half"
/>
</FormRow>

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import {Form, FormRow} from '@client/ui';
@@ -15,7 +15,7 @@ interface DiskUsageTabProps {
const DiskUsageTab: FC<DiskUsageTabProps> = (props: DiskUsageTabProps) => (
<Form>
<ModalFormSectionHeader>
<FormattedMessage id="settings.diskusage.mount.points" />
<Trans id="settings.diskusage.mount.points" />
</ModalFormSectionHeader>
<FormRow>
<MountPointsList onSettingsChange={props.onSettingsChange} />

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import {Checkbox, Form, FormRow, Textbox} from '@client/ui';
@@ -27,20 +27,20 @@ const ResourcesTab: FC<ResourcesTabProps> = ({onClientSettingsChange}: Resources
onClientSettingsChange(newChangedClientSettings);
}}>
<ModalFormSectionHeader>
<FormattedMessage id="settings.resources.disk.heading" />
<Trans id="settings.resources.disk.heading" />
</ModalFormSectionHeader>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'directoryDefault')}
id="directoryDefault"
label={<FormattedMessage id="settings.resources.disk.download.location.label" />}
label={<Trans id="settings.resources.disk.download.location.label" />}
/>
</FormRow>
<FormRow>
<Textbox
defaultValue={getChangedClientSetting(changedClientSettings, 'networkMaxOpenFiles')}
id="networkMaxOpenFiles"
label={<FormattedMessage id="settings.resources.max.open.files" />}
label={<Trans id="settings.resources.max.open.files" />}
width="one-half"
/>
<Checkbox
@@ -49,11 +49,11 @@ const ResourcesTab: FC<ResourcesTabProps> = ({onClientSettingsChange}: Resources
id="piecesHashOnCompletion"
labelOffset
matchTextboxHeight>
<FormattedMessage id="settings.resources.disk.check.hash.label" />
<Trans id="settings.resources.disk.check.hash.label" />
</Checkbox>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.resources.memory.heading" />
<Trans id="settings.resources.memory.heading" />
</ModalFormSectionHeader>
<FormRow>
<Textbox
@@ -61,7 +61,7 @@ const ResourcesTab: FC<ResourcesTabProps> = ({onClientSettingsChange}: Resources
id="piecesMemoryMax"
label={
<div>
<FormattedMessage id="settings.resources.memory.max.label" /> <em className="unit">(MB)</em>
<Trans id="settings.resources.memory.max.label" /> <em className="unit">(MB)</em>
</div>
}
width="one-half"

View File

@@ -1,5 +1,5 @@
import {FC, useState} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {useMedia} from 'react-use';
import type {ClientSettings} from '@shared/types/ClientSettings';
@@ -19,7 +19,7 @@ import UITab from './UITab';
import UIStore from '../../../stores/UIStore';
const SettingsModal: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
const isSmallScreen = useMedia('(max-width: 720px)');
const [changedClientSettings, setChangedClientSettings] = useState<Partial<ClientSettings>>({});
@@ -47,61 +47,47 @@ const SettingsModal: FC = () => {
onClientSettingsChange: handleClientSettingsChange,
onSettingsChange: handleFloodSettingsChange,
},
label: intl.formatMessage({
id: 'settings.tabs.bandwidth',
}),
label: i18n._('settings.tabs.bandwidth'),
},
connectivity: {
content: ConnectivityTab,
props: {
onClientSettingsChange: handleClientSettingsChange,
},
label: intl.formatMessage({
id: 'settings.tabs.connectivity',
}),
label: i18n._('settings.tabs.connectivity'),
},
resources: {
content: ResourcesTab,
props: {
onClientSettingsChange: handleClientSettingsChange,
},
label: intl.formatMessage({
id: 'settings.tabs.resources',
}),
label: i18n._('settings.tabs.resources'),
},
...(ConfigStore.authMethod !== 'none'
? {
authentication: {
content: AuthTab,
label: intl.formatMessage({
id: 'settings.tabs.authentication',
}),
label: i18n._('settings.tabs.authentication'),
},
}
: {}),
ui: {
content: UITab,
label: intl.formatMessage({
id: 'settings.tabs.userinterface',
}),
label: i18n._('settings.tabs.userinterface'),
props: {
onSettingsChange: handleFloodSettingsChange,
},
},
diskusage: {
content: DiskUsageTab,
label: intl.formatMessage({
id: 'settings.tabs.diskusage',
}),
label: i18n._('settings.tabs.diskusage'),
props: {
onSettingsChange: handleFloodSettingsChange,
},
},
about: {
content: AboutTab,
label: intl.formatMessage({
id: 'settings.tabs.about',
}),
label: i18n._('settings.tabs.about'),
},
};
@@ -110,9 +96,7 @@ const SettingsModal: FC = () => {
actions={[
{
clickHandler: null,
content: intl.formatMessage({
id: 'button.cancel',
}),
content: i18n._('button.cancel'),
triggerDismiss: true,
type: 'tertiary',
},
@@ -132,17 +116,13 @@ const SettingsModal: FC = () => {
});
},
isLoading: isSavingSettings,
content: intl.formatMessage({
id: 'button.save',
}),
content: i18n._('button.save'),
triggerDismiss: false,
type: 'primary',
},
]}
size="large"
heading={intl.formatMessage({
id: 'settings.tabs.heading',
})}
heading={i18n._('settings.tabs.heading')}
orientation={isSmallScreen ? 'horizontal' : 'vertical'}
tabs={tabs}
/>

View File

@@ -1,5 +1,5 @@
import {FormattedMessage, useIntl} from 'react-intl';
import {FC, useState} from 'react';
import {Trans, useLingui} from '@lingui/react';
import {Form, FormRow, Select, SelectItem, Radio} from '@client/ui';
import Languages from '@client/constants/Languages';
@@ -18,7 +18,7 @@ interface UITabProps {
}
const UITab: FC<UITabProps> = ({onSettingsChange}: UITabProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const [torrentListViewSize, setTorrentListViewSize] = useState(SettingStore.floodSettings.torrentListViewSize);
const [selectedLanguage, setSelectedLanguage] = useState(SettingStore.floodSettings.language);
const [UITagSelectorMode, setUITagSelectorMode] = useState(SettingStore.floodSettings.UITagSelectorMode);
@@ -46,55 +46,53 @@ const UITab: FC<UITabProps> = ({onSettingsChange}: UITabProps) => {
}
}}>
<ModalFormSectionHeader key="locale-header">
<FormattedMessage id="settings.ui.language" />
<Trans id="settings.ui.language" />
</ModalFormSectionHeader>
<FormRow key="locale-selection">
<Select defaultID={selectedLanguage} id="language">
{Object.keys(Languages).map((languageID) => (
<SelectItem key={languageID} id={languageID}>
{Languages[languageID as 'auto'].id != null
? intl.formatMessage({
id: Languages[languageID as 'auto'].id,
})
? i18n._(Languages[languageID as 'auto'].id)
: Languages[languageID as Language]}
</SelectItem>
))}
</Select>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.ui.tag.selector.mode" />
<Trans id="settings.ui.tag.selector.mode" />
</ModalFormSectionHeader>
<FormRow>
<Radio defaultChecked={UITagSelectorMode === 'single'} groupID="ui-tag-selector-mode" id="single" width="auto">
<FormattedMessage id="settings.ui.tag.selector.mode.single" />
<Trans id="settings.ui.tag.selector.mode.single" />
</Radio>
<Radio defaultChecked={UITagSelectorMode === 'multi'} groupID="ui-tag-selector-mode" id="multi" width="auto">
<FormattedMessage id="settings.ui.tag.selector.mode.multi" />
<Trans id="settings.ui.tag.selector.mode.multi" />
</Radio>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.ui.torrent.list" />
<Trans id="settings.ui.torrent.list" />
</ModalFormSectionHeader>
<FormRow>
<Radio defaultChecked={torrentListViewSize === 'expanded'} groupID="ui-torrent-size" id="expanded" width="auto">
<FormattedMessage id="settings.ui.torrent.size.expanded" />
<Trans id="settings.ui.torrent.size.expanded" />
</Radio>
<Radio
defaultChecked={torrentListViewSize === 'condensed'}
groupID="ui-torrent-size"
id="condensed"
width="auto">
<FormattedMessage id="settings.ui.torrent.size.condensed" />
<Trans id="settings.ui.torrent.size.condensed" />
</Radio>
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.ui.displayed.details" />
<Trans id="settings.ui.displayed.details" />
</ModalFormSectionHeader>
<FormRow>
<TorrentListColumnsList torrentListViewSize={torrentListViewSize} onSettingsChange={onSettingsChange} />
</FormRow>
<ModalFormSectionHeader>
<FormattedMessage id="settings.ui.displayed.context.menu.items" />
<Trans id="settings.ui.displayed.context.menu.items" />
</ModalFormSectionHeader>
<FormRow>
<TorrentContextMenuActionsList onSettingsChange={onSettingsChange} />

View File

@@ -1,5 +1,5 @@
import {Component} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import {Checkbox} from '@client/ui';
import DiskUsageStore from '@client/stores/DiskUsageStore';
@@ -88,7 +88,7 @@ class MountPointsList extends Component<MountPointsListProps, MountPointsListSta
<Checkbox
defaultChecked={visible}
onClick={(event) => this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}>
<FormattedMessage id="settings.diskusage.show" />
<Trans id="settings.diskusage.show" />
</Checkbox>
</span>
);

View File

@@ -1,5 +1,5 @@
import {Component} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import {Checkbox} from '@client/ui';
import SettingStore from '@client/stores/SettingStore';
@@ -76,7 +76,7 @@ class TorrentContextMenuActionsList extends Component<
<Checkbox
defaultChecked={visible}
onClick={(event) => this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}>
<FormattedMessage id="settings.ui.torrent.context.menu.items.show" />
<Trans id="settings.ui.torrent.context.menu.items.show" />
</Checkbox>
</span>
);
@@ -85,7 +85,7 @@ class TorrentContextMenuActionsList extends Component<
const content = (
<div className="sortable-list__content sortable-list__content__wrapper">
<span className="sortable-list__content sortable-list__content--primary">
<FormattedMessage id={TorrentContextMenuActions[id].id} />
<Trans id={TorrentContextMenuActions[id]} />
</span>
{checkbox}
</div>

View File

@@ -1,5 +1,5 @@
import {Component, ReactNode} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import {Checkbox} from '@client/ui';
import {Error} from '@client/ui/icons';
@@ -95,7 +95,7 @@ class TorrentListColumnsList extends Component<TorrentListColumnsListProps, Torr
<Checkbox
defaultChecked={visible}
onClick={(event) => this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}>
<FormattedMessage id="settings.ui.torrent.details.enabled" />
<Trans id="settings.ui.torrent.details.enabled" />
</Checkbox>
</span>
);
@@ -106,7 +106,7 @@ class TorrentListColumnsList extends Component<TorrentListColumnsListProps, Torr
this.props.torrentListViewSize === 'expanded' &&
index < this.state.torrentListColumns.length - 1
) {
const tooltipContent = <FormattedMessage id="settings.ui.torrent.details.tags.placement" />;
const tooltipContent = <Trans id="settings.ui.torrent.details.tags.placement" />;
warning = (
<Tooltip
@@ -128,7 +128,7 @@ class TorrentListColumnsList extends Component<TorrentListColumnsListProps, Torr
<div className="sortable-list__content sortable-list__content__wrapper">
{warning}
<span className="sortable-list__content sortable-list__content--primary">
<FormattedMessage id={TorrentListColumns[id].id} />
<Trans id={TorrentListColumns[id]} />
</span>
{checkbox}
</div>

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {FormattedMessage, useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import {FC, useEffect, useState} from 'react';
import {Trans, useLingui} from '@lingui/react';
import {Button, Checkbox, Form, FormRow, FormRowItem, Select, SelectItem} from '@client/ui';
import ConfigStore from '@client/stores/ConfigStore';
@@ -19,7 +19,7 @@ const TorrentContents: FC = observer(() => {
const [contents, setContents] = useState<TorrentContent[]>([]);
const [itemsTree, setItemsTree] = useState<TorrentContentSelectionTree>({});
const [selectedIndices, setSelectedIndices] = useState<number[]>([]);
const intl = useIntl();
const {i18n} = useLingui();
useEffect(() => {
if (UIStore.activeModal?.id === 'torrent-details') {
@@ -85,7 +85,7 @@ const TorrentContents: FC = observer(() => {
directoryHeadingIconContent = <Disk />;
fileDetailContent = (
<div className="directory-tree__node directory-tree__node--file">
<FormattedMessage id="torrents.details.files.loading" />
<Trans id="torrents.details.files.loading" />
</div>
);
}
@@ -128,7 +128,7 @@ const TorrentContents: FC = observer(() => {
<div className="directory-tree__selection-toolbar">
<FormRow align="center">
<FormRowItem width="one-quarter" grow={false} shrink={false}>
<FormattedMessage
<Trans
id="torrents.details.selected.files"
values={{
count: selectedIndices.length,
@@ -154,7 +154,7 @@ const TorrentContents: FC = observer(() => {
}}
grow={false}
shrink={false}>
<FormattedMessage
<Trans
id="torrents.details.files.download.file"
values={{
count: selectedIndices.length,
@@ -163,23 +163,11 @@ const TorrentContents: FC = observer(() => {
</Button>
<Select id="file-priority" persistentPlaceholder shrink={false} defaultID="">
<SelectItem id={-1} isPlaceholder>
<FormattedMessage id="torrents.details.selected.files.set.priority" />
</SelectItem>
<SelectItem id={0}>
{intl.formatMessage({
id: 'priority.dont.download',
})}
</SelectItem>
<SelectItem id={1}>
{intl.formatMessage({
id: 'priority.normal',
})}
</SelectItem>
<SelectItem id={2}>
{intl.formatMessage({
id: 'priority.high',
})}
<Trans id="torrents.details.selected.files.set.priority" />
</SelectItem>
<SelectItem id={0}>{i18n._('priority.dont.download')}</SelectItem>
<SelectItem id={1}>{i18n._('priority.normal')}</SelectItem>
<SelectItem id={2}>{i18n._('priority.high')}</SelectItem>
</Select>
</FormRow>
</div>

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {useMedia} from 'react-use';
import Modal from '../Modal';
@@ -11,40 +11,30 @@ import TorrentPeers from './TorrentPeers';
import TorrentTrackers from './TorrentTrackers';
const TorrentDetailsModal: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
const isSmallScreen = useMedia('(max-width: 720px)');
const tabs = {
'torrent-details': {
content: TorrentGeneralInfo,
label: intl.formatMessage({
id: 'torrents.details.details',
}),
label: i18n._('torrents.details.details'),
},
'torrent-contents': {
content: TorrentContents,
label: intl.formatMessage({
id: 'torrents.details.files',
}),
label: i18n._('torrents.details.files'),
modalContentClasses: 'modal__content--nested-scroll',
},
'torrent-peers': {
content: TorrentPeers,
label: intl.formatMessage({
id: 'torrents.details.peers',
}),
label: i18n._('torrents.details.peers'),
},
'torrent-trackers': {
content: TorrentTrackers,
label: intl.formatMessage({
id: 'torrents.details.trackers',
}),
label: i18n._('torrents.details.trackers'),
},
'torrent-mediainfo': {
content: TorrentMediainfo,
label: intl.formatMessage({
id: 'torrents.details.mediainfo',
}),
label: i18n._('torrents.details.mediainfo'),
modalContentClasses: 'modal__content--nested-scroll',
},
};

View File

@@ -1,6 +1,6 @@
import {FC} from 'react';
import {FormattedMessage, FormattedNumber, useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans, useLingui} from '@lingui/react';
import type {TorrentProperties} from '@shared/types/Torrent';
@@ -16,7 +16,7 @@ const getTags = (tags: TorrentProperties['tags']) =>
));
const TorrentGeneralInfo: FC = observer(() => {
const intl = useIntl();
const {i18n} = useLingui();
if (UIStore.activeModal?.id !== 'torrent-details') {
return null;
@@ -39,7 +39,7 @@ const TorrentGeneralInfo: FC = observer(() => {
const VALUE_NOT_AVAILABLE = (
<span className="not-available">
<FormattedMessage id="torrents.details.general.none" />
<Trans id="torrents.details.general.none" />
</span>
);
@@ -49,32 +49,34 @@ const TorrentGeneralInfo: FC = observer(() => {
<tbody>
<tr className="torrent-details__table__heading">
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
<FormattedMessage id="torrents.details.general.heading.general" />
<Trans id="torrents.details.general.heading.general" />
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--dateAdded">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.date.added" />
<Trans id="torrents.details.general.date.added" />
</td>
<td className="torrent-details__detail__value">
{dateAdded
? `${intl.formatDate(dateAdded, {
? `${i18n.date(dateAdded, {
year: 'numeric',
month: 'long',
day: '2-digit',
})} ${intl.formatTime(dateAdded)}`
hour: 'numeric',
minute: 'numeric',
})}`
: VALUE_NOT_AVAILABLE}
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--location">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.location" />
<Trans id="torrents.details.general.location" />
</td>
<td className="torrent-details__detail__value">{torrent.directory}</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--tags">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.tags" />
<Trans id="torrents.details.general.tags" />
</td>
<td className="torrent-details__detail__value">
{torrent.tags.length ? getTags(torrent.tags) : VALUE_NOT_AVAILABLE}
@@ -82,76 +84,78 @@ const TorrentGeneralInfo: FC = observer(() => {
</tr>
<tr className="torrent-details__table__heading">
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
<FormattedMessage id="torrents.details.general.heading.transfer" />
<Trans id="torrents.details.general.heading.transfer" />
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--downloaded">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.downloaded" />
<Trans id="torrents.details.general.downloaded" />
</td>
<td className="torrent-details__detail__value">
<FormattedNumber value={torrent.percentComplete} />
{i18n.number(torrent.percentComplete)}
<em className="unit">%</em>
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--peers">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.peers" />
<Trans id="torrents.details.general.peers" />
</td>
<td className="torrent-details__detail__value">
<FormattedMessage
<Trans
id="torrents.details.general.connected"
values={{
connectedCount: torrent.peersConnected,
connected: <FormattedNumber value={torrent.peersConnected} />,
total: <FormattedNumber value={torrent.peersTotal} />,
connected: i18n.number(torrent.peersConnected),
total: i18n.number(torrent.peersTotal),
}}
/>
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--seeds">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.seeds" />
<Trans id="torrents.details.general.seeds" />
</td>
<td className="torrent-details__detail__value">
<FormattedMessage
<Trans
id="torrents.details.general.connected"
values={{
connectedCount: torrent.seedsConnected,
connected: <FormattedNumber value={torrent.seedsConnected} />,
total: <FormattedNumber value={torrent.seedsTotal} />,
connected: i18n.number(torrent.seedsConnected),
total: i18n.number(torrent.seedsTotal),
}}
/>
</td>
</tr>
<tr className="torrent-details__table__heading">
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
<FormattedMessage id="torrents.details.general.heading.torrent" />
<Trans id="torrents.details.general.heading.torrent" />
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--created">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.date.created" />
<Trans id="torrents.details.general.date.created" />
</td>
<td className="torrent-details__detail__value">
{creation
? `${intl.formatDate(creation, {
? `${i18n.date(creation, {
year: 'numeric',
month: 'long',
day: '2-digit',
})} ${intl.formatTime(creation)}`
hour: 'numeric',
minute: 'numeric',
})}`
: VALUE_NOT_AVAILABLE}
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--hash">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.hash" />
<Trans id="torrents.details.general.hash" />
</td>
<td className="torrent-details__detail__value">{torrent.hash}</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--size">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.size" />
<Trans id="torrents.details.general.size" />
</td>
<td className="torrent-details__detail__value">
<Size value={torrent.sizeBytes} />
@@ -159,26 +163,22 @@ const TorrentGeneralInfo: FC = observer(() => {
</tr>
<tr className="torrent-details__detail torrent-details__detail--type">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.type" />
<Trans id="torrents.details.general.type" />
</td>
<td className="torrent-details__detail__value">
{torrent.isPrivate
? intl.formatMessage({
id: 'torrents.details.general.type.private',
})
: intl.formatMessage({
id: 'torrents.details.general.type.public',
})}
? i18n._('torrents.details.general.type.private')
: i18n._('torrents.details.general.type.public')}
</td>
</tr>
<tr className="torrent-details__table__heading">
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
<FormattedMessage id="torrents.details.general.heading.tracker" />
<Trans id="torrents.details.general.heading.tracker" />
</td>
</tr>
<tr className="torrent-details__detail torrent-details__detail--tracker-message">
<td className="torrent-details__detail__label">
<FormattedMessage id="torrents.details.general.tracker.message" />
<Trans id="torrents.details.general.tracker.message" />
</td>
<td className="torrent-details__detail__value">
{torrent.message ? torrent.message : VALUE_NOT_AVAILABLE}

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {FC, useEffect, useState} from 'react';
import {FormattedMessage, FormattedNumber} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans, useLingui} from '@lingui/react';
import {Clock, DownloadThick, Ratio, Start, Stop, UploadThick} from '@client/ui/icons';
import TorrentActions from '@client/actions/TorrentActions';
@@ -16,6 +16,7 @@ import ProgressBar from '../../general/ProgressBar';
import Size from '../../general/Size';
const TorrentHeading: FC = observer(() => {
const {i18n} = useLingui();
const torrent =
UIStore.activeModal?.id === 'torrent-details' ? TorrentStore.torrents[UIStore.activeModal.hash] : undefined;
const [torrentStatus, setTorrentStatus] = useState<'start' | 'stop'>('stop');
@@ -61,7 +62,7 @@ const TorrentHeading: FC = observer(() => {
</li>
<li className="torrent-details__sub-heading__tertiary">
<Ratio />
<FormattedNumber value={torrent.ratio} />
{i18n.number(torrent.ratio)}
</li>
<li className="torrent-details__sub-heading__tertiary">
<Clock />
@@ -95,7 +96,7 @@ const TorrentHeading: FC = observer(() => {
});
}}>
<Start />
<FormattedMessage id="torrents.details.actions.start" />
<Trans id="torrents.details.actions.start" />
</li>
<li
className={classnames('torrent-details__sub-heading__tertiary', 'torrent-details__action', {
@@ -109,7 +110,7 @@ const TorrentHeading: FC = observer(() => {
});
}}>
<Stop />
<FormattedMessage id="torrents.details.actions.stop" />
<Trans id="torrents.details.actions.stop" />
</li>
</ul>
</div>

View File

@@ -1,6 +1,6 @@
import axios, {CancelTokenSource} from 'axios';
import {FC, useEffect, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import {Button} from '@client/ui';
import {Checkmark, Clipboard} from '@client/ui/icons';
@@ -10,7 +10,7 @@ import UIStore from '@client/stores/UIStore';
import Tooltip from '../../general/Tooltip';
const TorrentMediainfo: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
const cancelToken = useRef<CancelTokenSource>(axios.CancelToken.source());
const clipboardRef = useRef<HTMLInputElement>(null);
const [mediainfo, setMediainfo] = useState<string | null>(null);
@@ -53,14 +53,12 @@ const TorrentMediainfo: FC = () => {
<div className="mediainfo__toolbar">
<div className="mediainfo__toolbar__item">
<span className="torrent-details__table__heading--tertiary">
<FormattedMessage id={headingMessageId} />
<Trans id={headingMessageId} />
</span>
</div>
{mediainfo && (
<Tooltip
content={intl.formatMessage({
id: isCopiedToClipboard ? 'general.clipboard.copied' : 'general.clipboard.copy',
})}
content={i18n._(isCopiedToClipboard ? 'general.clipboard.copied' : 'general.clipboard.copy')}
wrapperClassName="tooltip__wrapper mediainfo__toolbar__item">
<Button
priority="tertiary"

View File

@@ -1,5 +1,5 @@
import {FC, Suspense, useEffect, useState} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import {useInterval} from 'react-use';
import {CheckmarkThick, CountryFlag, Lock, Spinner} from '@client/ui/icons';
@@ -37,7 +37,7 @@ const TorrentPeers: FC = () => {
<thead className="torrent-details__table__heading">
<tr>
<th className="torrent-details__table__heading--primary">
<FormattedMessage id="torrents.details.peers" />
<Trans id="torrents.details.peers" />
<Badge>{peers.length}</Badge>
</th>
<th className="torrent-details__table__heading--secondary">DL</th>

View File

@@ -1,5 +1,5 @@
import {FC, useEffect, useState} from 'react';
import {FormattedMessage} from 'react-intl';
import {Trans} from '@lingui/react';
import type {TorrentTracker} from '@shared/types/TorrentTracker';
@@ -36,11 +36,11 @@ const TorrentTrackers: FC = () => {
<thead className="torrent-details__table__heading">
<tr>
<th className="torrent-details__table__heading--primary">
<FormattedMessage id="torrents.details.trackers" />
<Trans id="torrents.details.trackers" />
<Badge>{trackerCount}</Badge>
</th>
<th className="torrent-details__table__heading--secondary">
<FormattedMessage id="torrents.details.trackers.type" />
<Trans id="torrents.details.trackers.type" />
</th>
</tr>
</thead>

View File

@@ -1,6 +1,6 @@
import {FC, ReactNode, ReactNodeArray} from 'react';
import {FormattedMessage} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans} from '@lingui/react';
import type {Disk} from '@shared/types/DiskUsage';
@@ -46,9 +46,9 @@ const DiskUsage: FC = observer(() => {
<Tooltip
content={
<ul className="diskusage__details-list">
<DiskUsageTooltipItem value={d.used} label={<FormattedMessage id="status.diskusage.used" />} />
<DiskUsageTooltipItem value={d.avail} label={<FormattedMessage id="status.diskusage.free" />} />
<DiskUsageTooltipItem value={d.size} label={<FormattedMessage id="status.diskusage.total" />} />
<DiskUsageTooltipItem value={d.used} label={<Trans id="status.diskusage.used" />} />
<DiskUsageTooltipItem value={d.avail} label={<Trans id="status.diskusage.free" />} />
<DiskUsageTooltipItem value={d.size} label={<Trans id="status.diskusage.total" />} />
</ul>
}
position="top"
@@ -69,7 +69,7 @@ const DiskUsage: FC = observer(() => {
return (
<ul className="sidebar-filter sidebar__item">
<li className="sidebar-filter__item sidebar-filter__item--heading">
<FormattedMessage id="status.diskusage.title" />
<Trans id="status.diskusage.title" />
</li>
{diskNodes}
</ul>

View File

@@ -1,25 +1,18 @@
import {defineMessages, useIntl} from 'react-intl';
import {FC, useRef} from 'react';
import {useLingui} from '@lingui/react';
import {Feed} from '@client/ui/icons';
import UIActions from '@client/actions/UIActions';
import Tooltip from '../general/Tooltip';
const MESSAGES = defineMessages({
feeds: {
id: 'sidebar.button.feeds',
},
});
const FeedsButton: FC = () => {
const intl = useIntl();
const label = intl.formatMessage(MESSAGES.feeds);
const {i18n} = useLingui();
const tooltipRef = useRef<Tooltip>(null);
return (
<Tooltip
content={label}
content={i18n._('sidebar.button.feeds')}
onClick={() => {
if (tooltipRef.current != null) {
tooltipRef.current.dismissTooltip();

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import AuthActions from '@client/actions/AuthActions';
import ConfigStore from '@client/stores/ConfigStore';
@@ -8,7 +8,7 @@ import {Logout} from '@client/ui/icons';
import Tooltip from '../general/Tooltip';
const LogoutButton: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
if (ConfigStore.authMethod === 'none') {
return null;
@@ -16,9 +16,7 @@ const LogoutButton: FC = () => {
return (
<Tooltip
content={intl.formatMessage({
id: 'sidebar.button.log.out',
})}
content={i18n._('sidebar.button.log.out')}
onClick={() =>
AuthActions.logout().then(() => {
window.location.reload();

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {defineMessages, useIntl} from 'react-intl';
import {FC, useEffect, useRef, useState} from 'react';
import {observer} from 'mobx-react';
import {useLingui} from '@lingui/react';
import FloodActions from '@client/actions/FloodActions';
import {ChevronLeft, ChevronRight, LoadingIndicatorDots, Notification as NotificationIcon} from '@client/ui/icons';
@@ -20,27 +20,6 @@ const fetchNotifications = (paginationStart: number) =>
start: paginationStart,
});
const MESSAGES = defineMessages({
'notification.torrent.finished.heading': {
id: 'notification.torrent.finished.heading',
},
'notification.torrent.finished.body': {
id: 'notification.torrent.finished.body',
},
'notification.torrent.errored.heading': {
id: 'notification.torrent.errored.heading',
},
'notification.torrent.errored.body': {
id: 'notification.torrent.errored.body',
},
'notification.feed.torrent.added.heading': {
id: 'notification.feed.torrent.added.heading',
},
'notification.feed.torrent.added.body': {
id: 'notification.feed.torrent.added.body',
},
});
interface NotificationTopToolbarProps {
paginationStart: number;
notificationTotal: number;
@@ -50,7 +29,7 @@ const NotificationTopToolbar: FC<NotificationTopToolbarProps> = ({
paginationStart,
notificationTotal,
}: NotificationTopToolbarProps) => {
const intl = useIntl();
const {i18n} = useLingui();
if (notificationTotal > NOTIFICATIONS_PER_PAGE) {
let countStart = paginationStart + 1;
@@ -67,19 +46,13 @@ const NotificationTopToolbar: FC<NotificationTopToolbarProps> = ({
return (
<div className="toolbar toolbar--dark toolbar--top tooltip__toolbar tooltip__content--padding-surrogate">
<span className="toolbar__item toolbar__item--label">
{`${intl.formatMessage({
id: 'notification.showing',
})} `}
{`${i18n._('notification.showing')} `}
<strong>
{countStart}
{` ${intl.formatMessage({
id: 'general.to',
})} `}
{` ${i18n._('general.to')} `}
{countEnd}
</strong>
{` ${intl.formatMessage({
id: 'general.of',
})} `}
{` ${i18n._('general.of')} `}
<strong>{notificationTotal}</strong>
</span>
</div>
@@ -95,35 +68,24 @@ interface NotificationItemProps {
}
const NotificationItem: FC<NotificationItemProps> = ({index, notification}: NotificationItemProps) => {
const intl = useIntl();
const date = intl.formatDate(notification.ts, {
year: 'numeric',
month: 'long',
day: '2-digit',
});
const time = intl.formatTime(notification.ts);
const {i18n} = useLingui();
return (
<li className="notifications__list__item" key={index}>
<div className="notification__heading">
<span className="notification__category">
{intl.formatMessage(
MESSAGES[`${notification.id}.heading` as keyof typeof MESSAGES] || {id: 'general.error.unknown'},
)}
</span>
<span className="notification__category">{i18n._(`${notification.id}.heading`)}</span>
{' — '}
<span className="notification__timestamp">{`${date} ${intl.formatMessage({
id: 'general.at',
})} ${time}`}</span>
</div>
<div className="notification__message">
{intl.formatMessage(
MESSAGES[`${notification.id}.body` as keyof typeof MESSAGES] || {
id: 'general.error.unknown',
},
notification.data,
)}
<span className="notification__timestamp">
{i18n.date(new Date(notification.ts), {
year: 'numeric',
month: 'long',
day: '2-digit',
hour: 'numeric',
minute: 'numeric',
})}
</span>
</div>
<div className="notification__message">{i18n._(`${notification.id}.body`, notification.data)}</div>
</li>
);
};
@@ -143,7 +105,7 @@ const NotificationBottomToolbar: FC<NotificationBottomToolbarProps> = ({
onClearClick,
onNextClick,
}: NotificationBottomToolbarProps) => {
const intl = useIntl();
const {i18n} = useLingui();
if (notificationTotal > 0) {
const newerButtonClass = classnames('toolbar__item toolbar__item--button', 'tooltip__content--padding-surrogate', {
@@ -178,9 +140,7 @@ const NotificationBottomToolbar: FC<NotificationBottomToolbarProps> = ({
className="toolbar__item toolbar__item--button
tooltip__content--padding-surrogate"
onClick={onClearClick}>
{intl.formatMessage({
id: 'notification.clear.all',
})}
{i18n._('notification.clear.all')}
</li>
<li className={olderButtonClass} onClick={onNextClick}>
{`${olderFrom} - ${olderTo}`}
@@ -194,7 +154,7 @@ const NotificationBottomToolbar: FC<NotificationBottomToolbarProps> = ({
};
const NotificationsButton: FC = observer(() => {
const intl = useIntl();
const {i18n} = useLingui();
const tooltipRef = useRef<Tooltip>(null);
const notificationsListRef = useRef<HTMLUListElement>(null);
@@ -264,9 +224,7 @@ const NotificationsButton: FC = observer(() => {
</div>
) : (
<div className="notifications tooltip__content--padding-surrogate" style={{textAlign: 'center'}}>
{intl.formatMessage({
id: 'notification.no.notification',
})}
{i18n._('notification.no.notification')}
</div>
)
}

View File

@@ -1,14 +1,14 @@
import classnames from 'classnames';
import {FC, useEffect, useRef} from 'react';
import {observer} from 'mobx-react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Close, Search} from '@client/ui/icons';
import TorrentFilterStore from '@client/stores/TorrentFilterStore';
import UIActions from '@client/actions/UIActions';
const SearchBox: FC = observer(() => {
const intl = useIntl();
const {i18n} = useLingui();
const inputRef = useRef<HTMLInputElement>(null);
const {searchFilter} = TorrentFilterStore.filters;
@@ -48,9 +48,7 @@ const SearchBox: FC = observer(() => {
className="textbox"
ref={inputRef}
type="text"
placeholder={intl.formatMessage({
id: 'sidebar.search.placeholder',
})}
placeholder={i18n._('sidebar.search.placeholder')}
onChange={(event) => {
UIActions.setTorrentsSearchFilter(event.target.value);
}}

View File

@@ -1,25 +1,18 @@
import {defineMessages, useIntl} from 'react-intl';
import {FC, useRef} from 'react';
import {useLingui} from '@lingui/react';
import {Settings} from '@client/ui/icons';
import UIActions from '@client/actions/UIActions';
import Tooltip from '../general/Tooltip';
const MESSAGES = defineMessages({
settings: {
id: 'sidebar.button.settings',
},
});
const SettingsButton: FC = () => {
const intl = useIntl();
const label = intl.formatMessage(MESSAGES.settings);
const {i18n} = useLingui();
const tooltipRef = useRef<Tooltip>(null);
return (
<Tooltip
content={label}
content={i18n._('sidebar.button.settings')}
onClick={() => {
if (tooltipRef.current != null) {
tooltipRef.current.dismissTooltip();

View File

@@ -1,6 +1,6 @@
import classnames from 'classnames';
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import Badge from '../general/Badge';
@@ -15,7 +15,7 @@ interface SidebarFilterProps {
const SidebarFilter: FC<SidebarFilterProps> = (props: SidebarFilterProps) => {
const {isActive, count, slug, icon, handleClick} = props;
const intl = useIntl();
const {i18n} = useLingui();
const classNames = classnames('sidebar-filter__item', {
'is-active': isActive,
@@ -23,16 +23,12 @@ const SidebarFilter: FC<SidebarFilterProps> = (props: SidebarFilterProps) => {
let {name} = props;
if (name === '') {
name = intl.formatMessage({
id: 'filter.all',
});
name = i18n._('filter.all');
} else if (name === 'untagged') {
if (count === 0) {
return null;
}
name = intl.formatMessage({
id: 'filter.untagged',
});
name = i18n._('filter.untagged');
}
if (slug === 'checking' || slug === 'error') {

View File

@@ -1,7 +1,9 @@
import {FC, useRef} from 'react';
import {FormattedMessage, IntlShape, useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import sortedIndex from 'lodash/sortedIndex';
import {Trans, useLingui} from '@lingui/react';
import type {I18n} from '@lingui/core';
import ClientActions from '@client/actions/ClientActions';
import {Limits} from '@client/ui/icons';
@@ -16,16 +18,16 @@ import Tooltip from '../general/Tooltip';
import type {DropdownItem} from '../general/form-elements/Dropdown';
const HumanReadableSpeed: FC<{bytes: number}> = ({bytes}: {bytes: number}) =>
bytes === 0 ? <FormattedMessage id="speed.unlimited" /> : <Size value={bytes} isSpeed precision={1} />;
bytes === 0 ? <Trans id="speed.unlimited" /> : <Size value={bytes} isSpeed precision={1} />;
const getSpeedList = ({
intl,
i18n,
direction,
speedLimits,
throttleGlobalDownSpeed,
throttleGlobalUpSpeed,
}: {
intl: IntlShape;
i18n: I18n;
direction: TransferDirection;
speedLimits: {
download: Array<number>;
@@ -37,8 +39,8 @@ const getSpeedList = ({
const heading = {
className: `dropdown__label dropdown__label--${direction}`,
...(direction === 'download'
? {displayName: intl.formatMessage({id: 'sidebar.speedlimits.download'})}
: {displayName: intl.formatMessage({id: 'sidebar.speedlimits.upload'})}),
? {displayName: i18n._('sidebar.speedlimits.download')}
: {displayName: i18n._('sidebar.speedlimits.upload')}),
selectable: false,
value: null,
};
@@ -92,12 +94,12 @@ const getSpeedList = ({
};
const SpeedLimitDropdown: FC = observer(() => {
const intl = useIntl();
const {i18n} = useLingui();
const tooltipRef = useRef<Tooltip>(null);
const label = intl.formatMessage({id: 'sidebar.button.speedlimits'});
const label = i18n._('sidebar.button.speedlimits');
const speedListOptions = {
intl,
i18n,
speedLimits: SettingStore.floodSettings.speedLimits,
throttleGlobalDownSpeed: SettingStore.clientSettings?.throttleGlobalDownSpeed ?? 0,
throttleGlobalUpSpeed: SettingStore.clientSettings?.throttleGlobalUpSpeed ?? 0,

View File

@@ -1,6 +1,6 @@
import {FC} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans, useLingui} from '@lingui/react';
import {Active, All, Completed, DownloadSmall, Error, Inactive, Stop, Spinner, UploadSmall} from '@client/ui/icons';
import TorrentFilterStore from '@client/stores/TorrentFilterStore';
@@ -11,7 +11,7 @@ import type {TorrentStatus} from '@shared/constants/torrentStatusMap';
import SidebarFilter from './SidebarFilter';
const StatusFilters: FC = observer(() => {
const intl = useIntl();
const {i18n} = useLingui();
const filters: Array<{
label: string;
@@ -19,65 +19,47 @@ const StatusFilters: FC = observer(() => {
icon: JSX.Element;
}> = [
{
label: intl.formatMessage({
id: 'filter.all',
}),
label: i18n._('filter.all'),
slug: '',
icon: <All />,
},
{
label: intl.formatMessage({
id: 'filter.status.downloading',
}),
label: i18n._('filter.status.downloading'),
slug: 'downloading',
icon: <DownloadSmall />,
},
{
label: intl.formatMessage({
id: 'filter.status.seeding',
}),
label: i18n._('filter.status.seeding'),
slug: 'seeding',
icon: <UploadSmall />,
},
{
label: intl.formatMessage({
id: 'filter.status.checking',
}),
label: i18n._('filter.status.checking'),
slug: 'checking',
icon: <Spinner />,
},
{
label: intl.formatMessage({
id: 'filter.status.completed',
}),
label: i18n._('filter.status.completed'),
slug: 'complete',
icon: <Completed />,
},
{
label: intl.formatMessage({
id: 'filter.status.stopped',
}),
label: i18n._('filter.status.stopped'),
slug: 'stopped',
icon: <Stop />,
},
{
label: intl.formatMessage({
id: 'filter.status.active',
}),
label: i18n._('filter.status.active'),
slug: 'active',
icon: <Active />,
},
{
label: intl.formatMessage({
id: 'filter.status.inactive',
}),
label: i18n._('filter.status.inactive'),
slug: 'inactive',
icon: <Inactive />,
},
{
label: intl.formatMessage({
id: 'filter.status.error',
}),
label: i18n._('filter.status.error'),
slug: 'error',
icon: <Error />,
},
@@ -98,7 +80,7 @@ const StatusFilters: FC = observer(() => {
return (
<ul className="sidebar-filter sidebar__item">
<li className="sidebar-filter__item sidebar-filter__item--heading">
<FormattedMessage id="filter.status.title" />
<Trans id="filter.status.title" />
</li>
{filterElements}
</ul>

View File

@@ -1,6 +1,6 @@
import {FC} from 'react';
import {FormattedMessage} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans} from '@lingui/react';
import SidebarFilter from './SidebarFilter';
import TorrentFilterStore from '../../stores/TorrentFilterStore';
@@ -39,7 +39,7 @@ const TagFilters: FC = observer(() => {
return (
<ul className="sidebar-filter sidebar__item">
<li className="sidebar-filter__item sidebar-filter__item--heading">
<FormattedMessage id="filter.tag.title" />
<Trans id="filter.tag.title" />
</li>
{filterElements}
</ul>

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import ConfigStore from '@client/stores/ConfigStore';
import {ThemeSwitch} from '@client/ui/icons';
@@ -7,13 +7,11 @@ import {ThemeSwitch} from '@client/ui/icons';
import Tooltip from '../general/Tooltip';
const ThemeSwitchButton: FC = () => {
const intl = useIntl();
const {i18n} = useLingui();
return (
<Tooltip
content={intl.formatMessage({
id: ConfigStore.preferDark ? 'sidebar.button.theme.light' : 'sidebar.button.theme.dark',
})}
content={i18n._(ConfigStore.preferDark ? 'sidebar.button.theme.light' : 'sidebar.button.theme.dark')}
onClick={() => ConfigStore.setUserPreferDark(!ConfigStore.preferDark)}
position="bottom"
wrapperClassName="sidebar__action sidebar__icon-button

View File

@@ -1,6 +1,6 @@
import {FC} from 'react';
import {FormattedMessage} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans} from '@lingui/react';
import SidebarFilter from './SidebarFilter';
import TorrentFilterStore from '../../stores/TorrentFilterStore';
@@ -38,7 +38,7 @@ const TrackerFilters: FC = observer(() => {
return (
<ul className="sidebar-filter sidebar__item">
<li className="sidebar-filter__item sidebar-filter__item--heading">
<FormattedMessage id="filter.tracker.title" />
<Trans id="filter.tracker.title" />
</li>
{filterElements}
</ul>

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {defineMessages, useIntl} from 'react-intl';
import {FC} from 'react';
import {observer} from 'mobx-react';
import {useLingui} from '@lingui/react';
import ClientStatusStore from '@client/stores/ClientStatusStore';
import {Download, InfinityIcon, Upload} from '@client/ui/icons';
@@ -15,12 +15,6 @@ import Size from '../general/Size';
import type {TransferRateGraphInspectorPoint} from './TransferRateGraph';
const messages = defineMessages({
ago: {
id: 'general.ago',
},
});
const icons = {
download: <Download />,
infinity: <InfinityIcon />,
@@ -32,7 +26,7 @@ interface TransferRateDetailsProps {
}
const TransferRateDetails: FC<TransferRateDetailsProps> = observer(({inspectorPoint}: TransferRateDetailsProps) => {
const intl = useIntl();
const {i18n} = useLingui();
const getCurrentTransferRate = (direction: TransferDirection, options: {showHoverDuration?: boolean} = {}) => {
const {throttleGlobalDownSpeed = 0, throttleGlobalUpSpeed = 0} = SettingStore.clientSettings || {};
@@ -73,7 +67,7 @@ const TransferRateDetails: FC<TransferRateDetailsProps> = observer(({inspectorPo
timestamp = (
<div className={timestampClasses}>
<Duration
suffix={intl.formatMessage(messages.ago)}
suffix={i18n._('general.ago')}
value={Math.trunc((Date.now() - inspectorPoint.nearestTimestamp) / 1000)}
/>
</div>

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {FC} from 'react';
import {observer} from 'mobx-react';
import {useIntl} from 'react-intl';
import {useLingui} from '@lingui/react';
import {Add, Menu, Remove, Start, Stop} from '@client/ui/icons';
import SettingActions from '@client/actions/SettingActions';
@@ -14,7 +14,7 @@ import Action from './Action';
import SortDropdown from './SortDropdown';
const ActionBar: FC = observer(() => {
const intl = useIntl();
const {i18n} = useLingui();
const {sortTorrents: sortBy, torrentListViewSize} = SettingStore.floodSettings;
const classes = classnames('action-bar', {
@@ -49,9 +49,7 @@ const ActionBar: FC = observer(() => {
<div className="actions action-bar__item action-bar__item--torrent-operations">
<div className="action-bar__group">
<Action
label={intl.formatMessage({
id: 'actionbar.button.start.torrent',
})}
label={i18n._('actionbar.button.start.torrent')}
slug="start-torrent"
icon={<Start />}
clickHandler={() =>
@@ -61,9 +59,7 @@ const ActionBar: FC = observer(() => {
}
/>
<Action
label={intl.formatMessage({
id: 'actionbar.button.stop.torrent',
})}
label={i18n._('actionbar.button.stop.torrent')}
slug="stop-torrent"
icon={<Stop />}
clickHandler={() =>
@@ -75,17 +71,13 @@ const ActionBar: FC = observer(() => {
</div>
<div className="action-bar__group action-bar__group--has-divider">
<Action
label={intl.formatMessage({
id: 'actionbar.button.add.torrent',
})}
label={i18n._('actionbar.button.add.torrent')}
slug="add-torrent"
icon={<Add />}
clickHandler={() => UIActions.displayModal({id: 'add-torrents'})}
/>
<Action
label={intl.formatMessage({
id: 'actionbar.button.remove.torrent',
})}
label={i18n._('actionbar.button.remove.torrent')}
slug="remove-torrent"
icon={<Remove />}
clickHandler={() =>

View File

@@ -1,5 +1,5 @@
import {FC} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {Trans, useLingui} from '@lingui/react';
import type {FloodSettings} from '@shared/types/FloodSettings';
@@ -29,7 +29,7 @@ interface SortDropdownProps {
const SortDropdown: FC<SortDropdownProps> = (props: SortDropdownProps) => {
const {direction, selectedProperty, onSortChange} = props;
const intl = useIntl();
const {i18n} = useLingui();
if (selectedProperty == null) {
return null;
@@ -38,10 +38,10 @@ const SortDropdown: FC<SortDropdownProps> = (props: SortDropdownProps) => {
const header = (
<button className="dropdown__button" type="button">
<label className="dropdown__label">
<FormattedMessage id="torrents.sort.title" />
<Trans id="torrents.sort.title" />
</label>
<span className="dropdown__value">
<FormattedMessage id={TorrentListColumns[selectedProperty]?.id || TorrentListColumns.dateAdded.id} />
<Trans id={TorrentListColumns[selectedProperty]} />
</span>
</button>
);
@@ -56,7 +56,7 @@ const SortDropdown: FC<SortDropdownProps> = (props: SortDropdownProps) => {
return {
displayName: (
<div className="sort-dropdown__item">
{intl.formatMessage(TorrentListColumns[sortProp])}
{i18n._(TorrentListColumns[sortProp])}
{directionIndicator}
</div>
),

View File

@@ -1,7 +1,7 @@
import classnames from 'classnames';
import {forwardRef, MutableRefObject, ReactNodeArray, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {observer} from 'mobx-react';
import {Trans, useLingui} from '@lingui/react';
import {useEnsuredForwardedRef} from 'react-use';
import TorrentListColumns, {TorrentListColumn} from '../../constants/TorrentListColumns';
@@ -28,7 +28,7 @@ const TableHeading = observer(
const tableHeading = useEnsuredForwardedRef<HTMLDivElement>(ref as MutableRefObject<HTMLDivElement>);
const resizeLine = useRef<HTMLDivElement>(null);
const intl = useIntl();
const {i18n} = useLingui();
const handlePointerMove = (event: PointerEvent) => {
let widthDelta = 0;
@@ -79,7 +79,7 @@ const TableHeading = observer(
return accumulator;
}
const labelID = TorrentListColumns[id]?.id;
const labelID = TorrentListColumns[id];
if (labelID == null) {
return accumulator;
}
@@ -121,12 +121,8 @@ const TableHeading = observer(
accumulator.push(
<div className={classes} key={id} onClick={() => onCellClick(id)} style={{width: `${width}px`}}>
<span
className="table__heading__label"
title={intl.formatMessage({
id: labelID,
})}>
<FormattedMessage id={labelID} />
<span className="table__heading__label" title={i18n._(labelID)}>
<Trans id={labelID} />
</span>
{handle}
</div>,

View File

@@ -1,7 +1,7 @@
import {FC, ReactNode, useEffect, useRef} from 'react';
import {FormattedMessage} from 'react-intl';
import {observer} from 'mobx-react';
import {reaction} from 'mobx';
import {Trans} from '@lingui/react';
import type {FixedSizeList} from 'react-window';
@@ -54,7 +54,7 @@ const TorrentList: FC = observer(() => {
content = (
<div className="torrents__alert__wrapper">
<div className="torrents__alert">
<FormattedMessage id="torrents.list.cannot.connect" />
<Trans id="torrents.list.cannot.connect" />
</div>
</div>
);
@@ -62,7 +62,7 @@ const TorrentList: FC = observer(() => {
content = (
<div className="torrents__alert__wrapper">
<div className="torrents__alert">
<FormattedMessage id="torrents.list.no.torrents" />
<Trans id="torrents.list.no.torrents" />
</div>
{TorrentFilterStore.isFilterActive && (
<div className="torrents__alert__action">
@@ -71,7 +71,7 @@ const TorrentList: FC = observer(() => {
TorrentFilterStore.clearAllFilters();
}}
priority="tertiary">
<FormattedMessage id="torrents.list.clear.filters" />
<Trans id="torrents.list.clear.filters" />
</Button>
</div>
)}
@@ -148,7 +148,7 @@ const TorrentList: FC = observer(() => {
<div className="dropzone__icon">
<Files />
</div>
<FormattedMessage id="torrents.list.drop" />
<Trans id="torrents.list.drop" />
</div>
</div>
</TorrentListDropzone>

View File

@@ -39,7 +39,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'start',
label: TorrentContextMenuActions.start.id,
label: TorrentContextMenuActions.start,
clickHandler: () => {
TorrentActions.startTorrents({
hashes: TorrentStore.selectedTorrents,
@@ -49,7 +49,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'stop',
label: TorrentContextMenuActions.stop.id,
label: TorrentContextMenuActions.stop,
clickHandler: () => {
TorrentActions.stopTorrents({
hashes: TorrentStore.selectedTorrents,
@@ -59,7 +59,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'remove',
label: TorrentContextMenuActions.remove.id,
label: TorrentContextMenuActions.remove,
clickHandler: () => {
UIActions.displayModal({id: 'remove-torrents'});
},
@@ -67,7 +67,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'checkHash',
label: TorrentContextMenuActions.checkHash.id,
label: TorrentContextMenuActions.checkHash,
clickHandler: () => {
TorrentActions.checkHash({
hashes: TorrentStore.selectedTorrents,
@@ -80,7 +80,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'setTaxonomy',
label: TorrentContextMenuActions.setTaxonomy.id,
label: TorrentContextMenuActions.setTaxonomy,
clickHandler: () => {
UIActions.displayModal({id: 'set-taxonomy'});
},
@@ -88,7 +88,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'move',
label: TorrentContextMenuActions.move.id,
label: TorrentContextMenuActions.move,
clickHandler: () => {
UIActions.displayModal({id: 'move-torrents'});
},
@@ -96,7 +96,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'setTrackers',
label: TorrentContextMenuActions.setTrackers.id,
label: TorrentContextMenuActions.setTrackers,
clickHandler: () => {
UIActions.displayModal({id: 'set-trackers'});
},
@@ -107,7 +107,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'torrentDetails',
label: TorrentContextMenuActions.torrentDetails.id,
label: TorrentContextMenuActions.torrentDetails,
clickHandler: () => {
UIActions.displayModal({
id: 'torrent-details',
@@ -118,7 +118,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'downloadContents',
label: TorrentContextMenuActions.downloadContents.id,
label: TorrentContextMenuActions.downloadContents,
clickHandler: (e) => {
e.preventDefault();
@@ -136,7 +136,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'downloadMetainfo',
label: TorrentContextMenuActions.downloadMetainfo.id,
label: TorrentContextMenuActions.downloadMetainfo,
clickHandler: (e) => {
e.preventDefault();
@@ -154,7 +154,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'generateMagnet',
label: TorrentContextMenuActions.generateMagnet.id,
label: TorrentContextMenuActions.generateMagnet,
clickHandler: () => {
UIActions.displayModal({id: 'generate-magnet'});
},
@@ -162,7 +162,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'setInitialSeeding',
label: TorrentContextMenuActions.setInitialSeeding.id,
label: TorrentContextMenuActions.setInitialSeeding,
clickHandler: () => {
const {selectedTorrents} = TorrentStore;
TorrentActions.setInitialSeeding({
@@ -176,7 +176,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'setSequential',
label: TorrentContextMenuActions.setSequential.id,
label: TorrentContextMenuActions.setSequential,
clickHandler: () => {
const {selectedTorrents} = TorrentStore;
TorrentActions.setSequential({
@@ -190,7 +190,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
{
type: 'action',
action: 'setPriority',
label: TorrentContextMenuActions.setPriority.id,
label: TorrentContextMenuActions.setPriority,
clickHandler: () => {
if (changePriorityFuncRef.current != null) {
TorrentActions.setPriority({

View File

@@ -1,6 +1,6 @@
import {FormattedNumber} from 'react-intl';
import {forwardRef} from 'react';
import {observer} from 'mobx-react';
import {useLingui} from '@lingui/react';
import ProgressBar from '../general/ProgressBar';
import SettingStore from '../../stores/SettingStore';
@@ -35,6 +35,7 @@ const TorrentListRowExpanded = observer(
}: TorrentListRowExpandedProps,
ref,
) => {
const {i18n} = useLingui();
const columns = SettingStore.floodSettings.torrentListColumns;
const primarySection: React.ReactNodeArray = [
@@ -57,7 +58,7 @@ const TorrentListRowExpanded = observer(
column="percentComplete"
content={(torrent) => (
<span>
<FormattedNumber value={torrent.percentComplete} maximumFractionDigits={1} />
{i18n.number(torrent.percentComplete, {maximumFractionDigits: 1})}
<em className="unit">%</em>
&nbsp;&mdash;&nbsp;
<Size value={torrent.downTotal} />

View File

@@ -1,46 +1,18 @@
const TorrentContextMenuActions = {
start: {
id: 'torrents.list.context.start',
},
stop: {
id: 'torrents.list.context.stop',
},
remove: {
id: 'torrents.list.context.remove',
},
checkHash: {
id: 'torrents.list.context.check.hash',
},
setTaxonomy: {
id: 'torrents.list.context.set.tags',
},
move: {
id: 'torrents.list.context.move',
},
setTrackers: {
id: 'torrents.list.context.set.trackers',
},
torrentDetails: {
id: 'torrents.list.context.details',
},
downloadContents: {
id: 'torrents.list.context.download.contents',
},
downloadMetainfo: {
id: 'torrents.list.context.download.metainfo',
},
generateMagnet: {
id: 'torrents.list.context.generate.magnet',
},
setInitialSeeding: {
id: 'torrents.list.context.initial.seeding',
},
setSequential: {
id: 'torrents.list.context.sequential',
},
setPriority: {
id: 'torrents.list.context.priority',
},
start: 'torrents.list.context.start',
stop: 'torrents.list.context.stop',
remove: 'torrents.list.context.remove',
checkHash: 'torrents.list.context.check.hash',
setTaxonomy: 'torrents.list.context.set.tags',
move: 'torrents.list.context.move',
setTrackers: 'torrents.list.context.set.trackers',
torrentDetails: 'torrents.list.context.details',
downloadContents: 'torrents.list.context.download.contents',
downloadMetainfo: 'torrents.list.context.download.metainfo',
generateMagnet: 'torrents.list.context.generate.magnet',
setInitialSeeding: 'torrents.list.context.initial.seeding',
setSequential: 'torrents.list.context.sequential',
setPriority: 'torrents.list.context.priority',
} as const;
export default TorrentContextMenuActions;

View File

@@ -1,61 +1,23 @@
const TorrentListColumns = {
dateAdded: {
id: 'torrents.properties.date.added',
},
downRate: {
id: 'torrents.properties.download.speed',
},
downTotal: {
id: 'torrents.properties.download.total',
},
eta: {
id: 'torrents.properties.eta',
},
name: {
id: 'torrents.properties.name',
},
peers: {
id: 'torrents.properties.peers',
},
percentComplete: {
id: 'torrents.properties.percentage',
},
ratio: {
id: 'torrents.properties.ratio',
},
seeds: {
id: 'torrents.properties.seeds',
},
sizeBytes: {
id: 'torrents.properties.size',
},
tags: {
id: 'torrents.properties.tags',
},
upRate: {
id: 'torrents.properties.upload.speed',
},
upTotal: {
id: 'torrents.properties.upload.total',
},
dateCreated: {
id: 'torrents.properties.creation.date',
},
directory: {
id: 'torrents.properties.directory',
},
hash: {
id: 'torrents.properties.hash',
},
isPrivate: {
id: 'torrents.properties.is.private',
},
message: {
id: 'torrents.properties.tracker.message',
},
trackerURIs: {
id: 'torrents.properties.trackers',
},
dateAdded: 'torrents.properties.date.added',
downRate: 'torrents.properties.download.speed',
downTotal: 'torrents.properties.download.total',
eta: 'torrents.properties.eta',
name: 'torrents.properties.name',
peers: 'torrents.properties.peers',
percentComplete: 'torrents.properties.percentage',
ratio: 'torrents.properties.ratio',
seeds: 'torrents.properties.seeds',
sizeBytes: 'torrents.properties.size',
tags: 'torrents.properties.tags',
upRate: 'torrents.properties.upload.speed',
upTotal: 'torrents.properties.upload.total',
dateCreated: 'torrents.properties.creation.date',
directory: 'torrents.properties.directory',
hash: 'torrents.properties.hash',
isPrivate: 'torrents.properties.is.private',
message: 'torrents.properties.tracker.message',
trackerURIs: 'torrents.properties.trackers',
} as const;
export default TorrentListColumns;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More