mirror of
https://github.com/zoriya/flood.git
synced 2025-12-06 07:16:18 +00:00
client: migrate to lingui.js
This commit is contained in:
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: [15]
|
node: [15]
|
||||||
check: [check-compiled-i18n, check-source-formatting, check-types, lint]
|
check: [check-source-formatting, check-types, lint]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|||||||
51
.linguirc
Normal file
51
.linguirc
Normal 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"
|
||||||
|
}
|
||||||
@@ -111,7 +111,6 @@ module.exports = {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.cjs', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
|
extensions: ['.cjs', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
|
||||||
alias: {
|
alias: {
|
||||||
'react-intl': 'react-intl/react-intl-no-parser.umd.min.js',
|
|
||||||
'@client': path.resolve('./client/src/javascript'),
|
'@client': path.resolve('./client/src/javascript'),
|
||||||
'@shared': path.resolve('./shared'),
|
'@shared': path.resolve('./shared'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import AppWrapper from './components/AppWrapper';
|
|||||||
import LoadingOverlay from './components/general/LoadingOverlay';
|
import LoadingOverlay from './components/general/LoadingOverlay';
|
||||||
import AsyncIntlProvider from './i18n/languages';
|
import AsyncIntlProvider from './i18n/languages';
|
||||||
import ConfigStore from './stores/ConfigStore';
|
import ConfigStore from './stores/ConfigStore';
|
||||||
import SettingStore from './stores/SettingStore';
|
|
||||||
import UIStore from './stores/UIStore';
|
import UIStore from './stores/UIStore';
|
||||||
import history from './util/history';
|
import history from './util/history';
|
||||||
|
|
||||||
@@ -76,7 +75,7 @@ const FloodApp: FC = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<LoadingOverlay />}>
|
<Suspense fallback={<LoadingOverlay />}>
|
||||||
<AsyncIntlProvider language={SettingStore.floodSettings.language}>
|
<AsyncIntlProvider>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<QueryParamProvider ReactRouterRoute={Route}>
|
<QueryParamProvider ReactRouterRoute={Route}>
|
||||||
<AppWrapper className={ConfigStore.preferDark ? 'dark' : undefined}>
|
<AppWrapper className={ConfigStore.preferDark ? 'dark' : undefined}>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import AlertStore from '@client/stores/AlertStore';
|
import AlertStore from '@client/stores/AlertStore';
|
||||||
import {CircleCheckmark, CircleExclamation} from '@client/ui/icons';
|
import {CircleCheckmark, CircleExclamation} from '@client/ui/icons';
|
||||||
@@ -32,7 +32,7 @@ const Alert: FC<AlertProps> = observer((props: AlertProps) => {
|
|||||||
<li className={alertClasses}>
|
<li className={alertClasses}>
|
||||||
{icon}
|
{icon}
|
||||||
<span className="alert__content">
|
<span className="alert__content">
|
||||||
<FormattedMessage
|
<Trans
|
||||||
id={id}
|
id={id}
|
||||||
values={{
|
values={{
|
||||||
count,
|
count,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useRef, useState} from 'react';
|
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 {Button, Form, FormError, FormRow, Panel, PanelContent, PanelHeader, PanelFooter, Textbox} from '@client/ui';
|
||||||
import AuthActions from '@client/actions/AuthActions';
|
import AuthActions from '@client/actions/AuthActions';
|
||||||
@@ -20,7 +20,7 @@ interface AuthFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
|
const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const formRef = useRef<Form>(null);
|
const formRef = useRef<Form>(null);
|
||||||
const clientConnectionSettingsRef = useRef<ClientConnectionSettings | null>(null);
|
const clientConnectionSettingsRef = useRef<ClientConnectionSettings | null>(null);
|
||||||
const [errorMessage, setErrorMessage] = useState<string | {id: string} | undefined>(undefined);
|
const [errorMessage, setErrorMessage] = useState<string | {id: string} | undefined>(undefined);
|
||||||
@@ -102,30 +102,16 @@ const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
|
|||||||
}}
|
}}
|
||||||
ref={formRef}>
|
ref={formRef}>
|
||||||
<PanelHeader>
|
<PanelHeader>
|
||||||
<h1>
|
<h1>{isLoginMode ? i18n._('auth.login') : i18n._('auth.create.an.account')}</h1>
|
||||||
{isLoginMode
|
|
||||||
? intl.formatMessage({
|
|
||||||
id: 'auth.login',
|
|
||||||
})
|
|
||||||
: intl.formatMessage({
|
|
||||||
id: 'auth.create.an.account',
|
|
||||||
})}
|
|
||||||
</h1>
|
|
||||||
</PanelHeader>
|
</PanelHeader>
|
||||||
<PanelContent>
|
<PanelContent>
|
||||||
<p className="copy--lead">
|
<p className="copy--lead">
|
||||||
{isLoginMode
|
{isLoginMode ? i18n._('auth.login.intro') : i18n._('auth.create.an.account.intro')}
|
||||||
? intl.formatMessage({
|
|
||||||
id: 'auth.login.intro',
|
|
||||||
})
|
|
||||||
: intl.formatMessage({
|
|
||||||
id: 'auth.create.an.account.intro',
|
|
||||||
})}
|
|
||||||
</p>
|
</p>
|
||||||
{errorMessage != null ? (
|
{errorMessage != null ? (
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormError isLoading={isSubmitting}>
|
<FormError isLoading={isSubmitting}>
|
||||||
{typeof errorMessage === 'string' ? errorMessage : intl.formatMessage(errorMessage)}
|
{typeof errorMessage === 'string' ? errorMessage : i18n._(errorMessage)}
|
||||||
</FormError>
|
</FormError>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -159,18 +145,10 @@ const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
|
|||||||
formRef.current.resetForm();
|
formRef.current.resetForm();
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{intl.formatMessage({
|
{i18n._('auth.input.clear')}
|
||||||
id: 'auth.input.clear',
|
|
||||||
})}
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button isLoading={isSubmitting} type="submit">
|
<Button isLoading={isSubmitting} type="submit">
|
||||||
{isLoginMode
|
{isLoginMode ? i18n._('auth.log.in') : i18n._('auth.create.account')}
|
||||||
? intl.formatMessage({
|
|
||||||
id: 'auth.log.in',
|
|
||||||
})
|
|
||||||
: intl.formatMessage({
|
|
||||||
id: 'auth.create.account',
|
|
||||||
})}
|
|
||||||
</Button>
|
</Button>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</PanelFooter>
|
</PanelFooter>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC, ReactText, useRef, useState} from 'react';
|
import {FC, ReactText, useRef, useState} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -74,14 +74,14 @@ const ClientConnectionInterruption: FC = observer(() => {
|
|||||||
}}>
|
}}>
|
||||||
<PanelHeader>
|
<PanelHeader>
|
||||||
<h1>
|
<h1>
|
||||||
<FormattedMessage id="connection-interruption.heading" />
|
<Trans id="connection-interruption.heading" />
|
||||||
</h1>
|
</h1>
|
||||||
</PanelHeader>
|
</PanelHeader>
|
||||||
<PanelContent>
|
<PanelContent>
|
||||||
{error && (
|
{error && (
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormError>
|
<FormError>
|
||||||
<FormattedMessage id={error} />
|
<Trans id={error} />
|
||||||
</FormError>
|
</FormError>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
@@ -89,16 +89,16 @@ const ClientConnectionInterruption: FC = observer(() => {
|
|||||||
<FormRow>
|
<FormRow>
|
||||||
<Select id="action" onSelect={setSelection} defaultID="retry">
|
<Select id="action" onSelect={setSelection} defaultID="retry">
|
||||||
<SelectItem key="retry" id="retry">
|
<SelectItem key="retry" id="retry">
|
||||||
<FormattedMessage id="connection-interruption.action.selection.retry" />
|
<Trans id="connection-interruption.action.selection.retry" />
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem key="config" id="config">
|
<SelectItem key="config" id="config">
|
||||||
<FormattedMessage id="connection-interruption.action.selection.config" />
|
<Trans id="connection-interruption.action.selection.config" />
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
) : (
|
) : (
|
||||||
<p className="copy--lead">
|
<p className="copy--lead">
|
||||||
<FormattedMessage id="connection-interruption.not.admin" />
|
<Trans id="connection-interruption.not.admin" />
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{selection === 'config' && (
|
{selection === 'config' && (
|
||||||
@@ -113,12 +113,12 @@ const ClientConnectionInterruption: FC = observer(() => {
|
|||||||
<FormRow justify="end">
|
<FormRow justify="end">
|
||||||
{selection === 'retry' && (
|
{selection === 'retry' && (
|
||||||
<Button type="submit" isLoading={isSubmitting}>
|
<Button type="submit" isLoading={isSubmitting}>
|
||||||
<FormattedMessage id="button.retry" />
|
<Trans id="button.retry" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{selection === 'config' && (
|
{selection === 'config' && (
|
||||||
<Button type="submit" isLoading={isSubmitting}>
|
<Button type="submit" isLoading={isSubmitting}>
|
||||||
<FormattedMessage id="button.save" />
|
<Trans id="button.save" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</FormRow>
|
</FormRow>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
import {useKeyPressEvent} from 'react-use';
|
import {useKeyPressEvent} from 'react-use';
|
||||||
|
|
||||||
import {ContextMenu} from '@client/ui';
|
import {ContextMenu} from '@client/ui';
|
||||||
@@ -22,7 +22,7 @@ const ContextMenuMountPoint: FC<ContextMenuMountPointProps> = observer(({id}: Co
|
|||||||
y: 0,
|
y: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
useKeyPressEvent('Escape', () => UIActions.dismissContextMenu(id));
|
useKeyPressEvent('Escape', () => UIActions.dismissContextMenu(id));
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ const ContextMenuMountPoint: FC<ContextMenuMountPointProps> = observer(({id}: Co
|
|||||||
className={classnames('menu__item__label--primary', {
|
className={classnames('menu__item__label--primary', {
|
||||||
'has-action': item.labelAction,
|
'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 ? (
|
{item.labelAction ? (
|
||||||
<span className="menu__item__label__action">
|
<span className="menu__item__label__action">
|
||||||
<item.labelAction />
|
<item.labelAction />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, ReactNode} from 'react';
|
import {FC, ReactNode} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
const secondsToDuration = (
|
const secondsToDuration = (
|
||||||
cumSeconds: number,
|
cumSeconds: number,
|
||||||
@@ -59,19 +59,19 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
|
|||||||
const duration = value === -1 ? -1 : secondsToDuration(value);
|
const duration = value === -1 ? -1 : secondsToDuration(value);
|
||||||
|
|
||||||
if (duration === -1) {
|
if (duration === -1) {
|
||||||
content = <FormattedMessage id="unit.time.infinity" />;
|
content = <Trans id="unit.time.infinity" />;
|
||||||
} else if (duration.years != null && duration.years > 0) {
|
} else if (duration.years != null && duration.years > 0) {
|
||||||
content = [
|
content = [
|
||||||
<span className="duration--segment" key="years">
|
<span className="duration--segment" key="years">
|
||||||
{duration.years}
|
{duration.years}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.year" />
|
<Trans id="unit.time.year" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
<span className="duration--segment" key="weeks">
|
<span className="duration--segment" key="weeks">
|
||||||
{duration.weeks}
|
{duration.weeks}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.week" />
|
<Trans id="unit.time.week" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
];
|
];
|
||||||
@@ -80,13 +80,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
|
|||||||
<span className="duration--segment" key="weeks">
|
<span className="duration--segment" key="weeks">
|
||||||
{duration.weeks}
|
{duration.weeks}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.week" />
|
<Trans id="unit.time.week" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
<span className="duration--segment" key="days">
|
<span className="duration--segment" key="days">
|
||||||
{duration.days}
|
{duration.days}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.day" />
|
<Trans id="unit.time.day" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
];
|
];
|
||||||
@@ -95,13 +95,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
|
|||||||
<span className="duration--segment" key="days">
|
<span className="duration--segment" key="days">
|
||||||
{duration.days}
|
{duration.days}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.day" />
|
<Trans id="unit.time.day" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
<span className="duration--segment" key="hours">
|
<span className="duration--segment" key="hours">
|
||||||
{duration.hours}
|
{duration.hours}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.hour" />
|
<Trans id="unit.time.hour" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
];
|
];
|
||||||
@@ -110,13 +110,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
|
|||||||
<span className="duration--segment" key="hours">
|
<span className="duration--segment" key="hours">
|
||||||
{duration.hours}
|
{duration.hours}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.hour" />
|
<Trans id="unit.time.hour" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
<span className="duration--segment" key="minutes">
|
<span className="duration--segment" key="minutes">
|
||||||
{duration.minutes}
|
{duration.minutes}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.minute" />
|
<Trans id="unit.time.minute" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
];
|
];
|
||||||
@@ -125,13 +125,13 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
|
|||||||
<span className="duration--segment" key="minutes">
|
<span className="duration--segment" key="minutes">
|
||||||
{duration.minutes}
|
{duration.minutes}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.minute" />
|
<Trans id="unit.time.minute" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
<span className="duration--segment" key="seconds">
|
<span className="duration--segment" key="seconds">
|
||||||
{duration.seconds}
|
{duration.seconds}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.second" />
|
<Trans id="unit.time.second" />
|
||||||
</em>
|
</em>
|
||||||
</span>,
|
</span>,
|
||||||
];
|
];
|
||||||
@@ -140,7 +140,7 @@ const Duration: FC<DurationProps> = (props: DurationProps) => {
|
|||||||
<span className="duration--segment">
|
<span className="duration--segment">
|
||||||
{duration.seconds}
|
{duration.seconds}
|
||||||
<em className="unit">
|
<em className="unit">
|
||||||
<FormattedMessage id="unit.time.second" />
|
<Trans id="unit.time.second" />
|
||||||
</em>
|
</em>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {CheckmarkThick} from '@client/ui/icons';
|
import {CheckmarkThick} from '@client/ui/icons';
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ const ICONS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const LoadingDependencyList: FC<{dependencies: Dependencies}> = ({dependencies}: {dependencies: Dependencies}) => {
|
const LoadingDependencyList: FC<{dependencies: Dependencies}> = ({dependencies}: {dependencies: Dependencies}) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className="dependency-list">
|
<ul className="dependency-list">
|
||||||
@@ -28,7 +28,7 @@ const LoadingDependencyList: FC<{dependencies: Dependencies}> = ({dependencies}:
|
|||||||
<li className={classes} key={id}>
|
<li className={classes} key={id}>
|
||||||
{satisfied != null ? <span className="dependency-list__dependency__icon">{statusIcon}</span> : null}
|
{satisfied != null ? <span className="dependency-list__dependency__icon">{statusIcon}</span> : null}
|
||||||
<span className="dependency-list__dependency__message">
|
<span className="dependency-list__dependency__message">
|
||||||
{typeof message === 'string' ? message : intl.formatMessage(message)}
|
{typeof message === 'string' ? message : i18n._(message)}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, ReactNode, useState} from 'react';
|
import {FC, ReactNode, useState} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import PriorityLevels from '../../constants/PriorityLevels';
|
import PriorityLevels from '../../constants/PriorityLevels';
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ const PriorityMeter: FC<PriorityMeterProps> = ({
|
|||||||
changePriorityFuncRef,
|
changePriorityFuncRef,
|
||||||
onChange,
|
onChange,
|
||||||
}: PriorityMeterProps) => {
|
}: PriorityMeterProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [priorityLevel, setPriorityLevel] = useState<number>(level);
|
const [priorityLevel, setPriorityLevel] = useState<number>(level);
|
||||||
|
|
||||||
const changePriority = () => {
|
const changePriority = () => {
|
||||||
@@ -54,24 +54,16 @@ const PriorityMeter: FC<PriorityMeterProps> = ({
|
|||||||
let priorityLevelElement: ReactNode;
|
let priorityLevelElement: ReactNode;
|
||||||
switch (priorityLevels[priorityLevel as keyof typeof priorityLevels]) {
|
switch (priorityLevels[priorityLevel as keyof typeof priorityLevels]) {
|
||||||
case 'DONT_DOWNLOAD':
|
case 'DONT_DOWNLOAD':
|
||||||
priorityLevelElement = intl.formatMessage({
|
priorityLevelElement = i18n._('priority.dont.download');
|
||||||
id: 'priority.dont.download',
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 'HIGH':
|
case 'HIGH':
|
||||||
priorityLevelElement = intl.formatMessage({
|
priorityLevelElement = i18n._('priority.high');
|
||||||
id: 'priority.high',
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 'LOW':
|
case 'LOW':
|
||||||
priorityLevelElement = intl.formatMessage({
|
priorityLevelElement = i18n._('priority.low');
|
||||||
id: 'priority.low',
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
priorityLevelElement = intl.formatMessage({
|
priorityLevelElement = i18n._('priority.normal');
|
||||||
id: 'priority.normal',
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedNumber, useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {compute, getTranslationString} from '../../util/size';
|
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 {
|
interface SizeProps {
|
||||||
value: number;
|
value: number;
|
||||||
precision?: number;
|
precision?: number;
|
||||||
@@ -20,26 +12,19 @@ interface SizeProps {
|
|||||||
|
|
||||||
const Size: FC<SizeProps> = ({value, isSpeed, className, precision}: SizeProps) => {
|
const Size: FC<SizeProps> = ({value, isSpeed, className, precision}: SizeProps) => {
|
||||||
const computed = compute(value, precision);
|
const computed = compute(value, precision);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
let translatedUnit = intl.formatMessage({
|
let translatedUnit = i18n._(getTranslationString(computed.unit));
|
||||||
id: getTranslationString(computed.unit),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isSpeed) {
|
if (isSpeed) {
|
||||||
translatedUnit = intl.formatMessage(
|
translatedUnit = i18n._('unit.speed', {
|
||||||
{
|
baseUnit: translatedUnit,
|
||||||
id: 'unit.speed',
|
});
|
||||||
},
|
|
||||||
{
|
|
||||||
baseUnit: translatedUnit,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={className}>
|
<span className={className}>
|
||||||
{renderNumber(computed)}
|
{Number.isNaN(computed.value) ? '—' : i18n.number(computed.value)}
|
||||||
<em className="unit">{translatedUnit}</em>
|
<em className="unit">{translatedUnit}</em>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {compute, getTranslationString} from '../../util/size';
|
import {compute, getTranslationString} from '../../util/size';
|
||||||
import TransferDataStore from '../../stores/TransferDataStore';
|
import TransferDataStore from '../../stores/TransferDataStore';
|
||||||
|
|
||||||
const WindowTitle: FC = observer(() => {
|
const WindowTitle: FC = observer(() => {
|
||||||
const {transferSummary: summary} = TransferDataStore;
|
const {transferSummary: summary} = TransferDataStore;
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
let title = 'Flood';
|
let title = 'Flood';
|
||||||
|
|
||||||
@@ -15,25 +15,15 @@ const WindowTitle: FC = observer(() => {
|
|||||||
const down = compute(summary.downRate);
|
const down = compute(summary.downRate);
|
||||||
const up = compute(summary.upRate);
|
const up = compute(summary.upRate);
|
||||||
|
|
||||||
const formattedDownSpeed = intl.formatNumber(down.value);
|
const formattedDownSpeed = i18n.number(down.value);
|
||||||
const formattedUpSpeed = intl.formatNumber(up.value);
|
const formattedUpSpeed = i18n.number(up.value);
|
||||||
|
|
||||||
const translatedDownUnit = intl.formatMessage(
|
const translatedDownUnit = i18n._('unit.speed', {
|
||||||
{
|
baseUnit: i18n._(getTranslationString(down.unit)),
|
||||||
id: 'unit.speed',
|
});
|
||||||
},
|
const translatedUpUnit = i18n._('unit.speed', {
|
||||||
{
|
baseUnit: i18n._(getTranslationString(up.unit)),
|
||||||
baseUnit: intl.formatMessage({id: getTranslationString(down.unit)}),
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
const translatedUpUnit = intl.formatMessage(
|
|
||||||
{
|
|
||||||
id: 'unit.speed',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
baseUnit: intl.formatMessage({id: getTranslationString(up.unit)}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
title = `↓ ${formattedDownSpeed} ${translatedDownUnit} ↑ ${formattedUpSpeed} ${translatedUpUnit} - Flood`;
|
title = `↓ ${formattedDownSpeed} ${translatedDownUnit} ↑ ${formattedUpSpeed} ${translatedUpUnit} - Flood`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, ReactNode, useEffect, useState} from 'react';
|
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';
|
import {FormRow, Select, SelectItem} from '@client/ui';
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ interface ClientConnectionSettingsFormProps {
|
|||||||
const ClientConnectionSettingsForm: FC<ClientConnectionSettingsFormProps> = ({
|
const ClientConnectionSettingsForm: FC<ClientConnectionSettingsFormProps> = ({
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
}: ClientConnectionSettingsFormProps) => {
|
}: ClientConnectionSettingsFormProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [selectedClient, setSelectedClient] = useState<ClientConnectionSettings['client']>(DEFAULT_SELECTION);
|
const [selectedClient, setSelectedClient] = useState<ClientConnectionSettings['client']>(DEFAULT_SELECTION);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -47,16 +47,14 @@ const ClientConnectionSettingsForm: FC<ClientConnectionSettingsFormProps> = ({
|
|||||||
<FormRow>
|
<FormRow>
|
||||||
<Select
|
<Select
|
||||||
id="client"
|
id="client"
|
||||||
label={intl.formatMessage({
|
label={i18n._('connection.settings.client.select')}
|
||||||
id: 'connection.settings.client.select',
|
|
||||||
})}
|
|
||||||
onSelect={(newSelectedClient) => {
|
onSelect={(newSelectedClient) => {
|
||||||
setSelectedClient(newSelectedClient as ClientConnectionSettings['client']);
|
setSelectedClient(newSelectedClient as ClientConnectionSettings['client']);
|
||||||
}}
|
}}
|
||||||
defaultID={DEFAULT_SELECTION}>
|
defaultID={DEFAULT_SELECTION}>
|
||||||
{SUPPORTED_CLIENTS.map((client) => (
|
{SUPPORTED_CLIENTS.map((client) => (
|
||||||
<SelectItem key={client} id={client}>
|
<SelectItem key={client} id={client}>
|
||||||
<FormattedMessage id={`connection.settings.${client.toLowerCase()}`} />
|
<Trans id={`connection.settings.${client.toLowerCase()}`} />
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {FormGroup, FormRow, Textbox} from '@client/ui';
|
import {FormGroup, FormRow, Textbox} from '@client/ui';
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export interface QBittorrentConnectionSettingsProps {
|
|||||||
const QBittorrentConnectionSettingsForm: FC<QBittorrentConnectionSettingsProps> = ({
|
const QBittorrentConnectionSettingsForm: FC<QBittorrentConnectionSettingsProps> = ({
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
}: QBittorrentConnectionSettingsProps) => {
|
}: QBittorrentConnectionSettingsProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [settings, setSettings] = useState<QBittorrentConnectionSettings>({
|
const [settings, setSettings] = useState<QBittorrentConnectionSettings>({
|
||||||
client: 'qBittorrent',
|
client: 'qBittorrent',
|
||||||
type: 'web',
|
type: 'web',
|
||||||
@@ -44,29 +44,23 @@ const QBittorrentConnectionSettingsForm: FC<QBittorrentConnectionSettingsProps>
|
|||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('url', e.target.value)}
|
onChange={(e) => handleFormChange('url', e.target.value)}
|
||||||
id="url"
|
id="url"
|
||||||
label={<FormattedMessage id="connection.settings.qbittorrent.url" />}
|
label={<Trans id="connection.settings.qbittorrent.url" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.qbittorrent.url.input.placeholder')}
|
||||||
id: 'connection.settings.qbittorrent.url.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('username', e.target.value)}
|
onChange={(e) => handleFormChange('username', e.target.value)}
|
||||||
id="qbt-username"
|
id="qbt-username"
|
||||||
label={<FormattedMessage id="connection.settings.qbittorrent.username" />}
|
label={<Trans id="connection.settings.qbittorrent.username" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.qbittorrent.username.input.placeholder')}
|
||||||
id: 'connection.settings.qbittorrent.username.input.placeholder',
|
|
||||||
})}
|
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('password', e.target.value)}
|
onChange={(e) => handleFormChange('password', e.target.value)}
|
||||||
id="qbt-password"
|
id="qbt-password"
|
||||||
label={<FormattedMessage id="connection.settings.qbittorrent.password" />}
|
label={<Trans id="connection.settings.qbittorrent.password" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.qbittorrent.password.input.placeholder')}
|
||||||
id: 'connection.settings.qbittorrent.password.input.placeholder',
|
|
||||||
})}
|
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
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';
|
import {FormError, FormGroup, FormRow, FormRowGroup, Radio, Textbox} from '@client/ui';
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export interface RTorrentConnectionSettingsProps {
|
|||||||
const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
|
const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
}: RTorrentConnectionSettingsProps) => {
|
}: RTorrentConnectionSettingsProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [type, setType] = useState<'tcp' | 'socket'>('socket');
|
const [type, setType] = useState<'tcp' | 'socket'>('socket');
|
||||||
const [settings, setSettings] = useState<RTorrentConnectionSettings | null>(null);
|
const [settings, setSettings] = useState<RTorrentConnectionSettings | null>(null);
|
||||||
|
|
||||||
@@ -47,10 +47,7 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
|
|||||||
<FormRow>
|
<FormRow>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormGroup
|
<FormGroup label={i18n._('connection.settings.rtorrent.type')}>
|
||||||
label={intl.formatMessage({
|
|
||||||
id: 'connection.settings.rtorrent.type',
|
|
||||||
})}>
|
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Radio
|
<Radio
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -60,7 +57,7 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
|
|||||||
id="socket"
|
id="socket"
|
||||||
grow={false}
|
grow={false}
|
||||||
defaultChecked={type === 'socket'}>
|
defaultChecked={type === 'socket'}>
|
||||||
<FormattedMessage id="connection.settings.rtorrent.type.socket" />
|
<Trans id="connection.settings.rtorrent.type.socket" />
|
||||||
</Radio>
|
</Radio>
|
||||||
<Radio
|
<Radio
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -70,7 +67,7 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
|
|||||||
id="tcp"
|
id="tcp"
|
||||||
grow={false}
|
grow={false}
|
||||||
defaultChecked={type === 'tcp'}>
|
defaultChecked={type === 'tcp'}>
|
||||||
<FormattedMessage id="connection.settings.rtorrent.type.tcp" />
|
<Trans id="connection.settings.rtorrent.type.tcp" />
|
||||||
</Radio>
|
</Radio>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@@ -78,28 +75,20 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
|
|||||||
{type === 'tcp' ? (
|
{type === 'tcp' ? (
|
||||||
<FormRowGroup>
|
<FormRowGroup>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormError>
|
<FormError>{i18n._('connection.settings.rtorrent.type.tcp.warning')}</FormError>
|
||||||
{intl.formatMessage({
|
|
||||||
id: 'connection.settings.rtorrent.type.tcp.warning',
|
|
||||||
})}
|
|
||||||
</FormError>
|
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('host', e.target.value)}
|
onChange={(e) => handleFormChange('host', e.target.value)}
|
||||||
id="host"
|
id="host"
|
||||||
label={<FormattedMessage id="connection.settings.rtorrent.host" />}
|
label={<Trans id="connection.settings.rtorrent.host" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.rtorrent.host.input.placeholder')}
|
||||||
id: 'connection.settings.rtorrent.host.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('port', Number(e.target.value))}
|
onChange={(e) => handleFormChange('port', Number(e.target.value))}
|
||||||
id="port"
|
id="port"
|
||||||
label={<FormattedMessage id="connection.settings.rtorrent.port" />}
|
label={<Trans id="connection.settings.rtorrent.port" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.rtorrent.port.input.placeholder')}
|
||||||
id: 'connection.settings.rtorrent.port.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</FormRowGroup>
|
</FormRowGroup>
|
||||||
@@ -108,10 +97,8 @@ const RTorrentConnectionSettingsForm: FC<RTorrentConnectionSettingsProps> = ({
|
|||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('socket', e.target.value)}
|
onChange={(e) => handleFormChange('socket', e.target.value)}
|
||||||
id="socket"
|
id="socket"
|
||||||
label={<FormattedMessage id="connection.settings.rtorrent.socket" />}
|
label={<Trans id="connection.settings.rtorrent.socket" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.rtorrent.socket.input.placeholder')}
|
||||||
id: 'connection.settings.rtorrent.socket.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {FormGroup, FormRow, Textbox} from '@client/ui';
|
import {FormGroup, FormRow, Textbox} from '@client/ui';
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export interface TransmissionConnectionSettingsProps {
|
|||||||
const TransmissionConnectionSettingsForm: FC<TransmissionConnectionSettingsProps> = ({
|
const TransmissionConnectionSettingsForm: FC<TransmissionConnectionSettingsProps> = ({
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
}: TransmissionConnectionSettingsProps) => {
|
}: TransmissionConnectionSettingsProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [settings, setSettings] = useState<TransmissionConnectionSettings>({
|
const [settings, setSettings] = useState<TransmissionConnectionSettings>({
|
||||||
client: 'Transmission',
|
client: 'Transmission',
|
||||||
type: 'rpc',
|
type: 'rpc',
|
||||||
@@ -44,29 +44,23 @@ const TransmissionConnectionSettingsForm: FC<TransmissionConnectionSettingsProps
|
|||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('url', e.target.value)}
|
onChange={(e) => handleFormChange('url', e.target.value)}
|
||||||
id="url"
|
id="url"
|
||||||
label={<FormattedMessage id="connection.settings.transmission.url" />}
|
label={<Trans id="connection.settings.transmission.url" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.transmission.url.input.placeholder')}
|
||||||
id: 'connection.settings.transmission.url.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('username', e.target.value)}
|
onChange={(e) => handleFormChange('username', e.target.value)}
|
||||||
id="transmission-username"
|
id="transmission-username"
|
||||||
label={<FormattedMessage id="connection.settings.transmission.username" />}
|
label={<Trans id="connection.settings.transmission.username" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.transmission.username.input.placeholder')}
|
||||||
id: 'connection.settings.transmission.username.input.placeholder',
|
|
||||||
})}
|
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
onChange={(e) => handleFormChange('password', e.target.value)}
|
onChange={(e) => handleFormChange('password', e.target.value)}
|
||||||
id="transmission-password"
|
id="transmission-password"
|
||||||
label={<FormattedMessage id="connection.settings.transmission.password" />}
|
label={<Trans id="connection.settings.transmission.password" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('connection.settings.transmission.password.input.placeholder')}
|
||||||
id: 'connection.settings.transmission.password.input.placeholder',
|
|
||||||
})}
|
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,28 +1,15 @@
|
|||||||
import {defineMessages, WrappedComponentProps} from 'react-intl';
|
|
||||||
import {PureComponent, ReactNodeArray} from 'react';
|
import {PureComponent, ReactNodeArray} from 'react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Arrow, File, FolderClosedSolid} from '@client/ui/icons';
|
import {Arrow, File, FolderClosedSolid} from '@client/ui/icons';
|
||||||
import FloodActions from '@client/actions/FloodActions';
|
import FloodActions from '@client/actions/FloodActions';
|
||||||
|
|
||||||
const MESSAGES = defineMessages({
|
const MESSAGES = {
|
||||||
EACCES: {
|
EACCES: 'filesystem.error.eacces',
|
||||||
id: 'filesystem.error.eacces',
|
ENOENT: 'filesystem.error.enoent',
|
||||||
},
|
};
|
||||||
ENOENT: {
|
|
||||||
id: 'filesystem.error.enoent',
|
|
||||||
},
|
|
||||||
emptyDirectory: {
|
|
||||||
id: 'filesystem.empty.directory',
|
|
||||||
},
|
|
||||||
fetching: {
|
|
||||||
id: 'filesystem.fetching',
|
|
||||||
},
|
|
||||||
unknownError: {
|
|
||||||
id: 'filesystem.error.unknown',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
interface FilesystemBrowserProps extends WrappedComponentProps {
|
interface FilesystemBrowserProps {
|
||||||
selectable?: 'files' | 'directories';
|
selectable?: 'files' | 'directories';
|
||||||
directory: string;
|
directory: string;
|
||||||
onItemSelection?: (newDestination: string, isDirectory?: boolean) => void;
|
onItemSelection?: (newDestination: string, isDirectory?: boolean) => void;
|
||||||
@@ -108,7 +95,7 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {intl, selectable} = this.props;
|
const {selectable} = this.props;
|
||||||
const {directories, errorResponse, files} = this.state;
|
const {directories, errorResponse, files} = this.state;
|
||||||
let errorMessage = null;
|
let errorMessage = null;
|
||||||
let listItems = null;
|
let listItems = null;
|
||||||
@@ -119,7 +106,9 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
|
|||||||
shouldShowDirectoryList = false;
|
shouldShowDirectoryList = false;
|
||||||
errorMessage = (
|
errorMessage = (
|
||||||
<div className="filesystem__directory-list__item filesystem__directory-list__item--message">
|
<div className="filesystem__directory-list__item filesystem__directory-list__item--message">
|
||||||
<em>{intl.formatMessage(MESSAGES.fetching)}</em>
|
<em>
|
||||||
|
<Trans id="filesystem.fetching" />
|
||||||
|
</em>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -127,11 +116,11 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
|
|||||||
if (errorResponse && errorResponse.data && errorResponse.data.code) {
|
if (errorResponse && errorResponse.data && errorResponse.data.code) {
|
||||||
shouldShowDirectoryList = false;
|
shouldShowDirectoryList = false;
|
||||||
|
|
||||||
const messageConfig = MESSAGES[errorResponse.data.code as keyof typeof MESSAGES] || MESSAGES.unknownError;
|
|
||||||
|
|
||||||
errorMessage = (
|
errorMessage = (
|
||||||
<div className="filesystem__directory-list__item filesystem__directory-list__item--message">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -141,9 +130,7 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
|
|||||||
className="filesystem__directory-list__item filesystem__directory-list__item--parent"
|
className="filesystem__directory-list__item filesystem__directory-list__item--parent"
|
||||||
onClick={this.handleParentDirectoryClick}>
|
onClick={this.handleParentDirectoryClick}>
|
||||||
<Arrow />
|
<Arrow />
|
||||||
{intl.formatMessage({
|
<Trans id="filesystem.parent.directory" />
|
||||||
id: 'filesystem.parent.directory',
|
|
||||||
})}
|
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -188,7 +175,9 @@ class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, Filesystem
|
|||||||
if ((!listItems || listItems.length === 0) && !errorMessage) {
|
if ((!listItems || listItems.length === 0) && !errorMessage) {
|
||||||
errorMessage = (
|
errorMessage = (
|
||||||
<div className="filesystem__directory-list__item filesystem__directory-list__item--message">
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Dropzone from 'react-dropzone';
|
import Dropzone from 'react-dropzone';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {FC, useEffect, useState} from 'react';
|
import {FC, useEffect, useState} from 'react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Close, File, Files} from '@client/ui/icons';
|
import {Close, File, Files} from '@client/ui/icons';
|
||||||
import {FormRowItem} from '@client/ui';
|
import {FormRowItem} from '@client/ui';
|
||||||
@@ -21,7 +21,7 @@ const FileDropzone: FC<FileDropzoneProps> = ({onFilesChanged}: FileDropzoneProps
|
|||||||
return (
|
return (
|
||||||
<FormRowItem>
|
<FormRowItem>
|
||||||
<label className="form__element__label">
|
<label className="form__element__label">
|
||||||
<FormattedMessage id="torrents.add.torrents.label" />
|
<Trans id="torrents.add.torrents.label" />
|
||||||
</label>
|
</label>
|
||||||
{files.length > 0 ? (
|
{files.length > 0 ? (
|
||||||
<ul
|
<ul
|
||||||
@@ -76,9 +76,9 @@ const FileDropzone: FC<FileDropzoneProps> = ({onFilesChanged}: FileDropzoneProps
|
|||||||
<div className="dropzone__icon">
|
<div className="dropzone__icon">
|
||||||
<Files />
|
<Files />
|
||||||
</div>
|
</div>
|
||||||
<FormattedMessage id="torrents.add.tab.file.drop" />{' '}
|
<Trans id="torrents.add.tab.file.drop" />{' '}
|
||||||
<span className="dropzone__browse-button">
|
<span className="dropzone__browse-button">
|
||||||
<FormattedMessage id="torrents.add.tab.file.browse" />
|
<Trans id="torrents.add.tab.file.browse" />
|
||||||
</span>
|
</span>
|
||||||
.
|
.
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
|
||||||
import {forwardRef, MutableRefObject, ReactNode, useEffect, useRef, useState} from 'react';
|
import {forwardRef, MutableRefObject, ReactNode, useEffect, useRef, useState} from 'react';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
import {useEnsuredForwardedRef} from 'react-use';
|
import {useEnsuredForwardedRef} from 'react-use';
|
||||||
|
|
||||||
import {Checkbox, ContextMenu, FormElementAddon, FormRow, FormRowGroup, Portal, Textbox} from '@client/ui';
|
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 formRowRef = useRef<HTMLDivElement>(null);
|
||||||
const textboxRef = useEnsuredForwardedRef(ref as MutableRefObject<HTMLInputElement>);
|
const textboxRef = useEnsuredForwardedRef(ref as MutableRefObject<HTMLInputElement>);
|
||||||
|
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const closeDirectoryList = (): void => {
|
const closeDirectoryList = (): void => {
|
||||||
@@ -71,14 +71,14 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
|
|||||||
if (showBasePathToggle) {
|
if (showBasePathToggle) {
|
||||||
toggles.push(
|
toggles.push(
|
||||||
<Checkbox grow={false} id="isBasePath" key="isBasePath">
|
<Checkbox grow={false} id="isBasePath" key="isBasePath">
|
||||||
<FormattedMessage id="torrents.destination.base_path" />
|
<Trans id="torrents.destination.base_path" />
|
||||||
</Checkbox>,
|
</Checkbox>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (showCompletedToggle) {
|
if (showCompletedToggle) {
|
||||||
toggles.push(
|
toggles.push(
|
||||||
<Checkbox grow={false} id="isCompleted" key="isCompleted">
|
<Checkbox grow={false} id="isCompleted" key="isCompleted">
|
||||||
<FormattedMessage id="torrents.destination.completed" />
|
<Trans id="torrents.destination.completed" />
|
||||||
</Checkbox>,
|
</Checkbox>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
|
|||||||
// TODO: this is getting bloated. toggles can be moved to their own elements...
|
// TODO: this is getting bloated. toggles can be moved to their own elements...
|
||||||
toggles.push(
|
toggles.push(
|
||||||
<Checkbox grow={false} id="isSequential" key="isSequential">
|
<Checkbox grow={false} id="isSequential" key="isSequential">
|
||||||
<FormattedMessage id="torrents.destination.sequential" />
|
<Trans id="torrents.destination.sequential" />
|
||||||
</Checkbox>,
|
</Checkbox>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -118,9 +118,7 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
|
|||||||
{leading: true},
|
{leading: true},
|
||||||
)}
|
)}
|
||||||
onClick={(event) => event.nativeEvent.stopImmediatePropagation()}
|
onClick={(event) => event.nativeEvent.stopImmediatePropagation()}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('torrents.add.destination.placeholder')}
|
||||||
id: 'torrents.add.destination.placeholder',
|
|
||||||
})}
|
|
||||||
ref={textboxRef}>
|
ref={textboxRef}>
|
||||||
<FormElementAddon
|
<FormElementAddon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -140,7 +138,6 @@ const FilesystemBrowserTextbox = forwardRef<HTMLInputElement, FilesystemBrowserT
|
|||||||
triggerRef={textboxRef}>
|
triggerRef={textboxRef}>
|
||||||
<FilesystemBrowser
|
<FilesystemBrowser
|
||||||
directory={destination}
|
directory={destination}
|
||||||
intl={intl}
|
|
||||||
selectable={selectable}
|
selectable={selectable}
|
||||||
onItemSelection={(newDestination: string, isDirectory = true) => {
|
onItemSelection={(newDestination: string, isDirectory = true) => {
|
||||||
if (textboxRef.current != null) {
|
if (textboxRef.current != null) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FC, ReactNode, ReactNodeArray, useEffect, useRef, useState} from 'react';
|
import {FC, ReactNode, ReactNodeArray, useEffect, useRef, useState} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import sort from 'fast-sort';
|
import sort from 'fast-sort';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
import {useKeyPressEvent} from 'react-use';
|
import {useKeyPressEvent} from 'react-use';
|
||||||
|
|
||||||
import {ContextMenu, FormElementAddon, FormRowItem, Portal, SelectItem, Textbox} from '@client/ui';
|
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]);
|
setSelectedTags([...selectedTags.filter((key) => key !== ''), tag]);
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{tag === 'untagged' ? <FormattedMessage id="filter.untagged" /> : tag}
|
{tag === 'untagged' ? <Trans id="filter.untagged" /> : tag}
|
||||||
</SelectItem>,
|
</SelectItem>,
|
||||||
);
|
);
|
||||||
return accumulator;
|
return accumulator;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import ModalActions from '../ModalActions';
|
import ModalActions from '../ModalActions';
|
||||||
import SettingStore from '../../../stores/SettingStore';
|
import SettingStore from '../../../stores/SettingStore';
|
||||||
@@ -13,33 +13,27 @@ const AddTorrentsActions: FC<AddTorrentsActionsProps> = ({
|
|||||||
isAddingTorrents,
|
isAddingTorrents,
|
||||||
onAddTorrentsClick,
|
onAddTorrentsClick,
|
||||||
}: AddTorrentsActionsProps) => {
|
}: AddTorrentsActionsProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
return (
|
return (
|
||||||
<ModalActions
|
<ModalActions
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
checked: Boolean(SettingStore.floodSettings.startTorrentsOnLoad),
|
checked: Boolean(SettingStore.floodSettings.startTorrentsOnLoad),
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.add.start.label'),
|
||||||
id: 'torrents.add.start.label',
|
|
||||||
}),
|
|
||||||
id: 'start',
|
id: 'start',
|
||||||
triggerDismiss: false,
|
triggerDismiss: false,
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.cancel'),
|
||||||
id: 'button.cancel',
|
|
||||||
}),
|
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'tertiary',
|
type: 'tertiary',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
clickHandler: onAddTorrentsClick,
|
clickHandler: onAddTorrentsClick,
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.add.button.add'),
|
||||||
id: 'torrents.add.button.add',
|
|
||||||
}),
|
|
||||||
isLoading: isAddingTorrents,
|
isLoading: isAddingTorrents,
|
||||||
submit: true,
|
submit: true,
|
||||||
triggerDismiss: false,
|
triggerDismiss: false,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useRef, useState} from 'react';
|
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 {Checkbox, Form, FormRow, Textbox} from '@client/ui';
|
||||||
import {saveAddTorrentsUserPreferences} from '@client/util/userPreferences';
|
import {saveAddTorrentsUserPreferences} from '@client/util/userPreferences';
|
||||||
@@ -25,74 +25,49 @@ type AddTorrentsByCreationFormData = {
|
|||||||
|
|
||||||
const AddTorrentsByCreation: FC = () => {
|
const AddTorrentsByCreation: FC = () => {
|
||||||
const formRef = useRef<Form>(null);
|
const formRef = useRef<Form>(null);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [isCreatingTorrents, setIsCreatingTorrents] = useState<boolean>(false);
|
const [isCreatingTorrents, setIsCreatingTorrents] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className="inverse" ref={formRef}>
|
<Form className="inverse" ref={formRef}>
|
||||||
<FilesystemBrowserTextbox
|
<FilesystemBrowserTextbox id="sourcePath" label={i18n._('torrents.create.source.path.label')} />
|
||||||
id="sourcePath"
|
|
||||||
label={intl.formatMessage({
|
|
||||||
id: 'torrents.create.source.path.label',
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<TextboxRepeater
|
<TextboxRepeater
|
||||||
id="trackers"
|
id="trackers"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.create.trackers.label')}
|
||||||
id: 'torrents.create.trackers.label',
|
placeholder={i18n._('torrents.create.tracker.input.placeholder')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'torrents.create.tracker.input.placeholder',
|
|
||||||
})}
|
|
||||||
defaultValues={[{id: 0, value: ''}]}
|
defaultValues={[{id: 0, value: ''}]}
|
||||||
/>
|
/>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="name"
|
id="name"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.create.base.name.label')}
|
||||||
id: 'torrents.create.base.name.label',
|
placeholder={i18n._('torrents.create.base.name.input.placeholder')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'torrents.create.base.name.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="comment"
|
id="comment"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.create.comment.label')}
|
||||||
id: 'torrents.create.comment.label',
|
placeholder={i18n._('torrents.create.comment.input.placeholder')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'torrents.create.comment.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="infoSource"
|
id="infoSource"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.create.info.source.label')}
|
||||||
id: 'torrents.create.info.source.label',
|
placeholder={i18n._('torrents.create.info.source.input.placeholder')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'torrents.create.info.source.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Checkbox grow={false} id="isPrivate">
|
<Checkbox grow={false} id="isPrivate">
|
||||||
{intl.formatMessage({id: 'torrents.create.is.private.label'})}
|
{i18n._('torrents.create.is.private.label')}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<TagSelect
|
<TagSelect
|
||||||
id="tags"
|
id="tags"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.add.tags')}
|
||||||
id: 'torrents.add.tags',
|
placeholder={i18n._('torrents.create.tags.input.placeholder')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'torrents.create.tags.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<AddTorrentsActions
|
<AddTorrentsActions
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useRef, useState} from 'react';
|
import {FC, useRef, useState} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Form, FormRow} from '@client/ui';
|
import {Form, FormRow} from '@client/ui';
|
||||||
import {saveAddTorrentsUserPreferences} from '@client/util/userPreferences';
|
import {saveAddTorrentsUserPreferences} from '@client/util/userPreferences';
|
||||||
@@ -29,7 +29,7 @@ const AddTorrentsByFile: FC = () => {
|
|||||||
const textboxRef = useRef<HTMLInputElement>(null);
|
const textboxRef = useRef<HTMLInputElement>(null);
|
||||||
const [isAddingTorrents, setIsAddingTorrents] = useState<boolean>(false);
|
const [isAddingTorrents, setIsAddingTorrents] = useState<boolean>(false);
|
||||||
|
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className="inverse" ref={formRef}>
|
<Form className="inverse" ref={formRef}>
|
||||||
@@ -42,9 +42,7 @@ const AddTorrentsByFile: FC = () => {
|
|||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<TagSelect
|
<TagSelect
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.add.tags')}
|
||||||
id: 'torrents.add.tags',
|
|
||||||
})}
|
|
||||||
id="tags"
|
id="tags"
|
||||||
onTagSelected={(tags) => {
|
onTagSelected={(tags) => {
|
||||||
if (textboxRef.current != null) {
|
if (textboxRef.current != null) {
|
||||||
@@ -59,9 +57,7 @@ const AddTorrentsByFile: FC = () => {
|
|||||||
</FormRow>
|
</FormRow>
|
||||||
<FilesystemBrowserTextbox
|
<FilesystemBrowserTextbox
|
||||||
id="destination"
|
id="destination"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.add.destination.label')}
|
||||||
id: 'torrents.add.destination.label',
|
|
||||||
})}
|
|
||||||
ref={textboxRef}
|
ref={textboxRef}
|
||||||
selectable="directories"
|
selectable="directories"
|
||||||
showBasePathToggle
|
showBasePathToggle
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useRef, useState} from 'react';
|
import {FC, useRef, useState} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import ConfigStore from '@client/stores/ConfigStore';
|
import ConfigStore from '@client/stores/ConfigStore';
|
||||||
import {Form, FormRow} from '@client/ui';
|
import {Form, FormRow} from '@client/ui';
|
||||||
@@ -31,7 +31,7 @@ const AddTorrentsByURL: FC = () => {
|
|||||||
const textboxRef = useRef<HTMLInputElement>(null);
|
const textboxRef = useRef<HTMLInputElement>(null);
|
||||||
const [isAddingTorrents, setIsAddingTorrents] = useState<boolean>(false);
|
const [isAddingTorrents, setIsAddingTorrents] = useState<boolean>(false);
|
||||||
|
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form className="inverse" ref={formRef}>
|
<Form className="inverse" ref={formRef}>
|
||||||
@@ -39,9 +39,7 @@ const AddTorrentsByURL: FC = () => {
|
|||||||
id="urls"
|
id="urls"
|
||||||
label={
|
label={
|
||||||
<div>
|
<div>
|
||||||
{intl.formatMessage({
|
{i18n._('torrents.add.torrents.label')}
|
||||||
id: 'torrents.add.torrents.label',
|
|
||||||
})}
|
|
||||||
{typeof navigator.registerProtocolHandler === 'function' && (
|
{typeof navigator.registerProtocolHandler === 'function' && (
|
||||||
<em
|
<em
|
||||||
style={{cursor: 'pointer', fontSize: '0.8em', float: 'right'}}
|
style={{cursor: 'pointer', fontSize: '0.8em', float: 'right'}}
|
||||||
@@ -54,35 +52,25 @@ const AddTorrentsByURL: FC = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{intl.formatMessage({
|
{i18n._('torrents.add.tab.url.register.magnet.handler')}
|
||||||
id: 'torrents.add.tab.url.register.magnet.handler',
|
|
||||||
})}
|
|
||||||
</em>
|
</em>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('torrents.add.tab.url.input.placeholder')}
|
||||||
id: 'torrents.add.tab.url.input.placeholder',
|
|
||||||
})}
|
|
||||||
defaultValues={
|
defaultValues={
|
||||||
(UIStore.activeModal?.id === 'add-torrents' && UIStore.activeModal?.initialURLs) || [{id: 0, value: ''}]
|
(UIStore.activeModal?.id === 'add-torrents' && UIStore.activeModal?.initialURLs) || [{id: 0, value: ''}]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<TextboxRepeater
|
<TextboxRepeater
|
||||||
id="cookies"
|
id="cookies"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.add.cookies.label')}
|
||||||
id: 'torrents.add.cookies.label',
|
placeholder={i18n._('torrents.add.cookies.input.placeholder')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'torrents.add.cookies.input.placeholder',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<TagSelect
|
<TagSelect
|
||||||
id="tags"
|
id="tags"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.add.tags')}
|
||||||
id: 'torrents.add.tags',
|
|
||||||
})}
|
|
||||||
onTagSelected={(tags) => {
|
onTagSelected={(tags) => {
|
||||||
if (textboxRef.current != null) {
|
if (textboxRef.current != null) {
|
||||||
const suggestedPath = SettingStore.floodSettings.torrentDestinations?.[tags[0]];
|
const suggestedPath = SettingStore.floodSettings.torrentDestinations?.[tags[0]];
|
||||||
@@ -96,9 +84,7 @@ const AddTorrentsByURL: FC = () => {
|
|||||||
</FormRow>
|
</FormRow>
|
||||||
<FilesystemBrowserTextbox
|
<FilesystemBrowserTextbox
|
||||||
id="destination"
|
id="destination"
|
||||||
label={intl.formatMessage({
|
label={i18n._('torrents.add.destination.label')}
|
||||||
id: 'torrents.add.destination.label',
|
|
||||||
})}
|
|
||||||
ref={textboxRef}
|
ref={textboxRef}
|
||||||
selectable="directories"
|
selectable="directories"
|
||||||
showBasePathToggle
|
showBasePathToggle
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import AddTorrentsByCreation from './AddTorrentsByCreation';
|
import AddTorrentsByCreation from './AddTorrentsByCreation';
|
||||||
import AddTorrentsByFile from './AddTorrentsByFile';
|
import AddTorrentsByFile from './AddTorrentsByFile';
|
||||||
@@ -9,26 +9,20 @@ import SettingStore from '../../../stores/SettingStore';
|
|||||||
import UIStore from '../../../stores/UIStore';
|
import UIStore from '../../../stores/UIStore';
|
||||||
|
|
||||||
const AddTorrentsModal: FC = () => {
|
const AddTorrentsModal: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const tabs = {
|
const tabs = {
|
||||||
'by-url': {
|
'by-url': {
|
||||||
content: AddTorrentsByURL,
|
content: AddTorrentsByURL,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.add.tab.url.title'),
|
||||||
id: 'torrents.add.tab.url.title',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
'by-file': {
|
'by-file': {
|
||||||
content: AddTorrentsByFile,
|
content: AddTorrentsByFile,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.add.tab.file.title'),
|
||||||
id: 'torrents.add.tab.file.title',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
'by-creation': {
|
'by-creation': {
|
||||||
content: AddTorrentsByCreation,
|
content: AddTorrentsByCreation,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.add.tab.create.title'),
|
||||||
id: 'torrents.add.tab.create.title',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,15 +35,7 @@ const AddTorrentsModal: FC = () => {
|
|||||||
initialTabId = SettingStore.floodSettings.UITorrentsAddTab ?? initialTabId;
|
initialTabId = SettingStore.floodSettings.UITorrentsAddTab ?? initialTabId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <Modal heading={i18n._('torrents.add.heading')} tabs={tabs} initialTabId={initialTabId} />;
|
||||||
<Modal
|
|
||||||
heading={intl.formatMessage({
|
|
||||||
id: 'torrents.add.heading',
|
|
||||||
})}
|
|
||||||
tabs={tabs}
|
|
||||||
initialTabId={initialTabId}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddTorrentsModal;
|
export default AddTorrentsModal;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -34,30 +34,22 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
}: DownloadRuleFormProps) => {
|
}: DownloadRuleFormProps) => {
|
||||||
const {feeds} = FeedStore;
|
const {feeds} = FeedStore;
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormRowGroup>
|
<FormRowGroup>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox id="label" label={i18n._('feeds.label')} defaultValue={rule.label} />
|
||||||
id="label"
|
|
||||||
label={intl.formatMessage({
|
|
||||||
id: 'feeds.label',
|
|
||||||
})}
|
|
||||||
defaultValue={rule.label}
|
|
||||||
/>
|
|
||||||
<Select
|
<Select
|
||||||
disabled={!feeds.length}
|
disabled={!feeds.length}
|
||||||
id="feedID"
|
id="feedID"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.applicable.feed')}
|
||||||
id: 'feeds.applicable.feed',
|
|
||||||
})}
|
|
||||||
defaultID={rule.feedIDs?.[0]}>
|
defaultID={rule.feedIDs?.[0]}>
|
||||||
{feeds.length === 0
|
{feeds.length === 0
|
||||||
? [
|
? [
|
||||||
<SelectItem key="empty" id="placeholder" isPlaceholder>
|
<SelectItem key="empty" id="placeholder" isPlaceholder>
|
||||||
<em>
|
<em>
|
||||||
<FormattedMessage id="feeds.no.feeds.available" />
|
<Trans id="feeds.no.feeds.available" />
|
||||||
</em>
|
</em>
|
||||||
</SelectItem>,
|
</SelectItem>,
|
||||||
]
|
]
|
||||||
@@ -71,7 +63,7 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
|
|||||||
[
|
[
|
||||||
<SelectItem key="select-feed" id="placeholder" isPlaceholder>
|
<SelectItem key="select-feed" id="placeholder" isPlaceholder>
|
||||||
<em>
|
<em>
|
||||||
<FormattedMessage id="feeds.select.feed" />
|
<Trans id="feeds.select.feed" />
|
||||||
</em>
|
</em>
|
||||||
</SelectItem>,
|
</SelectItem>,
|
||||||
],
|
],
|
||||||
@@ -81,23 +73,15 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
|
|||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="match"
|
id="match"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.match.pattern')}
|
||||||
id: 'feeds.match.pattern',
|
placeholder={i18n._('feeds.regEx')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'feeds.regEx',
|
|
||||||
})}
|
|
||||||
defaultValue={rule.match}
|
defaultValue={rule.match}
|
||||||
width="three-eighths"
|
width="three-eighths"
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="exclude"
|
id="exclude"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.exclude.pattern')}
|
||||||
id: 'feeds.exclude.pattern',
|
placeholder={i18n._('feeds.regEx')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'feeds.regEx',
|
|
||||||
})}
|
|
||||||
defaultValue={rule.exclude}
|
defaultValue={rule.exclude}
|
||||||
width="three-eighths"
|
width="three-eighths"
|
||||||
/>
|
/>
|
||||||
@@ -106,12 +90,8 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
|
|||||||
<Textbox
|
<Textbox
|
||||||
addonPlacement="after"
|
addonPlacement="after"
|
||||||
id="check"
|
id="check"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.test.match')}
|
||||||
id: 'feeds.test.match',
|
placeholder={i18n._('feeds.check')}>
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'feeds.check',
|
|
||||||
})}>
|
|
||||||
{isPatternMatched && (
|
{isPatternMatched && (
|
||||||
<FormElementAddon>
|
<FormElementAddon>
|
||||||
<CheckmarkThick />
|
<CheckmarkThick />
|
||||||
@@ -123,9 +103,7 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
|
|||||||
<FormRowItem>
|
<FormRowItem>
|
||||||
<FilesystemBrowserTextbox
|
<FilesystemBrowserTextbox
|
||||||
id="destination"
|
id="destination"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.torrent.destination')}
|
||||||
id: 'feeds.torrent.destination',
|
|
||||||
})}
|
|
||||||
selectable="directories"
|
selectable="directories"
|
||||||
suggested={rule.destination}
|
suggested={rule.destination}
|
||||||
showBasePathToggle
|
showBasePathToggle
|
||||||
@@ -133,25 +111,21 @@ const DownloadRuleForm: FC<DownloadRuleFormProps> = ({
|
|||||||
</FormRowItem>
|
</FormRowItem>
|
||||||
<TagSelect
|
<TagSelect
|
||||||
id="tags"
|
id="tags"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.apply.tags')}
|
||||||
id: 'feeds.apply.tags',
|
placeholder={i18n._('feeds.tags')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'feeds.tags',
|
|
||||||
})}
|
|
||||||
defaultValue={rule.tags}
|
defaultValue={rule.tags}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<br />
|
<br />
|
||||||
<Checkbox id="startOnLoad" defaultChecked={rule.startOnLoad} matchTextboxHeight>
|
<Checkbox id="startOnLoad" defaultChecked={rule.startOnLoad} matchTextboxHeight>
|
||||||
<FormattedMessage id="feeds.start.on.load" />
|
<Trans id="feeds.start.on.load" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Button onClick={onCancel}>
|
<Button onClick={onCancel}>
|
||||||
<FormattedMessage id="button.cancel" />
|
<Trans id="button.cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" isLoading={isSubmitting}>
|
<Button type="submit" isLoading={isSubmitting}>
|
||||||
<FormattedMessage id="button.save.feed" />
|
<Trans id="button.save.feed" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</FormRowGroup>
|
</FormRowGroup>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Close, Edit} from '@client/ui/icons';
|
import {Close, Edit} from '@client/ui/icons';
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
|
|||||||
return (
|
return (
|
||||||
<ul className="interactive-list">
|
<ul className="interactive-list">
|
||||||
<li className="interactive-list__item">
|
<li className="interactive-list__item">
|
||||||
<FormattedMessage id="feeds.no.rules.defined" />
|
<Trans id="feeds.no.rules.defined" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
@@ -40,7 +40,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
|
|||||||
<li
|
<li
|
||||||
className="interactive-list__detail-list__item
|
className="interactive-list__detail-list__item
|
||||||
interactive-list__detail interactive-list__detail--tertiary">
|
interactive-list__detail interactive-list__detail--tertiary">
|
||||||
<FormattedMessage id="feeds.exclude" />
|
<Trans id="feeds.exclude" />
|
||||||
{': '}
|
{': '}
|
||||||
{rule.exclude}
|
{rule.exclude}
|
||||||
</li>
|
</li>
|
||||||
@@ -56,7 +56,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
|
|||||||
|
|
||||||
tags = (
|
tags = (
|
||||||
<li className="interactive-list__detail-list__item interactive-list__detail interactive-list__detail--tertiary">
|
<li className="interactive-list__detail-list__item interactive-list__detail interactive-list__detail--tertiary">
|
||||||
<FormattedMessage id="feeds.tags" />
|
<Trans id="feeds.tags" />
|
||||||
{': '}
|
{': '}
|
||||||
{tagNodes}
|
{tagNodes}
|
||||||
</li>
|
</li>
|
||||||
@@ -76,7 +76,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
|
|||||||
className="interactive-list__detail-list__item
|
className="interactive-list__detail-list__item
|
||||||
interactive-list__detail-list__item--overflow
|
interactive-list__detail-list__item--overflow
|
||||||
interactive-list__detail interactive-list__detail--secondary">
|
interactive-list__detail interactive-list__detail--secondary">
|
||||||
<FormattedMessage id="feeds.match.count" values={{count: matchedCount}} />
|
<Trans id="feeds.match.count" values={{count: matchedCount}} />
|
||||||
</li>
|
</li>
|
||||||
{rule === currentRule && (
|
{rule === currentRule && (
|
||||||
<li
|
<li
|
||||||
@@ -95,7 +95,7 @@ const DownloadRuleList: FC<DownloadRuleListProps> = observer(
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
}}>
|
}}>
|
||||||
<FormattedMessage id="feeds.match" />
|
<Trans id="feeds.match" />
|
||||||
{': '}
|
{': '}
|
||||||
{rule.match}
|
{rule.match}
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, ReactNodeArray, useRef, useState} from 'react';
|
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 {Button, Form, FormError, FormRow, FormRowItem} from '@client/ui';
|
||||||
import FeedActions from '@client/actions/FeedActions';
|
import FeedActions from '@client/actions/FeedActions';
|
||||||
@@ -71,7 +71,7 @@ interface RuleFormData {
|
|||||||
|
|
||||||
const DownloadRulesTab: FC = () => {
|
const DownloadRulesTab: FC = () => {
|
||||||
const formRef = useRef<Form>(null);
|
const formRef = useRef<Form>(null);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const [currentRule, setCurrentRule] = useState<Rule | null>(null);
|
const [currentRule, setCurrentRule] = useState<Rule | null>(null);
|
||||||
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
|
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
|
||||||
@@ -161,13 +161,13 @@ const DownloadRulesTab: FC = () => {
|
|||||||
}}
|
}}
|
||||||
ref={formRef}>
|
ref={formRef}>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="feeds.existing.rules" />
|
<Trans id="feeds.existing.rules" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
{Object.keys(errors).reduce((memo: ReactNodeArray, key) => {
|
{Object.keys(errors).reduce((memo: ReactNodeArray, key) => {
|
||||||
if (errors[key as ValidatedField] != null) {
|
if (errors[key as ValidatedField] != null) {
|
||||||
memo.push(
|
memo.push(
|
||||||
<FormRow key={`error-${key}`}>
|
<FormRow key={`error-${key}`}>
|
||||||
<FormError>{intl.formatMessage({id: errors?.[key as ValidatedField]})}</FormError>
|
<FormError>{i18n._(errors?.[key as ValidatedField] as string)}</FormError>
|
||||||
</FormRow>,
|
</FormRow>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -212,7 +212,7 @@ const DownloadRulesTab: FC = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsEditing(true);
|
setIsEditing(true);
|
||||||
}}>
|
}}>
|
||||||
<FormattedMessage id="button.new" />
|
<Trans id="button.new" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
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';
|
import {Button, FormRow, FormRowGroup, Select, SelectItem, Textbox} from '@client/ui';
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ const FeedForm: FC<FeedFormProps> = ({
|
|||||||
isSubmitting,
|
isSubmitting,
|
||||||
onCancel,
|
onCancel,
|
||||||
}: FeedFormProps) => {
|
}: FeedFormProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const feedInterval = currentFeed?.interval ?? defaultFeed.interval;
|
const feedInterval = currentFeed?.interval ?? defaultFeed.interval;
|
||||||
|
|
||||||
let defaultIntervalTextValue = feedInterval;
|
let defaultIntervalTextValue = feedInterval;
|
||||||
@@ -40,29 +40,21 @@ const FeedForm: FC<FeedFormProps> = ({
|
|||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="label"
|
id="label"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.label')}
|
||||||
id: 'feeds.label',
|
placeholder={i18n._('feeds.label')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'feeds.label',
|
|
||||||
})}
|
|
||||||
defaultValue={currentFeed?.label ?? defaultFeed.label}
|
defaultValue={currentFeed?.label ?? defaultFeed.label}
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="interval"
|
id="interval"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.select.interval')}
|
||||||
id: 'feeds.select.interval',
|
placeholder={i18n._('feeds.interval')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'feeds.interval',
|
|
||||||
})}
|
|
||||||
defaultValue={defaultIntervalTextValue}
|
defaultValue={defaultIntervalTextValue}
|
||||||
width="one-eighth"
|
width="one-eighth"
|
||||||
/>
|
/>
|
||||||
<Select labelOffset defaultID={defaultIntervalMultiplier} id="intervalMultiplier" width="one-eighth">
|
<Select labelOffset defaultID={defaultIntervalMultiplier} id="intervalMultiplier" width="one-eighth">
|
||||||
{intervalMultipliers.map((interval) => (
|
{intervalMultipliers.map((interval) => (
|
||||||
<SelectItem key={interval.value} id={interval.value}>
|
<SelectItem key={interval.value} id={interval.value}>
|
||||||
{intl.formatMessage({id: interval.message})}
|
{i18n._(interval.message)}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
@@ -70,17 +62,15 @@ const FeedForm: FC<FeedFormProps> = ({
|
|||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="url"
|
id="url"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.url')}
|
||||||
id: 'feeds.url',
|
placeholder={i18n._('feeds.url')}
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({id: 'feeds.url'})}
|
|
||||||
defaultValue={currentFeed?.url ?? defaultFeed?.url}
|
defaultValue={currentFeed?.url ?? defaultFeed?.url}
|
||||||
/>
|
/>
|
||||||
<Button labelOffset onClick={onCancel}>
|
<Button labelOffset onClick={onCancel}>
|
||||||
<FormattedMessage id="button.cancel" />
|
<Trans id="button.cancel" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button labelOffset type="submit" isLoading={isSubmitting}>
|
<Button labelOffset type="submit" isLoading={isSubmitting}>
|
||||||
<FormattedMessage id="button.save.feed" />
|
<Trans id="button.save.feed" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</FormRowGroup>
|
</FormRowGroup>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC, ReactNodeArray} from 'react';
|
import {FC, ReactNodeArray} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Checkbox, FormRow} from '@client/ui';
|
import {Checkbox, FormRow} from '@client/ui';
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ const FeedItems: FC<FeedItemsProps> = observer(({selectedFeedID}: FeedItemsProps
|
|||||||
<ul className="interactive-list">
|
<ul className="interactive-list">
|
||||||
<li className="interactive-list__item">
|
<li className="interactive-list__item">
|
||||||
<div className="interactive-list__label">
|
<div className="interactive-list__label">
|
||||||
<FormattedMessage id="feeds.no.items.matching" />
|
<Trans id="feeds.no.items.matching" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC, useRef, useState} from 'react';
|
import {FC, useRef, useState} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Button, Form, FormRow, Select, SelectItem, Textbox} from '@client/ui';
|
import {Button, Form, FormRow, Select, SelectItem, Textbox} from '@client/ui';
|
||||||
import FeedActions from '@client/actions/FeedActions';
|
import FeedActions from '@client/actions/FeedActions';
|
||||||
@@ -11,7 +11,7 @@ import FeedItems from './FeedItems';
|
|||||||
import ModalFormSectionHeader from '../ModalFormSectionHeader';
|
import ModalFormSectionHeader from '../ModalFormSectionHeader';
|
||||||
|
|
||||||
const FeedItemsForm: FC = observer(() => {
|
const FeedItemsForm: FC = observer(() => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const manualAddingFormRef = useRef<Form>(null);
|
const manualAddingFormRef = useRef<Form>(null);
|
||||||
const [selectedFeedID, setSelectedFeedID] = useState<string | null>(null);
|
const [selectedFeedID, setSelectedFeedID] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -55,22 +55,20 @@ const FeedItemsForm: FC = observer(() => {
|
|||||||
}}
|
}}
|
||||||
ref={manualAddingFormRef}>
|
ref={manualAddingFormRef}>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="feeds.browse.feeds" />
|
<Trans id="feeds.browse.feeds" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Select
|
<Select
|
||||||
disabled={!feeds.length}
|
disabled={!feeds.length}
|
||||||
grow={false}
|
grow={false}
|
||||||
id="feedID"
|
id="feedID"
|
||||||
label={intl.formatMessage({
|
label={i18n._('feeds.select.feed')}
|
||||||
id: 'feeds.select.feed',
|
|
||||||
})}
|
|
||||||
width="three-eighths">
|
width="three-eighths">
|
||||||
{!feeds.length
|
{!feeds.length
|
||||||
? [
|
? [
|
||||||
<SelectItem key="empty" id="placeholder" isPlaceholder>
|
<SelectItem key="empty" id="placeholder" isPlaceholder>
|
||||||
<em>
|
<em>
|
||||||
<FormattedMessage id="feeds.no.feeds.available" />
|
<Trans id="feeds.no.feeds.available" />
|
||||||
</em>
|
</em>
|
||||||
</SelectItem>,
|
</SelectItem>,
|
||||||
]
|
]
|
||||||
@@ -89,26 +87,18 @@ const FeedItemsForm: FC = observer(() => {
|
|||||||
[
|
[
|
||||||
<SelectItem key="select-feed" id="placeholder" isPlaceholder>
|
<SelectItem key="select-feed" id="placeholder" isPlaceholder>
|
||||||
<em>
|
<em>
|
||||||
<FormattedMessage id="feeds.select.feed" />
|
<Trans id="feeds.select.feed" />
|
||||||
</em>
|
</em>
|
||||||
</SelectItem>,
|
</SelectItem>,
|
||||||
],
|
],
|
||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
{selectedFeedID && (
|
{selectedFeedID && (
|
||||||
<Textbox
|
<Textbox id="search" label={i18n._('feeds.search.term')} placeholder={i18n._('feeds.search')} />
|
||||||
id="search"
|
|
||||||
label={intl.formatMessage({
|
|
||||||
id: 'feeds.search.term',
|
|
||||||
})}
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'feeds.search',
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{selectedFeedID && (
|
{selectedFeedID && (
|
||||||
<Button key="button" type="submit" labelOffset>
|
<Button key="button" type="submit" labelOffset>
|
||||||
<FormattedMessage id="button.download" />
|
<Trans id="button.download" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</FormRow>
|
</FormRow>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {observer} from 'mobx-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';
|
import {Close, Edit} from '@client/ui/icons';
|
||||||
|
|
||||||
@@ -18,13 +18,13 @@ interface FeedListProps {
|
|||||||
const FeedList: FC<FeedListProps> = observer(
|
const FeedList: FC<FeedListProps> = observer(
|
||||||
({currentFeed, intervalMultipliers, onSelect, onRemove}: FeedListProps) => {
|
({currentFeed, intervalMultipliers, onSelect, onRemove}: FeedListProps) => {
|
||||||
const {feeds} = FeedStore;
|
const {feeds} = FeedStore;
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
if (feeds.length === 0) {
|
if (feeds.length === 0) {
|
||||||
return (
|
return (
|
||||||
<ul className="interactive-list">
|
<ul className="interactive-list">
|
||||||
<li className="interactive-list__item">
|
<li className="interactive-list__item">
|
||||||
<FormattedMessage id="feeds.no.feeds.defined" />
|
<Trans id="feeds.no.feeds.defined" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
@@ -60,7 +60,7 @@ const FeedList: FC<FeedListProps> = observer(
|
|||||||
className="interactive-list__detail-list__item
|
className="interactive-list__detail-list__item
|
||||||
interactive-list__detail-list__item--overflow
|
interactive-list__detail-list__item--overflow
|
||||||
interactive-list__detail interactive-list__detail--secondary">
|
interactive-list__detail interactive-list__detail--secondary">
|
||||||
<FormattedMessage id="feeds.match.count" values={{count: matchedCount}} />
|
<Trans id="feeds.match.count" values={{count: matchedCount}} />
|
||||||
</li>
|
</li>
|
||||||
{feed === currentFeed && (
|
{feed === currentFeed && (
|
||||||
<li
|
<li
|
||||||
@@ -74,7 +74,7 @@ const FeedList: FC<FeedListProps> = observer(
|
|||||||
<li
|
<li
|
||||||
className="interactive-list__detail-list__item
|
className="interactive-list__detail-list__item
|
||||||
interactive-list__detail interactive-list__detail--tertiary">
|
interactive-list__detail interactive-list__detail--tertiary">
|
||||||
{`${intervalText} ${intl.formatMessage({id: intervalMultiplierMessage})}`}
|
{`${intervalText} ${i18n._(intervalMultiplierMessage)}`}
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
className="interactive-list__detail-list__item
|
className="interactive-list__detail-list__item
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useEffect} from 'react';
|
import {FC, useEffect} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import DownloadRulesTab from './DownloadRulesTab';
|
import DownloadRulesTab from './DownloadRulesTab';
|
||||||
import FeedActions from '../../../actions/FeedActions';
|
import FeedActions from '../../../actions/FeedActions';
|
||||||
@@ -7,7 +7,7 @@ import FeedsTab from './FeedsTab';
|
|||||||
import Modal from '../Modal';
|
import Modal from '../Modal';
|
||||||
|
|
||||||
const FeedsModal: FC = () => {
|
const FeedsModal: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
FeedActions.fetchFeedMonitors();
|
FeedActions.fetchFeedMonitors();
|
||||||
@@ -16,28 +16,15 @@ const FeedsModal: FC = () => {
|
|||||||
const tabs = {
|
const tabs = {
|
||||||
feeds: {
|
feeds: {
|
||||||
content: FeedsTab,
|
content: FeedsTab,
|
||||||
label: intl.formatMessage({
|
label: i18n._('feeds.tabs.feeds'),
|
||||||
id: 'feeds.tabs.feeds',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
downloadRules: {
|
downloadRules: {
|
||||||
content: DownloadRulesTab,
|
content: DownloadRulesTab,
|
||||||
label: intl.formatMessage({
|
label: i18n._('feeds.tabs.download.rules'),
|
||||||
id: 'feeds.tabs.download.rules',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <Modal heading={i18n._('feeds.tabs.heading')} orientation="horizontal" size="large" tabs={tabs} />;
|
||||||
<Modal
|
|
||||||
heading={intl.formatMessage({
|
|
||||||
id: 'feeds.tabs.heading',
|
|
||||||
})}
|
|
||||||
orientation="horizontal"
|
|
||||||
size="large"
|
|
||||||
tabs={tabs}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FeedsModal;
|
export default FeedsModal;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, ReactNodeArray, useRef, useState} from 'react';
|
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 {Button, Form, FormError, FormRow, FormRowItem} from '@client/ui';
|
||||||
import FeedActions from '@client/actions/FeedActions';
|
import FeedActions from '@client/actions/FeedActions';
|
||||||
@@ -63,7 +63,7 @@ const defaultFeed: AddFeedOptions = {
|
|||||||
|
|
||||||
const FeedsTab: FC = () => {
|
const FeedsTab: FC = () => {
|
||||||
const formRef = useRef<Form>(null);
|
const formRef = useRef<Form>(null);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [currentFeed, setCurrentFeed] = useState<Feed | null>(null);
|
const [currentFeed, setCurrentFeed] = useState<Feed | null>(null);
|
||||||
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
|
const [errors, setErrors] = useState<Record<string, string | undefined>>({});
|
||||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||||
@@ -134,13 +134,13 @@ const FeedsTab: FC = () => {
|
|||||||
}}
|
}}
|
||||||
ref={formRef}>
|
ref={formRef}>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="feeds.existing.feeds" />
|
<Trans id="feeds.existing.feeds" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
{Object.keys(errors).reduce((memo: ReactNodeArray, key) => {
|
{Object.keys(errors).reduce((memo: ReactNodeArray, key) => {
|
||||||
if (errors[key as ValidatedField] != null) {
|
if (errors[key as ValidatedField] != null) {
|
||||||
memo.push(
|
memo.push(
|
||||||
<FormRow key={`error-${key}`}>
|
<FormRow key={`error-${key}`}>
|
||||||
<FormError>{intl.formatMessage({id: errors?.[key as ValidatedField]})}</FormError>
|
<FormError>{i18n._(errors?.[key as ValidatedField] as string)}</FormError>
|
||||||
</FormRow>,
|
</FormRow>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ const FeedsTab: FC = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsEditing(true);
|
setIsEditing(true);
|
||||||
}}>
|
}}>
|
||||||
<FormattedMessage id="button.new" />
|
<Trans id="button.new" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useEffect, useRef, useState} from 'react';
|
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 {CheckmarkThick, Clipboard} from '@client/ui/icons';
|
||||||
import {Form, FormElementAddon, FormError, FormRow, Textbox} from '@client/ui';
|
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 GenerateMagnetModal: FC = () => {
|
||||||
const magnetTextboxRef = useRef<HTMLInputElement>(null);
|
const magnetTextboxRef = useRef<HTMLInputElement>(null);
|
||||||
const magnetTrackersTextboxRef = useRef<HTMLInputElement>(null);
|
const magnetTrackersTextboxRef = useRef<HTMLInputElement>(null);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const [isMagnetCopied, setIsMagnetCopied] = useState<boolean>(false);
|
const [isMagnetCopied, setIsMagnetCopied] = useState<boolean>(false);
|
||||||
const [isMagnetTrackersCopied, setIsMagnetTrackersCopied] = useState<boolean>(false);
|
const [isMagnetTrackersCopied, setIsMagnetTrackersCopied] = useState<boolean>(false);
|
||||||
@@ -55,15 +55,13 @@ const GenerateMagnetModal: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
heading={intl.formatMessage({
|
heading={i18n._('torrents.generate.magnet.heading')}
|
||||||
id: 'torrents.generate.magnet.heading',
|
|
||||||
})}
|
|
||||||
content={
|
content={
|
||||||
<div className="modal__content inverse">
|
<div className="modal__content inverse">
|
||||||
<Form>
|
<Form>
|
||||||
{TorrentStore.torrents[TorrentStore.selectedTorrents[0]]?.isPrivate ? (
|
{TorrentStore.torrents[TorrentStore.selectedTorrents[0]]?.isPrivate ? (
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormError>{intl.formatMessage({id: 'torrents.generate.magnet.private.torrent'})}</FormError>
|
<FormError>{i18n._('torrents.generate.magnet.private.torrent')}</FormError>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
) : null}
|
) : null}
|
||||||
<FormRow>
|
<FormRow>
|
||||||
@@ -71,7 +69,7 @@ const GenerateMagnetModal: FC = () => {
|
|||||||
id="magnet"
|
id="magnet"
|
||||||
ref={magnetTextboxRef}
|
ref={magnetTextboxRef}
|
||||||
addonPlacement="after"
|
addonPlacement="after"
|
||||||
label={intl.formatMessage({id: 'torrents.generate.magnet.magnet'})}
|
label={i18n._('torrents.generate.magnet.magnet')}
|
||||||
defaultValue={magnetLink}
|
defaultValue={magnetLink}
|
||||||
readOnly>
|
readOnly>
|
||||||
<FormElementAddon
|
<FormElementAddon
|
||||||
@@ -94,10 +92,8 @@ const GenerateMagnetModal: FC = () => {
|
|||||||
{trackerState.isLoadingTrackers ? (
|
{trackerState.isLoadingTrackers ? (
|
||||||
<Textbox
|
<Textbox
|
||||||
id="loading"
|
id="loading"
|
||||||
label={intl.formatMessage({id: 'torrents.generate.magnet.magnet.with.trackers'})}
|
label={i18n._('torrents.generate.magnet.magnet.with.trackers')}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('torrents.generate.magnet.loading.trackers')}
|
||||||
id: 'torrents.generate.magnet.loading.trackers',
|
|
||||||
})}
|
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -105,7 +101,7 @@ const GenerateMagnetModal: FC = () => {
|
|||||||
id="magnet-trackers"
|
id="magnet-trackers"
|
||||||
ref={magnetTrackersTextboxRef}
|
ref={magnetTrackersTextboxRef}
|
||||||
addonPlacement="after"
|
addonPlacement="after"
|
||||||
label={intl.formatMessage({id: 'torrents.generate.magnet.magnet.with.trackers'})}
|
label={i18n._('torrents.generate.magnet.magnet.with.trackers')}
|
||||||
defaultValue={trackerState.magnetTrackersLink}
|
defaultValue={trackerState.magnetTrackersLink}
|
||||||
readOnly>
|
readOnly>
|
||||||
<FormElementAddon
|
<FormElementAddon
|
||||||
@@ -131,9 +127,7 @@ const GenerateMagnetModal: FC = () => {
|
|||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.close'),
|
||||||
id: 'button.close',
|
|
||||||
}),
|
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'tertiary',
|
type: 'tertiary',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Form} from '@client/ui';
|
import {Form} from '@client/ui';
|
||||||
import TorrentActions from '@client/actions/TorrentActions';
|
import TorrentActions from '@client/actions/TorrentActions';
|
||||||
@@ -31,14 +31,12 @@ const getSuggestedPath = (sources: Array<string>): string | undefined => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const MoveTorrents: FC = () => {
|
const MoveTorrents: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [isSettingDownloadPath, setIsSettingDownloadPath] = useState<boolean>(false);
|
const [isSettingDownloadPath, setIsSettingDownloadPath] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
heading={intl.formatMessage({
|
heading={i18n._('torrents.move.heading')}
|
||||||
id: 'torrents.move.heading',
|
|
||||||
})}
|
|
||||||
content={
|
content={
|
||||||
<div className="modal__content">
|
<div className="modal__content">
|
||||||
<Form
|
<Form
|
||||||
@@ -71,31 +69,23 @@ const MoveTorrents: FC = () => {
|
|||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
checked: true,
|
checked: true,
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.move.data.label'),
|
||||||
id: 'torrents.move.data.label',
|
|
||||||
}),
|
|
||||||
id: 'moveFiles',
|
id: 'moveFiles',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
checked: false,
|
checked: false,
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.move.check_hash.label'),
|
||||||
id: 'torrents.move.check_hash.label',
|
|
||||||
}),
|
|
||||||
id: 'isCheckHash',
|
id: 'isCheckHash',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.cancel'),
|
||||||
id: 'button.cancel',
|
|
||||||
}),
|
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'tertiary',
|
type: 'tertiary',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.move.button.set.location'),
|
||||||
id: 'torrents.move.button.set.location',
|
|
||||||
}),
|
|
||||||
isLoading: isSettingDownloadPath,
|
isLoading: isSettingDownloadPath,
|
||||||
submit: true,
|
submit: true,
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Form, FormRow} from '@client/ui';
|
import {Form, FormRow} from '@client/ui';
|
||||||
import {saveDeleteTorrentsUserPreferences} from '@client/util/userPreferences';
|
import {saveDeleteTorrentsUserPreferences} from '@client/util/userPreferences';
|
||||||
@@ -12,21 +12,19 @@ import Modal from '../Modal';
|
|||||||
import ModalActions from '../ModalActions';
|
import ModalActions from '../ModalActions';
|
||||||
|
|
||||||
const RemoveTorrentsModal: FC = () => {
|
const RemoveTorrentsModal: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [isRemoving, setIsRemoving] = useState<boolean>(false);
|
const [isRemoving, setIsRemoving] = useState<boolean>(false);
|
||||||
const {selectedTorrents} = TorrentStore;
|
const {selectedTorrents} = TorrentStore;
|
||||||
|
|
||||||
if (selectedTorrents.length === 0) {
|
if (selectedTorrents.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
heading={intl.formatMessage({
|
heading={i18n._('torrents.remove')}
|
||||||
id: 'torrents.remove',
|
|
||||||
})}
|
|
||||||
content={
|
content={
|
||||||
<div className="modal__content inverse">
|
<div className="modal__content inverse">
|
||||||
<Form>
|
<Form>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormattedMessage id="torrents.remove.error.no.torrents.selected" />
|
<Trans id="torrents.remove.error.no.torrents.selected" />
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,9 +32,7 @@ const RemoveTorrentsModal: FC = () => {
|
|||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.ok'),
|
||||||
id: 'button.ok',
|
|
||||||
}),
|
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
},
|
},
|
||||||
@@ -47,9 +43,7 @@ const RemoveTorrentsModal: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
heading={intl.formatMessage({
|
heading={i18n._('torrents.remove')}
|
||||||
id: 'torrents.remove',
|
|
||||||
})}
|
|
||||||
content={
|
content={
|
||||||
<div className="modal__content">
|
<div className="modal__content">
|
||||||
<Form
|
<Form
|
||||||
@@ -69,29 +63,23 @@ const RemoveTorrentsModal: FC = () => {
|
|||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormattedMessage id="torrents.remove.are.you.sure" values={{count: selectedTorrents.length}} />
|
<Trans id="torrents.remove.are.you.sure" values={{count: selectedTorrents.length}} />
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalActions
|
<ModalActions
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
checked: SettingStore.floodSettings.deleteTorrentData,
|
checked: SettingStore.floodSettings.deleteTorrentData,
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.remove.delete.data'),
|
||||||
id: 'torrents.remove.delete.data',
|
|
||||||
}),
|
|
||||||
id: 'deleteData',
|
id: 'deleteData',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.no'),
|
||||||
id: 'button.no',
|
|
||||||
}),
|
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'tertiary',
|
type: 'tertiary',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.yes'),
|
||||||
id: 'button.yes',
|
|
||||||
}),
|
|
||||||
isLoading: isRemoving,
|
isLoading: isRemoving,
|
||||||
submit: true,
|
submit: true,
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useRef, useState} from 'react';
|
import {FC, useRef, useState} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Form, FormRow} from '@client/ui';
|
import {Form, FormRow} from '@client/ui';
|
||||||
import TorrentActions from '@client/actions/TorrentActions';
|
import TorrentActions from '@client/actions/TorrentActions';
|
||||||
@@ -10,14 +10,12 @@ import TagSelect from '../../general/form-elements/TagSelect';
|
|||||||
|
|
||||||
const SetTagsModal: FC = () => {
|
const SetTagsModal: FC = () => {
|
||||||
const formRef = useRef<Form>(null);
|
const formRef = useRef<Form>(null);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [isSettingTags, setIsSettingTags] = useState<boolean>(false);
|
const [isSettingTags, setIsSettingTags] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
heading={intl.formatMessage({
|
heading={i18n._('torrents.set.tags.heading')}
|
||||||
id: 'torrents.set.tags.heading',
|
|
||||||
})}
|
|
||||||
content={
|
content={
|
||||||
<div className="modal__content inverse">
|
<div className="modal__content inverse">
|
||||||
<Form ref={formRef}>
|
<Form ref={formRef}>
|
||||||
@@ -27,9 +25,7 @@ const SetTagsModal: FC = () => {
|
|||||||
.map((hash: string) => TorrentStore.torrents[hash].tags)[0]
|
.map((hash: string) => TorrentStore.torrents[hash].tags)[0]
|
||||||
.slice()}
|
.slice()}
|
||||||
id="tags"
|
id="tags"
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('torrents.set.tags.enter.tags')}
|
||||||
id: 'torrents.set.tags.enter.tags',
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</Form>
|
</Form>
|
||||||
@@ -37,17 +33,13 @@ const SetTagsModal: FC = () => {
|
|||||||
}
|
}
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.cancel'),
|
||||||
id: 'button.cancel',
|
|
||||||
}),
|
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'tertiary',
|
type: 'tertiary',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.set.tags.button.set'),
|
||||||
id: 'torrents.set.tags.button.set',
|
|
||||||
}),
|
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
if (formRef.current == null) {
|
if (formRef.current == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useEffect, useRef, useState} from 'react';
|
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 {Form, FormRow, Textbox} from '@client/ui';
|
||||||
import TorrentActions from '@client/actions/TorrentActions';
|
import TorrentActions from '@client/actions/TorrentActions';
|
||||||
@@ -13,7 +13,7 @@ import TextboxRepeater, {getTextArray} from '../../general/form-elements/Textbox
|
|||||||
|
|
||||||
const SetTrackersModal: FC = () => {
|
const SetTrackersModal: FC = () => {
|
||||||
const formRef = useRef<Form>(null);
|
const formRef = useRef<Form>(null);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [isSettingTrackers, setIsSettingTrackers] = useState<boolean>(false);
|
const [isSettingTrackers, setIsSettingTrackers] = useState<boolean>(false);
|
||||||
const [trackerState, setTrackerState] = useState<{
|
const [trackerState, setTrackerState] = useState<{
|
||||||
isLoadingTrackers: boolean;
|
isLoadingTrackers: boolean;
|
||||||
@@ -38,28 +38,18 @@ const SetTrackersModal: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
heading={intl.formatMessage({
|
heading={i18n._('torrents.set.trackers.heading')}
|
||||||
id: 'torrents.set.trackers.heading',
|
|
||||||
})}
|
|
||||||
content={
|
content={
|
||||||
<div className="modal__content inverse">
|
<div className="modal__content inverse">
|
||||||
<Form ref={formRef}>
|
<Form ref={formRef}>
|
||||||
{trackerState.isLoadingTrackers ? (
|
{trackerState.isLoadingTrackers ? (
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox id="loading" placeholder={i18n._('torrents.set.trackers.loading.trackers')} disabled />
|
||||||
id="loading"
|
|
||||||
placeholder={intl.formatMessage({
|
|
||||||
id: 'torrents.set.trackers.loading.trackers',
|
|
||||||
})}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</FormRow>
|
</FormRow>
|
||||||
) : (
|
) : (
|
||||||
<TextboxRepeater
|
<TextboxRepeater
|
||||||
id="trackers"
|
id="trackers"
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('torrents.set.trackers.enter.tracker')}
|
||||||
id: 'torrents.set.trackers.enter.tracker',
|
|
||||||
})}
|
|
||||||
defaultValues={
|
defaultValues={
|
||||||
trackerState.trackerURLs.length === 0
|
trackerState.trackerURLs.length === 0
|
||||||
? undefined
|
? undefined
|
||||||
@@ -76,9 +66,7 @@ const SetTrackersModal: FC = () => {
|
|||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.cancel'),
|
||||||
id: 'button.cancel',
|
|
||||||
}),
|
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'tertiary',
|
type: 'tertiary',
|
||||||
},
|
},
|
||||||
@@ -101,9 +89,7 @@ const SetTrackersModal: FC = () => {
|
|||||||
UIStore.dismissModal();
|
UIStore.dismissModal();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
content: intl.formatMessage({
|
content: i18n._('torrents.set.trackers.button.set'),
|
||||||
id: 'torrents.set.trackers.button.set',
|
|
||||||
}),
|
|
||||||
isLoading: isSettingTrackers || trackerState.isLoadingTrackers,
|
isLoading: isSettingTrackers || trackerState.isLoadingTrackers,
|
||||||
triggerDismiss: false,
|
triggerDismiss: false,
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {CSSTransition, TransitionGroup} from 'react-transition-group';
|
import {CSSTransition, TransitionGroup} from 'react-transition-group';
|
||||||
import {FC, useEffect, useRef, useState} from 'react';
|
import {FC, useEffect, useRef, useState} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Button, Checkbox, Form, FormError, FormRowItem, FormRow, LoadingRing, Textbox} from '@client/ui';
|
import {Button, Checkbox, Form, FormError, FormRowItem, FormRow, LoadingRing, Textbox} from '@client/ui';
|
||||||
import {Close} from '@client/ui/icons';
|
import {Close} from '@client/ui/icons';
|
||||||
@@ -29,7 +29,7 @@ const AuthTab: FC = observer(() => {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [isUserListFetched, setIsUserListFetched] = useState<boolean>(false);
|
const [isUserListFetched, setIsUserListFetched] = useState<boolean>(false);
|
||||||
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
|
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (AuthStore.currentUser.isAdmin) {
|
if (AuthStore.currentUser.isAdmin) {
|
||||||
@@ -43,11 +43,11 @@ const AuthTab: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="auth.user.accounts" />
|
<Trans id="auth.user.accounts" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormError>
|
<FormError>
|
||||||
<FormattedMessage id="auth.message.not.admin" />
|
<Trans id="auth.message.not.admin" />
|
||||||
</FormError>
|
</FormError>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</Form>
|
</Form>
|
||||||
@@ -106,7 +106,7 @@ const AuthTab: FC = observer(() => {
|
|||||||
}}
|
}}
|
||||||
ref={formRef}>
|
ref={formRef}>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="auth.user.accounts" />
|
<Trans id="auth.user.accounts" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormRowItem>
|
<FormRowItem>
|
||||||
@@ -139,7 +139,7 @@ const AuthTab: FC = observer(() => {
|
|||||||
} else {
|
} else {
|
||||||
badge = (
|
badge = (
|
||||||
<span className="interactive-list__label__tag tag">
|
<span className="interactive-list__label__tag tag">
|
||||||
<FormattedMessage id="auth.current.user" />
|
<Trans id="auth.current.user" />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -162,32 +162,28 @@ const AuthTab: FC = observer(() => {
|
|||||||
</FormRowItem>
|
</FormRowItem>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="auth.add.user" />
|
<Trans id="auth.add.user" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
{error && (
|
{error && (
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormError>{intl.formatMessage({id: error})}</FormError>
|
<FormError>{i18n._(error)}</FormError>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="username"
|
id="username"
|
||||||
label={<FormattedMessage id="auth.username" />}
|
label={<Trans id="auth.username" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('auth.username')}
|
||||||
id: 'auth.username',
|
|
||||||
})}
|
|
||||||
autoComplete="username"
|
autoComplete="username"
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
id="password"
|
id="password"
|
||||||
label={<FormattedMessage id="auth.password" />}
|
label={<Trans id="auth.password" />}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('auth.password')}
|
||||||
id: 'auth.password',
|
|
||||||
})}
|
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
/>
|
/>
|
||||||
<Checkbox grow={false} id="isAdmin" labelOffset matchTextboxHeight>
|
<Checkbox grow={false} id="isAdmin" labelOffset matchTextboxHeight>
|
||||||
<FormattedMessage id="auth.admin" />
|
<Trans id="auth.admin" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ClientConnectionSettingsForm
|
<ClientConnectionSettingsForm
|
||||||
@@ -198,7 +194,7 @@ const AuthTab: FC = observer(() => {
|
|||||||
<p />
|
<p />
|
||||||
<FormRow justify="end">
|
<FormRow justify="end">
|
||||||
<Button isLoading={isSubmitting} priority="primary" type="submit">
|
<Button isLoading={isSubmitting} priority="primary" type="submit">
|
||||||
<FormattedMessage id="button.add" />
|
<Trans id="button.add" />
|
||||||
</Button>
|
</Button>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {FC, FormEvent, useState} from 'react';
|
import {FC, FormEvent, useState} from 'react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Form, FormRow, Textbox} from '@client/ui';
|
import {Form, FormRow, Textbox} from '@client/ui';
|
||||||
import SettingStore from '@client/stores/SettingStore';
|
import SettingStore from '@client/stores/SettingStore';
|
||||||
@@ -62,7 +62,7 @@ const BandwidthTab: FC<BandwidthTabProps> = ({onSettingsChange, onClientSettings
|
|||||||
onClientSettingsChange(newChangedClientSettings);
|
onClientSettingsChange(newChangedClientSettings);
|
||||||
}}>
|
}}>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.bandwidth.transferrate.heading" />
|
<Trans id="settings.bandwidth.transferrate.heading" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
@@ -71,7 +71,7 @@ const BandwidthTab: FC<BandwidthTabProps> = ({onSettingsChange, onClientSettings
|
|||||||
? processSpeedsForDisplay(SettingStore.floodSettings.speedLimits.download)
|
? processSpeedsForDisplay(SettingStore.floodSettings.speedLimits.download)
|
||||||
: 0
|
: 0
|
||||||
}
|
}
|
||||||
label={<FormattedMessage id="settings.bandwidth.transferrate.dropdown.preset.download.label" />}
|
label={<Trans id="settings.bandwidth.transferrate.dropdown.preset.download.label" />}
|
||||||
id="dropdownPresetDownload"
|
id="dropdownPresetDownload"
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
@@ -82,46 +82,46 @@ const BandwidthTab: FC<BandwidthTabProps> = ({onSettingsChange, onClientSettings
|
|||||||
? processSpeedsForDisplay(SettingStore.floodSettings.speedLimits.upload)
|
? processSpeedsForDisplay(SettingStore.floodSettings.speedLimits.upload)
|
||||||
: 0
|
: 0
|
||||||
}
|
}
|
||||||
label={<FormattedMessage id="settings.bandwidth.transferrate.dropdown.preset.upload.label" />}
|
label={<Trans id="settings.bandwidth.transferrate.dropdown.preset.upload.label" />}
|
||||||
id="dropdownPresetUpload"
|
id="dropdownPresetUpload"
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleGlobalDownSpeed')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleGlobalDownSpeed')}
|
||||||
label={<FormattedMessage id="settings.bandwidth.transferrate.global.throttle.download" />}
|
label={<Trans id="settings.bandwidth.transferrate.global.throttle.download" />}
|
||||||
id="throttleGlobalDownSpeed"
|
id="throttleGlobalDownSpeed"
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleGlobalUpSpeed')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleGlobalUpSpeed')}
|
||||||
label={<FormattedMessage id="settings.bandwidth.transferrate.global.throttle.upload" />}
|
label={<Trans id="settings.bandwidth.transferrate.global.throttle.upload" />}
|
||||||
id="throttleGlobalUpSpeed"
|
id="throttleGlobalUpSpeed"
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.bandwidth.slots.heading" />
|
<Trans id="settings.bandwidth.slots.heading" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxUploads')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxUploads')}
|
||||||
label={<FormattedMessage id="settings.bandwidth.slots.upload.label" />}
|
label={<Trans id="settings.bandwidth.slots.upload.label" />}
|
||||||
id="throttleMaxUploads"
|
id="throttleMaxUploads"
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxUploadsGlobal')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxUploadsGlobal')}
|
||||||
label={<FormattedMessage id="settings.bandwidth.slots.upload.global.label" />}
|
label={<Trans id="settings.bandwidth.slots.upload.global.label" />}
|
||||||
id="throttleMaxUploadsGlobal"
|
id="throttleMaxUploadsGlobal"
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxDownloads')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxDownloads')}
|
||||||
label={<FormattedMessage id="settings.bandwidth.slots.download.label" />}
|
label={<Trans id="settings.bandwidth.slots.download.label" />}
|
||||||
id="throttleMaxDownloads"
|
id="throttleMaxDownloads"
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxDownloadsGlobal')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxDownloadsGlobal')}
|
||||||
label={<FormattedMessage id="settings.bandwidth.slots.download.global.label" />}
|
label={<Trans id="settings.bandwidth.slots.download.global.label" />}
|
||||||
id="throttleMaxDownloadsGlobal"
|
id="throttleMaxDownloadsGlobal"
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Checkbox, Form, FormRow, Textbox} from '@client/ui';
|
import {Checkbox, Form, FormRow, Textbox} from '@client/ui';
|
||||||
|
|
||||||
@@ -27,13 +27,13 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
|
|||||||
onClientSettingsChange(newChangedClientSettings);
|
onClientSettingsChange(newChangedClientSettings);
|
||||||
}}>
|
}}>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.connectivity.incoming.heading" />
|
<Trans id="settings.connectivity.incoming.heading" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'networkPortRange')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'networkPortRange')}
|
||||||
id="networkPortRange"
|
id="networkPortRange"
|
||||||
label={<FormattedMessage id="settings.connectivity.port.range.label" />}
|
label={<Trans id="settings.connectivity.port.range.label" />}
|
||||||
width="one-quarter"
|
width="one-quarter"
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -42,7 +42,7 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
|
|||||||
id="networkPortRandom"
|
id="networkPortRandom"
|
||||||
labelOffset
|
labelOffset
|
||||||
matchTextboxHeight>
|
matchTextboxHeight>
|
||||||
<FormattedMessage id="settings.connectivity.port.randomize.label" />
|
<Trans id="settings.connectivity.port.randomize.label" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={getChangedClientSetting(changedClientSettings, 'networkPortOpen')}
|
defaultChecked={getChangedClientSetting(changedClientSettings, 'networkPortOpen')}
|
||||||
@@ -50,29 +50,29 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
|
|||||||
id="networkPortOpen"
|
id="networkPortOpen"
|
||||||
labelOffset
|
labelOffset
|
||||||
matchTextboxHeight>
|
matchTextboxHeight>
|
||||||
<FormattedMessage id="settings.connectivity.port.open.label" />
|
<Trans id="settings.connectivity.port.open.label" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'networkLocalAddress')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'networkLocalAddress')}
|
||||||
id="networkLocalAddress"
|
id="networkLocalAddress"
|
||||||
label={<FormattedMessage id="settings.connectivity.ip.hostname.label" />}
|
label={<Trans id="settings.connectivity.ip.hostname.label" />}
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'networkHttpMaxOpen')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'networkHttpMaxOpen')}
|
||||||
id="networkHttpMaxOpen"
|
id="networkHttpMaxOpen"
|
||||||
label={<FormattedMessage id="settings.connectivity.max.http.connections" />}
|
label={<Trans id="settings.connectivity.max.http.connections" />}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.connectivity.dpd.heading" />
|
<Trans id="settings.connectivity.dpd.heading" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'dhtPort')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'dhtPort')}
|
||||||
id="dhtPort"
|
id="dhtPort"
|
||||||
label={<FormattedMessage id="settings.connectivity.dht.port.label" />}
|
label={<Trans id="settings.connectivity.dht.port.label" />}
|
||||||
width="one-quarter"
|
width="one-quarter"
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -81,7 +81,7 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
|
|||||||
id="dht"
|
id="dht"
|
||||||
labelOffset
|
labelOffset
|
||||||
matchTextboxHeight>
|
matchTextboxHeight>
|
||||||
<FormattedMessage id="settings.connectivity.dht.label" />
|
<Trans id="settings.connectivity.dht.label" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={getChangedClientSetting(changedClientSettings, 'protocolPex')}
|
defaultChecked={getChangedClientSetting(changedClientSettings, 'protocolPex')}
|
||||||
@@ -89,41 +89,41 @@ const ConnectivityTab: FC<ConnectivityTabProps> = ({onClientSettingsChange}: Con
|
|||||||
id="protocolPex"
|
id="protocolPex"
|
||||||
labelOffset
|
labelOffset
|
||||||
matchTextboxHeight>
|
matchTextboxHeight>
|
||||||
<FormattedMessage id="settings.connectivity.peer.exchange.label" />
|
<Trans id="settings.connectivity.peer.exchange.label" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.connectivity.peers.heading" />
|
<Trans id="settings.connectivity.peers.heading" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMinPeersNormal')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMinPeersNormal')}
|
||||||
id="throttleMinPeersNormal"
|
id="throttleMinPeersNormal"
|
||||||
label={<FormattedMessage id="settings.connectivity.peers.min.label" />}
|
label={<Trans id="settings.connectivity.peers.min.label" />}
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxPeersNormal')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxPeersNormal')}
|
||||||
id="throttleMaxPeersNormal"
|
id="throttleMaxPeersNormal"
|
||||||
label={<FormattedMessage id="settings.connectivity.peers.max.label" />}
|
label={<Trans id="settings.connectivity.peers.max.label" />}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMinPeersSeed')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMinPeersSeed')}
|
||||||
id="throttleMinPeersSeed"
|
id="throttleMinPeersSeed"
|
||||||
label={<FormattedMessage id="settings.connectivity.peers.seeding.min.label" />}
|
label={<Trans id="settings.connectivity.peers.seeding.min.label" />}
|
||||||
/>
|
/>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxPeersSeed')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'throttleMaxPeersSeed')}
|
||||||
id="throttleMaxPeersSeed"
|
id="throttleMaxPeersSeed"
|
||||||
label={<FormattedMessage id="settings.connectivity.peers.seeding.max.label" />}
|
label={<Trans id="settings.connectivity.peers.seeding.max.label" />}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'trackersNumWant')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'trackersNumWant')}
|
||||||
id="trackersNumWant"
|
id="trackersNumWant"
|
||||||
label={<FormattedMessage id="settings.connectivity.peers.desired.label" />}
|
label={<Trans id="settings.connectivity.peers.desired.label" />}
|
||||||
width="one-half"
|
width="one-half"
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Form, FormRow} from '@client/ui';
|
import {Form, FormRow} from '@client/ui';
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ interface DiskUsageTabProps {
|
|||||||
const DiskUsageTab: FC<DiskUsageTabProps> = (props: DiskUsageTabProps) => (
|
const DiskUsageTab: FC<DiskUsageTabProps> = (props: DiskUsageTabProps) => (
|
||||||
<Form>
|
<Form>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.diskusage.mount.points" />
|
<Trans id="settings.diskusage.mount.points" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<MountPointsList onSettingsChange={props.onSettingsChange} />
|
<MountPointsList onSettingsChange={props.onSettingsChange} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Checkbox, Form, FormRow, Textbox} from '@client/ui';
|
import {Checkbox, Form, FormRow, Textbox} from '@client/ui';
|
||||||
|
|
||||||
@@ -27,20 +27,20 @@ const ResourcesTab: FC<ResourcesTabProps> = ({onClientSettingsChange}: Resources
|
|||||||
onClientSettingsChange(newChangedClientSettings);
|
onClientSettingsChange(newChangedClientSettings);
|
||||||
}}>
|
}}>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.resources.disk.heading" />
|
<Trans id="settings.resources.disk.heading" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'directoryDefault')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'directoryDefault')}
|
||||||
id="directoryDefault"
|
id="directoryDefault"
|
||||||
label={<FormattedMessage id="settings.resources.disk.download.location.label" />}
|
label={<Trans id="settings.resources.disk.download.location.label" />}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
defaultValue={getChangedClientSetting(changedClientSettings, 'networkMaxOpenFiles')}
|
defaultValue={getChangedClientSetting(changedClientSettings, 'networkMaxOpenFiles')}
|
||||||
id="networkMaxOpenFiles"
|
id="networkMaxOpenFiles"
|
||||||
label={<FormattedMessage id="settings.resources.max.open.files" />}
|
label={<Trans id="settings.resources.max.open.files" />}
|
||||||
width="one-half"
|
width="one-half"
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -49,11 +49,11 @@ const ResourcesTab: FC<ResourcesTabProps> = ({onClientSettingsChange}: Resources
|
|||||||
id="piecesHashOnCompletion"
|
id="piecesHashOnCompletion"
|
||||||
labelOffset
|
labelOffset
|
||||||
matchTextboxHeight>
|
matchTextboxHeight>
|
||||||
<FormattedMessage id="settings.resources.disk.check.hash.label" />
|
<Trans id="settings.resources.disk.check.hash.label" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.resources.memory.heading" />
|
<Trans id="settings.resources.memory.heading" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Textbox
|
<Textbox
|
||||||
@@ -61,7 +61,7 @@ const ResourcesTab: FC<ResourcesTabProps> = ({onClientSettingsChange}: Resources
|
|||||||
id="piecesMemoryMax"
|
id="piecesMemoryMax"
|
||||||
label={
|
label={
|
||||||
<div>
|
<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>
|
</div>
|
||||||
}
|
}
|
||||||
width="one-half"
|
width="one-half"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
import {useMedia} from 'react-use';
|
import {useMedia} from 'react-use';
|
||||||
|
|
||||||
import type {ClientSettings} from '@shared/types/ClientSettings';
|
import type {ClientSettings} from '@shared/types/ClientSettings';
|
||||||
@@ -19,7 +19,7 @@ import UITab from './UITab';
|
|||||||
import UIStore from '../../../stores/UIStore';
|
import UIStore from '../../../stores/UIStore';
|
||||||
|
|
||||||
const SettingsModal: FC = () => {
|
const SettingsModal: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const isSmallScreen = useMedia('(max-width: 720px)');
|
const isSmallScreen = useMedia('(max-width: 720px)');
|
||||||
|
|
||||||
const [changedClientSettings, setChangedClientSettings] = useState<Partial<ClientSettings>>({});
|
const [changedClientSettings, setChangedClientSettings] = useState<Partial<ClientSettings>>({});
|
||||||
@@ -47,61 +47,47 @@ const SettingsModal: FC = () => {
|
|||||||
onClientSettingsChange: handleClientSettingsChange,
|
onClientSettingsChange: handleClientSettingsChange,
|
||||||
onSettingsChange: handleFloodSettingsChange,
|
onSettingsChange: handleFloodSettingsChange,
|
||||||
},
|
},
|
||||||
label: intl.formatMessage({
|
label: i18n._('settings.tabs.bandwidth'),
|
||||||
id: 'settings.tabs.bandwidth',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
connectivity: {
|
connectivity: {
|
||||||
content: ConnectivityTab,
|
content: ConnectivityTab,
|
||||||
props: {
|
props: {
|
||||||
onClientSettingsChange: handleClientSettingsChange,
|
onClientSettingsChange: handleClientSettingsChange,
|
||||||
},
|
},
|
||||||
label: intl.formatMessage({
|
label: i18n._('settings.tabs.connectivity'),
|
||||||
id: 'settings.tabs.connectivity',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
content: ResourcesTab,
|
content: ResourcesTab,
|
||||||
props: {
|
props: {
|
||||||
onClientSettingsChange: handleClientSettingsChange,
|
onClientSettingsChange: handleClientSettingsChange,
|
||||||
},
|
},
|
||||||
label: intl.formatMessage({
|
label: i18n._('settings.tabs.resources'),
|
||||||
id: 'settings.tabs.resources',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
...(ConfigStore.authMethod !== 'none'
|
...(ConfigStore.authMethod !== 'none'
|
||||||
? {
|
? {
|
||||||
authentication: {
|
authentication: {
|
||||||
content: AuthTab,
|
content: AuthTab,
|
||||||
label: intl.formatMessage({
|
label: i18n._('settings.tabs.authentication'),
|
||||||
id: 'settings.tabs.authentication',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
ui: {
|
ui: {
|
||||||
content: UITab,
|
content: UITab,
|
||||||
label: intl.formatMessage({
|
label: i18n._('settings.tabs.userinterface'),
|
||||||
id: 'settings.tabs.userinterface',
|
|
||||||
}),
|
|
||||||
props: {
|
props: {
|
||||||
onSettingsChange: handleFloodSettingsChange,
|
onSettingsChange: handleFloodSettingsChange,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
diskusage: {
|
diskusage: {
|
||||||
content: DiskUsageTab,
|
content: DiskUsageTab,
|
||||||
label: intl.formatMessage({
|
label: i18n._('settings.tabs.diskusage'),
|
||||||
id: 'settings.tabs.diskusage',
|
|
||||||
}),
|
|
||||||
props: {
|
props: {
|
||||||
onSettingsChange: handleFloodSettingsChange,
|
onSettingsChange: handleFloodSettingsChange,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
content: AboutTab,
|
content: AboutTab,
|
||||||
label: intl.formatMessage({
|
label: i18n._('settings.tabs.about'),
|
||||||
id: 'settings.tabs.about',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,9 +96,7 @@ const SettingsModal: FC = () => {
|
|||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
clickHandler: null,
|
clickHandler: null,
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.cancel'),
|
||||||
id: 'button.cancel',
|
|
||||||
}),
|
|
||||||
triggerDismiss: true,
|
triggerDismiss: true,
|
||||||
type: 'tertiary',
|
type: 'tertiary',
|
||||||
},
|
},
|
||||||
@@ -132,17 +116,13 @@ const SettingsModal: FC = () => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
isLoading: isSavingSettings,
|
isLoading: isSavingSettings,
|
||||||
content: intl.formatMessage({
|
content: i18n._('button.save'),
|
||||||
id: 'button.save',
|
|
||||||
}),
|
|
||||||
triggerDismiss: false,
|
triggerDismiss: false,
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
size="large"
|
size="large"
|
||||||
heading={intl.formatMessage({
|
heading={i18n._('settings.tabs.heading')}
|
||||||
id: 'settings.tabs.heading',
|
|
||||||
})}
|
|
||||||
orientation={isSmallScreen ? 'horizontal' : 'vertical'}
|
orientation={isSmallScreen ? 'horizontal' : 'vertical'}
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
|
||||||
import {FC, useState} from 'react';
|
import {FC, useState} from 'react';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Form, FormRow, Select, SelectItem, Radio} from '@client/ui';
|
import {Form, FormRow, Select, SelectItem, Radio} from '@client/ui';
|
||||||
import Languages from '@client/constants/Languages';
|
import Languages from '@client/constants/Languages';
|
||||||
@@ -18,7 +18,7 @@ interface UITabProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const UITab: FC<UITabProps> = ({onSettingsChange}: UITabProps) => {
|
const UITab: FC<UITabProps> = ({onSettingsChange}: UITabProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const [torrentListViewSize, setTorrentListViewSize] = useState(SettingStore.floodSettings.torrentListViewSize);
|
const [torrentListViewSize, setTorrentListViewSize] = useState(SettingStore.floodSettings.torrentListViewSize);
|
||||||
const [selectedLanguage, setSelectedLanguage] = useState(SettingStore.floodSettings.language);
|
const [selectedLanguage, setSelectedLanguage] = useState(SettingStore.floodSettings.language);
|
||||||
const [UITagSelectorMode, setUITagSelectorMode] = useState(SettingStore.floodSettings.UITagSelectorMode);
|
const [UITagSelectorMode, setUITagSelectorMode] = useState(SettingStore.floodSettings.UITagSelectorMode);
|
||||||
@@ -46,55 +46,53 @@ const UITab: FC<UITabProps> = ({onSettingsChange}: UITabProps) => {
|
|||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<ModalFormSectionHeader key="locale-header">
|
<ModalFormSectionHeader key="locale-header">
|
||||||
<FormattedMessage id="settings.ui.language" />
|
<Trans id="settings.ui.language" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow key="locale-selection">
|
<FormRow key="locale-selection">
|
||||||
<Select defaultID={selectedLanguage} id="language">
|
<Select defaultID={selectedLanguage} id="language">
|
||||||
{Object.keys(Languages).map((languageID) => (
|
{Object.keys(Languages).map((languageID) => (
|
||||||
<SelectItem key={languageID} id={languageID}>
|
<SelectItem key={languageID} id={languageID}>
|
||||||
{Languages[languageID as 'auto'].id != null
|
{Languages[languageID as 'auto'].id != null
|
||||||
? intl.formatMessage({
|
? i18n._(Languages[languageID as 'auto'].id)
|
||||||
id: Languages[languageID as 'auto'].id,
|
|
||||||
})
|
|
||||||
: Languages[languageID as Language]}
|
: Languages[languageID as Language]}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.ui.tag.selector.mode" />
|
<Trans id="settings.ui.tag.selector.mode" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Radio defaultChecked={UITagSelectorMode === 'single'} groupID="ui-tag-selector-mode" id="single" width="auto">
|
<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>
|
||||||
<Radio defaultChecked={UITagSelectorMode === 'multi'} groupID="ui-tag-selector-mode" id="multi" width="auto">
|
<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>
|
</Radio>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.ui.torrent.list" />
|
<Trans id="settings.ui.torrent.list" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<Radio defaultChecked={torrentListViewSize === 'expanded'} groupID="ui-torrent-size" id="expanded" width="auto">
|
<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>
|
||||||
<Radio
|
<Radio
|
||||||
defaultChecked={torrentListViewSize === 'condensed'}
|
defaultChecked={torrentListViewSize === 'condensed'}
|
||||||
groupID="ui-torrent-size"
|
groupID="ui-torrent-size"
|
||||||
id="condensed"
|
id="condensed"
|
||||||
width="auto">
|
width="auto">
|
||||||
<FormattedMessage id="settings.ui.torrent.size.condensed" />
|
<Trans id="settings.ui.torrent.size.condensed" />
|
||||||
</Radio>
|
</Radio>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.ui.displayed.details" />
|
<Trans id="settings.ui.displayed.details" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<TorrentListColumnsList torrentListViewSize={torrentListViewSize} onSettingsChange={onSettingsChange} />
|
<TorrentListColumnsList torrentListViewSize={torrentListViewSize} onSettingsChange={onSettingsChange} />
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<ModalFormSectionHeader>
|
<ModalFormSectionHeader>
|
||||||
<FormattedMessage id="settings.ui.displayed.context.menu.items" />
|
<Trans id="settings.ui.displayed.context.menu.items" />
|
||||||
</ModalFormSectionHeader>
|
</ModalFormSectionHeader>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<TorrentContextMenuActionsList onSettingsChange={onSettingsChange} />
|
<TorrentContextMenuActionsList onSettingsChange={onSettingsChange} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component} from 'react';
|
import {Component} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Checkbox} from '@client/ui';
|
import {Checkbox} from '@client/ui';
|
||||||
import DiskUsageStore from '@client/stores/DiskUsageStore';
|
import DiskUsageStore from '@client/stores/DiskUsageStore';
|
||||||
@@ -88,7 +88,7 @@ class MountPointsList extends Component<MountPointsListProps, MountPointsListSta
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={visible}
|
defaultChecked={visible}
|
||||||
onClick={(event) => this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}>
|
onClick={(event) => this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}>
|
||||||
<FormattedMessage id="settings.diskusage.show" />
|
<Trans id="settings.diskusage.show" />
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component} from 'react';
|
import {Component} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Checkbox} from '@client/ui';
|
import {Checkbox} from '@client/ui';
|
||||||
import SettingStore from '@client/stores/SettingStore';
|
import SettingStore from '@client/stores/SettingStore';
|
||||||
@@ -76,7 +76,7 @@ class TorrentContextMenuActionsList extends Component<
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={visible}
|
defaultChecked={visible}
|
||||||
onClick={(event) => this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}>
|
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>
|
</Checkbox>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -85,7 +85,7 @@ class TorrentContextMenuActionsList extends Component<
|
|||||||
const content = (
|
const content = (
|
||||||
<div className="sortable-list__content sortable-list__content__wrapper">
|
<div className="sortable-list__content sortable-list__content__wrapper">
|
||||||
<span className="sortable-list__content sortable-list__content--primary">
|
<span className="sortable-list__content sortable-list__content--primary">
|
||||||
<FormattedMessage id={TorrentContextMenuActions[id].id} />
|
<Trans id={TorrentContextMenuActions[id]} />
|
||||||
</span>
|
</span>
|
||||||
{checkbox}
|
{checkbox}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Component, ReactNode} from 'react';
|
import {Component, ReactNode} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import {Checkbox} from '@client/ui';
|
import {Checkbox} from '@client/ui';
|
||||||
import {Error} from '@client/ui/icons';
|
import {Error} from '@client/ui/icons';
|
||||||
@@ -95,7 +95,7 @@ class TorrentListColumnsList extends Component<TorrentListColumnsListProps, Torr
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={visible}
|
defaultChecked={visible}
|
||||||
onClick={(event) => this.handleCheckboxValueChange(id, (event.target as HTMLInputElement).checked)}>
|
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>
|
</Checkbox>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -106,7 +106,7 @@ class TorrentListColumnsList extends Component<TorrentListColumnsListProps, Torr
|
|||||||
this.props.torrentListViewSize === 'expanded' &&
|
this.props.torrentListViewSize === 'expanded' &&
|
||||||
index < this.state.torrentListColumns.length - 1
|
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 = (
|
warning = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -128,7 +128,7 @@ class TorrentListColumnsList extends Component<TorrentListColumnsListProps, Torr
|
|||||||
<div className="sortable-list__content sortable-list__content__wrapper">
|
<div className="sortable-list__content sortable-list__content__wrapper">
|
||||||
{warning}
|
{warning}
|
||||||
<span className="sortable-list__content sortable-list__content--primary">
|
<span className="sortable-list__content sortable-list__content--primary">
|
||||||
<FormattedMessage id={TorrentListColumns[id].id} />
|
<Trans id={TorrentListColumns[id]} />
|
||||||
</span>
|
</span>
|
||||||
{checkbox}
|
{checkbox}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import {FC, useEffect, useState} from '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 {Button, Checkbox, Form, FormRow, FormRowItem, Select, SelectItem} from '@client/ui';
|
||||||
import ConfigStore from '@client/stores/ConfigStore';
|
import ConfigStore from '@client/stores/ConfigStore';
|
||||||
@@ -19,7 +19,7 @@ const TorrentContents: FC = observer(() => {
|
|||||||
const [contents, setContents] = useState<TorrentContent[]>([]);
|
const [contents, setContents] = useState<TorrentContent[]>([]);
|
||||||
const [itemsTree, setItemsTree] = useState<TorrentContentSelectionTree>({});
|
const [itemsTree, setItemsTree] = useState<TorrentContentSelectionTree>({});
|
||||||
const [selectedIndices, setSelectedIndices] = useState<number[]>([]);
|
const [selectedIndices, setSelectedIndices] = useState<number[]>([]);
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (UIStore.activeModal?.id === 'torrent-details') {
|
if (UIStore.activeModal?.id === 'torrent-details') {
|
||||||
@@ -85,7 +85,7 @@ const TorrentContents: FC = observer(() => {
|
|||||||
directoryHeadingIconContent = <Disk />;
|
directoryHeadingIconContent = <Disk />;
|
||||||
fileDetailContent = (
|
fileDetailContent = (
|
||||||
<div className="directory-tree__node directory-tree__node--file">
|
<div className="directory-tree__node directory-tree__node--file">
|
||||||
<FormattedMessage id="torrents.details.files.loading" />
|
<Trans id="torrents.details.files.loading" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ const TorrentContents: FC = observer(() => {
|
|||||||
<div className="directory-tree__selection-toolbar">
|
<div className="directory-tree__selection-toolbar">
|
||||||
<FormRow align="center">
|
<FormRow align="center">
|
||||||
<FormRowItem width="one-quarter" grow={false} shrink={false}>
|
<FormRowItem width="one-quarter" grow={false} shrink={false}>
|
||||||
<FormattedMessage
|
<Trans
|
||||||
id="torrents.details.selected.files"
|
id="torrents.details.selected.files"
|
||||||
values={{
|
values={{
|
||||||
count: selectedIndices.length,
|
count: selectedIndices.length,
|
||||||
@@ -154,7 +154,7 @@ const TorrentContents: FC = observer(() => {
|
|||||||
}}
|
}}
|
||||||
grow={false}
|
grow={false}
|
||||||
shrink={false}>
|
shrink={false}>
|
||||||
<FormattedMessage
|
<Trans
|
||||||
id="torrents.details.files.download.file"
|
id="torrents.details.files.download.file"
|
||||||
values={{
|
values={{
|
||||||
count: selectedIndices.length,
|
count: selectedIndices.length,
|
||||||
@@ -163,23 +163,11 @@ const TorrentContents: FC = observer(() => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Select id="file-priority" persistentPlaceholder shrink={false} defaultID="">
|
<Select id="file-priority" persistentPlaceholder shrink={false} defaultID="">
|
||||||
<SelectItem id={-1} isPlaceholder>
|
<SelectItem id={-1} isPlaceholder>
|
||||||
<FormattedMessage id="torrents.details.selected.files.set.priority" />
|
<Trans 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',
|
|
||||||
})}
|
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
<SelectItem id={0}>{i18n._('priority.dont.download')}</SelectItem>
|
||||||
|
<SelectItem id={1}>{i18n._('priority.normal')}</SelectItem>
|
||||||
|
<SelectItem id={2}>{i18n._('priority.high')}</SelectItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
import {useMedia} from 'react-use';
|
import {useMedia} from 'react-use';
|
||||||
|
|
||||||
import Modal from '../Modal';
|
import Modal from '../Modal';
|
||||||
@@ -11,40 +11,30 @@ import TorrentPeers from './TorrentPeers';
|
|||||||
import TorrentTrackers from './TorrentTrackers';
|
import TorrentTrackers from './TorrentTrackers';
|
||||||
|
|
||||||
const TorrentDetailsModal: FC = () => {
|
const TorrentDetailsModal: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const isSmallScreen = useMedia('(max-width: 720px)');
|
const isSmallScreen = useMedia('(max-width: 720px)');
|
||||||
|
|
||||||
const tabs = {
|
const tabs = {
|
||||||
'torrent-details': {
|
'torrent-details': {
|
||||||
content: TorrentGeneralInfo,
|
content: TorrentGeneralInfo,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.details.details'),
|
||||||
id: 'torrents.details.details',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
'torrent-contents': {
|
'torrent-contents': {
|
||||||
content: TorrentContents,
|
content: TorrentContents,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.details.files'),
|
||||||
id: 'torrents.details.files',
|
|
||||||
}),
|
|
||||||
modalContentClasses: 'modal__content--nested-scroll',
|
modalContentClasses: 'modal__content--nested-scroll',
|
||||||
},
|
},
|
||||||
'torrent-peers': {
|
'torrent-peers': {
|
||||||
content: TorrentPeers,
|
content: TorrentPeers,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.details.peers'),
|
||||||
id: 'torrents.details.peers',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
'torrent-trackers': {
|
'torrent-trackers': {
|
||||||
content: TorrentTrackers,
|
content: TorrentTrackers,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.details.trackers'),
|
||||||
id: 'torrents.details.trackers',
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
'torrent-mediainfo': {
|
'torrent-mediainfo': {
|
||||||
content: TorrentMediainfo,
|
content: TorrentMediainfo,
|
||||||
label: intl.formatMessage({
|
label: i18n._('torrents.details.mediainfo'),
|
||||||
id: 'torrents.details.mediainfo',
|
|
||||||
}),
|
|
||||||
modalContentClasses: 'modal__content--nested-scroll',
|
modalContentClasses: 'modal__content--nested-scroll',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage, FormattedNumber, useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import type {TorrentProperties} from '@shared/types/Torrent';
|
import type {TorrentProperties} from '@shared/types/Torrent';
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ const getTags = (tags: TorrentProperties['tags']) =>
|
|||||||
));
|
));
|
||||||
|
|
||||||
const TorrentGeneralInfo: FC = observer(() => {
|
const TorrentGeneralInfo: FC = observer(() => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
if (UIStore.activeModal?.id !== 'torrent-details') {
|
if (UIStore.activeModal?.id !== 'torrent-details') {
|
||||||
return null;
|
return null;
|
||||||
@@ -39,7 +39,7 @@ const TorrentGeneralInfo: FC = observer(() => {
|
|||||||
|
|
||||||
const VALUE_NOT_AVAILABLE = (
|
const VALUE_NOT_AVAILABLE = (
|
||||||
<span className="not-available">
|
<span className="not-available">
|
||||||
<FormattedMessage id="torrents.details.general.none" />
|
<Trans id="torrents.details.general.none" />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -49,32 +49,34 @@ const TorrentGeneralInfo: FC = observer(() => {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr className="torrent-details__table__heading">
|
<tr className="torrent-details__table__heading">
|
||||||
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
||||||
<FormattedMessage id="torrents.details.general.heading.general" />
|
<Trans id="torrents.details.general.heading.general" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--dateAdded">
|
<tr className="torrent-details__detail torrent-details__detail--dateAdded">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.date.added" />
|
<Trans id="torrents.details.general.date.added" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
{dateAdded
|
{dateAdded
|
||||||
? `${intl.formatDate(dateAdded, {
|
? `${i18n.date(dateAdded, {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
})} ${intl.formatTime(dateAdded)}`
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
})}`
|
||||||
: VALUE_NOT_AVAILABLE}
|
: VALUE_NOT_AVAILABLE}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--location">
|
<tr className="torrent-details__detail torrent-details__detail--location">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.location" />
|
<Trans id="torrents.details.general.location" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">{torrent.directory}</td>
|
<td className="torrent-details__detail__value">{torrent.directory}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--tags">
|
<tr className="torrent-details__detail torrent-details__detail--tags">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.tags" />
|
<Trans id="torrents.details.general.tags" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
{torrent.tags.length ? getTags(torrent.tags) : VALUE_NOT_AVAILABLE}
|
{torrent.tags.length ? getTags(torrent.tags) : VALUE_NOT_AVAILABLE}
|
||||||
@@ -82,76 +84,78 @@ const TorrentGeneralInfo: FC = observer(() => {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__table__heading">
|
<tr className="torrent-details__table__heading">
|
||||||
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
||||||
<FormattedMessage id="torrents.details.general.heading.transfer" />
|
<Trans id="torrents.details.general.heading.transfer" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--downloaded">
|
<tr className="torrent-details__detail torrent-details__detail--downloaded">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.downloaded" />
|
<Trans id="torrents.details.general.downloaded" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
<FormattedNumber value={torrent.percentComplete} />
|
{i18n.number(torrent.percentComplete)}
|
||||||
<em className="unit">%</em>
|
<em className="unit">%</em>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--peers">
|
<tr className="torrent-details__detail torrent-details__detail--peers">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.peers" />
|
<Trans id="torrents.details.general.peers" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
<FormattedMessage
|
<Trans
|
||||||
id="torrents.details.general.connected"
|
id="torrents.details.general.connected"
|
||||||
values={{
|
values={{
|
||||||
connectedCount: torrent.peersConnected,
|
connectedCount: torrent.peersConnected,
|
||||||
connected: <FormattedNumber value={torrent.peersConnected} />,
|
connected: i18n.number(torrent.peersConnected),
|
||||||
total: <FormattedNumber value={torrent.peersTotal} />,
|
total: i18n.number(torrent.peersTotal),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--seeds">
|
<tr className="torrent-details__detail torrent-details__detail--seeds">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.seeds" />
|
<Trans id="torrents.details.general.seeds" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
<FormattedMessage
|
<Trans
|
||||||
id="torrents.details.general.connected"
|
id="torrents.details.general.connected"
|
||||||
values={{
|
values={{
|
||||||
connectedCount: torrent.seedsConnected,
|
connectedCount: torrent.seedsConnected,
|
||||||
connected: <FormattedNumber value={torrent.seedsConnected} />,
|
connected: i18n.number(torrent.seedsConnected),
|
||||||
total: <FormattedNumber value={torrent.seedsTotal} />,
|
total: i18n.number(torrent.seedsTotal),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__table__heading">
|
<tr className="torrent-details__table__heading">
|
||||||
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
||||||
<FormattedMessage id="torrents.details.general.heading.torrent" />
|
<Trans id="torrents.details.general.heading.torrent" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--created">
|
<tr className="torrent-details__detail torrent-details__detail--created">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.date.created" />
|
<Trans id="torrents.details.general.date.created" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
{creation
|
{creation
|
||||||
? `${intl.formatDate(creation, {
|
? `${i18n.date(creation, {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
})} ${intl.formatTime(creation)}`
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
})}`
|
||||||
: VALUE_NOT_AVAILABLE}
|
: VALUE_NOT_AVAILABLE}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--hash">
|
<tr className="torrent-details__detail torrent-details__detail--hash">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.hash" />
|
<Trans id="torrents.details.general.hash" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">{torrent.hash}</td>
|
<td className="torrent-details__detail__value">{torrent.hash}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--size">
|
<tr className="torrent-details__detail torrent-details__detail--size">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.size" />
|
<Trans id="torrents.details.general.size" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
<Size value={torrent.sizeBytes} />
|
<Size value={torrent.sizeBytes} />
|
||||||
@@ -159,26 +163,22 @@ const TorrentGeneralInfo: FC = observer(() => {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--type">
|
<tr className="torrent-details__detail torrent-details__detail--type">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.type" />
|
<Trans id="torrents.details.general.type" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
{torrent.isPrivate
|
{torrent.isPrivate
|
||||||
? intl.formatMessage({
|
? i18n._('torrents.details.general.type.private')
|
||||||
id: 'torrents.details.general.type.private',
|
: i18n._('torrents.details.general.type.public')}
|
||||||
})
|
|
||||||
: intl.formatMessage({
|
|
||||||
id: 'torrents.details.general.type.public',
|
|
||||||
})}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__table__heading">
|
<tr className="torrent-details__table__heading">
|
||||||
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
<td className="torrent-details__table__heading--tertiary" colSpan={2}>
|
||||||
<FormattedMessage id="torrents.details.general.heading.tracker" />
|
<Trans id="torrents.details.general.heading.tracker" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className="torrent-details__detail torrent-details__detail--tracker-message">
|
<tr className="torrent-details__detail torrent-details__detail--tracker-message">
|
||||||
<td className="torrent-details__detail__label">
|
<td className="torrent-details__detail__label">
|
||||||
<FormattedMessage id="torrents.details.general.tracker.message" />
|
<Trans id="torrents.details.general.tracker.message" />
|
||||||
</td>
|
</td>
|
||||||
<td className="torrent-details__detail__value">
|
<td className="torrent-details__detail__value">
|
||||||
{torrent.message ? torrent.message : VALUE_NOT_AVAILABLE}
|
{torrent.message ? torrent.message : VALUE_NOT_AVAILABLE}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FC, useEffect, useState} from 'react';
|
import {FC, useEffect, useState} from 'react';
|
||||||
import {FormattedMessage, FormattedNumber} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Clock, DownloadThick, Ratio, Start, Stop, UploadThick} from '@client/ui/icons';
|
import {Clock, DownloadThick, Ratio, Start, Stop, UploadThick} from '@client/ui/icons';
|
||||||
import TorrentActions from '@client/actions/TorrentActions';
|
import TorrentActions from '@client/actions/TorrentActions';
|
||||||
@@ -16,6 +16,7 @@ import ProgressBar from '../../general/ProgressBar';
|
|||||||
import Size from '../../general/Size';
|
import Size from '../../general/Size';
|
||||||
|
|
||||||
const TorrentHeading: FC = observer(() => {
|
const TorrentHeading: FC = observer(() => {
|
||||||
|
const {i18n} = useLingui();
|
||||||
const torrent =
|
const torrent =
|
||||||
UIStore.activeModal?.id === 'torrent-details' ? TorrentStore.torrents[UIStore.activeModal.hash] : undefined;
|
UIStore.activeModal?.id === 'torrent-details' ? TorrentStore.torrents[UIStore.activeModal.hash] : undefined;
|
||||||
const [torrentStatus, setTorrentStatus] = useState<'start' | 'stop'>('stop');
|
const [torrentStatus, setTorrentStatus] = useState<'start' | 'stop'>('stop');
|
||||||
@@ -61,7 +62,7 @@ const TorrentHeading: FC = observer(() => {
|
|||||||
</li>
|
</li>
|
||||||
<li className="torrent-details__sub-heading__tertiary">
|
<li className="torrent-details__sub-heading__tertiary">
|
||||||
<Ratio />
|
<Ratio />
|
||||||
<FormattedNumber value={torrent.ratio} />
|
{i18n.number(torrent.ratio)}
|
||||||
</li>
|
</li>
|
||||||
<li className="torrent-details__sub-heading__tertiary">
|
<li className="torrent-details__sub-heading__tertiary">
|
||||||
<Clock />
|
<Clock />
|
||||||
@@ -95,7 +96,7 @@ const TorrentHeading: FC = observer(() => {
|
|||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
<Start />
|
<Start />
|
||||||
<FormattedMessage id="torrents.details.actions.start" />
|
<Trans id="torrents.details.actions.start" />
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
className={classnames('torrent-details__sub-heading__tertiary', 'torrent-details__action', {
|
className={classnames('torrent-details__sub-heading__tertiary', 'torrent-details__action', {
|
||||||
@@ -109,7 +110,7 @@ const TorrentHeading: FC = observer(() => {
|
|||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
<Stop />
|
<Stop />
|
||||||
<FormattedMessage id="torrents.details.actions.stop" />
|
<Trans id="torrents.details.actions.stop" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import axios, {CancelTokenSource} from 'axios';
|
import axios, {CancelTokenSource} from 'axios';
|
||||||
import {FC, useEffect, useRef, useState} from 'react';
|
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 {Button} from '@client/ui';
|
||||||
import {Checkmark, Clipboard} from '@client/ui/icons';
|
import {Checkmark, Clipboard} from '@client/ui/icons';
|
||||||
@@ -10,7 +10,7 @@ import UIStore from '@client/stores/UIStore';
|
|||||||
import Tooltip from '../../general/Tooltip';
|
import Tooltip from '../../general/Tooltip';
|
||||||
|
|
||||||
const TorrentMediainfo: FC = () => {
|
const TorrentMediainfo: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const cancelToken = useRef<CancelTokenSource>(axios.CancelToken.source());
|
const cancelToken = useRef<CancelTokenSource>(axios.CancelToken.source());
|
||||||
const clipboardRef = useRef<HTMLInputElement>(null);
|
const clipboardRef = useRef<HTMLInputElement>(null);
|
||||||
const [mediainfo, setMediainfo] = useState<string | null>(null);
|
const [mediainfo, setMediainfo] = useState<string | null>(null);
|
||||||
@@ -53,14 +53,12 @@ const TorrentMediainfo: FC = () => {
|
|||||||
<div className="mediainfo__toolbar">
|
<div className="mediainfo__toolbar">
|
||||||
<div className="mediainfo__toolbar__item">
|
<div className="mediainfo__toolbar__item">
|
||||||
<span className="torrent-details__table__heading--tertiary">
|
<span className="torrent-details__table__heading--tertiary">
|
||||||
<FormattedMessage id={headingMessageId} />
|
<Trans id={headingMessageId} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{mediainfo && (
|
{mediainfo && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={intl.formatMessage({
|
content={i18n._(isCopiedToClipboard ? 'general.clipboard.copied' : 'general.clipboard.copy')}
|
||||||
id: isCopiedToClipboard ? 'general.clipboard.copied' : 'general.clipboard.copy',
|
|
||||||
})}
|
|
||||||
wrapperClassName="tooltip__wrapper mediainfo__toolbar__item">
|
wrapperClassName="tooltip__wrapper mediainfo__toolbar__item">
|
||||||
<Button
|
<Button
|
||||||
priority="tertiary"
|
priority="tertiary"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, Suspense, useEffect, useState} from 'react';
|
import {FC, Suspense, useEffect, useState} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
import {useInterval} from 'react-use';
|
import {useInterval} from 'react-use';
|
||||||
|
|
||||||
import {CheckmarkThick, CountryFlag, Lock, Spinner} from '@client/ui/icons';
|
import {CheckmarkThick, CountryFlag, Lock, Spinner} from '@client/ui/icons';
|
||||||
@@ -37,7 +37,7 @@ const TorrentPeers: FC = () => {
|
|||||||
<thead className="torrent-details__table__heading">
|
<thead className="torrent-details__table__heading">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="torrent-details__table__heading--primary">
|
<th className="torrent-details__table__heading--primary">
|
||||||
<FormattedMessage id="torrents.details.peers" />
|
<Trans id="torrents.details.peers" />
|
||||||
<Badge>{peers.length}</Badge>
|
<Badge>{peers.length}</Badge>
|
||||||
</th>
|
</th>
|
||||||
<th className="torrent-details__table__heading--secondary">DL</th>
|
<th className="torrent-details__table__heading--secondary">DL</th>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC, useEffect, useState} from 'react';
|
import {FC, useEffect, useState} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import type {TorrentTracker} from '@shared/types/TorrentTracker';
|
import type {TorrentTracker} from '@shared/types/TorrentTracker';
|
||||||
|
|
||||||
@@ -36,11 +36,11 @@ const TorrentTrackers: FC = () => {
|
|||||||
<thead className="torrent-details__table__heading">
|
<thead className="torrent-details__table__heading">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="torrent-details__table__heading--primary">
|
<th className="torrent-details__table__heading--primary">
|
||||||
<FormattedMessage id="torrents.details.trackers" />
|
<Trans id="torrents.details.trackers" />
|
||||||
<Badge>{trackerCount}</Badge>
|
<Badge>{trackerCount}</Badge>
|
||||||
</th>
|
</th>
|
||||||
<th className="torrent-details__table__heading--secondary">
|
<th className="torrent-details__table__heading--secondary">
|
||||||
<FormattedMessage id="torrents.details.trackers.type" />
|
<Trans id="torrents.details.trackers.type" />
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC, ReactNode, ReactNodeArray} from 'react';
|
import {FC, ReactNode, ReactNodeArray} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import type {Disk} from '@shared/types/DiskUsage';
|
import type {Disk} from '@shared/types/DiskUsage';
|
||||||
|
|
||||||
@@ -46,9 +46,9 @@ const DiskUsage: FC = observer(() => {
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
<ul className="diskusage__details-list">
|
<ul className="diskusage__details-list">
|
||||||
<DiskUsageTooltipItem value={d.used} label={<FormattedMessage id="status.diskusage.used" />} />
|
<DiskUsageTooltipItem value={d.used} label={<Trans id="status.diskusage.used" />} />
|
||||||
<DiskUsageTooltipItem value={d.avail} label={<FormattedMessage id="status.diskusage.free" />} />
|
<DiskUsageTooltipItem value={d.avail} label={<Trans id="status.diskusage.free" />} />
|
||||||
<DiskUsageTooltipItem value={d.size} label={<FormattedMessage id="status.diskusage.total" />} />
|
<DiskUsageTooltipItem value={d.size} label={<Trans id="status.diskusage.total" />} />
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
position="top"
|
position="top"
|
||||||
@@ -69,7 +69,7 @@ const DiskUsage: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<ul className="sidebar-filter sidebar__item">
|
<ul className="sidebar-filter sidebar__item">
|
||||||
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
||||||
<FormattedMessage id="status.diskusage.title" />
|
<Trans id="status.diskusage.title" />
|
||||||
</li>
|
</li>
|
||||||
{diskNodes}
|
{diskNodes}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,25 +1,18 @@
|
|||||||
import {defineMessages, useIntl} from 'react-intl';
|
|
||||||
import {FC, useRef} from 'react';
|
import {FC, useRef} from 'react';
|
||||||
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Feed} from '@client/ui/icons';
|
import {Feed} from '@client/ui/icons';
|
||||||
import UIActions from '@client/actions/UIActions';
|
import UIActions from '@client/actions/UIActions';
|
||||||
|
|
||||||
import Tooltip from '../general/Tooltip';
|
import Tooltip from '../general/Tooltip';
|
||||||
|
|
||||||
const MESSAGES = defineMessages({
|
|
||||||
feeds: {
|
|
||||||
id: 'sidebar.button.feeds',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const FeedsButton: FC = () => {
|
const FeedsButton: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const label = intl.formatMessage(MESSAGES.feeds);
|
|
||||||
const tooltipRef = useRef<Tooltip>(null);
|
const tooltipRef = useRef<Tooltip>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={label}
|
content={i18n._('sidebar.button.feeds')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (tooltipRef.current != null) {
|
if (tooltipRef.current != null) {
|
||||||
tooltipRef.current.dismissTooltip();
|
tooltipRef.current.dismissTooltip();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import AuthActions from '@client/actions/AuthActions';
|
import AuthActions from '@client/actions/AuthActions';
|
||||||
import ConfigStore from '@client/stores/ConfigStore';
|
import ConfigStore from '@client/stores/ConfigStore';
|
||||||
@@ -8,7 +8,7 @@ import {Logout} from '@client/ui/icons';
|
|||||||
import Tooltip from '../general/Tooltip';
|
import Tooltip from '../general/Tooltip';
|
||||||
|
|
||||||
const LogoutButton: FC = () => {
|
const LogoutButton: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
if (ConfigStore.authMethod === 'none') {
|
if (ConfigStore.authMethod === 'none') {
|
||||||
return null;
|
return null;
|
||||||
@@ -16,9 +16,7 @@ const LogoutButton: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={intl.formatMessage({
|
content={i18n._('sidebar.button.log.out')}
|
||||||
id: 'sidebar.button.log.out',
|
|
||||||
})}
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
AuthActions.logout().then(() => {
|
AuthActions.logout().then(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {defineMessages, useIntl} from 'react-intl';
|
|
||||||
import {FC, useEffect, useRef, useState} from 'react';
|
import {FC, useEffect, useRef, useState} from 'react';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import FloodActions from '@client/actions/FloodActions';
|
import FloodActions from '@client/actions/FloodActions';
|
||||||
import {ChevronLeft, ChevronRight, LoadingIndicatorDots, Notification as NotificationIcon} from '@client/ui/icons';
|
import {ChevronLeft, ChevronRight, LoadingIndicatorDots, Notification as NotificationIcon} from '@client/ui/icons';
|
||||||
@@ -20,27 +20,6 @@ const fetchNotifications = (paginationStart: number) =>
|
|||||||
start: paginationStart,
|
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 {
|
interface NotificationTopToolbarProps {
|
||||||
paginationStart: number;
|
paginationStart: number;
|
||||||
notificationTotal: number;
|
notificationTotal: number;
|
||||||
@@ -50,7 +29,7 @@ const NotificationTopToolbar: FC<NotificationTopToolbarProps> = ({
|
|||||||
paginationStart,
|
paginationStart,
|
||||||
notificationTotal,
|
notificationTotal,
|
||||||
}: NotificationTopToolbarProps) => {
|
}: NotificationTopToolbarProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
if (notificationTotal > NOTIFICATIONS_PER_PAGE) {
|
if (notificationTotal > NOTIFICATIONS_PER_PAGE) {
|
||||||
let countStart = paginationStart + 1;
|
let countStart = paginationStart + 1;
|
||||||
@@ -67,19 +46,13 @@ const NotificationTopToolbar: FC<NotificationTopToolbarProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="toolbar toolbar--dark toolbar--top tooltip__toolbar tooltip__content--padding-surrogate">
|
<div className="toolbar toolbar--dark toolbar--top tooltip__toolbar tooltip__content--padding-surrogate">
|
||||||
<span className="toolbar__item toolbar__item--label">
|
<span className="toolbar__item toolbar__item--label">
|
||||||
{`${intl.formatMessage({
|
{`${i18n._('notification.showing')} `}
|
||||||
id: 'notification.showing',
|
|
||||||
})} `}
|
|
||||||
<strong>
|
<strong>
|
||||||
{countStart}
|
{countStart}
|
||||||
{` ${intl.formatMessage({
|
{` ${i18n._('general.to')} `}
|
||||||
id: 'general.to',
|
|
||||||
})} `}
|
|
||||||
{countEnd}
|
{countEnd}
|
||||||
</strong>
|
</strong>
|
||||||
{` ${intl.formatMessage({
|
{` ${i18n._('general.of')} `}
|
||||||
id: 'general.of',
|
|
||||||
})} `}
|
|
||||||
<strong>{notificationTotal}</strong>
|
<strong>{notificationTotal}</strong>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,35 +68,24 @@ interface NotificationItemProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const NotificationItem: FC<NotificationItemProps> = ({index, notification}: NotificationItemProps) => {
|
const NotificationItem: FC<NotificationItemProps> = ({index, notification}: NotificationItemProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const date = intl.formatDate(notification.ts, {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
day: '2-digit',
|
|
||||||
});
|
|
||||||
const time = intl.formatTime(notification.ts);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="notifications__list__item" key={index}>
|
<li className="notifications__list__item" key={index}>
|
||||||
<div className="notification__heading">
|
<div className="notification__heading">
|
||||||
<span className="notification__category">
|
<span className="notification__category">{i18n._(`${notification.id}.heading`)}</span>
|
||||||
{intl.formatMessage(
|
|
||||||
MESSAGES[`${notification.id}.heading` as keyof typeof MESSAGES] || {id: 'general.error.unknown'},
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
{' — '}
|
{' — '}
|
||||||
<span className="notification__timestamp">{`${date} ${intl.formatMessage({
|
<span className="notification__timestamp">
|
||||||
id: 'general.at',
|
{i18n.date(new Date(notification.ts), {
|
||||||
})} ${time}`}</span>
|
year: 'numeric',
|
||||||
</div>
|
month: 'long',
|
||||||
<div className="notification__message">
|
day: '2-digit',
|
||||||
{intl.formatMessage(
|
hour: 'numeric',
|
||||||
MESSAGES[`${notification.id}.body` as keyof typeof MESSAGES] || {
|
minute: 'numeric',
|
||||||
id: 'general.error.unknown',
|
})}
|
||||||
},
|
</span>
|
||||||
notification.data,
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="notification__message">{i18n._(`${notification.id}.body`, notification.data)}</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -143,7 +105,7 @@ const NotificationBottomToolbar: FC<NotificationBottomToolbarProps> = ({
|
|||||||
onClearClick,
|
onClearClick,
|
||||||
onNextClick,
|
onNextClick,
|
||||||
}: NotificationBottomToolbarProps) => {
|
}: NotificationBottomToolbarProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
if (notificationTotal > 0) {
|
if (notificationTotal > 0) {
|
||||||
const newerButtonClass = classnames('toolbar__item toolbar__item--button', 'tooltip__content--padding-surrogate', {
|
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
|
className="toolbar__item toolbar__item--button
|
||||||
tooltip__content--padding-surrogate"
|
tooltip__content--padding-surrogate"
|
||||||
onClick={onClearClick}>
|
onClick={onClearClick}>
|
||||||
{intl.formatMessage({
|
{i18n._('notification.clear.all')}
|
||||||
id: 'notification.clear.all',
|
|
||||||
})}
|
|
||||||
</li>
|
</li>
|
||||||
<li className={olderButtonClass} onClick={onNextClick}>
|
<li className={olderButtonClass} onClick={onNextClick}>
|
||||||
{`${olderFrom} - ${olderTo}`}
|
{`${olderFrom} - ${olderTo}`}
|
||||||
@@ -194,7 +154,7 @@ const NotificationBottomToolbar: FC<NotificationBottomToolbarProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NotificationsButton: FC = observer(() => {
|
const NotificationsButton: FC = observer(() => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const tooltipRef = useRef<Tooltip>(null);
|
const tooltipRef = useRef<Tooltip>(null);
|
||||||
const notificationsListRef = useRef<HTMLUListElement>(null);
|
const notificationsListRef = useRef<HTMLUListElement>(null);
|
||||||
@@ -264,9 +224,7 @@ const NotificationsButton: FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="notifications tooltip__content--padding-surrogate" style={{textAlign: 'center'}}>
|
<div className="notifications tooltip__content--padding-surrogate" style={{textAlign: 'center'}}>
|
||||||
{intl.formatMessage({
|
{i18n._('notification.no.notification')}
|
||||||
id: 'notification.no.notification',
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FC, useEffect, useRef} from 'react';
|
import {FC, useEffect, useRef} from 'react';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Close, Search} from '@client/ui/icons';
|
import {Close, Search} from '@client/ui/icons';
|
||||||
import TorrentFilterStore from '@client/stores/TorrentFilterStore';
|
import TorrentFilterStore from '@client/stores/TorrentFilterStore';
|
||||||
import UIActions from '@client/actions/UIActions';
|
import UIActions from '@client/actions/UIActions';
|
||||||
|
|
||||||
const SearchBox: FC = observer(() => {
|
const SearchBox: FC = observer(() => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const {searchFilter} = TorrentFilterStore.filters;
|
const {searchFilter} = TorrentFilterStore.filters;
|
||||||
@@ -48,9 +48,7 @@ const SearchBox: FC = observer(() => {
|
|||||||
className="textbox"
|
className="textbox"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={intl.formatMessage({
|
placeholder={i18n._('sidebar.search.placeholder')}
|
||||||
id: 'sidebar.search.placeholder',
|
|
||||||
})}
|
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
UIActions.setTorrentsSearchFilter(event.target.value);
|
UIActions.setTorrentsSearchFilter(event.target.value);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,25 +1,18 @@
|
|||||||
import {defineMessages, useIntl} from 'react-intl';
|
|
||||||
import {FC, useRef} from 'react';
|
import {FC, useRef} from 'react';
|
||||||
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import {Settings} from '@client/ui/icons';
|
import {Settings} from '@client/ui/icons';
|
||||||
import UIActions from '@client/actions/UIActions';
|
import UIActions from '@client/actions/UIActions';
|
||||||
|
|
||||||
import Tooltip from '../general/Tooltip';
|
import Tooltip from '../general/Tooltip';
|
||||||
|
|
||||||
const MESSAGES = defineMessages({
|
|
||||||
settings: {
|
|
||||||
id: 'sidebar.button.settings',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const SettingsButton: FC = () => {
|
const SettingsButton: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const label = intl.formatMessage(MESSAGES.settings);
|
|
||||||
const tooltipRef = useRef<Tooltip>(null);
|
const tooltipRef = useRef<Tooltip>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={label}
|
content={i18n._('sidebar.button.settings')}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (tooltipRef.current != null) {
|
if (tooltipRef.current != null) {
|
||||||
tooltipRef.current.dismissTooltip();
|
tooltipRef.current.dismissTooltip();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import Badge from '../general/Badge';
|
import Badge from '../general/Badge';
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ interface SidebarFilterProps {
|
|||||||
|
|
||||||
const SidebarFilter: FC<SidebarFilterProps> = (props: SidebarFilterProps) => {
|
const SidebarFilter: FC<SidebarFilterProps> = (props: SidebarFilterProps) => {
|
||||||
const {isActive, count, slug, icon, handleClick} = props;
|
const {isActive, count, slug, icon, handleClick} = props;
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const classNames = classnames('sidebar-filter__item', {
|
const classNames = classnames('sidebar-filter__item', {
|
||||||
'is-active': isActive,
|
'is-active': isActive,
|
||||||
@@ -23,16 +23,12 @@ const SidebarFilter: FC<SidebarFilterProps> = (props: SidebarFilterProps) => {
|
|||||||
let {name} = props;
|
let {name} = props;
|
||||||
|
|
||||||
if (name === '') {
|
if (name === '') {
|
||||||
name = intl.formatMessage({
|
name = i18n._('filter.all');
|
||||||
id: 'filter.all',
|
|
||||||
});
|
|
||||||
} else if (name === 'untagged') {
|
} else if (name === 'untagged') {
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
name = intl.formatMessage({
|
name = i18n._('filter.untagged');
|
||||||
id: 'filter.untagged',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slug === 'checking' || slug === 'error') {
|
if (slug === 'checking' || slug === 'error') {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import {FC, useRef} from 'react';
|
import {FC, useRef} from 'react';
|
||||||
import {FormattedMessage, IntlShape, useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import sortedIndex from 'lodash/sortedIndex';
|
import sortedIndex from 'lodash/sortedIndex';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
|
import type {I18n} from '@lingui/core';
|
||||||
|
|
||||||
import ClientActions from '@client/actions/ClientActions';
|
import ClientActions from '@client/actions/ClientActions';
|
||||||
import {Limits} from '@client/ui/icons';
|
import {Limits} from '@client/ui/icons';
|
||||||
@@ -16,16 +18,16 @@ import Tooltip from '../general/Tooltip';
|
|||||||
import type {DropdownItem} from '../general/form-elements/Dropdown';
|
import type {DropdownItem} from '../general/form-elements/Dropdown';
|
||||||
|
|
||||||
const HumanReadableSpeed: FC<{bytes: number}> = ({bytes}: {bytes: number}) =>
|
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 = ({
|
const getSpeedList = ({
|
||||||
intl,
|
i18n,
|
||||||
direction,
|
direction,
|
||||||
speedLimits,
|
speedLimits,
|
||||||
throttleGlobalDownSpeed,
|
throttleGlobalDownSpeed,
|
||||||
throttleGlobalUpSpeed,
|
throttleGlobalUpSpeed,
|
||||||
}: {
|
}: {
|
||||||
intl: IntlShape;
|
i18n: I18n;
|
||||||
direction: TransferDirection;
|
direction: TransferDirection;
|
||||||
speedLimits: {
|
speedLimits: {
|
||||||
download: Array<number>;
|
download: Array<number>;
|
||||||
@@ -37,8 +39,8 @@ const getSpeedList = ({
|
|||||||
const heading = {
|
const heading = {
|
||||||
className: `dropdown__label dropdown__label--${direction}`,
|
className: `dropdown__label dropdown__label--${direction}`,
|
||||||
...(direction === 'download'
|
...(direction === 'download'
|
||||||
? {displayName: intl.formatMessage({id: 'sidebar.speedlimits.download'})}
|
? {displayName: i18n._('sidebar.speedlimits.download')}
|
||||||
: {displayName: intl.formatMessage({id: 'sidebar.speedlimits.upload'})}),
|
: {displayName: i18n._('sidebar.speedlimits.upload')}),
|
||||||
selectable: false,
|
selectable: false,
|
||||||
value: null,
|
value: null,
|
||||||
};
|
};
|
||||||
@@ -92,12 +94,12 @@ const getSpeedList = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SpeedLimitDropdown: FC = observer(() => {
|
const SpeedLimitDropdown: FC = observer(() => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const tooltipRef = useRef<Tooltip>(null);
|
const tooltipRef = useRef<Tooltip>(null);
|
||||||
|
|
||||||
const label = intl.formatMessage({id: 'sidebar.button.speedlimits'});
|
const label = i18n._('sidebar.button.speedlimits');
|
||||||
const speedListOptions = {
|
const speedListOptions = {
|
||||||
intl,
|
i18n,
|
||||||
speedLimits: SettingStore.floodSettings.speedLimits,
|
speedLimits: SettingStore.floodSettings.speedLimits,
|
||||||
throttleGlobalDownSpeed: SettingStore.clientSettings?.throttleGlobalDownSpeed ?? 0,
|
throttleGlobalDownSpeed: SettingStore.clientSettings?.throttleGlobalDownSpeed ?? 0,
|
||||||
throttleGlobalUpSpeed: SettingStore.clientSettings?.throttleGlobalUpSpeed ?? 0,
|
throttleGlobalUpSpeed: SettingStore.clientSettings?.throttleGlobalUpSpeed ?? 0,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
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 {Active, All, Completed, DownloadSmall, Error, Inactive, Stop, Spinner, UploadSmall} from '@client/ui/icons';
|
||||||
import TorrentFilterStore from '@client/stores/TorrentFilterStore';
|
import TorrentFilterStore from '@client/stores/TorrentFilterStore';
|
||||||
@@ -11,7 +11,7 @@ import type {TorrentStatus} from '@shared/constants/torrentStatusMap';
|
|||||||
import SidebarFilter from './SidebarFilter';
|
import SidebarFilter from './SidebarFilter';
|
||||||
|
|
||||||
const StatusFilters: FC = observer(() => {
|
const StatusFilters: FC = observer(() => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const filters: Array<{
|
const filters: Array<{
|
||||||
label: string;
|
label: string;
|
||||||
@@ -19,65 +19,47 @@ const StatusFilters: FC = observer(() => {
|
|||||||
icon: JSX.Element;
|
icon: JSX.Element;
|
||||||
}> = [
|
}> = [
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.all'),
|
||||||
id: 'filter.all',
|
|
||||||
}),
|
|
||||||
slug: '',
|
slug: '',
|
||||||
icon: <All />,
|
icon: <All />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.downloading'),
|
||||||
id: 'filter.status.downloading',
|
|
||||||
}),
|
|
||||||
slug: 'downloading',
|
slug: 'downloading',
|
||||||
icon: <DownloadSmall />,
|
icon: <DownloadSmall />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.seeding'),
|
||||||
id: 'filter.status.seeding',
|
|
||||||
}),
|
|
||||||
slug: 'seeding',
|
slug: 'seeding',
|
||||||
icon: <UploadSmall />,
|
icon: <UploadSmall />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.checking'),
|
||||||
id: 'filter.status.checking',
|
|
||||||
}),
|
|
||||||
slug: 'checking',
|
slug: 'checking',
|
||||||
icon: <Spinner />,
|
icon: <Spinner />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.completed'),
|
||||||
id: 'filter.status.completed',
|
|
||||||
}),
|
|
||||||
slug: 'complete',
|
slug: 'complete',
|
||||||
icon: <Completed />,
|
icon: <Completed />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.stopped'),
|
||||||
id: 'filter.status.stopped',
|
|
||||||
}),
|
|
||||||
slug: 'stopped',
|
slug: 'stopped',
|
||||||
icon: <Stop />,
|
icon: <Stop />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.active'),
|
||||||
id: 'filter.status.active',
|
|
||||||
}),
|
|
||||||
slug: 'active',
|
slug: 'active',
|
||||||
icon: <Active />,
|
icon: <Active />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.inactive'),
|
||||||
id: 'filter.status.inactive',
|
|
||||||
}),
|
|
||||||
slug: 'inactive',
|
slug: 'inactive',
|
||||||
icon: <Inactive />,
|
icon: <Inactive />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: i18n._('filter.status.error'),
|
||||||
id: 'filter.status.error',
|
|
||||||
}),
|
|
||||||
slug: 'error',
|
slug: 'error',
|
||||||
icon: <Error />,
|
icon: <Error />,
|
||||||
},
|
},
|
||||||
@@ -98,7 +80,7 @@ const StatusFilters: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<ul className="sidebar-filter sidebar__item">
|
<ul className="sidebar-filter sidebar__item">
|
||||||
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
||||||
<FormattedMessage id="filter.status.title" />
|
<Trans id="filter.status.title" />
|
||||||
</li>
|
</li>
|
||||||
{filterElements}
|
{filterElements}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import SidebarFilter from './SidebarFilter';
|
import SidebarFilter from './SidebarFilter';
|
||||||
import TorrentFilterStore from '../../stores/TorrentFilterStore';
|
import TorrentFilterStore from '../../stores/TorrentFilterStore';
|
||||||
@@ -39,7 +39,7 @@ const TagFilters: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<ul className="sidebar-filter sidebar__item">
|
<ul className="sidebar-filter sidebar__item">
|
||||||
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
||||||
<FormattedMessage id="filter.tag.title" />
|
<Trans id="filter.tag.title" />
|
||||||
</li>
|
</li>
|
||||||
{filterElements}
|
{filterElements}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import ConfigStore from '@client/stores/ConfigStore';
|
import ConfigStore from '@client/stores/ConfigStore';
|
||||||
import {ThemeSwitch} from '@client/ui/icons';
|
import {ThemeSwitch} from '@client/ui/icons';
|
||||||
@@ -7,13 +7,11 @@ import {ThemeSwitch} from '@client/ui/icons';
|
|||||||
import Tooltip from '../general/Tooltip';
|
import Tooltip from '../general/Tooltip';
|
||||||
|
|
||||||
const ThemeSwitchButton: FC = () => {
|
const ThemeSwitchButton: FC = () => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={intl.formatMessage({
|
content={i18n._(ConfigStore.preferDark ? 'sidebar.button.theme.light' : 'sidebar.button.theme.dark')}
|
||||||
id: ConfigStore.preferDark ? 'sidebar.button.theme.light' : 'sidebar.button.theme.dark',
|
|
||||||
})}
|
|
||||||
onClick={() => ConfigStore.setUserPreferDark(!ConfigStore.preferDark)}
|
onClick={() => ConfigStore.setUserPreferDark(!ConfigStore.preferDark)}
|
||||||
position="bottom"
|
position="bottom"
|
||||||
wrapperClassName="sidebar__action sidebar__icon-button
|
wrapperClassName="sidebar__action sidebar__icon-button
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import SidebarFilter from './SidebarFilter';
|
import SidebarFilter from './SidebarFilter';
|
||||||
import TorrentFilterStore from '../../stores/TorrentFilterStore';
|
import TorrentFilterStore from '../../stores/TorrentFilterStore';
|
||||||
@@ -38,7 +38,7 @@ const TrackerFilters: FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<ul className="sidebar-filter sidebar__item">
|
<ul className="sidebar-filter sidebar__item">
|
||||||
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
<li className="sidebar-filter__item sidebar-filter__item--heading">
|
||||||
<FormattedMessage id="filter.tracker.title" />
|
<Trans id="filter.tracker.title" />
|
||||||
</li>
|
</li>
|
||||||
{filterElements}
|
{filterElements}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {defineMessages, useIntl} from 'react-intl';
|
|
||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import ClientStatusStore from '@client/stores/ClientStatusStore';
|
import ClientStatusStore from '@client/stores/ClientStatusStore';
|
||||||
import {Download, InfinityIcon, Upload} from '@client/ui/icons';
|
import {Download, InfinityIcon, Upload} from '@client/ui/icons';
|
||||||
@@ -15,12 +15,6 @@ import Size from '../general/Size';
|
|||||||
|
|
||||||
import type {TransferRateGraphInspectorPoint} from './TransferRateGraph';
|
import type {TransferRateGraphInspectorPoint} from './TransferRateGraph';
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
ago: {
|
|
||||||
id: 'general.ago',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
download: <Download />,
|
download: <Download />,
|
||||||
infinity: <InfinityIcon />,
|
infinity: <InfinityIcon />,
|
||||||
@@ -32,7 +26,7 @@ interface TransferRateDetailsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TransferRateDetails: FC<TransferRateDetailsProps> = observer(({inspectorPoint}: TransferRateDetailsProps) => {
|
const TransferRateDetails: FC<TransferRateDetailsProps> = observer(({inspectorPoint}: TransferRateDetailsProps) => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const getCurrentTransferRate = (direction: TransferDirection, options: {showHoverDuration?: boolean} = {}) => {
|
const getCurrentTransferRate = (direction: TransferDirection, options: {showHoverDuration?: boolean} = {}) => {
|
||||||
const {throttleGlobalDownSpeed = 0, throttleGlobalUpSpeed = 0} = SettingStore.clientSettings || {};
|
const {throttleGlobalDownSpeed = 0, throttleGlobalUpSpeed = 0} = SettingStore.clientSettings || {};
|
||||||
@@ -73,7 +67,7 @@ const TransferRateDetails: FC<TransferRateDetailsProps> = observer(({inspectorPo
|
|||||||
timestamp = (
|
timestamp = (
|
||||||
<div className={timestampClasses}>
|
<div className={timestampClasses}>
|
||||||
<Duration
|
<Duration
|
||||||
suffix={intl.formatMessage(messages.ago)}
|
suffix={i18n._('general.ago')}
|
||||||
value={Math.trunc((Date.now() - inspectorPoint.nearestTimestamp) / 1000)}
|
value={Math.trunc((Date.now() - inspectorPoint.nearestTimestamp) / 1000)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {observer} from 'mobx-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 {Add, Menu, Remove, Start, Stop} from '@client/ui/icons';
|
||||||
import SettingActions from '@client/actions/SettingActions';
|
import SettingActions from '@client/actions/SettingActions';
|
||||||
@@ -14,7 +14,7 @@ import Action from './Action';
|
|||||||
import SortDropdown from './SortDropdown';
|
import SortDropdown from './SortDropdown';
|
||||||
|
|
||||||
const ActionBar: FC = observer(() => {
|
const ActionBar: FC = observer(() => {
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
const {sortTorrents: sortBy, torrentListViewSize} = SettingStore.floodSettings;
|
const {sortTorrents: sortBy, torrentListViewSize} = SettingStore.floodSettings;
|
||||||
|
|
||||||
const classes = classnames('action-bar', {
|
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="actions action-bar__item action-bar__item--torrent-operations">
|
||||||
<div className="action-bar__group">
|
<div className="action-bar__group">
|
||||||
<Action
|
<Action
|
||||||
label={intl.formatMessage({
|
label={i18n._('actionbar.button.start.torrent')}
|
||||||
id: 'actionbar.button.start.torrent',
|
|
||||||
})}
|
|
||||||
slug="start-torrent"
|
slug="start-torrent"
|
||||||
icon={<Start />}
|
icon={<Start />}
|
||||||
clickHandler={() =>
|
clickHandler={() =>
|
||||||
@@ -61,9 +59,7 @@ const ActionBar: FC = observer(() => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Action
|
<Action
|
||||||
label={intl.formatMessage({
|
label={i18n._('actionbar.button.stop.torrent')}
|
||||||
id: 'actionbar.button.stop.torrent',
|
|
||||||
})}
|
|
||||||
slug="stop-torrent"
|
slug="stop-torrent"
|
||||||
icon={<Stop />}
|
icon={<Stop />}
|
||||||
clickHandler={() =>
|
clickHandler={() =>
|
||||||
@@ -75,17 +71,13 @@ const ActionBar: FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="action-bar__group action-bar__group--has-divider">
|
<div className="action-bar__group action-bar__group--has-divider">
|
||||||
<Action
|
<Action
|
||||||
label={intl.formatMessage({
|
label={i18n._('actionbar.button.add.torrent')}
|
||||||
id: 'actionbar.button.add.torrent',
|
|
||||||
})}
|
|
||||||
slug="add-torrent"
|
slug="add-torrent"
|
||||||
icon={<Add />}
|
icon={<Add />}
|
||||||
clickHandler={() => UIActions.displayModal({id: 'add-torrents'})}
|
clickHandler={() => UIActions.displayModal({id: 'add-torrents'})}
|
||||||
/>
|
/>
|
||||||
<Action
|
<Action
|
||||||
label={intl.formatMessage({
|
label={i18n._('actionbar.button.remove.torrent')}
|
||||||
id: 'actionbar.button.remove.torrent',
|
|
||||||
})}
|
|
||||||
slug="remove-torrent"
|
slug="remove-torrent"
|
||||||
icon={<Remove />}
|
icon={<Remove />}
|
||||||
clickHandler={() =>
|
clickHandler={() =>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {FC} from 'react';
|
import {FC} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import type {FloodSettings} from '@shared/types/FloodSettings';
|
import type {FloodSettings} from '@shared/types/FloodSettings';
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ interface SortDropdownProps {
|
|||||||
|
|
||||||
const SortDropdown: FC<SortDropdownProps> = (props: SortDropdownProps) => {
|
const SortDropdown: FC<SortDropdownProps> = (props: SortDropdownProps) => {
|
||||||
const {direction, selectedProperty, onSortChange} = props;
|
const {direction, selectedProperty, onSortChange} = props;
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
if (selectedProperty == null) {
|
if (selectedProperty == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -38,10 +38,10 @@ const SortDropdown: FC<SortDropdownProps> = (props: SortDropdownProps) => {
|
|||||||
const header = (
|
const header = (
|
||||||
<button className="dropdown__button" type="button">
|
<button className="dropdown__button" type="button">
|
||||||
<label className="dropdown__label">
|
<label className="dropdown__label">
|
||||||
<FormattedMessage id="torrents.sort.title" />
|
<Trans id="torrents.sort.title" />
|
||||||
</label>
|
</label>
|
||||||
<span className="dropdown__value">
|
<span className="dropdown__value">
|
||||||
<FormattedMessage id={TorrentListColumns[selectedProperty]?.id || TorrentListColumns.dateAdded.id} />
|
<Trans id={TorrentListColumns[selectedProperty]} />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@@ -56,7 +56,7 @@ const SortDropdown: FC<SortDropdownProps> = (props: SortDropdownProps) => {
|
|||||||
return {
|
return {
|
||||||
displayName: (
|
displayName: (
|
||||||
<div className="sort-dropdown__item">
|
<div className="sort-dropdown__item">
|
||||||
{intl.formatMessage(TorrentListColumns[sortProp])}
|
{i18n._(TorrentListColumns[sortProp])}
|
||||||
{directionIndicator}
|
{directionIndicator}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import {forwardRef, MutableRefObject, ReactNodeArray, useRef, useState} from 'react';
|
import {forwardRef, MutableRefObject, ReactNodeArray, useRef, useState} from 'react';
|
||||||
import {FormattedMessage, useIntl} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {Trans, useLingui} from '@lingui/react';
|
||||||
import {useEnsuredForwardedRef} from 'react-use';
|
import {useEnsuredForwardedRef} from 'react-use';
|
||||||
|
|
||||||
import TorrentListColumns, {TorrentListColumn} from '../../constants/TorrentListColumns';
|
import TorrentListColumns, {TorrentListColumn} from '../../constants/TorrentListColumns';
|
||||||
@@ -28,7 +28,7 @@ const TableHeading = observer(
|
|||||||
const tableHeading = useEnsuredForwardedRef<HTMLDivElement>(ref as MutableRefObject<HTMLDivElement>);
|
const tableHeading = useEnsuredForwardedRef<HTMLDivElement>(ref as MutableRefObject<HTMLDivElement>);
|
||||||
const resizeLine = useRef<HTMLDivElement>(null);
|
const resizeLine = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const intl = useIntl();
|
const {i18n} = useLingui();
|
||||||
|
|
||||||
const handlePointerMove = (event: PointerEvent) => {
|
const handlePointerMove = (event: PointerEvent) => {
|
||||||
let widthDelta = 0;
|
let widthDelta = 0;
|
||||||
@@ -79,7 +79,7 @@ const TableHeading = observer(
|
|||||||
return accumulator;
|
return accumulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
const labelID = TorrentListColumns[id]?.id;
|
const labelID = TorrentListColumns[id];
|
||||||
if (labelID == null) {
|
if (labelID == null) {
|
||||||
return accumulator;
|
return accumulator;
|
||||||
}
|
}
|
||||||
@@ -121,12 +121,8 @@ const TableHeading = observer(
|
|||||||
|
|
||||||
accumulator.push(
|
accumulator.push(
|
||||||
<div className={classes} key={id} onClick={() => onCellClick(id)} style={{width: `${width}px`}}>
|
<div className={classes} key={id} onClick={() => onCellClick(id)} style={{width: `${width}px`}}>
|
||||||
<span
|
<span className="table__heading__label" title={i18n._(labelID)}>
|
||||||
className="table__heading__label"
|
<Trans id={labelID} />
|
||||||
title={intl.formatMessage({
|
|
||||||
id: labelID,
|
|
||||||
})}>
|
|
||||||
<FormattedMessage id={labelID} />
|
|
||||||
</span>
|
</span>
|
||||||
{handle}
|
{handle}
|
||||||
</div>,
|
</div>,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {FC, ReactNode, useEffect, useRef} from 'react';
|
import {FC, ReactNode, useEffect, useRef} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
import {reaction} from 'mobx';
|
import {reaction} from 'mobx';
|
||||||
|
import {Trans} from '@lingui/react';
|
||||||
|
|
||||||
import type {FixedSizeList} from 'react-window';
|
import type {FixedSizeList} from 'react-window';
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ const TorrentList: FC = observer(() => {
|
|||||||
content = (
|
content = (
|
||||||
<div className="torrents__alert__wrapper">
|
<div className="torrents__alert__wrapper">
|
||||||
<div className="torrents__alert">
|
<div className="torrents__alert">
|
||||||
<FormattedMessage id="torrents.list.cannot.connect" />
|
<Trans id="torrents.list.cannot.connect" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -62,7 +62,7 @@ const TorrentList: FC = observer(() => {
|
|||||||
content = (
|
content = (
|
||||||
<div className="torrents__alert__wrapper">
|
<div className="torrents__alert__wrapper">
|
||||||
<div className="torrents__alert">
|
<div className="torrents__alert">
|
||||||
<FormattedMessage id="torrents.list.no.torrents" />
|
<Trans id="torrents.list.no.torrents" />
|
||||||
</div>
|
</div>
|
||||||
{TorrentFilterStore.isFilterActive && (
|
{TorrentFilterStore.isFilterActive && (
|
||||||
<div className="torrents__alert__action">
|
<div className="torrents__alert__action">
|
||||||
@@ -71,7 +71,7 @@ const TorrentList: FC = observer(() => {
|
|||||||
TorrentFilterStore.clearAllFilters();
|
TorrentFilterStore.clearAllFilters();
|
||||||
}}
|
}}
|
||||||
priority="tertiary">
|
priority="tertiary">
|
||||||
<FormattedMessage id="torrents.list.clear.filters" />
|
<Trans id="torrents.list.clear.filters" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -148,7 +148,7 @@ const TorrentList: FC = observer(() => {
|
|||||||
<div className="dropzone__icon">
|
<div className="dropzone__icon">
|
||||||
<Files />
|
<Files />
|
||||||
</div>
|
</div>
|
||||||
<FormattedMessage id="torrents.list.drop" />
|
<Trans id="torrents.list.drop" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TorrentListDropzone>
|
</TorrentListDropzone>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'start',
|
action: 'start',
|
||||||
label: TorrentContextMenuActions.start.id,
|
label: TorrentContextMenuActions.start,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
TorrentActions.startTorrents({
|
TorrentActions.startTorrents({
|
||||||
hashes: TorrentStore.selectedTorrents,
|
hashes: TorrentStore.selectedTorrents,
|
||||||
@@ -49,7 +49,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'stop',
|
action: 'stop',
|
||||||
label: TorrentContextMenuActions.stop.id,
|
label: TorrentContextMenuActions.stop,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
TorrentActions.stopTorrents({
|
TorrentActions.stopTorrents({
|
||||||
hashes: TorrentStore.selectedTorrents,
|
hashes: TorrentStore.selectedTorrents,
|
||||||
@@ -59,7 +59,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'remove',
|
action: 'remove',
|
||||||
label: TorrentContextMenuActions.remove.id,
|
label: TorrentContextMenuActions.remove,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
UIActions.displayModal({id: 'remove-torrents'});
|
UIActions.displayModal({id: 'remove-torrents'});
|
||||||
},
|
},
|
||||||
@@ -67,7 +67,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'checkHash',
|
action: 'checkHash',
|
||||||
label: TorrentContextMenuActions.checkHash.id,
|
label: TorrentContextMenuActions.checkHash,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
TorrentActions.checkHash({
|
TorrentActions.checkHash({
|
||||||
hashes: TorrentStore.selectedTorrents,
|
hashes: TorrentStore.selectedTorrents,
|
||||||
@@ -80,7 +80,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'setTaxonomy',
|
action: 'setTaxonomy',
|
||||||
label: TorrentContextMenuActions.setTaxonomy.id,
|
label: TorrentContextMenuActions.setTaxonomy,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
UIActions.displayModal({id: 'set-taxonomy'});
|
UIActions.displayModal({id: 'set-taxonomy'});
|
||||||
},
|
},
|
||||||
@@ -88,7 +88,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'move',
|
action: 'move',
|
||||||
label: TorrentContextMenuActions.move.id,
|
label: TorrentContextMenuActions.move,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
UIActions.displayModal({id: 'move-torrents'});
|
UIActions.displayModal({id: 'move-torrents'});
|
||||||
},
|
},
|
||||||
@@ -96,7 +96,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'setTrackers',
|
action: 'setTrackers',
|
||||||
label: TorrentContextMenuActions.setTrackers.id,
|
label: TorrentContextMenuActions.setTrackers,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
UIActions.displayModal({id: 'set-trackers'});
|
UIActions.displayModal({id: 'set-trackers'});
|
||||||
},
|
},
|
||||||
@@ -107,7 +107,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'torrentDetails',
|
action: 'torrentDetails',
|
||||||
label: TorrentContextMenuActions.torrentDetails.id,
|
label: TorrentContextMenuActions.torrentDetails,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
UIActions.displayModal({
|
UIActions.displayModal({
|
||||||
id: 'torrent-details',
|
id: 'torrent-details',
|
||||||
@@ -118,7 +118,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'downloadContents',
|
action: 'downloadContents',
|
||||||
label: TorrentContextMenuActions.downloadContents.id,
|
label: TorrentContextMenuActions.downloadContents,
|
||||||
clickHandler: (e) => {
|
clickHandler: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'downloadMetainfo',
|
action: 'downloadMetainfo',
|
||||||
label: TorrentContextMenuActions.downloadMetainfo.id,
|
label: TorrentContextMenuActions.downloadMetainfo,
|
||||||
clickHandler: (e) => {
|
clickHandler: (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'generateMagnet',
|
action: 'generateMagnet',
|
||||||
label: TorrentContextMenuActions.generateMagnet.id,
|
label: TorrentContextMenuActions.generateMagnet,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
UIActions.displayModal({id: 'generate-magnet'});
|
UIActions.displayModal({id: 'generate-magnet'});
|
||||||
},
|
},
|
||||||
@@ -162,7 +162,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'setInitialSeeding',
|
action: 'setInitialSeeding',
|
||||||
label: TorrentContextMenuActions.setInitialSeeding.id,
|
label: TorrentContextMenuActions.setInitialSeeding,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
const {selectedTorrents} = TorrentStore;
|
const {selectedTorrents} = TorrentStore;
|
||||||
TorrentActions.setInitialSeeding({
|
TorrentActions.setInitialSeeding({
|
||||||
@@ -176,7 +176,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'setSequential',
|
action: 'setSequential',
|
||||||
label: TorrentContextMenuActions.setSequential.id,
|
label: TorrentContextMenuActions.setSequential,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
const {selectedTorrents} = TorrentStore;
|
const {selectedTorrents} = TorrentStore;
|
||||||
TorrentActions.setSequential({
|
TorrentActions.setSequential({
|
||||||
@@ -190,7 +190,7 @@ const getContextMenuItems = (torrent: TorrentProperties): Array<ContextMenuItem>
|
|||||||
{
|
{
|
||||||
type: 'action',
|
type: 'action',
|
||||||
action: 'setPriority',
|
action: 'setPriority',
|
||||||
label: TorrentContextMenuActions.setPriority.id,
|
label: TorrentContextMenuActions.setPriority,
|
||||||
clickHandler: () => {
|
clickHandler: () => {
|
||||||
if (changePriorityFuncRef.current != null) {
|
if (changePriorityFuncRef.current != null) {
|
||||||
TorrentActions.setPriority({
|
TorrentActions.setPriority({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {FormattedNumber} from 'react-intl';
|
|
||||||
import {forwardRef} from 'react';
|
import {forwardRef} from 'react';
|
||||||
import {observer} from 'mobx-react';
|
import {observer} from 'mobx-react';
|
||||||
|
import {useLingui} from '@lingui/react';
|
||||||
|
|
||||||
import ProgressBar from '../general/ProgressBar';
|
import ProgressBar from '../general/ProgressBar';
|
||||||
import SettingStore from '../../stores/SettingStore';
|
import SettingStore from '../../stores/SettingStore';
|
||||||
@@ -35,6 +35,7 @@ const TorrentListRowExpanded = observer(
|
|||||||
}: TorrentListRowExpandedProps,
|
}: TorrentListRowExpandedProps,
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
|
const {i18n} = useLingui();
|
||||||
const columns = SettingStore.floodSettings.torrentListColumns;
|
const columns = SettingStore.floodSettings.torrentListColumns;
|
||||||
|
|
||||||
const primarySection: React.ReactNodeArray = [
|
const primarySection: React.ReactNodeArray = [
|
||||||
@@ -57,7 +58,7 @@ const TorrentListRowExpanded = observer(
|
|||||||
column="percentComplete"
|
column="percentComplete"
|
||||||
content={(torrent) => (
|
content={(torrent) => (
|
||||||
<span>
|
<span>
|
||||||
<FormattedNumber value={torrent.percentComplete} maximumFractionDigits={1} />
|
{i18n.number(torrent.percentComplete, {maximumFractionDigits: 1})}
|
||||||
<em className="unit">%</em>
|
<em className="unit">%</em>
|
||||||
—
|
—
|
||||||
<Size value={torrent.downTotal} />
|
<Size value={torrent.downTotal} />
|
||||||
|
|||||||
@@ -1,46 +1,18 @@
|
|||||||
const TorrentContextMenuActions = {
|
const TorrentContextMenuActions = {
|
||||||
start: {
|
start: 'torrents.list.context.start',
|
||||||
id: 'torrents.list.context.start',
|
stop: 'torrents.list.context.stop',
|
||||||
},
|
remove: 'torrents.list.context.remove',
|
||||||
stop: {
|
checkHash: 'torrents.list.context.check.hash',
|
||||||
id: 'torrents.list.context.stop',
|
setTaxonomy: 'torrents.list.context.set.tags',
|
||||||
},
|
move: 'torrents.list.context.move',
|
||||||
remove: {
|
setTrackers: 'torrents.list.context.set.trackers',
|
||||||
id: 'torrents.list.context.remove',
|
torrentDetails: 'torrents.list.context.details',
|
||||||
},
|
downloadContents: 'torrents.list.context.download.contents',
|
||||||
checkHash: {
|
downloadMetainfo: 'torrents.list.context.download.metainfo',
|
||||||
id: 'torrents.list.context.check.hash',
|
generateMagnet: 'torrents.list.context.generate.magnet',
|
||||||
},
|
setInitialSeeding: 'torrents.list.context.initial.seeding',
|
||||||
setTaxonomy: {
|
setSequential: 'torrents.list.context.sequential',
|
||||||
id: 'torrents.list.context.set.tags',
|
setPriority: 'torrents.list.context.priority',
|
||||||
},
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default TorrentContextMenuActions;
|
export default TorrentContextMenuActions;
|
||||||
|
|||||||
@@ -1,61 +1,23 @@
|
|||||||
const TorrentListColumns = {
|
const TorrentListColumns = {
|
||||||
dateAdded: {
|
dateAdded: 'torrents.properties.date.added',
|
||||||
id: 'torrents.properties.date.added',
|
downRate: 'torrents.properties.download.speed',
|
||||||
},
|
downTotal: 'torrents.properties.download.total',
|
||||||
downRate: {
|
eta: 'torrents.properties.eta',
|
||||||
id: 'torrents.properties.download.speed',
|
name: 'torrents.properties.name',
|
||||||
},
|
peers: 'torrents.properties.peers',
|
||||||
downTotal: {
|
percentComplete: 'torrents.properties.percentage',
|
||||||
id: 'torrents.properties.download.total',
|
ratio: 'torrents.properties.ratio',
|
||||||
},
|
seeds: 'torrents.properties.seeds',
|
||||||
eta: {
|
sizeBytes: 'torrents.properties.size',
|
||||||
id: 'torrents.properties.eta',
|
tags: 'torrents.properties.tags',
|
||||||
},
|
upRate: 'torrents.properties.upload.speed',
|
||||||
name: {
|
upTotal: 'torrents.properties.upload.total',
|
||||||
id: 'torrents.properties.name',
|
dateCreated: 'torrents.properties.creation.date',
|
||||||
},
|
directory: 'torrents.properties.directory',
|
||||||
peers: {
|
hash: 'torrents.properties.hash',
|
||||||
id: 'torrents.properties.peers',
|
isPrivate: 'torrents.properties.is.private',
|
||||||
},
|
message: 'torrents.properties.tracker.message',
|
||||||
percentComplete: {
|
trackerURIs: 'torrents.properties.trackers',
|
||||||
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',
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default TorrentListColumns;
|
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
Reference in New Issue
Block a user