client: move to new React JSX factory styles

This commit is contained in:
Jesse Chan
2021-01-20 12:39:44 +08:00
parent b210289582
commit 73bc640c8e
47 changed files with 218 additions and 213 deletions

View File

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

View File

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

View File

@@ -1,5 +1,8 @@
import axios, {AxiosError, AxiosResponse} from 'axios';
import AuthStore from '@client/stores/AuthStore';
import ConfigStore from '@client/stores/ConfigStore';
import type {
AuthAuthenticationOptions,
AuthRegistrationOptions,
@@ -8,9 +11,7 @@ import type {
} from '@shared/schema/api/auth';
import type {Credentials} from '@shared/schema/Auth';
import AuthStore from '../stores/AuthStore';
import ClientActions from './ClientActions';
import ConfigStore from '../stores/ConfigStore';
import FloodActions from './FloodActions';
import SettingActions from './SettingActions';

View File

@@ -1,12 +1,12 @@
import axios from 'axios';
import ConfigStore from '@client/stores/ConfigStore';
import SettingStore from '@client/stores/SettingStore';
import AlertStore from '@client/stores/AlertStore';
import type {ClientSetting, ClientSettings} from '@shared/types/ClientSettings';
import type {SetClientSettingsOptions} from '@shared/types/api/client';
import ConfigStore from '../stores/ConfigStore';
import SettingStore from '../stores/SettingStore';
import AlertStore from '../stores/AlertStore';
const {baseURI} = ConfigStore;
const ClientActions = {

View File

@@ -1,9 +1,9 @@
import axios from 'axios';
import type {AddFeedOptions, AddRuleOptions, ModifyFeedOptions} from '@shared/types/api/feed-monitor';
import ConfigStore from '@client/stores/ConfigStore';
import FeedStore from '@client/stores/FeedStore';
import ConfigStore from '../stores/ConfigStore';
import FeedStore from '../stores/FeedStore';
import type {AddFeedOptions, AddRuleOptions, ModifyFeedOptions} from '@shared/types/api/feed-monitor';
const {baseURI} = ConfigStore;

View File

@@ -1,18 +1,18 @@
import axios from 'axios';
import ClientStatusStore from '@client/stores/ClientStatusStore';
import ConfigStore from '@client/stores/ConfigStore';
import DiskUsageStore from '@client/stores/DiskUsageStore';
import NotificationStore from '@client/stores/NotificationStore';
import TorrentFilterStore from '@client/stores/TorrentFilterStore';
import TorrentStore from '@client/stores/TorrentStore';
import TransferDataStore from '@client/stores/TransferDataStore';
import UIStore from '@client/stores/UIStore';
import type {HistorySnapshot} from '@shared/constants/historySnapshotTypes';
import type {NotificationFetchOptions} from '@shared/types/Notification';
import type {ServerEvents} from '@shared/types/ServerEvents';
import ClientStatusStore from '../stores/ClientStatusStore';
import ConfigStore from '../stores/ConfigStore';
import DiskUsageStore from '../stores/DiskUsageStore';
import NotificationStore from '../stores/NotificationStore';
import TorrentFilterStore from '../stores/TorrentFilterStore';
import TorrentStore from '../stores/TorrentStore';
import TransferDataStore from '../stores/TransferDataStore';
import UIStore from '../stores/UIStore';
interface ActivityStreamOptions {
historySnapshot: HistorySnapshot;
}

View File

@@ -1,12 +1,12 @@
import axios from 'axios';
import AlertStore from '@client/stores/AlertStore';
import ConfigStore from '@client/stores/ConfigStore';
import SettingStore from '@client/stores/SettingStore';
import type {FloodSetting, FloodSettings} from '@shared/types/FloodSettings';
import type {SetFloodSettingsOptions} from '@shared/types/api/index';
import AlertStore from '../stores/AlertStore';
import ConfigStore from '../stores/ConfigStore';
import SettingStore from '../stores/SettingStore';
const {baseURI} = ConfigStore;
const SettingActions = {

View File

@@ -1,6 +1,10 @@
import axios, {CancelToken} from 'axios';
import download from 'js-file-download';
import AlertStore from '@client/stores/AlertStore';
import ConfigStore from '@client/stores/ConfigStore';
import UIStore from '@client/stores/UIStore';
import type {
AddTorrentByFileOptions,
AddTorrentByURLOptions,
@@ -24,10 +28,6 @@ import type {TorrentPeer} from '@shared/types/TorrentPeer';
import type {TorrentTracker} from '@shared/types/TorrentTracker';
import type {TorrentProperties} from '@shared/types/Torrent';
import AlertStore from '../stores/AlertStore';
import ConfigStore from '../stores/ConfigStore';
import UIStore from '../stores/UIStore';
const {baseURI} = ConfigStore;
const emitTorrentAddedAlert = (count: number) => {

View File

@@ -1,5 +1,5 @@
import debounce from 'lodash/debounce';
import * as React from 'react';
import {MouseEvent, TouchEvent} from 'react';
import type {TorrentStatus} from '@shared/constants/torrentStatusMap';
@@ -30,7 +30,7 @@ const UIActions = {
UIStore.dismissModal();
},
handleTorrentClick: (data: {event: React.MouseEvent | React.TouchEvent; hash: string}) => {
handleTorrentClick: (data: {event: MouseEvent | TouchEvent; hash: string}) => {
TorrentStore.setSelectedTorrents(data);
},

View File

@@ -1,25 +1,25 @@
import {observer} from 'mobx-react';
import {QueryParamProvider} from 'use-query-params';
import {FC, lazy, Suspense, useEffect} from 'react';
import {Router} from 'react-router-dom';
import {Route, Switch} from 'react-router';
import ReactDOM from 'react-dom';
import {Route, Switch} from 'react-router';
import {Router} from 'react-router-dom';
import {useMedia} from 'react-use';
import {QueryParamProvider} from 'use-query-params';
import AsyncIntlProvider from './i18n/languages';
import AppWrapper from './components/AppWrapper';
import AuthActions from './actions/AuthActions';
import history from './util/history';
import AppWrapper from './components/AppWrapper';
import LoadingOverlay from './components/general/LoadingOverlay';
import AsyncIntlProvider from './i18n/languages';
import ConfigStore from './stores/ConfigStore';
import SettingStore from './stores/SettingStore';
import UIStore from './stores/UIStore';
import history from './util/history';
import '../sass/style.scss';
const Login = lazy(() => import(/* webpackPrefetch: true */ './components/views/Login'));
const Register = lazy(() => import(/* webpackPrefetch: true */ './components/views/Register'));
const TorrentClientOverview = lazy(() => import(/* webpackPreload: true */ './components/views/TorrentClientOverview'));
const Login = lazy(() => import(/* webpackPrefetch: true */ './routes/Login'));
const Register = lazy(() => import(/* webpackPrefetch: true */ './routes/Register'));
const TorrentClientOverview = lazy(() => import(/* webpackPreload: true */ './routes/TorrentClientOverview'));
const FloodApp: FC = observer(() => {
useEffect(() => {

View File

@@ -1,5 +1,5 @@
import {Component, createRef, FormEvent, RefObject} from 'react';
import {injectIntl, WrappedComponentProps} from 'react-intl';
import * as React from 'react';
import {AccessLevel} from '@shared/schema/constants/Auth';
@@ -24,9 +24,9 @@ interface AuthFormStates {
errorMessage?: string;
}
class AuthForm extends React.Component<AuthFormProps, AuthFormStates> {
class AuthForm extends Component<AuthFormProps, AuthFormStates> {
formRef?: Form | null;
settingsFormRef: React.RefObject<ClientConnectionSettingsFormType> = React.createRef();
settingsFormRef: RefObject<ClientConnectionSettingsFormType> = createRef();
constructor(props: AuthFormProps) {
super(props);
@@ -63,10 +63,7 @@ class AuthForm extends React.Component<AuthFormProps, AuthFormStates> {
});
}
handleFormSubmit = (submission: {
event: Event | React.FormEvent<HTMLFormElement>;
formData: Record<string, unknown>;
}) => {
handleFormSubmit = (submission: {event: Event | FormEvent<HTMLFormElement>; formData: Record<string, unknown>}) => {
submission.event.preventDefault();
this.setState({isSubmitting: true});

View File

@@ -1,8 +1,8 @@
import classnames from 'classnames';
import {Component, MouseEvent} from 'react';
import {DndProvider} from 'react-dnd-multi-backend';
import HTML5toTouch from 'react-dnd-multi-backend/dist/esm/HTML5toTouch';
import {injectIntl, WrappedComponentProps} from 'react-intl';
import * as React from 'react';
import SortableListItem from './SortableListItem';
@@ -18,7 +18,7 @@ interface SortableListProps extends WrappedComponentProps {
items: Array<ListItem>;
isDraggable?: boolean;
renderItem: (item: ListItem, index: number) => void;
onMouseDown?: (event: React.MouseEvent<HTMLUListElement>) => void;
onMouseDown?: (event: MouseEvent<HTMLUListElement>) => void;
onMove?: (items: this['items']) => void;
onDrop?: (items: this['items']) => void;
}
@@ -27,7 +27,7 @@ interface SortableListStates {
items: SortableListProps['items'];
}
class SortableList extends React.Component<SortableListProps, SortableListStates> {
class SortableList extends Component<SortableListProps, SortableListStates> {
sortableListRef: HTMLUListElement | null = null;
constructor(props: SortableListProps) {
@@ -77,7 +77,7 @@ class SortableList extends React.Component<SortableListProps, SortableListStates
}
};
handleMouseDown = (event: React.MouseEvent<HTMLUListElement>) => {
handleMouseDown = (event: MouseEvent<HTMLUListElement>) => {
const {onMouseDown} = this.props;
if (onMouseDown) {
onMouseDown(event);

View File

@@ -1,6 +1,6 @@
import {Component, CSSProperties, ReactNode} from 'react';
import classnames from 'classnames';
import ReactDOM from 'react-dom';
import * as React from 'react';
type Align = 'start' | 'center' | 'end';
@@ -29,7 +29,7 @@ interface TooltipProps {
className?: string;
contentClassName?: string;
wrapperClassName?: string;
content: React.ReactNode;
content: ReactNode;
onOpen: () => void;
onClose: () => void;
onClick: () => void;
@@ -139,7 +139,7 @@ const getAnchor = (
const ARROW_SIZE = 7;
class Tooltip extends React.Component<TooltipProps, TooltipStates> {
class Tooltip extends Component<TooltipProps, TooltipStates> {
container = window;
triggerNode: HTMLDivElement | null = null;
tooltipNode: HTMLDivElement | null = null;
@@ -342,7 +342,7 @@ class Tooltip extends React.Component<TooltipProps, TooltipStates> {
}
}
render(): React.ReactNode {
render(): ReactNode {
const {
anchor: defaultAnchor,
position: defaultPosition,
@@ -359,7 +359,7 @@ class Tooltip extends React.Component<TooltipProps, TooltipStates> {
onClick,
} = this.props;
const {anchor: stateAnchor, position: statePosition, coordinates, isOpen} = this.state;
let tooltipStyle: React.CSSProperties = {};
let tooltipStyle: CSSProperties = {};
// Get the anchor and position from state if possible. If not, get it from
// the props.

View File

@@ -1,5 +1,5 @@
import {Component, createRef, ReactNode, ReactNodeArray, RefObject} from 'react';
import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl';
import * as React from 'react';
import {SUPPORTED_CLIENTS} from '@shared/schema/constants/ClientConnectionSettings';
@@ -12,7 +12,7 @@ import {FormRow, Select, SelectItem} from '../../../ui';
const DEFAULT_SELECTION: ClientConnectionSettings['client'] = 'rTorrent' as const;
const getClientSelectItems = (): React.ReactNodeArray =>
const getClientSelectItems = (): ReactNodeArray =>
SUPPORTED_CLIENTS.map((client) => (
<SelectItem key={client} id={client}>
<FormattedMessage id={`connection.settings.${client.toLowerCase()}`} />
@@ -28,8 +28,8 @@ interface ClientConnectionSettingsFormStates {
client: ClientConnectionSettings['client'];
}
class ClientConnectionSettingsForm extends React.Component<WrappedComponentProps, ClientConnectionSettingsFormStates> {
settingsRef: React.RefObject<never> = React.createRef();
class ClientConnectionSettingsForm extends Component<WrappedComponentProps, ClientConnectionSettingsFormStates> {
settingsRef: RefObject<never> = createRef();
constructor(props: WrappedComponentProps) {
super(props);
@@ -40,7 +40,7 @@ class ClientConnectionSettingsForm extends React.Component<WrappedComponentProps
}
getConnectionSettings(): ClientConnectionSettings | null {
const settingsForm = this.settingsRef as React.RefObject<ConnectionSettingsForm>;
const settingsForm = this.settingsRef as RefObject<ConnectionSettingsForm>;
if (settingsForm.current == null) {
return null;
@@ -53,7 +53,7 @@ class ClientConnectionSettingsForm extends React.Component<WrappedComponentProps
const {intl} = this.props;
const {client} = this.state;
let settingsForm: React.ReactNode = null;
let settingsForm: ReactNode = null;
switch (client) {
case 'qBittorrent':
settingsForm = <QBittorrentConnectionSettingsForm intl={intl} ref={this.settingsRef} />;

View File

@@ -1,5 +1,5 @@
import {Component, ChangeEvent, MouseEvent} from 'react';
import {FormattedMessage, IntlShape} from 'react-intl';
import * as React from 'react';
import type {QBittorrentConnectionSettings} from '@shared/schema/ClientConnectionSettings';
@@ -15,7 +15,7 @@ export interface QBittorrentConnectionSettingsFormData {
password: string;
}
class QBittorrentConnectionSettingsForm extends React.Component<
class QBittorrentConnectionSettingsForm extends Component<
QBittorrentConnectionSettingsProps,
QBittorrentConnectionSettingsFormData
> {
@@ -47,7 +47,7 @@ class QBittorrentConnectionSettingsForm extends React.Component<
};
handleFormChange = (
event: React.MouseEvent<HTMLInputElement> | KeyboardEvent | React.ChangeEvent<HTMLInputElement>,
event: MouseEvent<HTMLInputElement> | KeyboardEvent | ChangeEvent<HTMLInputElement>,
field: keyof QBittorrentConnectionSettingsFormData,
): void => {
const inputElement = event.target as HTMLInputElement;

View File

@@ -1,5 +1,5 @@
import {Component, ChangeEvent, MouseEvent} from 'react';
import {FormattedMessage, IntlShape} from 'react-intl';
import * as React from 'react';
import type {
RTorrentConnectionSettings,
@@ -20,7 +20,7 @@ export interface RTorrentConnectionSettingsFormData {
port?: string;
}
class RTorrentConnectionSettingsForm extends React.Component<
class RTorrentConnectionSettingsForm extends Component<
RTorrentConnectionSettingsProps,
RTorrentConnectionSettingsFormData
> {
@@ -67,7 +67,7 @@ class RTorrentConnectionSettingsForm extends React.Component<
};
handleFormChange = (
event: React.MouseEvent<HTMLInputElement> | KeyboardEvent | React.ChangeEvent<HTMLInputElement>,
event: MouseEvent<HTMLInputElement> | KeyboardEvent | ChangeEvent<HTMLInputElement>,
field: keyof RTorrentConnectionSettingsFormData,
) => {
const inputElement = event.target as HTMLInputElement;

View File

@@ -1,5 +1,5 @@
import {Component, ChangeEvent, MouseEvent} from 'react';
import {FormattedMessage, IntlShape} from 'react-intl';
import * as React from 'react';
import type {TransmissionConnectionSettings} from '@shared/schema/ClientConnectionSettings';
@@ -15,7 +15,7 @@ export interface TransmissionConnectionSettingsFormData {
password: string;
}
class TransmissionConnectionSettingsForm extends React.Component<
class TransmissionConnectionSettingsForm extends Component<
TransmissionConnectionSettingsProps,
TransmissionConnectionSettingsFormData
> {
@@ -47,7 +47,7 @@ class TransmissionConnectionSettingsForm extends React.Component<
};
handleFormChange = (
event: React.MouseEvent<HTMLInputElement> | KeyboardEvent | React.ChangeEvent<HTMLInputElement>,
event: MouseEvent<HTMLInputElement> | KeyboardEvent | ChangeEvent<HTMLInputElement>,
field: keyof TransmissionConnectionSettingsFormData,
): void => {
const inputElement = event.target as HTMLInputElement;

View File

@@ -1,5 +1,5 @@
import classnames from 'classnames';
import * as React from 'react';
import {Component, ReactText} from 'react';
import type {TorrentContent, TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent';
import type {TorrentProperties} from '@shared/types/Torrent';
@@ -19,7 +19,7 @@ interface DirectoryFilesProps {
onItemSelect: (selection: TorrentContentSelection) => void;
}
class DirectoryFiles extends React.Component<DirectoryFilesProps> {
class DirectoryFiles extends Component<DirectoryFilesProps> {
static defaultProps = {
path: [],
items: {},
@@ -48,7 +48,7 @@ class DirectoryFiles extends React.Component<DirectoryFilesProps> {
);
}
handlePriorityChange = (fileIndex: React.ReactText, priorityLevel: number): void => {
handlePriorityChange = (fileIndex: ReactText, priorityLevel: number): void => {
const {hash} = this.props;
TorrentActions.setFilePriority(hash, {

View File

@@ -1,5 +1,5 @@
import {defineMessages, WrappedComponentProps} from 'react-intl';
import * as React from 'react';
import {PureComponent, ReactNodeArray} from 'react';
import ArrowIcon from '../../icons/ArrowIcon';
import File from '../../icons/File';
@@ -37,7 +37,7 @@ interface FilesystemBrowserStates {
files?: Array<string>;
}
class FilesystemBrowser extends React.PureComponent<FilesystemBrowserProps, FilesystemBrowserStates> {
class FilesystemBrowser extends PureComponent<FilesystemBrowserProps, FilesystemBrowserStates> {
constructor(props: FilesystemBrowserProps) {
super(props);
@@ -150,7 +150,7 @@ class FilesystemBrowser extends React.PureComponent<FilesystemBrowserProps, File
);
if (shouldShowDirectoryList) {
const directoryList: React.ReactNodeArray =
const directoryList: ReactNodeArray =
directories != null
? directories.map((directory, index) => (
<li
@@ -167,7 +167,7 @@ class FilesystemBrowser extends React.PureComponent<FilesystemBrowserProps, File
))
: [];
const filesList: React.ReactNodeArray =
const filesList: ReactNodeArray =
files != null
? files.map((file, index) => (
<li

View File

@@ -1,5 +1,5 @@
import {FormattedMessage} from 'react-intl';
import * as React from 'react';
import {FormEvent} from 'react';
import {Form, FormRow, Textbox} from '../../../ui';
import ModalFormSectionHeader from '../ModalFormSectionHeader';
@@ -30,7 +30,7 @@ export default class BandwidthTab extends SettingsTab {
event,
formData,
}: {
event: Event | React.FormEvent<HTMLFormElement>;
event: Event | FormEvent<HTMLFormElement>;
formData: Record<string, unknown>;
}) => {
const inputElement = event.target as HTMLInputElement;

View File

@@ -1,5 +1,5 @@
import {Component, FormEvent} from 'react';
import {WrappedComponentProps} from 'react-intl';
import * as React from 'react';
import type {ClientSetting, ClientSettings} from '@shared/types/ClientSettings';
import type {FloodSettings} from '@shared/types/FloodSettings';
@@ -15,7 +15,7 @@ interface SettingsTabStates {
changedClientSettings: Partial<ClientSettings>;
}
class SettingsTab extends React.Component<SettingsTabProps, SettingsTabStates> {
class SettingsTab extends Component<SettingsTabProps, SettingsTabStates> {
constructor(props: SettingsTabProps) {
super(props);
@@ -32,7 +32,7 @@ class SettingsTab extends React.Component<SettingsTabProps, SettingsTabStates> {
return SettingStore.clientSettings?.[property];
}
handleClientSettingChange(event: React.FormEvent<HTMLFormElement> | Event) {
handleClientSettingChange(event: FormEvent<HTMLFormElement> | Event) {
const inputElement = event.target as HTMLInputElement;
const property = inputElement.name as ClientSetting;
const {value, type, checked} = inputElement;

View File

@@ -1,5 +1,5 @@
import {FormattedMessage, injectIntl} from 'react-intl';
import * as React from 'react';
import {FormEvent} from 'react';
import type {FloodSettings} from '@shared/types/FloodSettings';
@@ -34,7 +34,7 @@ class UITab extends SettingsTab {
event,
formData,
}: {
event: Event | React.FormEvent<HTMLFormElement>;
event: Event | FormEvent<HTMLFormElement>;
formData: Record<string, unknown>;
}) => {
const inputElement = event.target as HTMLInputElement;

View File

@@ -1,5 +1,5 @@
import {Component, ReactNode} from 'react';
import {FormattedMessage} from 'react-intl';
import * as React from 'react';
import type {FloodSettings} from '@shared/types/FloodSettings';
@@ -22,7 +22,7 @@ interface TorrentListColumnsListStates {
torrentListColumns: FloodSettings['torrentListColumns'];
}
class TorrentListColumnsList extends React.Component<TorrentListColumnsListProps, TorrentListColumnsListStates> {
class TorrentListColumnsList extends Component<TorrentListColumnsListProps, TorrentListColumnsListStates> {
tooltipRef: Tooltip | null = null;
constructor(props: TorrentListColumnsListProps) {
@@ -82,7 +82,7 @@ class TorrentListColumnsList extends React.Component<TorrentListColumnsListProps
this.props.onSettingsChange({torrentListColumns: changedItems});
};
renderItem = (item: ListItem, index: number): React.ReactNode => {
renderItem = (item: ListItem, index: number): ReactNode => {
const {id, visible} = item as FloodSettings['torrentListColumns'][number];
let checkbox = null;
let warning = null;
@@ -135,7 +135,7 @@ class TorrentListColumnsList extends React.Component<TorrentListColumnsListProps
return content;
};
render(): React.ReactNode {
render(): ReactNode {
const lockedIDs = this.getLockedIDs();
return (

View File

@@ -1,7 +1,7 @@
import {injectIntl, WrappedComponentProps} from 'react-intl';
import {Component, ChangeEvent} from 'react';
import classnames from 'classnames';
import {injectIntl, WrappedComponentProps} from 'react-intl';
import {reaction} from 'mobx';
import * as React from 'react';
import Close from '../icons/Close';
import Search from '../icons/Search';
@@ -13,7 +13,7 @@ interface SearchBoxStates {
isSearchActive: boolean;
}
class SearchBox extends React.Component<WrappedComponentProps, SearchBoxStates> {
class SearchBox extends Component<WrappedComponentProps, SearchBoxStates> {
constructor(props: WrappedComponentProps) {
super(props);
@@ -32,7 +32,7 @@ class SearchBox extends React.Component<WrappedComponentProps, SearchBoxStates>
};
}
handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {
const {value} = event.target;
this.setState({isSearchActive: value !== ''});
UIActions.setTorrentsSearchFilter(value);

View File

@@ -1,36 +0,0 @@
import {FC, lazy, useEffect} from 'react';
import ActionBar from '../torrent-list/ActionBar';
import ApplicationContent from '../layout/ApplicationContent';
import ApplicationPanel from '../layout/ApplicationPanel';
import ApplicationView from '../layout/ApplicationView';
import FloodActions from '../../actions/FloodActions';
import Sidebar from '../sidebar/Sidebar';
import TorrentList from '../torrent-list/TorrentList';
import 'overlayscrollbars/css/OverlayScrollbars.css';
const Alerts = lazy(() => import('../alerts/Alerts'));
const Modals = lazy(() => import('../modals/Modals'));
const TorrentClientOverview: FC = () => {
useEffect(() => {
FloodActions.startActivityStream();
}, []);
return (
<ApplicationView>
<Sidebar />
<ApplicationContent>
<ApplicationPanel modifier="torrent-list" className="view--torrent-list">
<ActionBar />
<TorrentList />
</ApplicationPanel>
<Modals />
<Alerts />
</ApplicationContent>
</ApplicationView>
);
};
export default TorrentClientOverview;

View File

@@ -1,14 +1,15 @@
import {FC, ReactNode} from 'react';
import {IntlProvider} from 'react-intl';
import * as React from 'react';
import type {MessageFormatElement} from 'intl-messageformat-parser';
import detectLocale from '../util/detectLocale';
import EN from './strings.compiled.json';
import Languages from '../constants/Languages';
import detectLocale from '@client/util/detectLocale';
import Languages from '@client/constants/Languages';
import type {Language} from '../constants/Languages';
import type {LocaleConfig} from '../util/detectLocale';
import type {Language} from '@client/constants/Languages';
import type {LocaleConfig} from '@client/util/detectLocale';
import EN from './strings.compiled.json';
const messagesCache: Partial<Record<Exclude<Language, 'auto'>, Record<string, MessageFormatElement[]>>> = {en: EN};
@@ -47,10 +48,10 @@ function getMessages(locale: Exclude<Language, 'auto'>) {
interface AsyncIntlProviderProps {
language?: Language;
children: React.ReactNode;
children: ReactNode;
}
const AsyncIntlProvider: React.FC<AsyncIntlProviderProps> = ({language, children}: AsyncIntlProviderProps) => {
const AsyncIntlProvider: FC<AsyncIntlProviderProps> = ({language, children}: AsyncIntlProviderProps) => {
let validatedLocale: LocaleConfig;
if (language == null || language === 'auto' || !Object.prototype.hasOwnProperty.call(Languages, language)) {
validatedLocale = detectLocale();

View File

@@ -1,7 +1,7 @@
import {FC} from 'react';
import ApplicationView from '../layout/ApplicationView';
import AuthForm from '../auth/AuthForm';
import ApplicationView from '@client/components/layout/ApplicationView';
import AuthForm from '@client/components/auth/AuthForm';
const LoginView: FC = () => (
<ApplicationView modifier="auth-form">

View File

@@ -1,7 +1,7 @@
import {FC} from 'react';
import ApplicationView from '../layout/ApplicationView';
import AuthForm from '../auth/AuthForm';
import AuthForm from '@client/components/auth/AuthForm';
import ApplicationView from '@client/components/layout/ApplicationView';
const LoginView: FC = () => (
<ApplicationView modifier="auth-form">

View File

@@ -0,0 +1,36 @@
import {FC, lazy, useEffect} from 'react';
import ActionBar from '@client/components/torrent-list/ActionBar';
import ApplicationContent from '@client/components/layout/ApplicationContent';
import ApplicationPanel from '@client/components/layout/ApplicationPanel';
import ApplicationView from '@client/components/layout/ApplicationView';
import FloodActions from '@client/actions/FloodActions';
import Sidebar from '@client/components/sidebar/Sidebar';
import TorrentList from '@client/components/torrent-list/TorrentList';
import 'overlayscrollbars/css/OverlayScrollbars.css';
const Alerts = lazy(() => import('@client/components/alerts/Alerts'));
const Modals = lazy(() => import('@client/components/modals/Modals'));
const TorrentClientOverview: FC = () => {
useEffect(() => {
FloodActions.startActivityStream();
}, []);
return (
<ApplicationView>
<Sidebar />
<ApplicationContent>
<ApplicationPanel modifier="torrent-list" className="view--torrent-list">
<ActionBar />
<TorrentList />
</ApplicationPanel>
<Modals />
<Alerts />
</ApplicationContent>
</ApplicationView>
);
};
export default TorrentClientOverview;

View File

@@ -1,12 +1,12 @@
import {makeAutoObservable} from 'mobx';
import FloodActions from '@client/actions/FloodActions';
import {AccessLevel} from '@shared/schema/constants/Auth';
import type {AuthAuthenticationResponse, AuthVerificationResponse} from '@shared/schema/api/auth';
import type {Credentials} from '@shared/schema/Auth';
import FloodActions from '../actions/FloodActions';
class AuthStore {
isAuthenticating = false;
isAuthenticated = false;

View File

@@ -1,13 +1,14 @@
import {applyPatch, Operation} from 'fast-json-patch';
import {computed, makeAutoObservable} from 'mobx';
import filterTorrents from '@client/util/filterTorrents';
import searchTorrents from '@client/util/searchTorrents';
import selectTorrents from '@client/util/selectTorrents';
import sortTorrents from '@client/util/sortTorrents';
import type {TorrentProperties, TorrentList} from '@shared/types/Torrent';
import filterTorrents from '../util/filterTorrents';
import searchTorrents from '../util/searchTorrents';
import selectTorrents from '../util/selectTorrents';
import SettingStore from './SettingStore';
import sortTorrents from '../util/sortTorrents';
import TorrentFilterStore from './TorrentFilterStore';
class TorrentStore {

View File

@@ -1,7 +1,7 @@
import {makeAutoObservable} from 'mobx';
import {FC, MouseEvent} from 'react';
import type {TorrentContextMenuAction} from '../constants/TorrentContextMenuActions';
import type {TorrentContextMenuAction} from '@client/constants/TorrentContextMenuActions';
export type ContextMenuItem =
| {

View File

@@ -1,13 +1,13 @@
import {ButtonHTMLAttributes, Children, cloneElement, Component, FC, ReactElement, ReactNodeArray, Ref} from 'react';
import classnames from 'classnames';
import * as React from 'react';
import FadeIn from './FadeIn';
import FormElementAddon from './FormElementAddon';
import FormRowItem from './FormRowItem';
import LoadingRing from '../icons/LoadingRing';
export type ButtonProps = Pick<React.ButtonHTMLAttributes<HTMLButtonElement>, 'disabled' | 'onClick' | 'onChange'> & {
buttonRef?: React.Ref<HTMLButtonElement>;
export type ButtonProps = Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled' | 'onClick' | 'onChange'> & {
buttonRef?: Ref<HTMLButtonElement>;
isLoading?: boolean;
additionalClassNames?: string;
labelOffset?: boolean;
@@ -16,13 +16,13 @@ export type ButtonProps = Pick<React.ButtonHTMLAttributes<HTMLButtonElement>, 'd
type?: 'submit' | 'button';
wrap?: boolean;
wrapper?: string | React.FunctionComponent;
wrapper?: string | FC;
wrapperProps?: Record<string, unknown>;
grow?: boolean;
shrink?: boolean;
};
export default class Button extends React.Component<ButtonProps> {
export default class Button extends Component<ButtonProps> {
static defaultProps = {
additionalClassNames: '',
disabled: false,
@@ -38,18 +38,18 @@ export default class Button extends React.Component<ButtonProps> {
getButtonContent() {
const {children, addonPlacement} = this.props;
const buttonContent = React.Children.toArray(children).reduce(
const buttonContent = Children.toArray(children).reduce(
(
accumulator: {
addonNodes: Array<React.ReactNode>;
childNodes: Array<React.ReactNode>;
addonNodes: ReactNodeArray;
childNodes: ReactNodeArray;
},
child,
) => {
const childAsElement = child as React.ReactElement;
const childAsElement = child as ReactElement;
if (childAsElement.type === FormElementAddon) {
accumulator.addonNodes.push(
React.cloneElement(childAsElement, {
cloneElement(childAsElement, {
addonPlacement,
key: childAsElement.props.className,
}),
@@ -78,8 +78,8 @@ export default class Button extends React.Component<ButtonProps> {
doesButtonContainIcon() {
const {children} = this.props;
return React.Children.toArray(children).some((child) => {
const childAsElement = child as React.ReactElement;
return Children.toArray(children).some((child) => {
const childAsElement = child as ReactElement;
return childAsElement.type === FormElementAddon;
});
}
@@ -129,7 +129,7 @@ export default class Button extends React.Component<ButtonProps> {
);
if (wrap) {
const WrapperComponent = wrapper as React.FunctionComponent;
const WrapperComponent = wrapper as FC;
return (
<WrapperComponent
{...{

View File

@@ -1,9 +1,9 @@
import classnames from 'classnames';
import * as React from 'react';
import {MouseEventHandler, PureComponent} from 'react';
export default class ContextMenuItem extends React.PureComponent<{
export default class ContextMenuItem extends PureComponent<{
className?: string;
onClick: React.MouseEventHandler<HTMLDivElement>;
onClick: MouseEventHandler<HTMLDivElement>;
}> {
render() {
const {onClick, children, className} = this.props;

View File

@@ -1,4 +1,4 @@
import * as React from 'react';
import {Component, FormEvent} from 'react';
import {getDataFromForm, resetFormData} from './util/forms';
@@ -8,19 +8,19 @@ interface FormProps {
event,
formData,
}: {
event: Event | React.FormEvent<HTMLFormElement>;
event: Event | FormEvent<HTMLFormElement>;
formData: Record<string, unknown>;
}) => void;
onSubmit?: ({
event,
formData,
}: {
event: Event | React.FormEvent<HTMLFormElement>;
event: Event | FormEvent<HTMLFormElement>;
formData: Record<string, unknown>;
}) => void;
}
class Form extends React.Component<FormProps> {
class Form extends Component<FormProps> {
formRef?: HTMLFormElement | null = null;
componentDidMount() {
if (this.formRef != null) {
@@ -47,14 +47,14 @@ class Form extends React.Component<FormProps> {
}
};
handleFormChange = (event: Event | React.FormEvent<HTMLFormElement>) => {
handleFormChange = (event: Event | FormEvent<HTMLFormElement>) => {
if (this.formRef != null && this.props.onChange) {
const formData = getDataFromForm(this.formRef);
this.props.onChange({event, formData});
}
};
handleFormSubmit = (event: Event | React.FormEvent<HTMLFormElement>) => {
handleFormSubmit = (event: Event | FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (this.props.onSubmit) {

View File

@@ -1,14 +1,14 @@
import * as React from 'react';
import {Component, ReactNode} from 'react';
import FormRowItem from './FormRowItem';
import type {FormRowItemProps} from './FormRowItem';
export default class FormRowItemGroup extends React.Component<{
export default class FormRowItemGroup extends Component<{
label?: string;
width?: FormRowItemProps['width'];
}> {
getLabel(): React.ReactNode {
getLabel(): ReactNode {
const {label} = this.props;
if (label) {

View File

@@ -1,10 +1,10 @@
import * as React from 'react';
import {forwardRef, ReactNode} from 'react';
interface FormRowGroupProps {
children: React.ReactNode;
children: ReactNode;
}
const FormRowGroup = React.forwardRef<HTMLDivElement, FormRowGroupProps>(({children}: FormRowGroupProps, ref) => (
const FormRowGroup = forwardRef<HTMLDivElement, FormRowGroupProps>(({children}: FormRowGroupProps, ref) => (
<div className="form__row form__row--group" ref={ref}>
{children}
</div>

View File

@@ -1,8 +1,8 @@
import classnames from 'classnames';
import * as React from 'react';
import {forwardRef, ReactNode} from 'react';
export interface FormRowItemProps {
children?: React.ReactNode;
children?: ReactNode;
className?: string;
type?: string;
@@ -20,7 +20,7 @@ export interface FormRowItemProps {
| 'seven-eighths';
}
const FormRowItem = React.forwardRef<HTMLDivElement, FormRowItemProps>(
const FormRowItem = forwardRef<HTMLDivElement, FormRowItemProps>(
({children, className, type, width, grow, shrink}: FormRowItemProps, ref) => {
const classes = classnames('form__row__item', className, {
[`form__row__item--${width}`]: width,

View File

@@ -1,11 +1,11 @@
import {Component, ReactNode} from 'react';
import ReactDOM from 'react-dom';
import * as React from 'react';
interface PortalProps {
children: React.ReactNode;
children: ReactNode;
}
class Portal extends React.Component<PortalProps> {
class Portal extends Component<PortalProps> {
mountPoint: HTMLDivElement | null = null;
componentDidMount() {

View File

@@ -1,7 +1,7 @@
import {getUserLocales} from 'get-user-locale';
import Languages from '../constants/Languages';
import type {Language} from '../constants/Languages';
import Languages from '@client/constants/Languages';
import type {Language} from '@client/constants/Languages';
export interface LocaleConfig {
locale: string;

View File

@@ -1,7 +1,8 @@
import {createBrowserHistory} from 'history';
import ConfigStore from '@client/stores/ConfigStore';
import stringUtil from '@shared/util/stringUtil';
import ConfigStore from '../stores/ConfigStore';
const history = createBrowserHistory({
basename: stringUtil.withoutTrailingSlash(ConfigStore.baseURI),

View File

@@ -1,9 +1,9 @@
import * as React from 'react';
import {MouseEvent, TouchEvent} from 'react';
import type {TorrentProperties} from '@shared/types/Torrent';
interface SelectTorrentOptions {
event: React.MouseEvent | React.TouchEvent;
event: MouseEvent | TouchEvent;
hash: string;
selectedTorrents: Array<string>;
torrentList: Array<TorrentProperties>;

View File

@@ -1,13 +1,13 @@
import {FormattedDate, FormattedMessage, FormattedNumber} from 'react-intl';
import Checkmark from '@client/components/icons/Checkmark';
import Duration from '@client/components/general/Duration';
import Size from '@client/components/general/Size';
import type {TorrentListColumn} from '@client/constants/TorrentListColumns';
import type {TorrentProperties} from '@shared/types/Torrent';
import Checkmark from '../components/icons/Checkmark';
import Duration from '../components/general/Duration';
import Size from '../components/general/Size';
import type {TorrentListColumn} from '../constants/TorrentListColumns';
const booleanTransformer = (value: boolean) =>
value ? <Checkmark className="torrent__detail__icon torrent__detail__icon--checkmark" /> : null;
const dateTransformer = (date: number) => <FormattedDate value={date * 1000} />;

View File

@@ -1,19 +1,19 @@
import CalendarCreatedIcon from '../components/icons/CalendarCreatedIcon';
import CalendarIcon from '../components/icons/CalendarIcon';
import ClockIcon from '../components/icons/ClockIcon';
import DiskIcon from '../components/icons/DiskIcon';
import DownloadThickIcon from '../components/icons/DownloadThickIcon';
import HashIcon from '../components/icons/HashIcon';
import FolderClosedSolid from '../components/icons/FolderClosedSolid';
import PeersIcon from '../components/icons/PeersIcon';
import LockIcon from '../components/icons/LockIcon';
import RadarIcon from '../components/icons/RadarIcon';
import RatioIcon from '../components/icons/RatioIcon';
import SeedsIcon from '../components/icons/SeedsIcon';
import TrackerMessageIcon from '../components/icons/TrackerMessageIcon';
import UploadThickIcon from '../components/icons/UploadThickIcon';
import CalendarCreatedIcon from '@client/components/icons/CalendarCreatedIcon';
import CalendarIcon from '@client/components/icons/CalendarIcon';
import ClockIcon from '@client/components/icons/ClockIcon';
import DiskIcon from '@client/components/icons/DiskIcon';
import DownloadThickIcon from '@client/components/icons/DownloadThickIcon';
import HashIcon from '@client/components/icons/HashIcon';
import FolderClosedSolid from '@client/components/icons/FolderClosedSolid';
import PeersIcon from '@client/components/icons/PeersIcon';
import LockIcon from '@client/components/icons/LockIcon';
import RadarIcon from '@client/components/icons/RadarIcon';
import RatioIcon from '@client/components/icons/RatioIcon';
import SeedsIcon from '@client/components/icons/SeedsIcon';
import TrackerMessageIcon from '@client/components/icons/TrackerMessageIcon';
import UploadThickIcon from '@client/components/icons/UploadThickIcon';
import type {TorrentListColumn} from '../constants/TorrentListColumns';
import type {TorrentListColumn} from '@client/constants/TorrentListColumns';
const ICONS: Partial<Record<TorrentListColumn, JSX.Element>> = {
eta: <ClockIcon />,

View File

@@ -1,9 +1,9 @@
import type {TorrentStatus} from '@shared/constants/torrentStatusMap';
import ErrorIcon from '../components/icons/ErrorIcon';
import SpinnerIcon from '../components/icons/SpinnerIcon';
import StartIcon from '../components/icons/StartIcon';
import StopIcon from '../components/icons/StopIcon';
import ErrorIcon from '@client/components/icons/ErrorIcon';
import SpinnerIcon from '@client/components/icons/SpinnerIcon';
import StartIcon from '@client/components/icons/StartIcon';
import StopIcon from '@client/components/icons/StopIcon';
const STATUS_ICON_MAP: Partial<Record<TorrentStatus, JSX.Element>> = {
error: <ErrorIcon />,

View File

@@ -1,9 +1,9 @@
import SettingActions from '@client/actions/SettingActions';
import SettingStore from '@client/stores/SettingStore';
import type {FloodSettings} from '@shared/types/FloodSettings';
import type {TorrentProperties} from '@shared/types/Torrent';
import SettingActions from '../actions/SettingActions';
import SettingStore from '../stores/SettingStore';
export const saveAddTorrentsUserPreferences = ({
start,
destination,

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"jsx": "preserve",
"jsx": "react-jsx",
"sourceMap": true,
"target": "esnext",
"moduleResolution": "node",
@@ -16,6 +16,8 @@
"useDefineForClassFields": true,
"baseUrl": "./",
"paths": {
"@client/*": ["client/src/javascript/*"],
"@server/*": ["server/*"],
"@shared/*": ["shared/*"]
}
},